-
Notifications
You must be signed in to change notification settings - Fork 267
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This PR adds regression tests for #727, ensuring that soundfonts are correctly unloaded via the lazy-timer-unloading mechanism.
- Loading branch information
Showing
6 changed files
with
267 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
|
||
#include "test.h" | ||
#include "fluidsynth.h" | ||
#include "synth/fluid_synth.h" | ||
#include "utils/fluid_sys.h" | ||
|
||
void wait_and_free(fluid_synth_t* synth, int id, const char* calling_func) | ||
{ | ||
fluid_list_t *list, *list_orig; | ||
list_orig = list = synth->fonts_to_be_unloaded; | ||
synth->fonts_to_be_unloaded = NULL; | ||
delete_fluid_synth(synth); | ||
|
||
for(; list; list = fluid_list_next(list)) | ||
{ | ||
fluid_timer_t* timer = fluid_list_get(list); | ||
FLUID_LOG(FLUID_INFO, "%s(): Start waiting for soundfont %d to unload", calling_func, id); | ||
if(fluid_timer_is_running(timer)) | ||
{ | ||
/* timer still running, wait a bit*/ | ||
fluid_msleep(50 * fluid_timer_get_interval(timer)); | ||
TEST_ASSERT(!fluid_timer_is_running(timer)); | ||
} | ||
delete_fluid_timer(timer); | ||
FLUID_LOG(FLUID_INFO, "%s(): End waiting for soundfont %d to unload", calling_func, id); | ||
} | ||
delete_fluid_list(list_orig); | ||
} | ||
|
||
static void test_without_rendering(fluid_settings_t* settings) | ||
{ | ||
int id; | ||
fluid_synth_t *synth = new_fluid_synth(settings); | ||
TEST_ASSERT(synth != NULL); | ||
|
||
TEST_ASSERT(fluid_is_soundfont(TEST_SOUNDFONT) == TRUE); | ||
|
||
// load a sfont to synth | ||
TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); | ||
// one sfont loaded | ||
TEST_ASSERT(fluid_synth_sfcount(synth) == 1); | ||
|
||
TEST_SUCCESS(fluid_synth_noteon(synth, 0, 60, 127)); | ||
|
||
TEST_SUCCESS(fluid_synth_sfunload(synth, id, 1)); | ||
|
||
TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 60)); | ||
|
||
// there must be one font scheduled for lazy unloading | ||
TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); | ||
|
||
wait_and_free(synth, id, __func__); | ||
} | ||
|
||
// this should work fine after applying JJCs fix a4ac56502fec5f0c20a60187d965c94ba1dc81c2 | ||
static void test_after_polyphony_exceeded(fluid_settings_t* settings) | ||
{ | ||
int id; | ||
fluid_synth_t *synth = new_fluid_synth(settings); | ||
TEST_ASSERT(synth != NULL); | ||
|
||
TEST_ASSERT(fluid_is_soundfont(TEST_SOUNDFONT) == TRUE); | ||
|
||
// load a sfont to synth | ||
TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); | ||
// one sfont loaded | ||
TEST_ASSERT(fluid_synth_sfcount(synth) == 1); | ||
|
||
TEST_SUCCESS(fluid_synth_noteon(synth, 0, 60, 127)); | ||
FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): note on C4, voice count=%d", | ||
fluid_synth_get_active_voice_count(synth)); | ||
|
||
// need to render a bit to make synth->ticks_since_start advance, to make the previous voice "killable" | ||
TEST_SUCCESS(fluid_synth_process(synth, 2048, 0, NULL, 0, NULL)); | ||
|
||
// polyphony exceeded - killing the killable voice from above | ||
TEST_SUCCESS(fluid_synth_noteon(synth, 0, 61, 127)); | ||
|
||
// need to render again, to make the synth thread assign rvoice->dsp.sample, so that sample_unref() later really unrefs | ||
TEST_SUCCESS(fluid_synth_process(synth, 2048, 0, NULL, 0, NULL)); | ||
FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): note on C#4, voice count=%d", | ||
fluid_synth_get_active_voice_count(synth)); | ||
|
||
FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): unload sounfont"); | ||
TEST_SUCCESS(fluid_synth_sfunload(synth, id, 1)); | ||
|
||
TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 61)); | ||
|
||
// need to render yet again, to make the synth thread release the rvoice so it can be reclaimed by | ||
// fluid_synth_check_finished_voices() | ||
// need to render may more samples this time, so the voice makes it pass the release phase... | ||
TEST_SUCCESS(fluid_synth_process(synth, 204800, 0, NULL, 0, NULL)); | ||
|
||
// make any API call to execute fluid_synth_check_finished_voices() | ||
FLUID_LOG(FLUID_INFO, "test_after_polyphony_exceeded(): note off C#4, voice count=%d", | ||
fluid_synth_get_active_voice_count(synth)); | ||
|
||
// there must be one font scheduled for lazy unloading | ||
TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); | ||
|
||
wait_and_free(synth, id, __func__); | ||
} | ||
|
||
static void test_default_polyphony(fluid_settings_t* settings, int with_rendering) | ||
{ | ||
enum { BUFSIZE = 128 }; | ||
fluid_voice_t* buf[BUFSIZE]; | ||
|
||
int id; | ||
fluid_synth_t *synth = new_fluid_synth(settings); | ||
TEST_ASSERT(synth != NULL); | ||
|
||
TEST_ASSERT(fluid_is_soundfont(TEST_SOUNDFONT) == TRUE); | ||
|
||
// load a sfont to synth | ||
TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); | ||
// one sfont loaded | ||
TEST_ASSERT(fluid_synth_sfcount(synth) == 1); | ||
|
||
TEST_SUCCESS(fluid_synth_noteon(synth, 0, 60, 127)); | ||
|
||
if(with_rendering) | ||
{ | ||
TEST_SUCCESS(fluid_synth_process(synth, fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); | ||
} | ||
|
||
TEST_SUCCESS(fluid_synth_noteon(synth, 0, 61, 127)); | ||
|
||
fluid_synth_get_voicelist(synth, buf, BUFSIZE, -1); | ||
|
||
TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); | ||
|
||
if(with_rendering) | ||
{ | ||
// make the synth thread assign rvoice->dsp.sample | ||
TEST_SUCCESS(fluid_synth_process(synth, 2 * fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); | ||
|
||
TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); | ||
} | ||
|
||
TEST_ASSERT(synth->fonts_to_be_unloaded == NULL); | ||
|
||
TEST_SUCCESS(fluid_synth_sfunload(synth, id, 1)); | ||
|
||
// now, there must be one font scheduled for lazy unloading | ||
TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); | ||
TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); | ||
|
||
// noteoff the second note and render something | ||
TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 61)); | ||
if(with_rendering) | ||
{ | ||
TEST_SUCCESS(fluid_synth_process(synth, fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); | ||
} | ||
|
||
// still 4 because key 61 is playing in release phase now | ||
TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); | ||
// must be still running | ||
TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); | ||
TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); | ||
|
||
// noteoff the first note and render something | ||
TEST_SUCCESS(fluid_synth_noteoff(synth, 0, 60)); | ||
if(with_rendering) | ||
{ | ||
TEST_SUCCESS(fluid_synth_process(synth, fluid_synth_get_internal_bufsize(synth), 0, NULL, 0, NULL)); | ||
} | ||
|
||
// still 4 because keys 60 + 61 are playing in release phase now | ||
TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 4); | ||
// must be still running | ||
TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); | ||
TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); | ||
|
||
if(with_rendering) | ||
{ | ||
// render enough, to make the synth thread release the rvoice so it can be reclaimed by | ||
// fluid_synth_check_finished_voices() | ||
TEST_SUCCESS(fluid_synth_process(synth, 2048000, 0, NULL, 0, NULL)); | ||
|
||
// this API call should reclaim the rvoices and call fluid_voice_stop() | ||
TEST_ASSERT(fluid_synth_get_active_voice_count(synth) == 0); | ||
} | ||
|
||
TEST_ASSERT(synth->fonts_to_be_unloaded != NULL); | ||
if(with_rendering) | ||
{ | ||
// We want to see that the timer thread unloads the soundfont before we call delete_fluid_synth(). | ||
// Wait to give the timer thread a chance to unload and finish. | ||
fluid_msleep(10 * fluid_timer_get_interval(fluid_list_get(synth->fonts_to_be_unloaded))); | ||
TEST_ASSERT(!fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); | ||
} | ||
else | ||
{ | ||
TEST_ASSERT(fluid_timer_is_running(fluid_list_get(synth->fonts_to_be_unloaded))); | ||
} | ||
|
||
wait_and_free(synth, id, __func__); | ||
} | ||
|
||
// this tests the soundfont loading API of the synth. | ||
// might be expanded to test the soundfont loader as well... | ||
int main(void) | ||
{ | ||
fluid_settings_t *settings = new_fluid_settings(); | ||
TEST_ASSERT(settings != NULL); | ||
|
||
FLUID_LOG(FLUID_INFO, "Begin test_default_polyphony() with rendering"); | ||
test_default_polyphony(settings, TRUE); | ||
FLUID_LOG(FLUID_INFO, "End test_default_polyphony()\n"); | ||
|
||
FLUID_LOG(FLUID_INFO, "Begin test_default_polyphony() without rendering"); | ||
test_default_polyphony(settings, FALSE); | ||
FLUID_LOG(FLUID_INFO, "End test_default_polyphony()\n"); | ||
|
||
fluid_settings_setint(settings, "synth.polyphony", 2); | ||
|
||
FLUID_LOG(FLUID_INFO, "Begin test_after_polyphony_exceeded()"); | ||
test_after_polyphony_exceeded(settings); | ||
FLUID_LOG(FLUID_INFO, "End test_after_polyphony_exceeded()\n"); | ||
|
||
FLUID_LOG(FLUID_INFO, "Begin test_without_rendering()"); | ||
test_without_rendering(settings); | ||
FLUID_LOG(FLUID_INFO, "End test_without_rendering()"); | ||
|
||
delete_fluid_settings(settings); | ||
|
||
return EXIT_SUCCESS; | ||
} |