#include "track_fx.h" #include "../project/project_internal.h" #include "xtc/xtc_dsp.h" #include #include struct xtc_track_fx { xtc_plan *plans[XTC_PROJECT_MAX_TRACKS]; float *raw[XTC_PROJECT_MAX_TRACKS]; uint64_t next; float scratch[256]; }; /* Public metadata graph: the graph displayed by the editor is also compiled. */ xtc_result xtc_track_chain_graph(const xtc_track_chain *c,xtc_graph **out) { xtc_graph *g=NULL;size_t previous=0,output=0,i;xtc_result r; if(!out || xtc_track_chain_validate(c)!=XTC_OK)return XTC_ERR_INVALID_ARGUMENT; r=xtc_graph_create(64,&g); if(r==XTC_OK)r=xtc_graph_add_gain(g,1,&previous); for(i=0;r==XTC_OK && icount;++i) { const xtc_track_insert *s=c->slots+i;size_t plugin=0,wet=0,dry=0,sum=0; if(!s->enabled || s->wet==0)continue; r=xtc_graph_add_plugin(g,&s->plugin,&plugin); if(r==XTC_OK)r=xtc_graph_connect(g,previous,plugin); if(s->wet==1){previous=plugin;continue;} if(r==XTC_OK)r=xtc_graph_add_gain(g,s->wet,&wet); if(r==XTC_OK)r=xtc_graph_add_gain(g,1-s->wet,&dry); if(r==XTC_OK)r=xtc_graph_add_gain(g,1,&sum); if(r==XTC_OK)r=xtc_graph_connect(g,plugin,wet); if(r==XTC_OK)r=xtc_graph_connect(g,previous,dry); if(r==XTC_OK)r=xtc_graph_connect(g,wet,sum); if(r==XTC_OK)r=xtc_graph_connect(g,dry,sum); previous=sum; } if(r==XTC_OK)r=xtc_graph_add_gain(g,c->output_gain,&output); if(r==XTC_OK)r=xtc_graph_connect(g,previous,output); previous=output; if(r==XTC_OK)r=xtc_graph_add_output(g,&output); if(r==XTC_OK)r=xtc_graph_connect(g,previous,output); if(r!=XTC_OK){xtc_graph_destroy(g);return r;} *out=g;return XTC_OK; } void xtc_track_fx_destroy(xtc_track_fx *f) { size_t i;if(!f)return; for(i=0;iplans[i]);free(f->raw[i]);} free(f); } xtc_result xtc_track_fx_create(const xtc_project *p,unsigned int rate,int midi,xtc_track_fx **out) { size_t i,j;xtc_track_fx *f=NULL;xtc_result r=XTC_OK;int needed=0; if(!p || !out || !rate)return XTC_ERR_INVALID_ARGUMENT; *out=NULL; for(i=0;itrack_count;++i)if(p->chains[i].count || p->chains[i].output_gain!=1)needed=1; if(!needed)return XTC_OK; f=calloc(1,sizeof(*f));if(!f)return XTC_ERR_OUT_OF_MEMORY;f->next=UINT64_MAX; for(i=0;r==XTC_OK && itrack_count;++i) { const xtc_track_chain *c=p->chains+i;xtc_graph *g=NULL; int has_midi=p->instruments[i].instrument || p->live_instrument==p->tracks[i].id,has_audio=0; if(!c->count && c->output_gain==1)continue; for(j=0;jclip_count;++j)if(p->clips[j].track_id==p->tracks[i].id) { if(p->clips[j].midi)has_midi=1;else if(p->clips[j].asset_path[0])has_audio=1; } if(p->tracks[i].is_bus || (has_midi && has_audio)){r=XTC_ERR_UNSUPPORTED;break;} if(has_midi!=midi)continue; r=xtc_track_chain_graph(c,&g); if(r==XTC_OK)r=xtc_graph_compile(g,rate,256,&f->plans[i]); xtc_graph_destroy(g); if(r==XTC_OK){f->raw[i]=calloc(65536,sizeof(float));if(!f->raw[i])r=XTC_ERR_OUT_OF_MEMORY;} } if(r!=XTC_OK){xtc_track_fx_destroy(f);return r;} *out=f;return XTC_OK; } void xtc_track_fx_begin(xtc_track_fx *f,uint64_t position,size_t frames) { size_t i;if(!f)return; for(i=0;iraw[i]) { memset(f->raw[i],0,frames*sizeof(float)); if(f->next!=position)xtc_plan_reset_history(f->plans[i]); } } int xtc_track_fx_add(xtc_track_fx *f,size_t track,size_t frame,float sample) { if(!f || track>=XTC_PROJECT_MAX_TRACKS || !f->raw[track] || frame>=65536)return 0; f->raw[track][frame]+=sample;return 1; } xtc_result xtc_track_fx_finish(xtc_track_fx *f,const xtc_mix_snapshot *mix,uint64_t position,float *left,float *right,size_t frames,float *monitor,size_t stride) { size_t i,offset,j;if(!f)return XTC_OK; for(i=0;icount;++i)if(f->raw[i])for(offset=0;offsetplans[i],position+offset); if(r==XTC_OK)r=xtc_plan_process(f->plans[i],f->raw[i]+offset,f->scratch,n); if(r!=XTC_OK)return r; for(j=0;jscratch[j]); if(monitor)xtc_mix_monitor(mix,monitor,stride,i,position+frame,frame,sample,right!=NULL); left[frame]+=sample*xtc_mix_level(mix,i,position+frame,right ? 0U : 2U); if(right)right[frame]+=sample*xtc_mix_level(mix,i,position+frame,1); } } f->next=position+frames;return XTC_OK; }