void compute_curve(VorbisPsy *psy, float *audio, float *curve) { int i; float work[psy->n]; float scale=4.f/psy->n; float scale_dB; scale_dB=todB(scale); /* window the PCM data; use a BH4 window, not vorbis */ for(i=0;i<psy->n;i++) work[i]=audio[i] * psy->window[i]; { static int seq=0; //_analysis_output("win",seq,work,psy->n,0,0); seq++; } /* FFT yields more accurate tonal estimation (not phase sensitive) */ spx_drft_forward(&psy->lookup,work); /* magnitudes */ work[0]=scale_dB+todB(work[0]); for(i=1;i<psy->n-1;i+=2){ float temp = work[i]*work[i] + work[i+1]*work[i+1]; work[(i+1)>>1] = scale_dB+.5f * todB(temp); } /* derive a noise curve */ _vp_noisemask(psy,work,curve); #define SIDEL 12 for (i=0;i<SIDEL;i++) { curve[i]=curve[SIDEL]; } #define SIDEH 12 for (i=0;i<SIDEH;i++) { curve[(psy->n>>1)-i-1]=curve[(psy->n>>1)-SIDEH]; } for(i=0;i<((psy->n)>>1);i++) curve[i] = fromdB(1.2*curve[i]+.2*i); //curve[i] = fromdB(0.8*curve[i]+.35*i); //curve[i] = fromdB(0.9*curve[i])*pow(1.0*i+45,1.3); }
/* called only by playback thread */ time_linkage *mix_read(time_linkage *in, time_linkage *inA, // reverb channel time_linkage *inB){ // reverb channel int i,j,k,outch=ms.out.channels; int outactive[outch]; float peak[MIX_BLOCKS+5][input_ch]; float rms[MIX_BLOCKS+5][input_ch]; int bypass=1; if(in->samples==0){ ms.out.samples=0; return &ms.out; } memset(outactive,0,sizeof(outactive)); memset(peak,0,sizeof(peak)); memset(rms,0,sizeof(rms)); /* eliminate asynch change possibility */ memcpy(ms.curr,mix_set,sizeof(*mix_set)*input_ch); /* fillstate here is only used for lazy initialization/reset */ if(ms.fillstate==0){ /* zero the cache */ for(i=0;i<input_ch;i++){ memset(ms.cacheP[i],0,sizeof(**ms.cacheP)*input_size); memset(ms.cachePP[i],0,sizeof(**ms.cachePP)*input_size); memset(ms.cachePA[i],0,sizeof(**ms.cachePA)*input_size); memset(ms.cachePPA[i],0,sizeof(**ms.cachePPA)*input_size); memset(ms.cachePB[i],0,sizeof(**ms.cachePB)*input_size); memset(ms.cachePPB[i],0,sizeof(**ms.cachePPB)*input_size); } memcpy(ms.prev,ms.curr,sizeof(*mix_set)*input_ch); ms.fillstate=1; } /* zero the output block; we'll me mixing into it input-by-input */ for(i=0;i<outch;i++) memset(ms.out.data[i],0,sizeof(**ms.out.data)*input_size); /* a bit of laziness that may actually save CPU time by avoiding special-cases later */ for(i=0;i<input_ch;i++){ if(mute_channel_muted(in->active,i)) memset(in->data[i],0,sizeof(**in->data)*input_size); if(mute_channel_muted(inA->active,i)) memset(inA->data[i],0,sizeof(**inA->data)*input_size); if(mute_channel_muted(inB->active,i)) memset(inB->data[i],0,sizeof(**inB->data)*input_size); } /* input-by-input */ for(i=0;i<input_ch;i++){ int feedit=mixpanel_visible[i]; int feeditM=atten_visible; /* master feedback is a bit of a pain; the metrics we need aren't produced by any of the mixdowns below. Do it by hand */ if(feeditM){ float mix[input_size]; float att=fromdB(ms.curr[i].master_att * .1); int del=rint(ms.curr[i].master_delay*.00001*input_rate); float acc=0.; if(!mute_channel_muted(in->active,i)){ memset(mix,0,sizeof(mix)); mixwork(in->data[i],ms.cacheP[i],ms.cachePP[i], mix,att,del,0,att,del,0); bypass=0; for(j=0;j<input_size;j++){ float val=mix[j]*mix[j]; if(val>peak[0][i])peak[0][i]=val; acc+=val; } rms[0][i]=acc/input_size; } acc=0.; if(inA && !mute_channel_muted(inA->active,i)){ memset(mix,0,sizeof(mix)); mixwork(inA->data[i],ms.cachePA[i],ms.cachePPA[i], mix,att,del,0,att,del,0); bypass=0; for(j=0;j<input_size;j++){ float val=mix[j]*mix[j]; if(val>peak[1][i])peak[1][i]=val; acc+=val; } rms[1][i]=acc/input_size; } acc=0.; if(inB && !mute_channel_muted(inB->active,i)){ memset(mix,0,sizeof(mix)); mixwork(inB->data[i],ms.cachePB[i],ms.cachePPB[i], mix,att,del,0,att,del,0); bypass=0; for(j=0;j<input_size;j++){ float val=mix[j]*mix[j]; if(val>peak[2][i])peak[2][i]=val; acc+=val; } rms[2][i]=acc/input_size; } } /* placer settings; translate to final numbers */ int placer=ms.curr[i].placer_place; int placerP=ms.prev[i].placer_place; /* place mix */ { int mixedA=0,mixedB=0; float mixA[input_size],mixB[input_size]; for(j=0;j<OUTPUT_CHANNELS;j++){ int destA=ms.curr[i].placer_destA[j]; int destAP=ms.prev[i].placer_destA[j]; int destB=ms.curr[i].placer_destB[j]; int destBP=ms.prev[i].placer_destB[j]; if(destA || destAP){ outactive[j]=1; if(!mixedA){ float relA=(placer>100 ? placer*.01-1. : 0.); float relAP=(placerP>100 ? placerP*.01-1. : 0.); float attA=fromdB((ms.curr[i].master_att + ms.curr[i].placer_att * relA)*.1); float attAP=fromdB((ms.prev[i].master_att + ms.prev[i].placer_att * relAP)*.1); int delA=rint((ms.curr[i].master_delay + ms.curr[i].placer_delay * relA)*.00001*input_rate); int delAP=rint((ms.prev[i].master_delay + ms.prev[i].placer_delay * relAP)*.00001*input_rate); float attA_r=fromdB(ms.curr[i].master_att*.1); float attAP_r=fromdB(ms.prev[i].master_att*.1); int delA_r=rint(ms.curr[i].master_delay*.00001*input_rate); int delAP_r=rint(ms.prev[i].master_delay*.00001*input_rate); memset(mixA,0,sizeof(mixA)); mixwork(in->data[i],ms.cacheP[i],ms.cachePP[i], mixA,attA,delA,0,attAP,delAP,0); mixwork(inA->data[i],ms.cachePA[i],ms.cachePPA[i], mixA,attA_r,delA_r,0,attAP_r,delAP_r,0); mixedA=1; } mixadd(mixA,ms.out.data[j],destA,destAP); } if(destB || destBP){ outactive[j]=1; if(!mixedB){ float relB=(placer<100 ? 1.-placer*.01 : 0.); float relBP=(placerP<100 ? 1.-placerP*.01 : 0.); float attB=fromdB((ms.curr[i].master_att + ms.curr[i].placer_att * relB)*.1); float attBP=fromdB((ms.prev[i].master_att + ms.prev[i].placer_att * relBP)*.1); int delB=rint((ms.curr[i].master_delay + ms.curr[i].placer_delay * relB)*.00001*input_rate); int delBP= rint((ms.prev[i].master_delay + ms.prev[i].placer_delay * relBP)*.00001*input_rate); float attB_r=fromdB(ms.curr[i].master_att*.1); float attBP_r=fromdB(ms.prev[i].master_att*.1); int delB_r=rint(ms.curr[i].master_delay*.00001*input_rate); int delBP_r= rint(ms.prev[i].master_delay*.00001*input_rate); memset(mixB,0,sizeof(mixB)); mixwork(in->data[i],ms.cacheP[i],ms.cachePP[i], mixB,attB,delB,0,attBP,delBP,0); mixwork(inB->data[i],ms.cachePB[i],ms.cachePPB[i], mixB,attB_r,delB_r,0,attBP_r,delBP_r,0); mixedB=1; } mixadd(mixB,ms.out.data[j],destB,destBP); } } /* feedback for A */ if(feedit){ float acc=0.; bypass=0; if(mixedA){ for(j=0;j<input_size;j++){ float val=mixA[j]*mixA[j]; if(val>peak[3][i])peak[3][i]=val; acc+=val; } peak[3][i]=peak[3][i]; rms[3][i]=acc/input_size; } } /* feedback for B */ if(feedit){ float acc=0.; bypass=0; if(mixedB){ for(j=0;j<input_size;j++){ float val=mixB[j]*mixB[j]; if(val>peak[4][i])peak[4][i]=val; acc+=val; } peak[4][i]=peak[4][i]; rms[4][i]=acc/input_size; } } } /* direct block mix */ for(k=0;k<MIX_BLOCKS;k++){ float mix[input_size]; int sourceM=ms.curr[i].insert_source[k][0]; int sourceMP=ms.prev[i].insert_source[k][0]; int sourceA=ms.curr[i].insert_source[k][1]; int sourceAP=ms.prev[i].insert_source[k][1]; int sourceB=ms.curr[i].insert_source[k][2]; int sourceBP=ms.prev[i].insert_source[k][2]; float att= fromdB((ms.curr[i].master_att + ms.curr[i].insert_att[k])*.1); int del= rint((ms.curr[i].master_delay + ms.curr[i].insert_delay[k])*.00001*input_rate); float attP= fromdB((ms.prev[i].master_att + ms.prev[i].insert_att[k])*.1); int delP= rint((ms.prev[i].master_delay + ms.prev[i].insert_delay[k])*.00001*input_rate); if(sourceM || sourceMP || sourceA || sourceAP || sourceB || sourceBP){ memset(mix,0,sizeof(mix)); /* master */ if(sourceM || sourceMP) mixwork(in->data[i],ms.cacheP[i],ms.cachePP[i], mix, (sourceM ? att : 0), del,ms.curr[i].insert_invert[k], (sourceMP ? attP : 0), delP,ms.prev[i].insert_invert[k]); /* reverbA */ if(sourceA || sourceAP) if(inA) mixwork(inA->data[i],ms.cachePA[i],ms.cachePPA[i], mix, (sourceA ? att : 0), del,ms.curr[i].insert_invert[k], (sourceAP ? attP : 0), delP,ms.prev[i].insert_invert[k]); /* reverbB */ if(sourceB || sourceBP) if(inB) mixwork(inB->data[i],ms.cachePB[i],ms.cachePPB[i], mix, (sourceB ? att : 0), del,ms.curr[i].insert_invert[k], (sourceBP ? attP : 0), delP,ms.prev[i].insert_invert[k]); /* mix into output */ for(j=0;j<OUTPUT_CHANNELS;j++){ int dest=ms.curr[i].insert_dest[k][j]; int destP=ms.prev[i].insert_dest[k][j]; if(dest || destP){ outactive[j]=1; mixadd(mix,ms.out.data[j],dest,destP); } } /* feedback */ if(feedit){ float acc=0.; bypass=0; for(j=0;j<input_size;j++){ float val=mix[j]*mix[j]; if(val>peak[5+k][i])peak[5+k][i]=val; acc+=val; } peak[5+k][i]=peak[5+k][i]; rms[5+k][i]=acc/input_size; } } } /* rotate data cache */ { float *temp=ms.cachePP[i]; ms.cachePP[i]=ms.cacheP[i]; ms.cacheP[i]=in->data[i]; in->data[i]=temp; if(inA){ temp=ms.cachePPA[i]; ms.cachePPA[i]=ms.cachePA[i]; ms.cachePA[i]=inA->data[i]; inA->data[i]=temp; } if(inB){ temp=ms.cachePPB[i]; ms.cachePPB[i]=ms.cachePB[i]; ms.cachePB[i]=inB->data[i]; inB->data[i]=temp; } } } /* finish output data */ ms.out.samples=in->samples; ms.out.active=0; for(i=0;i<OUTPUT_CHANNELS;i++) if(outactive[i]) ms.out.active|=(1<<i); /* rotate settings cache */ { mix_settings *temp=ms.curr; ms.curr=ms.prev; ms.prev=temp; } /* push feedback */ if(bypass){ mix_feedback *mf= (mix_feedback *)feedback_new(&ms.feedpool,new_mix_feedback); mf->bypass=1; feedback_push(&ms.feedpool,(feedback_generic *)mf); }else{ mix_feedback *mf= (mix_feedback *)feedback_new(&ms.feedpool,new_mix_feedback); if(!mf->peak){ mf->peak=malloc((MIX_BLOCKS+5)*sizeof(*mf->peak)); mf->rms=malloc((MIX_BLOCKS+5)*sizeof(*mf->rms)); for(i=0;i<MIX_BLOCKS+5;i++) mf->rms[i]=malloc(input_ch*sizeof(**mf->rms)); for(i=0;i<MIX_BLOCKS+5;i++) mf->peak[i]=malloc(input_ch*sizeof(**mf->peak)); } for(i=0;i<MIX_BLOCKS+5;i++){ memcpy(mf->peak[i],peak[i],input_ch*sizeof(**peak)); memcpy(mf->rms[i],rms[i],input_ch*sizeof(**rms)); } mf->bypass=0; feedback_push(&ms.feedpool,(feedback_generic *)mf); } return &ms.out; }