Phase 26: add common parameter targets, step automation and MIDI control mapping

This commit is contained in:
Johannes Findeisen 2026-10-27 16:37:59 +01:00
commit 901a536f10
38 changed files with 740 additions and 67 deletions

View file

@ -1,4 +1,5 @@
#include "xtc/xtc_midi.h"
#include "xtc/xtc_control.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
@ -29,10 +30,55 @@ int main(void)
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 CC7/CC10/sustain, retrigger, note-off and block partition checks passed.");
puts("Sample-timed default/remapped/disabled controllers, immutable maps, sustain, retrigger, note-off and block partition checks passed.");
return 0;
}