#define _POSIX_C_SOURCE 200809L #include "xtc/xtc_plugin.h" #include "xtc/xtc_dsp.h" #include "../../src/audio/wave_internal.h" #include #include #include "xtc/xtc_midi.h" #include #include #include #include static void effects(unsigned int id) { xtc_plugin_spec s; xtc_plugin *a=NULL,*b=NULL; float in[256]={0},x[256],y[256]; size_t k,i; double energy=0; assert(xtc_plugin_probe(XTC_BUILTIN_PATH,id,48000,&s)==XTC_OK); assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK); assert(xtc_plugin_create(&s,48000,256,&b)==XTC_OK); for(k=0;k<600;++k) { in[0]=k==0 ? 1 : 0; assert(xtc_plugin_process(a,in,x,256)==XTC_OK); assert(xtc_plugin_process(b,in,y,128)==XTC_OK); assert(xtc_plugin_process(b,in+128,y+128,128)==XTC_OK); for(i=0;i<256;++i) {assert(isfinite(x[i]));assert(fabsf(x[i]-y[i])<1e-6f);if(k>0)energy+=fabs(x[i]);} } assert(energy>.01);xtc_plugin_destroy(a);xtc_plugin_destroy(b); s.values[2]=0; assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK); in[0]=1;assert(xtc_plugin_process(a,in,x,256)==XTC_OK);assert(!memcmp(in,x,sizeof(in)));xtc_plugin_destroy(a); s.values[1]=2;assert(xtc_plugin_spec_validate(&s)!=XTC_OK); } static void synth(unsigned int id) { xtc_plugin_spec s; xtc_plugin *p=NULL; float out[256]; size_t i,k; double energy=0; xtc_midi_event e={0,XTC_MIDI_NOTE_ON,0,60,100}; assert(xtc_plugin_probe(XTC_BUILTIN_PATH,id,48000,&s)==XTC_OK); assert(xtc_plugin_create(&s,48000,256,&p)==XTC_OK); assert(xtc_plugin_process_midi(p,&e,1,out,256)==XTC_OK); for(i=0;i<256;++i)energy+=fabs(out[i]); assert(energy>0); e.type=XTC_MIDI_NOTE_OFF;assert(xtc_plugin_process_midi(p,&e,1,out,256)==XTC_OK); for(k=0;k<200;++k)assert(xtc_plugin_process_midi(p,NULL,0,out,256)==XTC_OK); for(i=0;i<256;++i)assert(fabsf(out[i])<1e-5f); e.type=XTC_MIDI_ALL_OFF;assert(xtc_plugin_process_midi(p,&e,1,out,256)==XTC_OK); for(i=0;i<256;++i)assert(out[i]==0); xtc_plugin_destroy(p); } static void automation(void) { xtc_plugin_spec s; xtc_plugin *p=NULL; float in[128],out[128]; size_t i; assert(xtc_plugin_probe(XTC_BUILTIN_PATH,2,960,&s)==XTC_OK); s.tempo=60000; s.values[0]=2000; s.point_count=2; s.points[0]=(xtc_plugin_point){0,2,0,0}; s.points[1]=(xtc_plugin_point){100,2,0,1}; assert(xtc_plugin_create(&s,960,128,&p)==XTC_OK); for(i=0;i<128;++i)in[i]=1; assert(xtc_plugin_process_at(p,in,out,128,0)==XTC_OK); for(i=0;i<128;++i) assert(fabsf(out[i]-(i<100 ? 1-(float)i/100 : 0))<1e-6f); xtc_plugin_destroy(p); } static void roundtrip(unsigned int instrument, unsigned int effect) { xtc_project *p=NULL,*q=NULL; xtc_plugin_spec s,t; uint64_t track; size_t id; xtc_graph *g=NULL; xtc_plugin *a=NULL,*b=NULL; float input[256]={0},x[256],y[256]; size_t k; char path[]="/tmp/xtc-builtin-XXXXXX"; int fd=mkstemp(path); assert(fd>=0); close(fd); assert(xtc_project_create(&p)==XTC_OK); assert(xtc_project_create(&q)==XTC_OK); assert(xtc_project_add_track(p,"Keys",&track)==XTC_OK); assert(xtc_plugin_probe(XTC_BUILTIN_PATH,instrument,48000,&s)==XTC_OK); assert(xtc_project_set_instrument(p,track,&s)==XTC_OK); assert(xtc_plugin_probe(XTC_BUILTIN_PATH,effect,48000,&s)==XTC_OK); s.point_count=2; s.points[0]=(xtc_plugin_point){0,2,0,.1f}; s.points[1]=(xtc_plugin_point){960,2,0,.6f}; assert(xtc_graph_clone(xtc_project_graph(p),&g)==XTC_OK); assert(xtc_graph_add_plugin(g,&s,&id)==XTC_OK); assert(xtc_project_set_graph(p,g)==XTC_OK);xtc_graph_destroy(g); assert(xtc_project_save(p,path)==XTC_OK); assert(xtc_project_load(q,path)==XTC_OK); assert(xtc_graph_get_plugin(xtc_project_graph(q),id,&t)==XTC_OK); assert(!memcmp(&s,&t,sizeof(s))); assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK && xtc_plugin_create(&t,48000,256,&b)==XTC_OK); for(k=0;k<256;++k) { input[0]=k==0 ? 1 : 0; assert(xtc_plugin_process(a,input,x,256)==XTC_OK && xtc_plugin_process(b,input,y,256)==XTC_OK); assert(!memcmp(x,y,sizeof(x))); } xtc_plugin_destroy(a);xtc_plugin_destroy(b); assert(xtc_project_get_instrument(q,track,&t)==XTC_OK && t.index==instrument && t.format==XTC_PLUGIN_BUILTIN); assert(xtc_project_get_instrument(p,track,&s)==XTC_OK); assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK && xtc_plugin_create(&t,48000,256,&b)==XTC_OK); { xtc_midi_event e={19,XTC_MIDI_NOTE_ON,0,60,100}; assert(xtc_plugin_process_midi(a,&e,1,x,256)==XTC_OK && xtc_plugin_process_midi(b,&e,1,y,256)==XTC_OK); assert(!memcmp(x,y,sizeof(x))); } xtc_plugin_destroy(a);xtc_plugin_destroy(b); /* Corrupt a serialized identity, retaining a valid destination project. */ { FILE *file=fopen(path,"r+"); char data[32768]; size_t n; char *label; assert(file);n=fread(data,1,sizeof(data)-1,file);data[n]=0; label=strstr(data,effect==2 ? "44656c6179" : "526576657262"); /* Names are hex encoded; module corruption is independent of name codec. */ (void)label; label=strstr(data,"2f7874632f696e7465726e616c"); assert(label);label[0]='3';rewind(file);assert(fwrite(data,1,n,file)==n);fclose(file); assert(xtc_project_load(q,path)!=XTC_OK); assert(xtc_project_get_instrument(q,track,&t)==XTC_OK && t.index==instrument); } xtc_project_destroy(p);xtc_project_destroy(q);unlink(path); } static void synth_partition(unsigned int id) { xtc_plugin_spec s; xtc_plugin *a=NULL,*b=NULL; float x[256],y[256]; size_t i,k; xtc_midi_event events[40]; assert(xtc_plugin_probe(XTC_BUILTIN_PATH,id,48000,&s)==XTC_OK); assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK && xtc_plugin_create(&s,48000,256,&b)==XTC_OK); /* Exhaust voices, repeat a key, sustain its note-off, then release pedal. */ for(i=0;i<36;++i) events[i]=(xtc_midi_event){(unsigned int)i,XTC_MIDI_NOTE_ON,0,40+(unsigned int)i,100}; events[36]=(xtc_midi_event){40,XTC_MIDI_NOTE_ON,0,60,80}; events[37]=(xtc_midi_event){50,XTC_MIDI_CONTROL,0,64,127}; events[38]=(xtc_midi_event){60,XTC_MIDI_NOTE_OFF,0,60,0}; events[39]=(xtc_midi_event){80,XTC_MIDI_CONTROL,0,64,0}; assert(xtc_plugin_process_midi(a,events,40,x,256)==XTC_OK); assert(xtc_plugin_process_midi(b,events,40,y,128)==XTC_OK); assert(xtc_plugin_process_midi(b,NULL,0,y+128,128)==XTC_OK); assert(!memcmp(x,y,sizeof(x))); for(k=0;k<100;++k) { assert(xtc_plugin_process_midi(a,NULL,0,x,256)==XTC_OK); assert(xtc_plugin_process_midi(b,NULL,0,y,128)==XTC_OK); assert(xtc_plugin_process_midi(b,NULL,0,y+128,128)==XTC_OK); for(i=0;i<256;++i) assert(isfinite(x[i]) && x[i]==y[i]); } events[0]=(xtc_midi_event){0,XTC_MIDI_ALL_OFF,0,0,0}; assert(xtc_plugin_process_midi(a,events,1,x,256)==XTC_OK); for(i=0;i<256;++i) assert(x[i]==0); xtc_plugin_destroy(a);xtc_plugin_destroy(b); } static void transport(unsigned int id) { xtc_project *project=NULL;xtc_midi_plan *plan=NULL,*fresh=NULL;xtc_plugin_spec spec; uint64_t track,clip,note;float x[256],y[256];size_t k; assert(xtc_project_create(&project)==XTC_OK); assert(xtc_project_add_track(project,"Transport",&track)==XTC_OK); assert(xtc_project_add_midi_clip(project,track,0,3840,&clip)==XTC_OK); assert(xtc_project_add_note(project,clip,0,1920,60,100,0,¬e)==XTC_OK); assert(xtc_plugin_probe(XTC_BUILTIN_PATH,id,48000,&spec)==XTC_OK); assert(xtc_project_set_instrument(project,track,&spec)==XTC_OK); assert(xtc_midi_plan_create(project,48000,&plan)==XTC_OK); assert(xtc_midi_plan_create(project,48000,&fresh)==XTC_OK); assert(xtc_midi_plan_render(plan,0,NULL,0,x,256)==XTC_OK); /* Discontinuity chases with a fresh envelope, identical to a fresh plan. */ assert(xtc_midi_plan_render(plan,1000,NULL,0,x,256)==XTC_OK); assert(xtc_midi_plan_render(fresh,1000,NULL,0,y,256)==XTC_OK); assert(!memcmp(x,y,sizeof(x))); xtc_midi_plan_panic(plan,1256); for(k=0;k<200;++k)assert(xtc_midi_plan_render(plan,1256+k*256,NULL,0,x,256)==XTC_OK); for(k=0;k<256;++k)assert(fabsf(x[k])<1e-5f); xtc_midi_plan_reset(plan); assert(xtc_midi_plan_render(plan,0,NULL,0,x,256)==XTC_OK); xtc_midi_plan_reset(fresh); assert(xtc_midi_plan_render(fresh,0,NULL,0,y,256)==XTC_OK); assert(!memcmp(x,y,sizeof(x))); xtc_midi_plan_destroy(plan);xtc_midi_plan_destroy(fresh);xtc_project_destroy(project); } static void editable(void) { xtc_plugin_spec s,legacy,t;xtc_plugin *a=NULL,*b=NULL;float x[256],y[256];size_t k; xtc_midi_event events[]={{0,XTC_MIDI_NOTE_ON,0,60,100},{64,XTC_MIDI_BEND,0,0,127}}; char path[]="/tmp/xtc-preset-XXXXXX";int fd=mkstemp(path);assert(fd>=0);close(fd); assert(xtc_plugin_probe(XTC_BUILTIN_PATH,0,48000,&s)==XTC_OK && s.control_count==5); legacy=s;legacy.control_count=0; assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK && xtc_plugin_create(&legacy,48000,256,&b)==XTC_OK); assert(xtc_plugin_process_midi(a,events,1,x,256)==XTC_OK && xtc_plugin_process_midi(b,events,1,y,256)==XTC_OK); assert(!memcmp(x,y,sizeof(x)));xtc_plugin_destroy(a);xtc_plugin_destroy(b); assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK && xtc_plugin_create(&s,48000,256,&b)==XTC_OK); assert(xtc_plugin_process_midi(a,events,2,x,256)==XTC_OK && xtc_plugin_process_midi(b,events,1,y,256)==XTC_OK); assert(!memcmp(x,y,64*sizeof(float)) && memcmp(x+65,y+65,191*sizeof(float))); xtc_plugin_destroy(a);xtc_plugin_destroy(b); /* Migrate an actual v13 fixed-preset binding; no new records existed there. */ assert(xtc_plugin_preset_save(&legacy,path)==XTC_OK); {FILE *f=fopen(path,"r");char lines[32768],line[2048];size_t used=0;assert(f); while(fgets(line,sizeof(line),f)) { if(!strncmp(line,"MARKERS ",8) || !strncmp(line,"SAMPLE ",7) || !strncmp(line,"INSTRUMENT_CONTROLS",19) || !strncmp(line,"INSTRUMENT_AUTOMATION",21))continue; if(!strncmp(line,"XTC_PROJECT 16",14))strcpy(line,"XTC_PROJECT 13\n"); assert(used+strlen(line)=0 && pd>=0);close(fd);close(pd); file=fopen(path,"wb");assert(file && xtc_wave_header(file,24000,4) && xtc_wave_write(file,data,4));fclose(file); assert(xtc_plugin_probe(XTC_BUILTIN_PATH,4,48000,&s)==XTC_OK && s.instrument); strcpy(s.sample_path,path);s.values[2]=.1f; assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK && !xtc_plugin_asset_issues(a) && xtc_plugin_sample_frames(a)==4); assert(xtc_plugin_process_midi(a,&e,1,x,256)==XTC_OK); for(i=0;i<8;++i) { double expected=i<6 ? (double)i*.125 : .75; envelope+=(1-envelope)*(1-exp(-1/(.1*.001*48000))); assert(fabs(x[i]-expected*envelope)<1e-5); } for(i=8;i<256;++i)assert(x[i]==0); xtc_plugin_destroy(a);s.values[1]=1; assert(xtc_plugin_preset_save(&s,preset)==XTC_OK && xtc_plugin_preset_load(preset,&t)==XTC_OK); assert(!strcmp(t.sample_path,path) && t.values[1]==1); assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK && xtc_plugin_create(&t,48000,256,&b)==XTC_OK); assert(xtc_plugin_process_midi(a,&e,1,x,256)==XTC_OK && xtc_plugin_process_midi(b,&e,1,y,128)==XTC_OK); assert(xtc_plugin_process_midi(b,NULL,0,y+128,128)==XTC_OK && !memcmp(x,y,sizeof(x))); e.type=XTC_MIDI_NOTE_OFF;assert(xtc_plugin_process_midi(a,&e,1,x,256)==XTC_OK); for(i=0;i<200;++i)assert(xtc_plugin_process_midi(a,NULL,0,x,256)==XTC_OK); for(i=0;i<256;++i)assert(x[i]==0); xtc_plugin_destroy(a);xtc_plugin_destroy(b);unlink(path); assert(xtc_plugin_preset_load(preset,&t)==XTC_OK && !strcmp(t.sample_path,path)); assert(xtc_plugin_create(&t,48000,256,&a)==XTC_OK && xtc_plugin_asset_issues(a)==1); e.type=XTC_MIDI_NOTE_ON;assert(xtc_plugin_process_midi(a,&e,1,x,256)==XTC_OK); for(i=0;i<256;++i)assert(x[i]==0); xtc_plugin_destroy(a);unlink(preset); } static void stereo_effect(unsigned int id) { xtc_plugin_spec s;xtc_plugin *a=NULL,*b=NULL;float in[256]={0},zero[256]={0},l[256],r[256],x[256],y[256]; size_t i,k;double left_energy=0,right_energy=0; assert(xtc_plugin_probe(XTC_BUILTIN_PATH,id,48000,&s)==XTC_OK); s.values[2]=1; s.point_count=2;s.points[0]=(xtc_plugin_point){0,0,1,id==5 ? 10 : .5f}; s.points[1]=(xtc_plugin_point){960,0,1,id==5 ? 40 : .95f}; if(id==5)s.values[1]=.9f;else {s.values[3]=1;s.values[4]=1;} assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK && xtc_plugin_create(&s,48000,256,&b)==XTC_OK); for(k=0;k<1200;++k) { in[0]=k==0 ? 1 : 0; assert(xtc_plugin_process_stereo_at(a,in,zero,l,r,256,k*256)==XTC_OK); assert(xtc_plugin_process_stereo_at(b,in,zero,x,y,128,k*256)==XTC_OK); assert(xtc_plugin_process_stereo_at(b,in+128,zero+128,x+128,y+128,128,k*256+128)==XTC_OK); assert(!memcmp(l,x,sizeof(l)) && !memcmp(r,y,sizeof(r))); for(i=0;i<256;++i){assert(isfinite(l[i]) && isfinite(r[i]) && fabsf(l[i])<=1 && fabsf(r[i])<=1);left_energy+=fabs(l[i]);right_energy+=fabs(r[i]);} } assert(left_energy>0 && right_energy>0);xtc_plugin_destroy(a);xtc_plugin_destroy(b); { xtc_graph *graph=NULL;xtc_plan *plan=NULL;size_t source,node,output; assert(xtc_graph_create(3,&graph)==XTC_OK); assert(xtc_graph_add_audio(graph,&source)==XTC_OK && xtc_graph_add_plugin(graph,&s,&node)==XTC_OK && xtc_graph_add_output(graph,&output)==XTC_OK); assert(xtc_graph_connect(graph,source,node)==XTC_OK && xtc_graph_connect(graph,node,output)==XTC_OK); assert(xtc_graph_compile(graph,48000,256,&plan)==XTC_OK); assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK); for(k=0;k<200;++k) { in[0]=k==0 ? 1 : 0; assert(xtc_plan_process_stereo(plan,NULL,in,zero,NULL,NULL,l,r,256)==XTC_OK); assert(xtc_plugin_process_stereo_at(a,in,zero,x,y,256,k*256)==XTC_OK); for(i=0;i<256;++i)assert(l[i]==xtc_dsp_finite(x[i]) && r[i]==xtc_dsp_finite(y[i])); } xtc_plugin_destroy(a);xtc_plan_destroy(plan);xtc_graph_destroy(graph); } if(id==5) { s.point_count=0;s.values[3]=1;s.values[1]=0;s.tempo=120000; assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK); for(k=0;k<100;++k) { in[0]=k==0 ? 1 : 0; assert(xtc_plugin_process_stereo_at(a,in,zero,l,r,256,k*256)==XTC_OK); for(i=0;i<256;++i)assert(l[i]==(k*256+i==24000 ? 1 : 0)); }xtc_plugin_destroy(a); } } static void histories(void) { unsigned int ids[]={2,3,5,6},j;size_t k,i; for(j=0;j<4;++j) { xtc_plugin_spec s;xtc_plugin *p=NULL;float in[256]={0},out[256]; assert(xtc_plugin_probe(XTC_BUILTIN_PATH,ids[j],48000,&s)==XTC_OK); assert(xtc_plugin_create(&s,48000,256,&p)==XTC_OK); in[0]=1;assert(xtc_plugin_process(p,in,out,256)==XTC_OK);in[0]=0; for(k=0;k<200;++k)assert(xtc_plugin_process(p,in,out,256)==XTC_OK); xtc_plugin_reset_history(p); for(k=0;k<600;++k) { assert(xtc_plugin_process(p,in,out,256)==XTC_OK); for(i=0;i<256;++i)assert(out[i]==0); } xtc_plugin_destroy(p); } } static void exporting(void) { xtc_project *project=NULL;xtc_plugin_spec spec;xtc_graph *graph=NULL;xtc_plan *plan=NULL; xtc_midi_plan *midi=NULL;xtc_export *job=NULL;xtc_wave wave={0}; uint64_t track,clip,note,total,position;size_t source,node,output,i;int done=0; char path[]="/tmp/xtc-export-XXXXXX";int fd=mkstemp(path);assert(fd>=0);close(fd);unlink(path); assert(xtc_project_create(&project)==XTC_OK && xtc_project_add_track(project,"Export",&track)==XTC_OK); assert(xtc_project_add_midi_clip(project,track,0,120,&clip)==XTC_OK); assert(xtc_project_add_note(project,clip,0,100,60,100,0,¬e)==XTC_OK); assert(xtc_graph_create(3,&graph)==XTC_OK && xtc_graph_add_instrument(graph,&source)==XTC_OK); assert(xtc_plugin_probe(XTC_BUILTIN_PATH,5,48000,&spec)==XTC_OK);spec.values[0]=100; assert(xtc_graph_add_plugin(graph,&spec,&node)==XTC_OK && xtc_graph_add_output(graph,&output)==XTC_OK); assert(xtc_graph_connect(graph,source,node)==XTC_OK && xtc_graph_connect(graph,node,output)==XTC_OK); assert(xtc_project_set_graph(project,graph)==XTC_OK); assert(xtc_export_create(project,path,48000,500,&job)==XTC_OK); assert(xtc_export_step(job,&done)==XTC_OK && !done && access(path,F_OK)!=0); xtc_export_destroy(job);job=NULL;assert(access(path,F_OK)!=0); assert(xtc_export_create(project,path,48000,500,&job)==XTC_OK); while(!done)assert(xtc_export_step(job,&done)==XTC_OK); assert(xtc_export_progress(job,&position,&total)==XTC_OK && position==total && total==27000); xtc_export_destroy(job);job=NULL; assert(xtc_wave_load(path,&wave)==XTC_OK && wave.frames==27000); {double energy=0;for(i=3000;i1); } assert(xtc_graph_compile(graph,48000,31,&plan)==XTC_OK && xtc_midi_plan_create(project,48000,&midi)==XTC_OK); for(position=0;position