diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9752c11a..d3105d36 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,8 @@ ADD_FLUID_TEST(test_synth_chorus_reverb) ADD_FLUID_TEST(test_snprintf) ADD_FLUID_TEST(test_synth_process) ADD_FLUID_TEST(test_ct2hz) +ADD_FLUID_TEST(test_seq_event_queue_sort) +ADD_FLUID_TEST(test_seq_scale) # if ( LIBSNDFILE_HASVORBIS ) # ADD_FLUID_TEST(test_sf3_sfont_loading) diff --git a/test/test_seq_event_queue_sort.c b/test/test_seq_event_queue_sort.c new file mode 100644 index 00000000..52666d4c --- /dev/null +++ b/test/test_seq_event_queue_sort.c @@ -0,0 +1,115 @@ + +#include "test.h" +#include "fluidsynth.h" // use local fluidsynth header +#include "fluid_event.h" + +#include + +static short order = 0; +void callback_stable_sort(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) +{ + static const enum fluid_seq_event_type expected_type_order[] = + { FLUID_SEQ_SYSTEMRESET, FLUID_SEQ_UNREGISTERING, FLUID_SEQ_NOTEOFF, FLUID_SEQ_NOTEON }; + + TEST_ASSERT(fluid_event_get_type(event) == expected_type_order[order++]); +} + +void test_order_same_tick(fluid_sequencer_t *seq, fluid_event_t *evt) +{ + // silently creates a fluid_seqbind_t + int i, seqid = fluid_sequencer_register_client(seq, "test order at same tick", callback_stable_sort, NULL); + TEST_SUCCESS(seqid); + TEST_ASSERT(fluid_sequencer_count_clients(seq) == 1); + + fluid_event_set_source(evt, -1); + fluid_event_set_dest(evt, seqid); + + for(i = 1; i <= 2; i++) + { + fluid_event_system_reset(evt); + TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 1)); + fluid_event_unregistering(evt); + TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 1)); + fluid_event_noteoff(evt, 0, 64); + TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 1)); + fluid_event_noteon(evt, 0, 64, 127); + TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 1)); + } + + fluid_sequencer_process(seq, 1); + TEST_ASSERT(order == 4); + + order = 0; + + fluid_sequencer_process(seq, 2); + TEST_ASSERT(order == 4); + + fluid_sequencer_unregister_client(seq, seqid); + TEST_ASSERT(fluid_sequencer_count_clients(seq) == 0); +} + +static unsigned int prev_time; +void callback_correct_order(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) +{ + TEST_ASSERT(fluid_event_get_type(event) == FLUID_SEQ_CONTROLCHANGE); + TEST_ASSERT(prev_time <= fluid_event_get_time(event) && fluid_event_get_time(event) <= prev_time + 1); + prev_time = fluid_event_get_time(event); +} + +void test_correct_order(fluid_sequencer_t *seq, fluid_event_t *evt) +{ + // silently creates a fluid_seqbind_t + unsigned int i, offset; + int seqid = fluid_sequencer_register_client(seq, "correct order test", callback_correct_order, NULL); + TEST_SUCCESS(seqid); + + fluid_event_set_source(evt, -1); + fluid_event_set_dest(evt, seqid); + fluid_event_control_change(evt, 0, 1, 127); + + for(i = 0; i < 10000; i++) + { + TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, 10000 - i, 0)); + } + + for(; i <= 10000 + 20000; i++) + { + TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 0)); + } + + for(; i < 80000; i++) + { + TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, 80000 - (i - 10000 - 20000), 0)); + } + + for(; i < 200000; i++) + { + TEST_SUCCESS(fluid_sequencer_send_at(seq, evt, i, 0)); + } + + offset = prev_time = fluid_sequencer_get_tick(seq); + fluid_sequencer_process(seq, i + offset); + TEST_ASSERT(prev_time == (i - 1) + offset); + + fluid_sequencer_unregister_client(seq, seqid); +} + +// simple test to ensure that manually unregistering and deleting the internal fluid_seqbind_t works without crashing +int main(void) +{ + fluid_event_t *evt; + fluid_sequencer_t *seq = new_fluid_sequencer2(0 /*i.e. use sample timer*/); + TEST_ASSERT(seq != NULL); + TEST_ASSERT(fluid_sequencer_get_use_system_timer(seq) == 0); + evt = new_fluid_event(); + TEST_ASSERT(evt != NULL); + + test_order_same_tick(seq, evt); + test_correct_order(seq, evt); + + // client should be removed, deleting the seq should not free the struct again + delete_fluid_event(evt); + delete_fluid_sequencer(seq); + + return EXIT_SUCCESS; +} diff --git a/test/test_seq_scale.c b/test/test_seq_scale.c new file mode 100644 index 00000000..2d89dc45 --- /dev/null +++ b/test/test_seq_scale.c @@ -0,0 +1,181 @@ +#include "test.h" +#include "fluidsynth.h" +#include "utils/fluid_sys.h" +#include "synth/fluid_event.h" + +static fluid_sequencer_t *sequencer; +static short synthSeqID, mySeqID; + +static void sendnoteon(int chan, short key, short velocity, unsigned int time) +{ + fluid_event_t *evt = new_fluid_event(); + fluid_event_set_source(evt, -1); + fluid_event_set_dest(evt, synthSeqID); + fluid_event_noteon(evt, chan, key, velocity); + TEST_SUCCESS(fluid_sequencer_send_at(sequencer, evt, time, 0)); + delete_fluid_event(evt); +} + +static void sendpc(int chan, short val, unsigned int time) +{ + fluid_event_t *evt = new_fluid_event(); + fluid_event_set_source(evt, -1); + fluid_event_set_dest(evt, synthSeqID); + fluid_event_program_change(evt, chan, val); + TEST_SUCCESS(fluid_sequencer_send_at(sequencer, evt, time, 0)); + delete_fluid_event(evt); +} + +static void schedule_next_callback(unsigned int time) +{ + fluid_event_t *evt = new_fluid_event(); + fluid_event_set_source(evt, -1); + fluid_event_set_dest(evt, mySeqID); + fluid_event_timer(evt, NULL); + TEST_SUCCESS(fluid_sequencer_send_at(sequencer, evt, time, 0)); + delete_fluid_event(evt); +} + +static const unsigned int expected_callback_times[] = +{ + 0, 0, 120, 120, 240, 240, 360, 360, 480, 1440, + 1560, 1560, 1680, 1680, 1800, 1800, 1920, 2700, + 2820, 2820, 2940, 2940, 3060, 3060, 3180, 4275, + 4395, 4395, 4515, 4515, 4635, 4635, 4755, + 799, 919, 919, 1039, 1039, 1159, 1159, 1279 +}; + +static void fake_synth_callback(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) +{ + static int callback_idx = 0; + + TEST_ASSERT(time == expected_callback_times[callback_idx++]); + FLUID_LOG(FLUID_INFO, "synth callback time: %u %u", time, fluid_event_get_time(event)); +} + +static void seq_callback(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) +{ + static int phase = 0; + + switch(phase) + { + case 0: + sendpc(0, 47, 0); + fluid_sequencer_set_time_scale(seq, 320); + sendnoteon(0, 47, 60, 0); + sendnoteon(0, 47, 0, 120); + sendnoteon(0, 47, 90, 120); + sendnoteon(0, 47, 0, 240); + sendnoteon(0, 47, 110, 240); + sendnoteon(0, 47, 0, 360); + sendnoteon(0, 47, 127, 360); + sendnoteon(0, 47, 0, 480); + schedule_next_callback(480 + 240); + break; + + case 1: + fluid_sequencer_set_time_scale(seq, 1280 / 2); + sendnoteon(0, 47, 60, 0); + sendnoteon(0, 47, 0, 120); + sendnoteon(0, 47, 80, 120); + sendnoteon(0, 47, 0, 240); + sendnoteon(0, 47, 90, 240); + sendnoteon(0, 47, 0, 360); + sendnoteon(0, 47, 100, 360); + sendnoteon(0, 47, 0, 480); + schedule_next_callback(480 + 240); + break; + + case 2: + fluid_sequencer_set_time_scale(seq, 800); + sendnoteon(0, 47, 60, 0); + sendnoteon(0, 47, 0, 120); + sendnoteon(0, 47, 80, 120); + sendnoteon(0, 47, 0, 240); + sendnoteon(0, 47, 90, 240); + sendnoteon(0, 47, 0, 360); + sendnoteon(0, 47, 100, 360); + sendnoteon(0, 47, 0, 480); + schedule_next_callback(480 + 240); + break; + + case 3: + fluid_sequencer_set_time_scale(seq, 1000); + sendnoteon(0, 47, 60, 0); + sendnoteon(0, 47, 0, 120); + sendnoteon(0, 47, 80, 120); + sendnoteon(0, 47, 0, 240); + sendnoteon(0, 47, 90, 240); + sendnoteon(0, 47, 0, 360); + sendnoteon(0, 47, 100, 360); + sendnoteon(0, 47, 0, 480); + schedule_next_callback(480 + 240); + break; + + case 4: + fluid_sequencer_set_time_scale(seq, 320 / 2); + sendnoteon(0, 47, 60, 0); + sendnoteon(0, 47, 0, 120); + sendnoteon(0, 47, 80, 120); + sendnoteon(0, 47, 0, 240); + sendnoteon(0, 47, 90, 240); + sendnoteon(0, 47, 0, 360); + sendnoteon(0, 47, 100, 360); + sendnoteon(0, 47, 0, 480); + break; + + default: + TEST_ASSERT(0); + } + + phase++; +} + +// for debug, uncomment below to hear the notes +// #define SOUND + +int main(void) +{ + int i; +#ifdef SOUND + fluid_settings_t *settings = new_fluid_settings(); + fluid_settings_setstr(settings, "audio.driver", "alsa"); + fluid_synth_t *synth = new_fluid_synth(settings); + TEST_SUCCESS(fluid_synth_sfload(synth, TEST_SOUNDFONT, 1)); +#endif + + sequencer = new_fluid_sequencer2(0); + TEST_ASSERT(sequencer != NULL); + +#ifdef SOUND + synthSeqID = fluid_sequencer_register_fluidsynth(sequencer, synth); +#else + // register fake synth as first destination + synthSeqID = fluid_sequencer_register_client(sequencer, "fake synth", fake_synth_callback, NULL); +#endif + TEST_SUCCESS(synthSeqID); + // register myself as scheduling destination + mySeqID = fluid_sequencer_register_client(sequencer, "me", seq_callback, NULL); + TEST_SUCCESS(mySeqID); + + // initialize our absolute date + schedule_next_callback(0); + +#ifdef SOUND + fluid_audio_driver_t *adriver = new_fluid_audio_driver(settings, synth); + fluid_msleep(100000); + delete_fluid_audio_driver(adriver); + delete_fluid_synth(synth); + delete_fluid_settings(settings); +#else + + for(i = 0; i < 100000; i++) + { + fluid_sequencer_process(sequencer, i); + } + +#endif + + delete_fluid_sequencer(sequencer); + return EXIT_SUCCESS; +}