void lpc_post_filter(kiss_fft_cfg fft_fwd_cfg, MODEL *model, COMP Pw[], float ak[], int order, int dump, float beta, float gamma, int bass_boost) { int i; COMP x[FFT_ENC]; /* input to FFTs */ COMP Aw[FFT_ENC]; /* LPC analysis filter spectrum */ COMP Ww[FFT_ENC]; /* weighting spectrum */ float Rw[FFT_ENC]; /* R = WA */ float e_before, e_after, gain; float Pfw[FFT_ENC]; /* Post filter mag spectrum */ float max_Rw, min_Rw; float coeff; TIMER_VAR(tstart, tfft1, taw, tfft2, tww, tr); TIMER_SAMPLE(tstart); /* Determine LPC inverse filter spectrum 1/A(exp(jw)) -----------*/ /* we actually want the synthesis filter A(exp(jw)) but the inverse (analysis) filter is easier to find as it's FIR, we just use the inverse of 1/A to get the synthesis filter A(exp(jw)) */ for(i=0; i<FFT_ENC; i++) { x[i].real = 0.0; x[i].imag = 0.0; } for(i=0; i<=order; i++) x[i].real = ak[i]; kiss_fft(fft_fwd_cfg, (kiss_fft_cpx *)x, (kiss_fft_cpx *)Aw); TIMER_SAMPLE_AND_LOG(tfft1, tstart, " fft1"); for(i=0; i<FFT_ENC/2; i++) { Aw[i].real = 1.0/(Aw[i].real*Aw[i].real + Aw[i].imag*Aw[i].imag); } TIMER_SAMPLE_AND_LOG(taw, tfft1, " Aw"); /* Determine weighting filter spectrum W(exp(jw)) ---------------*/ for(i=0; i<FFT_ENC; i++) { x[i].real = 0.0; x[i].imag = 0.0; } x[0].real = ak[0]; coeff = gamma; for(i=1; i<=order; i++) { x[i].real = ak[i] * coeff; coeff *= gamma; } kiss_fft(fft_fwd_cfg, (kiss_fft_cpx *)x, (kiss_fft_cpx *)Ww); TIMER_SAMPLE_AND_LOG(tfft2, taw, " fft2"); for(i=0; i<FFT_ENC/2; i++) { Ww[i].real = Ww[i].real*Ww[i].real + Ww[i].imag*Ww[i].imag; } TIMER_SAMPLE_AND_LOG(tww, tfft2, " Ww"); /* Determined combined filter R = WA ---------------------------*/ max_Rw = 0.0; min_Rw = 1E32; for(i=0; i<FFT_ENC/2; i++) { Rw[i] = sqrtf(Ww[i].real * Aw[i].real); if (Rw[i] > max_Rw) max_Rw = Rw[i]; if (Rw[i] < min_Rw) min_Rw = Rw[i]; } TIMER_SAMPLE_AND_LOG(tr, tww, " R"); #ifdef DUMP if (dump) dump_Rw(Rw); #endif /* create post filter mag spectrum and apply ------------------*/ /* measure energy before post filtering */ e_before = 1E-4; for(i=0; i<FFT_ENC/2; i++) e_before += Pw[i].real; /* apply post filter and measure energy */ #ifdef DUMP if (dump) dump_Pwb(Pw); #endif e_after = 1E-4; for(i=0; i<FFT_ENC/2; i++) { Pfw[i] = powf(Rw[i], beta); Pw[i].real *= Pfw[i] * Pfw[i]; e_after += Pw[i].real; } gain = e_before/e_after; /* apply gain factor to normalise energy */ for(i=0; i<FFT_ENC/2; i++) { Pw[i].real *= gain; } if (bass_boost) { /* add 3dB to first 1 kHz to account for LP effect of PF */ for(i=0; i<FFT_ENC/8; i++) { Pw[i].real *= 1.4*1.4; } } TIMER_SAMPLE_AND_LOG2(tr, " filt"); }
void aks_to_M2( kiss_fft_cfg fft_fwd_cfg, float ak[], /* LPC's */ int order, MODEL *model, /* sinusoidal model parameters for this frame */ float E, /* energy term */ float *snr, /* signal to noise ratio for this frame in dB */ int dump, /* true to dump sample to dump file */ int sim_pf, /* true to simulate a post filter */ int pf, /* true to LPC post filter */ int bass_boost, /* enable LPC filter 0-1khz 3dB boost */ float beta, float gamma /* LPC post filter parameters */ ) { COMP pw[FFT_ENC]; /* input to FFT for power spectrum */ COMP Pw[FFT_ENC]; /* output power spectrum */ int i,m; /* loop variables */ int am,bm; /* limits of current band */ float r; /* no. rads/bin */ float Em; /* energy in band */ float Am; /* spectral amplitude sample */ float signal, noise; TIMER_VAR(tstart, tfft, tpw, tpf); TIMER_SAMPLE(tstart); r = TWO_PI/(FFT_ENC); /* Determine DFT of A(exp(jw)) --------------------------------------------*/ for(i=0; i<FFT_ENC; i++) { pw[i].real = 0.0; pw[i].imag = 0.0; } for(i=0; i<=order; i++) pw[i].real = ak[i]; kiss_fft(fft_fwd_cfg, (kiss_fft_cpx *)pw, (kiss_fft_cpx *)Pw); TIMER_SAMPLE_AND_LOG(tfft, tstart, " fft"); /* Determine power spectrum P(w) = E/(A(exp(jw))^2 ------------------------*/ for(i=0; i<FFT_ENC/2; i++) Pw[i].real = E/(Pw[i].real*Pw[i].real + Pw[i].imag*Pw[i].imag); TIMER_SAMPLE_AND_LOG(tpw, tfft, " Pw"); if (pf) lpc_post_filter(fft_fwd_cfg, model, Pw, ak, order, dump, beta, gamma, bass_boost); TIMER_SAMPLE_AND_LOG(tpf, tpw, " LPC post filter"); #ifdef DUMP if (dump) dump_Pw(Pw); #endif /* Determine magnitudes from P(w) ----------------------------------------*/ /* when used just by decoder {A} might be all zeroes so init signal and noise to prevent log(0) errors */ signal = 1E-30; noise = 1E-32; for(m=1; m<=model->L; m++) { am = (int)((m - 0.5)*model->Wo/r + 0.5); bm = (int)((m + 0.5)*model->Wo/r + 0.5); Em = 0.0; for(i=am; i<bm; i++) Em += Pw[i].real; Am = sqrtf(Em); signal += model->A[m]*model->A[m]; noise += (model->A[m] - Am)*(model->A[m] - Am); /* This code significantly improves perf of LPC model, in particular when combined with phase0. The LPC spectrum tends to track just under the peaks of the spectral envelope, and just above nulls. This algorithm does the reverse to compensate - raising the amplitudes of spectral peaks, while attenuating the null. This enhances the formants, and supresses the energy between formants. */ if (sim_pf) { if (Am > model->A[m]) Am *= 0.7; if (Am < model->A[m]) Am *= 1.4; } model->A[m] = Am; } *snr = 10.0*log10f(signal/noise); TIMER_SAMPLE_AND_LOG2(tpf, " rec"); }
float nlp( void *nlp_state, float Sn[], /* input speech vector */ int n, /* frames shift (no. new samples in Sn[]) */ int pmin, /* minimum pitch value */ int pmax, /* maximum pitch value */ float *pitch, /* estimated pitch period in samples */ COMP Sw[], /* Freq domain version of Sn[] */ COMP W[], /* Freq domain window */ float *prev_Wo ) { NLP *nlp; float notch; /* current notch filter output */ COMP fw[PE_FFT_SIZE]; /* DFT of squared signal (input) */ COMP Fw[PE_FFT_SIZE]; /* DFT of squared signal (output) */ float gmax; int gmax_bin; int m, i,j; float best_f0; TIMER_VAR(start, tnotch, filter, peakpick, window, fft, magsq, shiftmem); assert(nlp_state != NULL); nlp = (NLP*)nlp_state; m = nlp->m; TIMER_SAMPLE(start); /* Square, notch filter at DC, and LP filter vector */ for(i=m-n; i<m; i++) /* square latest speech samples */ nlp->sq[i] = Sn[i]*Sn[i]; for(i=m-n; i<m; i++) { /* notch filter at DC */ notch = nlp->sq[i] - nlp->mem_x; notch += COEFF*nlp->mem_y; nlp->mem_x = nlp->sq[i]; nlp->mem_y = notch; nlp->sq[i] = notch + 1.0; /* With 0 input vectors to codec, kiss_fft() would take a long time to execute when running in real time. Problem was traced to kiss_fft function call in this function. Adding this small constant fixed problem. Not exactly sure why. */ } TIMER_SAMPLE_AND_LOG(tnotch, start, " square and notch"); for(i=m-n; i<m; i++) { /* FIR filter vector */ for(j=0; j<NLP_NTAP-1; j++) nlp->mem_fir[j] = nlp->mem_fir[j+1]; nlp->mem_fir[NLP_NTAP-1] = nlp->sq[i]; nlp->sq[i] = 0.0; for(j=0; j<NLP_NTAP; j++) nlp->sq[i] += nlp->mem_fir[j]*nlp_fir[j]; } TIMER_SAMPLE_AND_LOG(filter, tnotch, " filter"); /* Decimate and DFT */ for(i=0; i<PE_FFT_SIZE; i++) { fw[i].real = 0.0; fw[i].imag = 0.0; } for(i=0; i<m/DEC; i++) { fw[i].real = nlp->sq[i*DEC]*nlp->w[i]; } TIMER_SAMPLE_AND_LOG(window, filter, " window"); #ifdef DUMP dump_dec(Fw); #endif kiss_fft(nlp->fft_cfg, (kiss_fft_cpx *)fw, (kiss_fft_cpx *)Fw); TIMER_SAMPLE_AND_LOG(fft, window, " fft"); for(i=0; i<PE_FFT_SIZE; i++) Fw[i].real = Fw[i].real*Fw[i].real + Fw[i].imag*Fw[i].imag; TIMER_SAMPLE_AND_LOG(magsq, fft, " mag sq"); #ifdef DUMP dump_sq(nlp->sq); dump_Fw(Fw); #endif /* find global peak */ gmax = 0.0; gmax_bin = PE_FFT_SIZE*DEC/pmax; for(i=PE_FFT_SIZE*DEC/pmax; i<=PE_FFT_SIZE*DEC/pmin; i++) { if (Fw[i].real > gmax) { gmax = Fw[i].real; gmax_bin = i; } } TIMER_SAMPLE_AND_LOG(peakpick, magsq, " peak pick"); //#define POST_PROCESS_MBE #ifdef POST_PROCESS_MBE best_f0 = post_process_mbe(Fw, pmin, pmax, gmax, Sw, W, prev_Wo); #else best_f0 = post_process_sub_multiples(Fw, pmin, pmax, gmax, gmax_bin, prev_Wo); #endif TIMER_SAMPLE_AND_LOG(shiftmem, peakpick, " post process"); /* Shift samples in buffer to make room for new samples */ for(i=0; i<m-n; i++) nlp->sq[i] = nlp->sq[i+n]; /* return pitch and F0 estimate */ *pitch = (float)SAMPLE_RATE/best_f0; TIMER_SAMPLE_AND_LOG2(shiftmem, " shift mem"); TIMER_SAMPLE_AND_LOG2(start, " nlp int"); return(best_f0); }