Sampled Instrument Player with static and monolithic design. All instruments are built-in.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

742 lines
26 KiB

#include "module.h"
#include "engine.h"
#include "sampler.h"
#include "sfzloader.h"
#include "tests.h"
static struct sampler_module *create_sampler_instance(struct test_env *env, const char *cfg_section, const char *instance_name)
{
extern struct cbox_module_manifest sampler_module;
GError *error = NULL;
struct cbox_module *module = cbox_module_manifest_create_module(&sampler_module, cfg_section, env->doc, NULL, env->engine, instance_name, &error);
if (!module)
{
if (error)
fprintf(stderr, "Error: %s\n", error->message);
test_assert(module);
}
test_assert_equal_str(module->engine_name, "sampler");
test_assert_equal_str(module->instance_name, instance_name);
return (struct sampler_module *)module;
}
static int count_free_voices(struct test_env *env, struct sampler_module *m)
{
int count = 0;
for (struct sampler_voice *v = m->voices_free; v; count++, v = v->next)
test_assert(count < MAX_SAMPLER_VOICES);
return count;
}
static int count_channel_voices_if(struct test_env *env, struct sampler_module *m, int channel, gboolean (*cond_func)(struct sampler_voice *v, void *user_data), void *user_data)
{
struct sampler_channel *c = &m->channels[channel];
int count = 0;
for (struct sampler_voice *v = c->voices_running; v; v = v->next)
{
test_assert(count < MAX_SAMPLER_VOICES);
count += cond_func ? (cond_func(v, user_data) ? 1 : 0) : 1;
}
return count;
}
static void verify_sampler_voices_if(struct test_env *env, struct sampler_module *m, int voices[16], gboolean (*cond_func)(struct sampler_voice *v, void *user_data), void *user_data)
{
int total = 0;
for (int i = 0; i < 16; ++i)
{
int count = count_channel_voices_if(env, m, i, cond_func, user_data);
test_assert_equal(int, count, voices[i]);
total += count;
}
if (!cond_func)
test_assert_equal(int, count_free_voices(env, m), MAX_SAMPLER_VOICES - total);
}
static void verify_sampler_voices(struct test_env *env, struct sampler_module *m, int voices[16])
{
verify_sampler_voices_if(env, m, voices, NULL, NULL);
}
static gboolean is_voice_released(struct sampler_voice *v, void *ignore)
{
return v->released;
}
static struct sampler_program *load_sfz_into_sampler(struct test_env *env, struct sampler_module *m, const char *sfz_data)
{
GError *error = NULL;
struct sampler_program *prg = sampler_program_new(m, 0, "note_test", NULL, NULL, &error);
test_assert(prg);
test_assert_no_error(error);
test_assert(sampler_module_load_program_sfz(m, prg, sfz_data, 1, &error));
test_assert_no_error(error);
sampler_register_program(m, prg);
test_assert(sampler_select_program(m, 0, prg->name, &error));
test_assert_no_error(error);
return prg;
}
////////////////////////////////////////////////////////////////////////////////
void test_sampler_setup(struct test_env *env)
{
struct sampler_module *m = create_sampler_instance(env, "test_setup", "smp1");
int expected_voices[16] = {};
verify_sampler_voices(env, m, expected_voices);
CBOX_DELETE(&m->module);
}
////////////////////////////////////////////////////////////////////////////////
void test_sampler_midicurve(struct test_env *env)
{
struct sampler_midi_curve curve;
float values[128];
for (int i = 0; i < 128; ++i)
{
curve.values[i] = SAMPLER_CURVE_GAP;
values[i] = -100;
}
curve.values[0] = 0;
curve.values[64] = 0;
curve.values[127] = 1;
// Linear
sampler_midi_curve_interpolate(&curve, values, 0, 1, FALSE);
for (int i = 0; i < 128; ++i)
{
float expected = i < 64 ? 0 : (i - 64) / 63.0;
test_assert(fabs(values[i] - expected) < 0.001);
}
// Quadratic
sampler_midi_curve_interpolate(&curve, values, 0, 1, TRUE);
for (int i = 0; i < 128; ++i)
{
float expected = i < 64 ? 0 : (i - 64) / 63.0;
expected = expected * expected;
test_assert(fabs(values[i] - expected) < 0.001);
}
}
////////////////////////////////////////////////////////////////////////////////
void test_sampler_midicurve2(struct test_env *env)
{
struct sampler_module *m = create_sampler_instance(env, "test_setup", "smp1");
struct sampler_program *prg = load_sfz_into_sampler(env, m,
"<curve> curve_index=8 v0=-1 v32=0 v96=0 v127=1\n"
"<curve> curve_index=9 v32=0\n"
);
for (int i = 0; i < 128; ++i)
{
float expected, actual;
if (i < 32)
expected = (-1 + (i / 32.0));
else if (i <= 96)
expected = 0;
else
expected = (i - 96) * 1.f / (127 - 96);
actual = sampler_program_get_curve_value(prg, 8, i / 127.0);
test_assert(fabs(actual - expected) < 0.001);
// Test interpolation
expected = 0.25f * actual + 0.75f * sampler_program_get_curve_value(prg, 8, (i + 1) / 127.0);
actual = sampler_program_get_curve_value(prg, 8, (i + 0.75) / 127.0);
test_assert(fabs(actual - expected) < 0.001);
// Another curve
expected = i < 32 ? 0 : (i - 32) * 1.f / (127 - 32);
actual = sampler_program_get_curve_value(prg, 9, i / 127.0);
test_assert(fabs(actual - expected) < 0.001);
}
#define VERIFY_BUILTIN_CURVE(curve, point, expected) \
test_assert(fabs(sampler_program_get_curve_value(prg, curve, point) - expected) < 0.001)
VERIFY_BUILTIN_CURVE(0, 0.f, 0.f);
VERIFY_BUILTIN_CURVE(0, 0.5f, 0.5f);
VERIFY_BUILTIN_CURVE(0, 1.0f, 1.0f);
VERIFY_BUILTIN_CURVE(1, 0.0f, -1.0f);
VERIFY_BUILTIN_CURVE(1, 63.f/127.f, 0.0f);
VERIFY_BUILTIN_CURVE(1, 0.5f, 0.0f);
VERIFY_BUILTIN_CURVE(1, 64.f/127.f, 0.0f);
VERIFY_BUILTIN_CURVE(1, 1.0f, 1.0f);
VERIFY_BUILTIN_CURVE(2, 0.f, 1.f);
VERIFY_BUILTIN_CURVE(2, 0.5f, 0.5f);
VERIFY_BUILTIN_CURVE(2, 1.0f, 0.f);
VERIFY_BUILTIN_CURVE(3, 0.0f, 1.0f);
VERIFY_BUILTIN_CURVE(3, 63.f/127.f, 0.0f);
VERIFY_BUILTIN_CURVE(3, 0.5f, 0.0f);
VERIFY_BUILTIN_CURVE(3, 64.f/127.f, 0.0f);
VERIFY_BUILTIN_CURVE(3, 1.0f, -1.0f);
VERIFY_BUILTIN_CURVE(4, 0.0f, 0.0f);
VERIFY_BUILTIN_CURVE(4, 0.5f, 0.25f);
VERIFY_BUILTIN_CURVE(4, 1.0f, 1.0f);
VERIFY_BUILTIN_CURVE(5, 0.0f, 0.0f);
VERIFY_BUILTIN_CURVE(5, 0.25f, 0.5f);
VERIFY_BUILTIN_CURVE(5, 1.0f, 1.0f);
sampler_unselect_program(m, prg);
CBOX_DELETE(prg);
CBOX_DELETE(&m->module);
}
////////////////////////////////////////////////////////////////////////////////
void test_sampler_note_basic(struct test_env *env)
{
struct sampler_module *m = create_sampler_instance(env, "test_setup", "smp1");
struct sampler_program *prg = load_sfz_into_sampler(env, m,
"<region> sample=*saw loop_mode=loop_continuous\n");
for (int i = 0; i < 5; ++i)
{
uint8_t midi_data[3] = { 0x90, 48 + i, 127 };
m->module.process_event(&m->module, midi_data, sizeof(midi_data));
int expected_voices[16] = {[0] = 1 + i};
verify_sampler_voices(env, m, expected_voices);
}
for (int i = 0; i < 5; ++i)
{
uint8_t midi_data[3] = { 0x91, 48 + i, 127 };
m->module.process_event(&m->module, midi_data, sizeof(midi_data));
int expected_voices[16] = {[0] = 5, [1] = 1 + i};
verify_sampler_voices(env, m, expected_voices);
int expected_released_voices[16] = {};
verify_sampler_voices_if(env, m, expected_released_voices, is_voice_released, NULL);
}
// Send some MIDI off to the first channel
for (int i = 0; i < 5; ++i)
{
uint8_t midi_data[3] = { (i & 1) ? 0x90 : 0x80, 48 + i, (i & 1) ? 0 : 127 };
m->module.process_event(&m->module, midi_data, sizeof(midi_data));
int expected_voices[16] = {[0] = 5, [1] = 5};
verify_sampler_voices(env, m, expected_voices);
int expected_released_voices[16] = {[0] = 1 + i};
verify_sampler_voices_if(env, m, expected_released_voices, is_voice_released, NULL);
}
sampler_unselect_program(m, prg);
CBOX_DELETE(prg);
CBOX_DELETE(&m->module);
}
////////////////////////////////////////////////////////////////////////////////
struct region_logic_test_setup_step
{
const uint8_t *midi_data;
uint32_t midi_data_len;
uint32_t voices[16];
};
struct region_logic_test_setup
{
const char *name;
const char *sfz_data;
const struct region_logic_test_setup_step *steps;
};
void test_sampler_note_region_logic(struct test_env *env)
{
struct region_logic_test_setup *setup = env->arg;
struct sampler_module *m = create_sampler_instance(env, "test_setup", "smp1");
struct sampler_program *prg = load_sfz_into_sampler(env, m, setup->sfz_data);
int expected_voices[16] = {};
for (int i = 0; setup->steps[i].midi_data; ++i)
{
env->context = g_strdup_printf("%s[%d]", setup->name, i);
const struct region_logic_test_setup_step *step = &setup->steps[i];
m->module.process_event(&m->module, step->midi_data, step->midi_data_len);
for (int c = 0; c < 16; ++c)
expected_voices[c] += step->voices[c];
verify_sampler_voices(env, m, expected_voices);
g_free(env->context);
env->context = NULL;
}
sampler_unselect_program(m, prg);
CBOX_DELETE(prg);
CBOX_DELETE(&m->module);
}
////////////////////////////////////////////////////////////////////////////////
#define MIDI_DATA_STEP(data, voices) { (const uint8_t *)data, sizeof(data) - 1, {voices} }
#define MIDI_DATA_END { NULL, 0, {} }
#define MIDI_DATA_STEP_MT(data, ...) { (const uint8_t *)data, sizeof(data) - 1, {__VA_ARGS__} }
#define REGION_LOGIC_TEST_SETUP(_name, sfz) \
struct region_logic_test_setup setup_##_name = { \
.name = #_name, \
.sfz_data = sfz, \
.steps = steps_##_name \
}
struct region_logic_test_setup_step steps_lokeyhikey[] = {
MIDI_DATA_STEP("\x90\x24\x7F", 0),
MIDI_DATA_STEP("\x90\x1F\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 1),
MIDI_DATA_STEP("\x90\x21\x7F", 1),
MIDI_DATA_STEP("\x90\x22\x7F", 1),
MIDI_DATA_STEP("\x90\x23\x7F", 1),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(lokeyhikey,
"<region>lokey=32 hikey=35 sample=*saw"
);
struct region_logic_test_setup_step steps_lokeyhikey2[] = {
MIDI_DATA_STEP("\x90\x0E\x7F", 0),
MIDI_DATA_STEP("\x90\x0F\x7F", 1),
MIDI_DATA_STEP("\x90\x10\x7F", 1),
MIDI_DATA_STEP("\x90\x11\x7F", 0),
MIDI_DATA_STEP("\x90\x1F\x7F", 0),
MIDI_DATA_STEP("\x90\x24\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 1),
MIDI_DATA_STEP("\x90\x21\x7F", 2),
MIDI_DATA_STEP("\x90\x22\x7F", 2),
MIDI_DATA_STEP("\x90\x23\x7F", 1),
MIDI_DATA_STEP("\x90\x47\x7F", 0),
MIDI_DATA_STEP("\x90\x48\x7F", 1),
MIDI_DATA_STEP("\x90\x49\x7F", 0),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(lokeyhikey2,
"<region>lokey=15 hikey=16 sample=*saw\n"
"<region>lokey=32 hikey=35 sample=*saw\n"
"<region>lokey=33 hikey=34 sample=*saw\n"
"<region>key=72 sample=*saw\n"
);
struct region_logic_test_setup_step steps_lovelhivel[] = {
MIDI_DATA_STEP("\x90\x20\x1F", 0),
MIDI_DATA_STEP("\x90\x20\x24", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\x90\x20\x21", 1),
MIDI_DATA_STEP("\x90\x20\x22", 1),
MIDI_DATA_STEP("\x90\x20\x23", 1),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(lovelhivel,
"<region>lovel=32 hivel=35 sample=*saw"
);
struct region_logic_test_setup_step steps_lochanhichan[] = {
MIDI_DATA_STEP_MT("\x90\x20\x7F", 0, 0, 0, 0, 0, 0, 0),
MIDI_DATA_STEP_MT("\x91\x20\x7F", 0, 1, 0, 0, 0, 0, 0),
MIDI_DATA_STEP_MT("\x92\x20\x7F", 0, 0, 1, 0, 0, 0, 0),
MIDI_DATA_STEP_MT("\x93\x20\x7F", 0, 0, 0, 0, 0, 0, 0),
MIDI_DATA_STEP_MT("\x94\x20\x7F", 0, 0, 0, 0, 2, 0, 0),
MIDI_DATA_STEP_MT("\x95\x20\x7F", 0, 0, 0, 0, 0, 2, 0),
MIDI_DATA_STEP_MT("\x96\x20\x7F", 0, 0, 0, 0, 0, 0, 0),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(lochanhichan,
"<region>lochan=2 hichan=3 sample=*saw "
"<region>lochan=5 hichan=6 sample=*saw "
"<region>lochan=5 hichan=6 sample=*saw "
);
struct region_logic_test_setup_step steps_chanaft[] = {
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xD0\x1F", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xD0\x20", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xD0\x21", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xD0\x22", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(chanaft,
"<region>lochanaft=32 hichanaft=33 sample=*saw"
);
struct region_logic_test_setup_step steps_polyaft[] = {
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xA0\x10\x1F", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xA0\x10\x20", 0), // note that this does not care about which key - it uses the last poly aft value
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xA0\x10\x21", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xA0\x10\x22", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(polyaft,
"<region>lopolyaft=32 hipolyaft=33 sample=*saw"
);
struct region_logic_test_setup_step steps_cc[] = {
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x10\x1F", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x10\x20", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x11\x7F", 0), // try a different CC, just in case (positive test)
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x10\x21", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x10\x22", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x11\x21", 0), // try a different CC, just in case (negative test)
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x10\x1F", 0),
MIDI_DATA_STEP("\xB0\x11\x20", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(cc,
"<region>locc16=32 hicc16=33 sample=*saw"
);
struct region_logic_test_setup_step steps_cc2[] = {
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x10\x1F", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x10\x20", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x11\x41", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x10\x21", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x10\x22", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x10\x21", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x11\x42", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x11\x41", 0),
MIDI_DATA_STEP("\xB0\x10\x22", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x11\x3F", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x10\x22", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(cc2,
"<region>locc16=32 hicc16=33 locc17=64 hicc17=65 sample=*saw"
);
struct region_logic_test_setup_step steps_cc3[] = { // CC16 <= 33, CC17 >= 64
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x10\x1F", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x10\x20", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x11\x41", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x11\x71", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x10\x21", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x10\x22", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x10\x21", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x10\x1F", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x11\x42", 0),
MIDI_DATA_STEP("\x90\x20\x20", 1),
MIDI_DATA_STEP("\xB0\x11\x41", 0),
MIDI_DATA_STEP("\xB0\x10\x22", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x11\x3F", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_STEP("\xB0\x10\x22", 0),
MIDI_DATA_STEP("\x90\x20\x20", 0),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(cc3,
"<region>hicc16=33 locc17=64 sample=*saw"
);
struct region_logic_test_setup_step steps_oncc[] = {
MIDI_DATA_STEP("\xB0\x10\x1F", 0),
MIDI_DATA_STEP("\xB0\x10\x20", 1),
MIDI_DATA_STEP("\xB0\x10\x21", 1), // should probably be 1 according to test file 16, but that's madness
MIDI_DATA_STEP("\xB0\x10\x22", 0),
MIDI_DATA_STEP("\xB0\x10\x21", 1),
MIDI_DATA_STEP("\xB0\x10\x20", 1), // same
MIDI_DATA_STEP("\xB0\x10\x1F", 0),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(oncc,
"<region>on_locc16=32 on_hicc16=33 sample=*saw"
);
struct region_logic_test_setup_step steps_release[] = {
MIDI_DATA_STEP("\x90\x20\x7F", 0),
MIDI_DATA_STEP("\x80\x20\x7F", 1),
MIDI_DATA_STEP("\x80\x20\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x00", 1),
MIDI_DATA_STEP("\x90\x20\x00", 0),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(release,
"<region>trigger=release sample=*saw"
);
struct region_logic_test_setup_step steps_firstlegato[] = {
MIDI_DATA_STEP_MT("\x90\x20\x7F", 1),
MIDI_DATA_STEP_MT("\x90\x21\x7F", 2),
MIDI_DATA_STEP_MT("\x90\x22\x7F", 2),
MIDI_DATA_STEP_MT("\x91\x20\x7F", 0, 1), // a different channel has its own counter
MIDI_DATA_STEP_MT("\x91\x21\x7F", 0, 2),
MIDI_DATA_STEP_MT("\x91\x22\x7F", 0, 2),
MIDI_DATA_STEP_MT("\x80\x20\x7F", 0),
MIDI_DATA_STEP_MT("\x80\x21\x7F", 0),
MIDI_DATA_STEP_MT("\x80\x22\x7F", 0),
MIDI_DATA_STEP_MT("\x90\x20\x7F", 1),
MIDI_DATA_STEP_MT("\x90\x21\x7F", 2),
MIDI_DATA_STEP_MT("\x90\x22\x7F", 2),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(firstlegato,
"<region>trigger=first sample=*saw"
"<region>trigger=legato sample=*saw"
"<region>trigger=legato sample=*saw"
);
struct region_logic_test_setup_step steps_switches[] = {
MIDI_DATA_STEP("\x90\x12\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 0),
MIDI_DATA_STEP("\x90\x10\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 1),
MIDI_DATA_STEP("\x90\x11\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_STEP("\x90\x10\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 1),
MIDI_DATA_STEP("\x90\x0F\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 1),
MIDI_DATA_STEP("\x90\x14\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 1),
MIDI_DATA_STEP("\x90\x12\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 0),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(switches,
"<region>sw_lokey=16 sw_hikey=19 sw_last=16 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw"
);
struct region_logic_test_setup_step steps_switches2[] = {
MIDI_DATA_STEP("\x90\x20\x7F", 0),
MIDI_DATA_STEP("\x90\x10\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 1),
MIDI_DATA_STEP("\x80\x10\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 0),
MIDI_DATA_STEP("\x90\x11\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_STEP("\x80\x11\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 0),
MIDI_DATA_STEP("\x90\x10\x7F", 0),
MIDI_DATA_STEP("\x90\x11\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 3),
MIDI_DATA_STEP("\x80\x10\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_STEP("\x80\x11\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 0),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(switches2,
"<region>sw_lokey=16 sw_hikey=19 sw_down=16 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=16 sw_hikey=19 sw_down=17 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=16 sw_hikey=19 sw_down=17 lokey=32 hikey=35 sample=*saw"
);
struct region_logic_test_setup_step steps_switches3[] = {
MIDI_DATA_STEP("\x90\x20\x7F", 2), // [0]
MIDI_DATA_STEP("\x90\x12\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 1),
MIDI_DATA_STEP("\x90\x10\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_STEP("\x90\x11\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 3),
MIDI_DATA_STEP("\x90\x10\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_STEP("\x90\x0F\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 2), // [10]
MIDI_DATA_STEP("\x90\x14\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_STEP("\x90\x12\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 1),
MIDI_DATA_STEP("\x90\x09\x7F", 0),
MIDI_DATA_STEP("\x90\x12\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_STEP("\x90\x10\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 3),
MIDI_DATA_STEP("\x90\x11\x7F", 0), // [20]
MIDI_DATA_STEP("\x90\x20\x7F", 4),
MIDI_DATA_STEP("\x90\x10\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 3),
MIDI_DATA_STEP("\x90\x0F\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 3),
MIDI_DATA_STEP("\x90\x14\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 3),
MIDI_DATA_STEP("\x90\x12\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_STEP("\xB0\x79\x7F", 0), // [30] reset all controllers
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(switches3,
"<region>lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=8 sw_hikey=9 sw_last=9 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=16 sw_hikey=19 sw_last=16 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw"
);
struct region_logic_test_setup_step steps_switches4[] = {
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_STEP("\x90\x10\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 1),
MIDI_DATA_STEP("\x90\x11\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_STEP("\x90\x10\x7F", 0),
MIDI_DATA_STEP("\xB0\x79\x7F", 0), // reset all controllers
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_STEP("\x90\x08\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 3),
MIDI_DATA_STEP("\x90\x09\x7F", 0),
MIDI_DATA_STEP("\x90\x20\x7F", 4),
MIDI_DATA_STEP("\xB0\x79\x7F", 0), // reset all controllers
MIDI_DATA_STEP("\x90\x20\x7F", 2),
MIDI_DATA_END,
};
REGION_LOGIC_TEST_SETUP(switches4,
"<region>sw_lokey=16 sw_hikey=19 sw_default=17 sw_last=16 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=16 sw_hikey=19 sw_last=17 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=8 sw_hikey=10 sw_last=8 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=8 sw_hikey=10 sw_last=9 lokey=32 hikey=35 sample=*saw"
"<region>sw_lokey=8 sw_hikey=10 sw_default=10 sw_last=9 lokey=32 hikey=35 sample=*saw"
);
////////////////////////////////////////////////////////////////////////////////
void test_assert_failed(struct test_env *env, const char *file, int line, const char *check)
{
if (env->context)
fprintf(stderr, "FAIL @%s:%d Assertion '%s' failed, context: %s.\n", file, line, check, env->context);
else
fprintf(stderr, "FAIL @%s:%d Assertion '%s' failed.\n", file, line, check);
longjmp(env->on_fail, 1);
}
void test_assert_failed_free(struct test_env *env, const char *file, int line, gchar *check)
{
if (env->context)
fprintf(stderr, "FAIL @%s:%d %s, context: %s\n", file, line, check, env->context);
else
fprintf(stderr, "FAIL @%s:%d %s.\n", file, line, check);
g_free(check);
longjmp(env->on_fail, 1);
}
////////////////////////////////////////////////////////////////////////////////
struct test_info {
const char *name;
void (*func)(struct test_env *env);
void *arg;
} tests[] = {
{ "test_sampler_setup", test_sampler_setup },
{ "test_sampler_midicurve", test_sampler_midicurve },
{ "test_sampler_midicurve2", test_sampler_midicurve2 },
{ "test_sampler_note_basic", test_sampler_note_basic },
{ "test_sampler_note_region_logic/key", test_sampler_note_region_logic, &setup_lokeyhikey },
{ "test_sampler_note_region_logic/key2", test_sampler_note_region_logic, &setup_lokeyhikey2 },
{ "test_sampler_note_region_logic/vel", test_sampler_note_region_logic, &setup_lovelhivel },
{ "test_sampler_note_region_logic/ch", test_sampler_note_region_logic, &setup_lochanhichan },
{ "test_sampler_note_region_logic/chanaft", test_sampler_note_region_logic, &setup_chanaft },
{ "test_sampler_note_region_logic/polyaft", test_sampler_note_region_logic, &setup_polyaft },
{ "test_sampler_note_region_logic/cc", test_sampler_note_region_logic, &setup_cc },
{ "test_sampler_note_region_logic/cc2", test_sampler_note_region_logic, &setup_cc2 },
{ "test_sampler_note_region_logic/cc3", test_sampler_note_region_logic, &setup_cc3 },
{ "test_sampler_note_region_logic/oncc", test_sampler_note_region_logic, &setup_oncc },
{ "test_sampler_note_region_logic/release", test_sampler_note_region_logic, &setup_release },
{ "test_sampler_note_region_logic/firstlegato", test_sampler_note_region_logic, &setup_firstlegato },
{ "test_sampler_note_region_logic/switches", test_sampler_note_region_logic, &setup_switches },
{ "test_sampler_note_region_logic/switches2", test_sampler_note_region_logic, &setup_switches2 },
{ "test_sampler_note_region_logic/switches3", test_sampler_note_region_logic, &setup_switches3 },
{ "test_sampler_note_region_logic/switches4", test_sampler_note_region_logic, &setup_switches4 },
};
int main(int argc, char *argv[])
{
uint32_t tests_run = 0, tests_failed = 0;
for (unsigned int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i)
{
struct test_env env;
env.doc = cbox_document_new();
env.engine = cbox_engine_new(env.doc, NULL);
env.arg = tests[i].arg;
env.context = NULL;
cbox_config_init("");
cbox_wavebank_init();
tests_run++;
if (0 == setjmp(env.on_fail))
{
printf("Running %s... ", tests[i].name);
fflush(stdout);
tests[i].func(&env);
printf("PASS\n");
}
else
tests_failed++;
CBOX_DELETE(env.engine);
env.engine = NULL;
cbox_document_destroy(env.doc);
env.doc = NULL;
cbox_wavebank_close();
cbox_config_close();
if (env.context)
{
g_free(env.context);
env.context = NULL;
}
}
printf("%d tests ran, %d tests failed.\n", tests_run, tests_failed);
return tests_failed != 0;
}