Merge pull request #598 from FluidSynth/seq-test

Add unit tests for the sequencer
This commit is contained in:
Tom M 2020-01-03 21:05:07 +01:00 committed by GitHub
commit 9b069faf87
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 298 additions and 0 deletions

View file

@ -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)

View file

@ -0,0 +1,115 @@
#include "test.h"
#include "fluidsynth.h" // use local fluidsynth header
#include "fluid_event.h"
#include <limits.h>
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;
}

181
test/test_seq_scale.c Normal file
View file

@ -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;
}