int WDL_ConvolutionEngine::SetImpulse(WDL_ImpulseBuffer *impulse, int fft_size, int impulse_sample_offset, int max_imp_size, bool forceBrute) { int impulse_len=0; int x; int nch=impulse->GetNumChannels(); for (x = 0; x < nch; x ++) { int l=impulse->impulses[x].GetSize()-impulse_sample_offset; if (max_imp_size && l>max_imp_size) l=max_imp_size; if (impulse_len < l) impulse_len=l; } m_impulse_nch=nch; if (m_impulse_nch>1) // detect mono signals pretending to be multichannel { for (x = 1; x < m_impulse_nch; x ++) { if (impulse->impulses[x].GetSize()!=impulse->impulses[0].GetSize()|| memcmp(impulse->impulses[x].Get(),impulse->impulses[0].Get(), impulse->impulses[0].GetSize()*sizeof(WDL_FFT_REAL))) break; } if (x >= m_impulse_nch) m_impulse_nch=1; } m_impulse_len=impulse_len; m_proc_nch=-1; if (forceBrute) { m_fft_size=0; // save impulse for (x = 0; x < m_impulse_nch; x ++) { WDL_FFT_REAL *imp=impulse->impulses[x].Get()+impulse_sample_offset; int lenout=impulse->impulses[x].GetSize()-impulse_sample_offset; if (max_imp_size && lenout>max_imp_size) lenout=max_imp_size; WDL_CONVO_IMPULSEBUFf *impout=m_impulse[x].Resize(lenout)+lenout; while (lenout-->0) *--impout = (WDL_CONVO_IMPULSEBUFf) *imp++; } for (x = 0; x < WDL_CONVO_MAX_PROC_NCH; x ++) { m_samplesout_delay[x]=0; m_samplesin[x].Clear(); m_samplesin2[x].Clear(); m_samplesout[x].Clear(); } return 0; } if (fft_size<=0) { int msz=fft_size<=-16? -fft_size*2 : 32768; fft_size=32; while (fft_size < impulse_len*2 && fft_size < msz) fft_size*=2; } m_fft_size=fft_size; int impchunksize=fft_size/2; int nblocks=(impulse_len+impchunksize-1)/impchunksize; //char buf[512]; //sprintf(buf,"il=%d, ffts=%d, cs=%d, nb=%d\n",impulse_len,fft_size,impchunksize,nblocks); //OutputDebugString(buf); const bool smallerSizeMode=sizeof(WDL_CONVO_IMPULSEBUFf)!=sizeof(WDL_FFT_REAL); WDL_FFT_REAL scale=(WDL_FFT_REAL) (1.0/fft_size); for (x = 0; x < m_impulse_nch; x ++) { WDL_FFT_REAL *imp=impulse->impulses[x].Get()+impulse_sample_offset; WDL_FFT_REAL *imp2=x < m_impulse_nch-1 ? impulse->impulses[x+1].Get()+impulse_sample_offset : NULL; WDL_CONVO_IMPULSEBUFf *impout=m_impulse[x].Resize((nblocks+!!smallerSizeMode)*fft_size*2); char *zbuf=m_impulse_zflag[x].Resize(nblocks); int lenout=impulse->impulses[x].GetSize()-impulse_sample_offset; if (max_imp_size && lenout>max_imp_size) lenout=max_imp_size; int bl; for (bl = 0; bl < nblocks; bl ++) { int thissz=lenout; if (thissz > impchunksize) thissz=impchunksize; lenout -= thissz; int i=0; WDL_FFT_REAL mv=0.0; WDL_FFT_REAL mv2=0.0; WDL_FFT_REAL *imptmp = (WDL_FFT_REAL *)impout; for (; i < thissz; i ++) { WDL_FFT_REAL v=*imp++; WDL_FFT_REAL v2=(WDL_FFT_REAL)fabs(v); if (v2 > mv) mv=v2; imptmp[i*2]=v * scale; if (imp2) { v=*imp2++; v2=(WDL_FFT_REAL)fabs(v); if (v2>mv2) mv2=v2; imptmp[i*2+1]=v*scale; } else imptmp[i*2+1]=0.0; } for (; i < fft_size; i ++) { imptmp[i*2]=0.0; imptmp[i*2+1]=0.0; } if (mv>1.0e-14||mv2>1.0e-14) { *zbuf++=mv>1.0e-14 ? 2 : 1; // 1 means only second channel has content WDL_fft((WDL_FFT_COMPLEX*)impout,fft_size,0); if (smallerSizeMode) { int x,n=fft_size*2; for(x=0;x<n;x++) impout[x]=(WDL_CONVO_IMPULSEBUFf)imptmp[x]; } } else *zbuf++=0; impout+=fft_size*2; } } return m_fft_size/2; }
int WDL_ConvolutionEngine::Avail(int want) { if (m_fft_size<1) { return m_samplesout[0].Available()/sizeof(WDL_FFT_REAL); } int chunksize=m_fft_size/2; int nblocks=(m_impulse_len+chunksize-1)/chunksize; // clear combining buffer WDL_FFT_REAL *workbuf2 = m_combinebuf.Resize(m_fft_size*4); // temp space int ch=0; int sz=m_fft_size/2; for (ch = 0; ch < m_proc_nch; ch ++) { if (!m_samplehist[ch].GetSize()||!m_overlaphist[ch].GetSize()) continue; int srcc=ch; if (srcc>=m_impulse_nch) srcc=m_impulse_nch-1; bool allow_mono_input_mode=true; bool mono_impulse_mode=false; if (m_impulse_nch==1 && ch<m_proc_nch-1 && m_samplehist[ch+1].GetSize()&&m_overlaphist[ch+1].GetSize() && m_samplesin[ch].Available()==m_samplesin[ch+1].Available() && m_samplesout[ch].Available()==m_samplesout[ch+1].Available() ) { // 2x processing mode mono_impulse_mode=true; allow_mono_input_mode=false; } int in_needed=sz; // if on an odd channel, make sure we delay this channel a bit so that the FFTs are more evenly distribyted // if we comment out this line we can always disable this behavior if ((ch&1) && m_samplesout_delay[ch] < sz/2) in_needed = sz/2-m_samplesout_delay[ch]; // useSilentList[x] = 1 for mono signal, 2 for stereo, 0 for silent char *useSilentList=m_samplehist_zflag[ch].GetSize()==nblocks ? m_samplehist_zflag[ch].Get() : NULL; while (m_samplesin[ch].Available()/(int)sizeof(WDL_FFT_REAL) >= in_needed && m_samplesout[ch].Available() < (want+m_samplesout_delay[ch])*(int)sizeof(WDL_FFT_REAL)) { int histpos; if ((histpos=++m_hist_pos[ch]) >= nblocks) histpos=m_hist_pos[ch]=0; // get samples from input, to history WDL_FFT_REAL *optr = m_samplehist[ch].Get()+histpos*m_fft_size*2; if (in_needed<sz) { memset(optr+sz,0,(sz-in_needed)*sizeof(WDL_FFT_REAL)); m_samplesin[ch].GetToBuf(0,optr+sz+sz-in_needed,in_needed*sizeof(WDL_FFT_REAL)); m_samplesout_delay[ch] += (sz-in_needed); } else m_samplesin[ch].GetToBuf(0,optr+sz,in_needed*sizeof(WDL_FFT_REAL)); m_samplesin[ch].Advance(in_needed*sizeof(WDL_FFT_REAL)); in_needed=sz; bool mono_input_mode=false; bool nonzflag=false; if (mono_impulse_mode) { if (++m_hist_pos[ch+1] >= nblocks) m_hist_pos[ch+1]=0; m_samplesin[ch+1].GetToBuf(0,workbuf2,sz*sizeof(WDL_FFT_REAL)); m_samplesin[ch+1].Advance(sz*sizeof(WDL_FFT_REAL)); int i; for (i = 0; i < sz; i ++) // unpack samples { WDL_FFT_REAL f = optr[i*2]=denormal_filter_aggressive(optr[sz+i]); if (!nonzflag && (f<-1.0e-6 || f>1.0e-6)) nonzflag=true; f=optr[i*2+1]=denormal_filter_aggressive(workbuf2[i]); if (!nonzflag && (f<-1.0e-6 || f>1.0e-6)) nonzflag=true; } } else { if (allow_mono_input_mode && ch < m_proc_nch-1 && srcc<m_impulse_nch-1 && !CompareQueueToBuf(&m_samplesin[ch+1],optr+sz,sz*sizeof(WDL_FFT_REAL)) ) { mono_input_mode=true; } else allow_mono_input_mode=false; int i; for (i = 0; i < sz; i ++) // unpack samples { WDL_FFT_REAL f=optr[i*2]=denormal_filter_aggressive(optr[sz+i]); optr[i*2+1]=0.0; if (!nonzflag && (f<-1.0e-6 || f>1.0e-6)) nonzflag=true; } } int i; for (i = 1; mono_input_mode && i < nblocks; i ++) // start @ 1, since hist[histpos] is no longer used for here { int srchistpos = histpos-i; if (srchistpos < 0) srchistpos += nblocks; if (useSilentList[srchistpos]==2) mono_input_mode=false; } if (nonzflag||!useSilentList) memset(optr+sz*2,0,sz*2*sizeof(WDL_FFT_REAL)); #ifdef WDLCONVO_ZL_ACCOUNTING m_zl_fftcnt++; #endif if (nonzflag) WDL_fft((WDL_FFT_COMPLEX*)optr,m_fft_size,0); if (useSilentList) useSilentList[histpos]=nonzflag ? (mono_input_mode ? 1 : 2) : 0; int mzfl=2; if (mono_input_mode) { mzfl=1; m_samplesin[ch+1].Advance(sz*sizeof(WDL_FFT_REAL)); // save a valid copy in sample hist incase we switch from mono to stereo if (++m_hist_pos[ch+1] >= nblocks) m_hist_pos[ch+1]=0; WDL_FFT_REAL *optr2 = m_samplehist[ch+1].Get()+m_hist_pos[ch+1]*m_fft_size*2; memcpy(optr2,optr,m_fft_size*2*sizeof(WDL_FFT_REAL)); } int applycnt=0; char *useImpSilentList=m_impulse_zflag[srcc].GetSize() == nblocks ? m_impulse_zflag[srcc].Get() : NULL; WDL_CONVO_IMPULSEBUFf *impulseptr=m_impulse[srcc].Get(); for (i = 0; i < nblocks; i ++, impulseptr+=m_fft_size*2) { int srchistpos = histpos-i; if (srchistpos < 0) srchistpos += nblocks; if (useImpSilentList && useImpSilentList[i]<mzfl) continue; if (useSilentList && !useSilentList[srchistpos]) continue; // silent block WDL_FFT_REAL *samplehist=m_samplehist[ch].Get() + m_fft_size*srchistpos*2; if (applycnt++) // add to output WDL_CONVO_CplxMul3((WDL_FFT_COMPLEX*)workbuf2,(WDL_FFT_COMPLEX*)samplehist,(WDL_CONVO_IMPULSEBUFCPLXf*)impulseptr,m_fft_size); else // replace output WDL_CONVO_CplxMul2((WDL_FFT_COMPLEX*)workbuf2,(WDL_FFT_COMPLEX*)samplehist,(WDL_CONVO_IMPULSEBUFCPLXf*)impulseptr,m_fft_size); } if (!applycnt) memset(workbuf2,0,m_fft_size*2*sizeof(WDL_FFT_REAL)); else WDL_fft((WDL_FFT_COMPLEX*)workbuf2,m_fft_size,1); WDL_FFT_REAL *olhist=m_overlaphist[ch].Get(); // errors from last time WDL_FFT_REAL *p1=workbuf2,*p3=workbuf2+m_fft_size,*p1o=workbuf2; if (mono_impulse_mode||mono_input_mode) { WDL_FFT_REAL *p2o=workbuf2+m_fft_size*2; WDL_FFT_REAL *olhist2=m_overlaphist[ch+1].Get(); // errors from last time int s=sz/2; while (s--) { p2o[0] = p1[1]+olhist2[0]; p2o[1] = p1[3]+olhist2[1]; p1o[0] = p1[0]+olhist[0]; p1o[1] = p1[2]+olhist[1]; p1o+=2; p2o+=2; p1+=4; olhist[0]=p3[0]; olhist[1]=p3[2]; olhist2[0]=p3[1]; olhist2[1]=p3[3]; p3+=4; olhist+=2; olhist2+=2; } // add samples to output m_samplesout[ch].Add(workbuf2,sz*sizeof(WDL_FFT_REAL)); m_samplesout[ch+1].Add(workbuf2+m_fft_size*2,sz*sizeof(WDL_FFT_REAL)); } else { int s=sz/2; while (s--) { p1o[0] = p1[0]+olhist[0]; p1o[1] = p1[2]+olhist[1]; p1o+=2; p1+=4; olhist[0]=p3[0]; olhist[1]=p3[2]; p3+=4; olhist+=2; } // add samples to output m_samplesout[ch].Add(workbuf2,sz*sizeof(WDL_FFT_REAL)); } } // while available if (mono_impulse_mode) ch++; } int mv = want; for (ch=0;ch<m_proc_nch;ch++) { int v = m_samplesout[ch].Available()/sizeof(WDL_FFT_REAL) - m_samplesout_delay[ch]; if (!ch || v<mv)mv=v; } return mv; }