#define _POSIX_C_SOURCE 200809L #include "xtc/xtc_control.h" #include "xtc/xtc_midi.h" #include "../../src/mixer/mix_internal.h" #include #include #include #include #include #include int main(void) { xtc_project *p = NULL, *loaded = NULL; xtc_midi_plan *reference = NULL, *whole = NULL, *split = NULL; xtc_parameter_state state, sentinel; uint64_t track, clip, note; float base[80], left[80], right[80], sl[80], sr[80]; unsigned int map[3] = {1,2,3}, actual[3]; size_t i, cursor; char path[] = "/tmp/xtc-parameters-XXXXXX"; int fd = mkstemp(path); assert(fd >= 0); close(fd); assert(xtc_project_create(&p) == XTC_OK); assert(xtc_project_add_track(p,"Parameters",&track) == XTC_OK); assert(xtc_project_add_midi_clip(p,track,0,960,&clip) == XTC_OK); assert(xtc_project_add_note(p,clip,0,960,69,127,0,¬e) == XTC_OK); assert(xtc_midi_plan_create(p,48000,&reference) == XTC_OK); assert(xtc_midi_plan_render(reference,0,NULL,0,base,80) == XTC_OK); { xtc_parameter_key gain = {XTC_PARAMETER_TRACK,track,0}, pan = {XTC_PARAMETER_TRACK,track,1}; xtc_parameter_key missing = {XTC_PARAMETER_TRACK,UINT64_MAX,0}; xtc_mix_snapshot mix; assert(xtc_project_parameter(p,gain,48000,&state) == XTC_OK && !state.automated && state.value == 1); assert(!strcmp(state.info.unit,"linear") && state.info.minimum == 0 && state.info.maximum == 4); sentinel = state; assert(xtc_project_parameter(p,missing,48000,&state) == XTC_ERR_NOT_FOUND && !memcmp(&state,&sentinel,sizeof(state))); assert(xtc_project_automate(p,missing,48000,0,0,1,0) == XTC_ERR_NOT_FOUND); assert(xtc_project_automate(p,gain,48000,0,1,.25f,0) == XTC_OK); assert(xtc_project_automate(p,gain,48000,1,0,1,0) == XTC_OK); assert(xtc_project_automate(p,gain,48000,2,0,.5f,0) == XTC_OK); assert(xtc_project_automate(p,pan,48000,0,1,-1,0) == XTC_OK); assert(xtc_project_automate(p,pan,48000,2,1,1,0) == XTC_OK); assert(xtc_project_automate(p,gain,48000,3,2,.5f,0) == XTC_ERR_INVALID_ARGUMENT); assert(xtc_project_set_parameter(p,gain,48000,3) == XTC_OK); assert(xtc_project_parameter(p,gain,48000,&state) == XTC_OK && state.automated && state.value == 3); assert(xtc_project_set_parameter(p,gain,48000,NAN) == XTC_ERR_INVALID_ARGUMENT); assert(xtc_mix_prepare(p,1,&mix) == XTC_OK && xtc_mix_level(&mix,0,0,2) == .5f); // Colliding ticks: latest wins. } assert(xtc_project_get_control_map(p,0,actual) == XTC_OK && actual[0] == 7 && actual[1] == 10 && actual[2] == 64); assert(xtc_project_set_control_map(p,0,map) == XTC_OK); map[1] = 1; assert(xtc_project_set_control_map(p,0,map) == XTC_ERR_INVALID_ARGUMENT); map[1] = 120; assert(xtc_project_set_control_map(p,0,map) == XTC_ERR_INVALID_ARGUMENT); assert(xtc_project_get_control_map(p,0,actual) == XTC_OK && actual[0] == 1 && actual[1] == 2 && actual[2] == 3); assert(xtc_project_save(p,path) == XTC_OK); assert(xtc_project_create(&loaded) == XTC_OK && xtc_project_load(loaded,path) == XTC_OK); assert(xtc_project_get_control_map(loaded,0,actual) == XTC_OK && actual[0] == 1 && actual[1] == 2 && actual[2] == 3); { xtc_automation_point point; assert(xtc_project_get_automation(loaded,0,&point) == XTC_OK && point.step == 1); } assert(xtc_midi_plan_create(loaded,48000,&whole) == XTC_OK && xtc_midi_plan_create(loaded,48000,&split) == XTC_OK); assert(xtc_midi_plan_render_stereo(whole,0,NULL,0,left,right,80) == XTC_OK); for (cursor = 0; cursor < 80;) { size_t frames = 13; if (frames > 80-cursor) frames = 80-cursor; assert(xtc_midi_plan_render_stereo(split,cursor,NULL,0,sl+cursor,sr+cursor,frames) == XTC_OK); cursor += frames; } for (i = 0; i < 80; ++i) { float gain = i < 25 ? .25f : i < 50 ? 1-(float)(i-25)/50 : .5f; assert(left[i] == sl[i] && right[i] == sr[i]); assert(fabsf(left[i] - (i < 50 ? base[i]*gain : 0)) < .000001f); assert(fabsf(right[i] - (i < 50 ? 0 : base[i]*gain)) < .000001f); } assert(xtc_midi_plan_render_stereo(split,29,NULL,0,sl,sr,13) == XTC_OK); for (i = 0; i < 13; ++i) assert(sl[i] == left[i+29] && sr[i] == right[i+29]); // Explicit backward transport wrap (no loop transport feature is implied). assert(xtc_midi_plan_render_stereo(split,0,NULL,0,sl,sr,80) == XTC_OK); for (i = 0; i < 80; ++i) assert(sl[i] == left[i] && sr[i] == right[i]); { FILE *source = fopen(path,"r"), *dest; char bad[128], line[1024]; snprintf(bad,sizeof(bad),"%s-bad",path); dest = fopen(bad,"w"); assert(source && dest); while (fgets(line,sizeof(line),source)) { if (!strncmp(line,"MAP 0 ",6)) fputs("MAP 0 1 1 3\n",dest); else fputs(line,dest); } fclose(source); assert(fclose(dest) == 0); assert(xtc_project_load(loaded,bad) == XTC_ERR_FORMAT); assert(xtc_project_get_control_map(loaded,0,actual) == XTC_OK && actual[1] == 2); unlink(bad); } xtc_midi_plan_destroy(reference); xtc_midi_plan_destroy(whole); xtc_midi_plan_destroy(split); xtc_project_destroy(p); xtc_project_destroy(loaded); unlink(path); puts("Common parameter identity, hold/linear lanes, static precedence, persistence, seeks and wrap passed."); return 0; }