feat: render bounded effect tails and reset internal history on transport changes

This commit is contained in:
Johannes Findeisen 2026-11-05 23:55:56 +01:00
commit 836cdedc6b
22 changed files with 284 additions and 18 deletions

View file

@ -1046,6 +1046,11 @@ int main(int argc, char **argv)
CHECK(window.settings().loop_enabled==1 && window.settings().loop_start==960);
CHECK(window.set_loop(4000,2000,true)==XTC_ERR_INVALID_ARGUMENT);
settings=window.settings();CHECK(window.apply_preferences(settings)==XTC_OK);
settings=window.settings();settings.loop_enabled=0;settings.stop_after_tail=1;settings.tail_ms=0;
CHECK(window.apply_preferences(settings)==XTC_OK);
CHECK(window.create_track(true)==XTC_OK);press(find(&window,"Play"));
engine_status.sample_position=1000000;pump();CHECK(!handle.running);
settings.loop_enabled=1;settings.stop_after_tail=0;CHECK(window.apply_preferences(settings)==XTC_OK);
settings.note_velocity=128;CHECK(window.apply_preferences(settings)==XTC_ERR_INVALID_ARGUMENT);
PreferencesDialog dialog(window.settings(),path,[&](const xtc_settings &v){return window.apply_preferences(v);});
auto *input=dynamic_cast<Fl_Input *>(find(&dialog,"New note velocity"));CHECK(input);

View file

@ -170,6 +170,7 @@ static void builtin_failures(void)
assert(xtc_plugin_process_midi(plugin,&event,1,out,256)==XTC_OK);
} else if(id>=5) {float right[256];assert(xtc_plugin_process_stereo_at(plugin,in,in,out,right,256,0)==XTC_OK);}
else assert(xtc_plugin_process(plugin,in,out,256)==XTC_OK);
xtc_plugin_reset_history(plugin);
processing=0;xtc_plugin_destroy(plugin);succeeded=1;break;
}
assert(succeeded);

View file

@ -266,7 +266,61 @@ static void stereo_effect(unsigned int id)
}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,&note)==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;i<wave.frames;++i)energy+=fabs(wave.samples[i]);assert(energy>1); }
assert(xtc_graph_compile(graph,48000,31,&plan)==XTC_OK && xtc_midi_plan_create(project,48000,&midi)==XTC_OK);
for(position=0;position<total;position+=31) {
float l[31],r[31],ml[31],mr[31];size_t n=total-position<31 ? (size_t)(total-position) : 31;
assert(xtc_midi_plan_render_stereo(midi,position,NULL,0,ml,mr,n)==XTC_OK);
assert(xtc_plan_process_stereo(plan,NULL,NULL,NULL,ml,mr,l,r,n)==XTC_OK);
for(i=0;i<n;++i)assert(fabsf(wave.samples[position+i]-(l[i]+r[i])*.5f)<1e-6f);
}
/* Existing destination survives a complete attempted export. */
done=0;assert(xtc_export_create(project,path,48000,0,&job)==XTC_OK);
while(!done){xtc_result result=xtc_export_step(job,&done);if(result!=XTC_OK){assert(result==XTC_ERR_INVALID_STATE);break;}}
assert(xtc_export_step(job,&done)==XTC_ERR_INVALID_STATE);xtc_export_destroy(job);
assert(xtc_project_render_length(project,48000,60001,&total)==XTC_ERR_INVALID_ARGUMENT);
free(wave.samples);xtc_plan_destroy(plan);xtc_midi_plan_destroy(midi);xtc_graph_destroy(graph);xtc_project_destroy(project);unlink(path);
}
int main(void) {
stereo_effect(5);stereo_effect(6);sampler();editable();transport(0);transport(1);automation();roundtrip(0,2);roundtrip(1,3);effects(2);effects(3);synth(0);synth(1);
histories();exporting();stereo_effect(5);stereo_effect(6);sampler();editable();transport(0);transport(1);automation();roundtrip(0,2);roundtrip(1,3);effects(2);effects(3);synth(0);synth(1);
synth_partition(0);synth_partition(1);puts("Built-in devices passed.");return 0;
}