#include "xtc/xtc_midi.h" #include "xtc/xtc_control.h" #include #include #include int main(void) { xtc_project *project = NULL; xtc_midi_plan *a = NULL, *b = NULL; float whole[400], split[400]; size_t i, j, cursor = 0; xtc_midi_event decoded; const unsigned char cc[] = {0xb2,7,0}; xtc_midi_event events[] = { {0,XTC_MIDI_NOTE_ON,2,69,127}, {3,XTC_MIDI_CONTROL,2,64,127}, {5,XTC_MIDI_NOTE_OFF,2,69,0}, {9,XTC_MIDI_NOTE_ON,2,69,127}, {10,XTC_MIDI_CONTROL,2,7,0}, {11,XTC_MIDI_CONTROL,2,10,127}, {260,XTC_MIDI_NOTE_OFF,2,69,0}, {270,XTC_MIDI_CONTROL,2,64,0}}; assert(xtc_midi_decode(cc,3,10,&decoded) == XTC_OK && decoded.type == XTC_MIDI_CONTROL && decoded.pitch == 7); assert(xtc_project_create(&project) == XTC_OK); assert(xtc_midi_plan_create(project,48000,&a) == XTC_OK); assert(xtc_midi_plan_create(project,48000,&b) == XTC_OK); assert(xtc_midi_plan_render(a,0,events,8,whole,400) == XTC_OK); while (cursor < 400) { xtc_midi_event local[8]; size_t count = 0, n = 13; if (n > 400-cursor) n = 400-cursor; for (j = 0; j < 8; ++j) if (events[j].offset >= cursor && events[j].offset < cursor+n) { local[count] = events[j]; local[count++].offset -= (unsigned int)cursor; } assert(xtc_midi_plan_render(b,cursor,local,count,split+cursor,n) == XTC_OK); cursor += n; } for (i = 0; i < 400; ++i) assert(whole[i] == split[i]); assert(whole[6] != 0); /* Sustain retains the released note. */ for (i = 250; i < 400; ++i) assert(whole[i] == 0); /* CC7 ramp reaches zero in 240 samples. */ { xtc_midi_plan *mapped = NULL, *disabled = NULL; xtc_parameter_state state; xtc_parameter_key key = {XTC_PARAMETER_LIVE,2,2}; unsigned int bindings[3] = {1,74,11}; xtc_midi_event remapped[9]; float actual[400], right[400]; unsigned char message[3] = {0xb2,1,0}; assert(xtc_midi_decode(message,3,0,&decoded) == XTC_OK && decoded.type == XTC_MIDI_CONTROL); message[1] = 120; assert(xtc_midi_decode(message,3,0,&decoded) == XTC_OK && decoded.type == XTC_MIDI_ALL_OFF); assert(xtc_project_parameter(project,key,48000,&state) == XTC_OK && !state.automated && (state.flags & XTC_PLUGIN_TOGGLED) && state.value == 0); assert(xtc_project_automate(project,key,48000,0,1,1,0) == XTC_ERR_UNSUPPORTED); assert(xtc_project_set_control_map(project,2,bindings) == XTC_OK); assert(xtc_midi_plan_create(project,48000,&mapped) == XTC_OK); for (i = 0; i < 8; ++i) { remapped[i+(i ? 1 : 0)] = events[i]; if (events[i].type == XTC_MIDI_CONTROL) remapped[i+1].pitch = events[i].pitch == 7 ? 1 : events[i].pitch == 10 ? 74 : 11; } // Former CC7 binding is ignored, not an additional volume binding. remapped[1] = (xtc_midi_event){1,XTC_MIDI_CONTROL,2,7,0}; assert(xtc_midi_plan_render(mapped,0,remapped,9,actual,400) == XTC_OK); for (i = 0; i < 400; ++i) assert(actual[i] == whole[i]); { const xtc_midi_event pan[] = {{0,XTC_MIDI_NOTE_ON,2,69,127},{0,XTC_MIDI_CONTROL,2,74,127}}; xtc_midi_plan_reset(mapped); assert(xtc_midi_plan_render_stereo(mapped,0,pan,2,actual,right,400) == XTC_OK); assert(actual[300] == 0 && right[300] != 0); } // Mutating the model does not change an already prepared mapping. bindings[0] = bindings[1] = bindings[2] = XTC_CC_DISABLED; assert(xtc_project_set_control_map(project,2,bindings) == XTC_OK); xtc_midi_plan_reset(mapped); assert(xtc_midi_plan_render(mapped,0,remapped,9,actual,400) == XTC_OK); for (i = 0; i < 400; ++i) assert(actual[i] == whole[i]); assert(xtc_midi_plan_create(project,48000,&disabled) == XTC_OK); { const xtc_midi_event unbound[] = {{0,XTC_MIDI_NOTE_ON,2,69,127},{3,XTC_MIDI_CONTROL,2,1,0}}; assert(xtc_midi_plan_render(disabled,0,unbound,2,actual,400) == XTC_OK); assert(actual[300] != 0); // No mapped volume ramp reaches zero. } xtc_midi_plan_destroy(mapped); xtc_midi_plan_destroy(disabled); } xtc_midi_plan_reset(a); assert(xtc_midi_plan_render(a,400,NULL,0,whole,400) == XTC_OK); for (i = 0; i < 400; ++i) assert(whole[i] == 0); xtc_midi_plan_destroy(a); xtc_midi_plan_destroy(b); xtc_project_destroy(project); puts("Sample-timed default/remapped/disabled controllers, immutable maps, sustain, retrigger, note-off and block partition checks passed."); return 0; }