Commit 99b9bb84 authored by Monty Montgomery's avatar Monty Montgomery

Full revamp of Singleband Compander signal path

Implement per-channel singleband compander panels



git-svn-id: https://svn.xiph.org/trunk/postfish@6589 0101bb08-14d6-0310-b084-bc0e0c8e3800
parent 1946be5d
......@@ -955,7 +955,7 @@ void mainpanel_create(postfish_mainpanel *panel,char **chlabels){
mainpanel_chentry(panel,channeltable,"Mute ",0,0,0,mutedummy_create);
mainpanel_chentry(panel,channeltable,"_Declip ",1,1,0,clippanel_create);
mainpanel_chentry(panel,channeltable,"_Multicomp ",2,0,1,compandpanel_create_channel);
mainpanel_chentry(panel,channeltable,"_Singlecomp ",3,0,1,0);
mainpanel_chentry(panel,channeltable,"_Singlecomp ",3,0,1,singlepanel_create_channel);
mainpanel_chentry(panel,channeltable,"De_verb ",4,1,0,suppresspanel_create_channel);
mainpanel_chentry(panel,channeltable,"_Reverb ",5,1,0,0);
mainpanel_chentry(panel,channeltable,"_EQ ",6,0,1,0);
......@@ -983,7 +983,7 @@ void mainpanel_create(postfish_mainpanel *panel,char **chlabels){
mainpanel_masterentry(panel,mastertable,"_Crossmix "," c ",GDK_c,0,0);
mainpanel_masterentry(panel,mastertable,"_Multicomp "," m ",GDK_m,1,compandpanel_create_master);
mainpanel_masterentry(panel,mastertable,"_Singlecomp "," s ",GDK_s,2,singlepanel_create);
mainpanel_masterentry(panel,mastertable,"_Singlecomp "," s ",GDK_s,2,singlepanel_create_master);
mainpanel_masterentry(panel,mastertable,"_Reverb "," r ",GDK_r,3,0);
mainpanel_masterentry(panel,mastertable,"_EQ "," e ",GDK_e,4,eqpanel_create);
mainpanel_masterentry(panel,mastertable,"_Limit "," l ",GDK_l,5,limitpanel_create);
......
......@@ -240,13 +240,20 @@ void *playback_thread(void *dummy){
result|=link->samples;
link=multicompand_read_channel(link);
result|=link->samples;
link=singlecomp_read_channel(link);
result|=link->samples;
link=suppress_read_channel(link);
result|=link->samples;
link=multicompand_read_master(link);
result|=link->samples;
link=singlecomp_read(link);
link=singlecomp_read_master(link);
result|=link->samples;
for(i=0;i<input_ch;i++)
if(mute_channel_muted(link->active,i))
memset(link->data[i],0,sizeof(**link->data)*input_size);
link=eq_read(link);
result|=link->samples;
......
......@@ -30,9 +30,6 @@ extern int input_size;
extern int input_rate;
extern int input_ch;
sig_atomic_t singlecomp_active;
sig_atomic_t singlecomp_visible;
typedef struct {
int loc;
float val;
......@@ -50,23 +47,35 @@ typedef struct{
peak_state *u_peak;
peak_state *b_peak;
iir_filter o_attack;
iir_filter o_decay;
iir_filter u_attack;
iir_filter u_decay;
iir_filter b_attack;
iir_filter b_decay;
iir_filter *o_attack;
iir_filter *o_decay;
iir_filter *u_attack;
iir_filter *u_decay;
iir_filter *b_attack;
iir_filter *b_decay;
int fillstate;
float **cache;
int cache_samples;
u_int32_t mutemask0;
int *activeP;
int *active0;
int mutemaskP;
int mutemask0;
} singlecomp_state;
singlecomp_settings scset;
singlecomp_state scs;
float *window;
singlecomp_settings singlecomp_master_set;
singlecomp_settings *singlecomp_channel_set;
static singlecomp_settings **master_set_bundle;
static singlecomp_settings **channel_set_bundle;
static singlecomp_state master_state;
static singlecomp_state channel_state;
/* feedback! */
typedef struct singlecomp_feedback{
......@@ -80,8 +89,8 @@ static feedback_generic *new_singlecomp_feedback(void){
return (feedback_generic *)ret;
}
int pull_singlecomp_feedback(float *peak,float *rms){
singlecomp_feedback *f=(singlecomp_feedback *)feedback_pull(&scs.feedpool);
static int pull_singlecomp_feedback(singlecomp_state *scs, float *peak,float *rms){
singlecomp_feedback *f=(singlecomp_feedback *)feedback_pull(&scs->feedpool);
if(!f)return 0;
......@@ -89,36 +98,75 @@ int pull_singlecomp_feedback(float *peak,float *rms){
memcpy(peak,f->peak,sizeof(*peak)*input_ch);
if(rms)
memcpy(rms,f->rms,sizeof(*rms)*input_ch);
feedback_old(&scs.feedpool,(feedback_generic *)f);
feedback_old(&scs->feedpool,(feedback_generic *)f);
return 1;
}
/* called only by initial setup */
int singlecomp_load(void){
int i;
memset(&scs,0,sizeof(scs));
scs.o_iir=calloc(input_ch,sizeof(*scs.o_iir));
scs.b_iir=calloc(input_ch,sizeof(*scs.b_iir));
scs.u_iir=calloc(input_ch,sizeof(*scs.u_iir));
int pull_singlecomp_feedback_master(float *peak,float *rms){
return pull_singlecomp_feedback(&master_state,peak,rms);
}
scs.o_peak=calloc(input_ch,sizeof(*scs.o_peak));
scs.b_peak=calloc(input_ch,sizeof(*scs.b_peak));
scs.u_peak=calloc(input_ch,sizeof(*scs.u_peak));
int pull_singlecomp_feedback_channel(float *peak,float *rms){
return pull_singlecomp_feedback(&channel_state,peak,rms);
}
scs.out.size=input_size;
scs.out.channels=input_ch;
scs.out.rate=input_rate;
scs.out.data=malloc(input_ch*sizeof(*scs.out.data));
static void singlecomp_load_helper(singlecomp_state *scs){
int i;
memset(scs,0,sizeof(scs));
scs->activeP=calloc(input_ch,sizeof(*scs->activeP));
scs->active0=calloc(input_ch,sizeof(*scs->active0));
scs->o_attack=calloc(input_ch,sizeof(*scs->o_attack));
scs->o_decay=calloc(input_ch,sizeof(*scs->o_decay));
scs->u_attack=calloc(input_ch,sizeof(*scs->u_attack));
scs->u_decay=calloc(input_ch,sizeof(*scs->u_decay));
scs->b_attack=calloc(input_ch,sizeof(*scs->b_attack));
scs->b_decay=calloc(input_ch,sizeof(*scs->b_decay));
scs->o_iir=calloc(input_ch,sizeof(*scs->o_iir));
scs->b_iir=calloc(input_ch,sizeof(*scs->b_iir));
scs->u_iir=calloc(input_ch,sizeof(*scs->u_iir));
scs->o_peak=calloc(input_ch,sizeof(*scs->o_peak));
scs->b_peak=calloc(input_ch,sizeof(*scs->b_peak));
scs->u_peak=calloc(input_ch,sizeof(*scs->u_peak));
scs->out.size=input_size;
scs->out.channels=input_ch;
scs->out.rate=input_rate;
scs->out.data=malloc(input_ch*sizeof(*scs->out.data));
for(i=0;i<input_ch;i++)
scs.out.data[i]=malloc(input_size*sizeof(**scs.out.data));
scs->out.data[i]=malloc(input_size*sizeof(**scs->out.data));
scs.fillstate=0;
scs.cache=malloc(input_ch*sizeof(*scs.cache));
scs->fillstate=0;
scs->cache=malloc(input_ch*sizeof(*scs->cache));
for(i=0;i<input_ch;i++)
scs.cache[i]=malloc(input_size*sizeof(**scs.cache));
scs->cache[i]=malloc(input_size*sizeof(**scs->cache));
}
return(0);
/* called only by initial setup */
int singlecomp_load(void){
int i;
singlecomp_load_helper(&master_state);
singlecomp_load_helper(&channel_state);
window=malloc(input_size/2*sizeof(*window));
for(i=0;i<input_size/2;i++){
window[i]=sin( (i+.5)/input_size*M_PIl);
window[i]*=window[i];
}
singlecomp_channel_set=calloc(input_ch,sizeof(*singlecomp_channel_set));
master_set_bundle=malloc(input_ch*sizeof(*master_set_bundle));
channel_set_bundle=malloc(input_ch*sizeof(*channel_set_bundle));
for(i=0;i<input_ch;i++){
master_set_bundle[i]=&singlecomp_master_set;
channel_set_bundle[i]=&singlecomp_channel_set[i];
}
return 0;
}
static void filter_set(float msec,
......@@ -139,18 +187,25 @@ static void filter_set(float msec,
filter->ms=msec;
}
static void reset_filter(singlecomp_state *scs){
memset(scs->o_peak,0,input_ch*sizeof(&scs->o_peak));
memset(scs->u_peak,0,input_ch*sizeof(&scs->u_peak));
memset(scs->b_peak,0,input_ch*sizeof(&scs->b_peak));
memset(scs->o_iir,0,input_ch*sizeof(&scs->o_iir));
memset(scs->u_iir,0,input_ch*sizeof(&scs->u_iir));
memset(scs->b_iir,0,input_ch*sizeof(&scs->b_iir));
}
/* called only in playback thread */
int singlecomp_reset(void ){
/* reset cached pipe state */
scs.fillstate=0;
while(pull_singlecomp_feedback(NULL,NULL));
memset(scs.o_peak,0,input_ch*sizeof(&scs.o_peak));
memset(scs.u_peak,0,input_ch*sizeof(&scs.u_peak));
memset(scs.b_peak,0,input_ch*sizeof(&scs.b_peak));
memset(scs.o_iir,0,input_ch*sizeof(&scs.o_iir));
memset(scs.u_iir,0,input_ch*sizeof(&scs.u_iir));
memset(scs.b_iir,0,input_ch*sizeof(&scs.b_iir));
master_state.fillstate=0;
channel_state.fillstate=0;
while(pull_singlecomp_feedback_master(NULL,NULL));
while(pull_singlecomp_feedback_channel(NULL,NULL));
reset_filter(&master_state);
reset_filter(&channel_state);
return 0;
}
......@@ -166,8 +221,8 @@ static void prepare_peak(float *peak, float *x, int n, int ahead,int hold,
if(loc==0 && val==0){
for(ii=0;ii<ahead;ii++)
if(fabs(x[ii])>val){
val=fabs(x[ii]);
if((x[ii]*x[ii])>val){
val=(x[ii]*x[ii]);
loc=ii+hold;
}
}
......@@ -175,18 +230,18 @@ static void prepare_peak(float *peak, float *x, int n, int ahead,int hold,
if(val>peak[0])peak[0]=val;
for(ii=1;ii<n;ii++){
if(fabs(x[ii+ahead])>val){
val=fabs(x[ii+ahead]);
if((x[ii+ahead]*x[ii+ahead])>val){
val=(x[ii+ahead]*x[ii+ahead]);
loc=ii+ahead+hold;
}
if(ii>=loc){
/* backfill */
val=0;
for(jj=ii+ahead-1;jj>=ii;jj--){
if(fabs(x[jj])>val)val=fabs(x[jj]);
if((x[jj]*x[jj])>val)val=(x[jj]*x[jj]);
if(jj<n && val>peak[jj])peak[jj]=val;
}
val=fabs(x[ii+ahead-1]);
val=(x[ii+ahead-1]*x[ii+ahead-1]);
loc=ii+ahead+hold;
}
if(val>peak[ii])peak[ii]=val;
......@@ -224,12 +279,8 @@ static void run_filter(float *cache, float *in, float *work,
compute_iir2(work, input_size, iir, attack, decay);
if(mode==0)
for(k=0;k<input_size;k++)
work[k]=todB_a(work+k)*.5f;
else
for(k=0;k<input_size;k++)
work[k]=todB_a(work+k);
for(k=0;k<input_size;k++)
work[k]=todB_a(work+k)*.5f;
}
static float soft_knee(float x){
......@@ -312,190 +363,285 @@ static void base_compand(float *A,float *B,float *adj,
}
time_linkage *singlecomp_read(time_linkage *in){
static void work_and_lapping(singlecomp_state *scs,
singlecomp_settings **scset,
time_linkage *in,
time_linkage *out,
int *active){
int i;
int have_feedback=0;
u_int32_t mutemaskC=in->active;
u_int32_t mutemask0=scs->mutemask0;
u_int32_t mutemaskP=scs->mutemaskP;
float peakfeed[input_ch];
float rmsfeed[input_ch];
memset(peakfeed,0,sizeof(peakfeed));
memset(rmsfeed,0,sizeof(rmsfeed));
for(i=0;i<input_ch;i++){
int activeC= active[i] && !mute_channel_muted(mutemaskC,i);
int active0= scs->active0[i];
int activeP= scs->activeP[i];
int mutedC=mute_channel_muted(mutemaskC,i);
int muted0=mute_channel_muted(mutemask0,i);
int mutedP=mute_channel_muted(mutemaskP,i);
float o_attackms=scset[i]->o_attack*.1;
float o_decayms=scset[i]->o_decay*.1;
float u_attackms=scset[i]->u_attack*.1;
float u_decayms=scset[i]->u_decay*.1;
float b_attackms=scset[i]->b_attack*.1;
float b_decayms=scset[i]->b_decay*.1;
if(o_attackms!=scs->o_attack[i].ms)filter_set(o_attackms,&scs->o_attack[i],1);
if(o_decayms!=scs->o_decay[i].ms)filter_set(o_decayms,&scs->o_decay[i],0);
if(u_attackms!=scs->u_attack[i].ms)filter_set(u_attackms,&scs->u_attack[i],1);
if(u_decayms!=scs->u_decay[i].ms)filter_set(u_decayms,&scs->u_decay[i],0);
if(b_attackms!=scs->b_attack[i].ms)filter_set(b_attackms,&scs->b_attack[i],1);
if(b_decayms!=scs->b_decay[i].ms)filter_set(b_decayms,&scs->b_decay[i],0);
if(!active0 && !activeC){
int active=singlecomp_active;
int i;
if(activeP) reset_filter(scs); /* just became inactive; reset all filters */
float o_attackms=scset.o_attack*.1;
float o_decayms=scset.o_decay*.1;
float u_attackms=scset.u_attack*.1;
float u_decayms=scset.u_decay*.1;
float b_attackms=scset.b_attack*.1;
float b_decayms=scset.b_decay*.1;
if(o_attackms!=scs.o_attack.ms)filter_set(o_attackms,&scs.o_attack,1);
if(o_decayms!=scs.o_decay.ms)filter_set(o_decayms,&scs.o_decay,0);
if(u_attackms!=scs.u_attack.ms)filter_set(u_attackms,&scs.u_attack,1);
if(u_decayms!=scs.u_decay.ms)filter_set(u_decayms,&scs.u_decay,0);
if(b_attackms!=scs.b_attack.ms)filter_set(b_attackms,&scs.b_attack,1);
if(b_decayms!=scs.b_decay.ms)filter_set(b_decayms,&scs.b_decay,0);
switch(scs.fillstate){
case 0: /* prime the cache */
if(in->samples==0){
scs.out.samples=0;
return &scs.out;
}
scs.mutemask0=in->active;
/* feedabck */
if(scset[i]->panel_visible){
int k;
float rms=0.;
float peak=0.;
float *x=scs->cache[i];
have_feedback=1;
if(!muted0){
for(k=0;k<input_size;k++){
float val=x[k]*x[k];
rms+= val;
if(peak<val)peak=val;
}
}
peakfeed[i]=todB_a(&peak)*.5;
rms/=input_size;
rmsfeed[i]=todB_a(&rms)*.5;
}
for(i=0;i<input_ch;i++){
float *temp=in->data[i];
float adj[input_size]; // under will set it
/* rotate data vectors */
if(out){
float *temp=out->data[i];
out->data[i]=scs->cache[i];
scs->cache[i]=temp;
}
memset(scs.o_iir+i,0,sizeof(*scs.o_iir));
memset(scs.u_iir+i,0,sizeof(*scs.u_iir));
memset(scs.b_iir+i,0,sizeof(*scs.b_iir));
memset(scs.cache[i],0,sizeof(**scs.cache)*input_size);
under_compand(scs.cache[i],in->data[i],adj,
(float)(scset.u_thresh),
1.-1./(scset.u_ratio/1000.),
scset.u_lookahead/1000.,
scset.u_mode,
scset.u_softknee,
&scs.u_attack,&scs.u_decay,
scs.u_iir+i,scs.u_peak+i,
active);
over_compand(scs.cache[i],in->data[i],adj,
(float)(scset.o_thresh),
1.-1./(scset.o_ratio/1000.),
scset.o_lookahead/1000.,
scset.o_mode,
scset.o_softknee,
&scs.o_attack,&scs.o_decay,
scs.o_iir+i,scs.o_peak+i,
active);
base_compand(scs.cache[i],in->data[i],adj,
1.-1./(scset.b_ratio/1000.),
scset.b_mode,
&scs.b_attack,&scs.b_decay,
scs.b_iir+i,scs.b_peak+i,
active);
in->data[i]=scs.cache[i];
scs.cache[i]=temp;
}
scs.cache_samples=in->samples;
scs.fillstate=1;
scs.out.samples=0;
if(in->samples==in->size)goto tidy_up;
for(i=0;i<input_ch;i++)
memset(in->data[i],0,sizeof(**in->data)*in->size);
in->samples=0;
/* fall through */
case 1: /* nominal processing */
}else if(active0 || activeC){
for(i=0;i<input_ch;i++){
/* run the filters */
float adj[input_size]; // under will set it
under_compand(scs.cache[i],in->data[i],adj,
(float)(scset.u_thresh),
1.-1./(scset.u_ratio/1000.),
scset.u_lookahead/1000.,
scset.u_mode,
scset.u_softknee,
&scs.u_attack,&scs.u_decay,
scs.u_iir+i,scs.u_peak+i,
active);
under_compand(scs->cache[i],in->data[i],adj,
(float)(scset[i]->u_thresh),
1.-1000./scset[i]->u_ratio,
scset[i]->u_lookahead/1000.,
scset[i]->u_mode,
scset[i]->u_softknee,
scs->u_attack+i,scs->u_decay+i,
scs->u_iir+i,scs->u_peak+i,
active0);
over_compand(scs->cache[i],in->data[i],adj,
(float)(scset[i]->o_thresh),
1.-1000./scset[i]->o_ratio,
scset[i]->o_lookahead/1000.,
scset[i]->o_mode,
scset[i]->o_softknee,
scs->o_attack+i,scs->o_decay+i,
scs->o_iir+i,scs->o_peak+i,
active0);
over_compand(scs.cache[i],in->data[i],adj,
(float)(scset.o_thresh),
1.-1./(scset.o_ratio/1000.),
scset.o_lookahead/1000.,
scset.o_mode,
scset.o_softknee,
&scs.o_attack,&scs.o_decay,
scs.o_iir+i,scs.o_peak+i,
active);
/* feedback before base */
{
if(scset[i]->panel_visible){
int k;
float rms=0.;
float peak=0.;
float *x=scs.cache[i];
float *x=scs->cache[i];
have_feedback=1;
for(k=0;k<input_size;k++){
float mul=fromdB_a(adj[k]);
float val=x[k]*mul;
if(!muted0){
for(k=0;k<input_size;k++){
float mul=fromdB_a(adj[k]);
float val=x[k]*mul;
val*=val;
rms+= val;
if(peak<val)peak=val;
val*=val;
rms+= val;
if(peak<val)peak=val;
}
}
peakfeed[i]=todB_a(&peak)*.5;
rms/=input_size;
rmsfeed[i]=todB_a(&rms)*.5;
}
base_compand(scs.cache[i],in->data[i],adj,
1.-1./(scset.b_ratio/1000.),
scset.b_mode,
&scs.b_attack,&scs.b_decay,
scs.b_iir+i,scs.b_peak+i,
active);
if(active){
base_compand(scs->cache[i],in->data[i],adj,
1.-1000./scset[i]->b_ratio,
scset[i]->b_mode,
scs->b_attack+i,scs->b_decay+i,
scs->b_iir+i,scs->b_peak+i,
active0);
if(active0 && out){
/* current frame should be manipulated; render into out,
handle transitioning after */
int k;
float *x=scs.cache[i];
float *out=scs.out.data[i];
float *ix=scs->cache[i];
float *ox=out->data[i];
for(k=0;k<input_size;k++)
out[k]=x[k]*fromdB_a(adj[k]);
}else
memcpy(scs.out.data[i],scs.cache[i],input_size*sizeof(*scs.cache[i]));
{
float *temp=scs.cache[i];
scs.cache[i]=in->data[i];
in->data[i]=temp;
ox[k]=ix[k]*fromdB_a(adj[k]);
/* is this frame preceeded/followed by an 'inactive' or muted frame?
If so, smooth the transition */
if(!activeP){
if(mutedP){
for(k=0;k<input_size/2;k++)
ox[k]*=window[k];
}else{
for(k=0;k<input_size/2;k++){
float w=window[k];
ox[k]= ox[k]*w + ix[k]*(1.-w);
}
}
}
if(!activeC){
float *cox=ox+input_size/2;
if(mutedC){
for(k=0;k<input_size/2;k++){
float w=1.-window[k];
cox[k]*=w;
}
}else{
float *cix=ix+input_size/2;
for(k=0;k<input_size/2;k++){
float w=window[k];
cox[k]= cox[k]*(1.-w) + cix[k]*w;
}
}
}
}else if(out){
float *temp=out->data[i];
out->data[i]=scs->cache[i];
scs->cache[i]=temp;
}
}
scs.out.samples=scs.cache_samples;
scs.cache_samples=in->samples;
if(scs.out.samples<scs.out.size)scs.fillstate=2;
break;
case 2: /* we've pushed out EOF already */
scs.out.samples=0;
{
float *temp=scs->cache[i];
scs->cache[i]=in->data[i];
in->data[i]=temp;
}
scs->activeP[i]=active0;
scs->active0[i]=activeC;
}
/* finish up the state feedabck */
{
if(out){
/* feedback is also triggered off of output */
singlecomp_feedback *ff=
(singlecomp_feedback *)feedback_new(&scs.feedpool,new_singlecomp_feedback);
(singlecomp_feedback *)feedback_new(&scs->feedpool,new_singlecomp_feedback);
if(!ff->peak)
ff->peak=malloc(input_ch*sizeof(*ff->peak));
if(!ff->rms)
ff->rms=malloc(input_ch*sizeof(*ff->rms));
memcpy(ff->peak,peakfeed,sizeof(peakfeed));
memcpy(ff->rms,rmsfeed,sizeof(rmsfeed));
feedback_push(&scs->feedpool,(feedback_generic *)ff);
feedback_push(&scs.feedpool,(feedback_generic *)ff);
out->active=mutemask0;
out->samples=scs->cache_samples;
}
scs->cache_samples=in->samples;
scs->mutemaskP=mutemask0;
scs->mutemask0=mutemaskC;
}
time_linkage *singlecomp_read_helper(time_linkage *in,
singlecomp_state *scs,
singlecomp_settings **scset,
int *active){
int i;
switch(scs->fillstate){
case 0: /* prime the cache */
if(in->samples==0){
scs->out.samples=0;
return &scs->out;
}
for(i=0;i<input_ch;i++){
memset(scs->o_iir+i,0,sizeof(*scs->o_iir));
memset(scs->u_iir+i,0,sizeof(*scs->u_iir));
memset(scs->b_iir+i,0,sizeof(*scs->b_iir));
memset(scs->o_peak+i,0,sizeof(*scs->o_peak));
memset(scs->u_peak+i,0,sizeof(*scs->u_peak));
memset(scs->b_peak+i,0,sizeof(*scs->b_peak));
memset(scs->cache[i],0,sizeof(**scs->cache)*input_size);
scs->activeP[i]=scs->active0[i]=active[i];
}
scs->mutemaskP=scs->mutemask0=in->active;
work_and_lapping(scs,scset,in,0,active);
scs->fillstate=1;
scs->out.samples=0;