1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
|
#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)
{
int i;
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);
for(i = 0; fluid_timer_is_running(timer) && i < 5; i++)
{
/* timer still running, wait a bit */
fluid_msleep(fluid_timer_get_interval(timer));
}
// In worst case we've waited 5*timer_interval, i.e. soundfont should be really unloaded by now.
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;
}
|