101 lines
4.6 KiB
C
101 lines
4.6 KiB
C
|
|
#include "track_fx.h"
|
||
|
|
#include "../project/project_internal.h"
|
||
|
|
#include "xtc/xtc_dsp.h"
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
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 && i<c->count;++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;i<XTC_PROJECT_MAX_TRACKS;++i){xtc_plan_destroy(f->plans[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;i<p->track_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 && i<p->track_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;j<p->clip_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;i<XTC_PROJECT_MAX_TRACKS;++i)if(f->raw[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;i<mix->count;++i)if(f->raw[i])for(offset=0;offset<frames;offset+=256) {
|
||
|
|
size_t n=frames-offset<256 ? frames-offset : 256;xtc_result r=xtc_plan_position(f->plans[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;j<n;++j) {
|
||
|
|
size_t frame=offset+j;float sample=xtc_dsp_finite(f->scratch[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;
|
||
|
|
}
|