first pass at LPC post filter, sig improvement to hts1a/hts2a, morig, mmt1
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 27 Aug 2012 08:20:47 +0000 (08:20 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Mon, 27 Aug 2012 08:20:47 +0000 (08:20 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@642 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/src/c2sim.c
codec2-dev/src/codec2.c
codec2-dev/src/interp.c
codec2-dev/src/quantise.c
codec2-dev/src/quantise.h

index eb08cb9ad1191d89ea924fc942fda0ee7eea6d6a..6b326483b72640067e349cb3f138225b33d82f70 100644 (file)
@@ -99,6 +99,7 @@ int main(int argc, char *argv[])
     float bg_est;
 
     int   hand_voicing = 0, phaseexp = 0, ampexp = 0, hi = 0, simlpcpf = 0;
+    int   lpcpf;
     FILE *fvoicing = 0;
 
     MODEL prev_model, interp_model;
@@ -145,6 +146,7 @@ int main(int argc, char *argv[])
         { "dt", no_argument, &dt, 1 },
         { "hi", no_argument, &hi, 1 },
         { "simlpcpf", no_argument, &simlpcpf, 1 },
+        { "lpcpf", no_argument, &lpcpf, 1 },
         { "dump_pitch_e", required_argument, &dump_pitch_e, 1 },
         { "sq_pitch_e", no_argument, &scalar_quant_Wo_e, 1 },
         { "vq_pitch_e", no_argument, &vector_quant_Wo_e, 1 },
@@ -412,10 +414,6 @@ int main(int argc, char *argv[])
            autocorrelate(Wn,Rk,M,order);
            levinson_durbin(Rk,ak,order);
 
-            #ifdef DUMP
-           dump_ak(ak, LPC_ORD);
-            #endif
-       
            /* determine voicing */
 
            snr = est_voicing_mbe(&model, Sw, W, Sw_, Ew, prev_uq_Wo);
@@ -612,7 +610,11 @@ int main(int argc, char *argv[])
 
            }
 
-           aks_to_M2(fft_fwd_cfg, ak, order, &model, e, &snr, 1, simlpcpf); 
+            #ifdef DUMP
+           dump_ak(ak, LPC_ORD);
+            #endif
+       
+           aks_to_M2(fft_fwd_cfg, ak, order, &model, e, &snr, 1, simlpcpf, lpcpf); 
 
            /* note SNR on interpolated frames can't be measured properly
               by comparing Am as L has changed.  We can dump interp lsps
index 4e41e1a08e1316f62ed8972c233968fc2afd0a80..d521aa1a67452e5bef8be8c0204503fec12ca878 100644 (file)
@@ -356,7 +356,7 @@ void codec2_decode_2400(struct CODEC2 *c2, short speech[], const unsigned char *
     interpolate_lsp_ver2(&lsps[0][0], c2->prev_lsps_dec, &lsps[1][0], 0.5);
     for(i=0; i<2; i++) {
        lsp_to_lpc(&lsps[i][0], &ak[i][0], LPC_ORD);
-       aks_to_M2(c2->fft_fwd_cfg, &ak[i][0], LPC_ORD, &model[i], e[i], &snr, 1, 0); 
+       aks_to_M2(c2->fft_fwd_cfg, &ak[i][0], LPC_ORD, &model[i], e[i], &snr, 1, 0, 1); 
        apply_lpc_correction(&model[i]);
     }
 
@@ -528,7 +528,7 @@ void codec2_decode_1400(struct CODEC2 *c2, short speech[], const unsigned char *
     }
     for(i=0; i<4; i++) {
        lsp_to_lpc(&lsps[i][0], &ak[i][0], LPC_ORD);
-       aks_to_M2(c2->fft_fwd_cfg, &ak[i][0], LPC_ORD, &model[i], e[i], &snr, 1, 0); 
+       aks_to_M2(c2->fft_fwd_cfg, &ak[i][0], LPC_ORD, &model[i], e[i], &snr, 1, 0, 1); 
        apply_lpc_correction(&model[i]);
     }
 
@@ -705,7 +705,7 @@ void codec2_decode_1200(struct CODEC2 *c2, short speech[], const unsigned char *
     }
     for(i=0; i<4; i++) {
        lsp_to_lpc(&lsps[i][0], &ak[i][0], LPC_ORD);
-       aks_to_M2(c2->fft_fwd_cfg, &ak[i][0], LPC_ORD, &model[i], e[i], &snr, 1, 0); 
+       aks_to_M2(c2->fft_fwd_cfg, &ak[i][0], LPC_ORD, &model[i], e[i], &snr, 1, 0, 1); 
        apply_lpc_correction(&model[i]);
     }
 
index bd7389fe44ac04a71bf8a30a05bfd35782fe4ca9..ebb579ab51a3bdcc9b4c9f12a3a511c92c2d260c 100644 (file)
@@ -201,7 +201,7 @@ void interpolate_lsp(
     /* convert back to amplitudes */
 
     lsp_to_lpc(lsps_interp, ak_interp, LPC_ORD);
-    aks_to_M2(fft_fwd_cfg, ak_interp, LPC_ORD, interp, e, &snr, 0, 0); 
+    aks_to_M2(fft_fwd_cfg, ak_interp, LPC_ORD, interp, e, &snr, 0, 0, 1); 
     //printf("  interp: ak[1]: %f A[1] %f\n", ak_interp[1], interp->A[1]);
 }
 
index e07b2d7e985573ed846c8686680924097d97293a..91bc3027a974572205fb66e7601e029035a681cc 100644 (file)
@@ -645,12 +645,121 @@ float lpc_model_amplitudes(
   }
   #endif
 
-  aks_to_M2(ak,order,model,E,&snr, 1, 0);   /* {ak} -> {Am} LPC decode */
+  aks_to_M2(ak,order,model,E,&snr, 1, 0, 1);   /* {ak} -> {Am} LPC decode */
 
   return snr;
 }
 #endif
 
+/*---------------------------------------------------------------------------*\
+                                                                         
+   lpc_post_filter()
+   
+   Applies a post filter to the LPC synthesis filter power spectrum
+   Pw, which supresses the inter-formant energy.
+
+   The algorithm is from p267 (Section 8.6) of "Digital Speech",
+   edited by A.M. Kondoz, 1994 published by Wiley and Sons.  Chapter 8
+   of this text is on the MBE vocoder, and this is a freq domain
+   adaptation of post filtering commonly used in CELP.
+
+   I used the Octave simulation lpcpf.m to get an understaing of the
+   algorithm.
+
+   Requires two more FFTs which is significantly more MIPs.  However
+   it should be possible to implement this more efficiently in the
+   time domain.  Just not sure how to handle relative time delays
+   between the synthesis stage and updating these coeffs.  A smaller
+   FFT size might also be accetable to save CPU.  
+
+   TODO:
+   [ ] sync var names between Octave and C version
+   [ ] doc gain normalisation
+   [ ] I think the first FFT is not rqd as we do the same
+       thing in aks_to_M2().
+
+\*---------------------------------------------------------------------------*/
+
+#define LPCPF_GAMMA 0.5
+#define LPCPF_BETA  0.2
+
+void lpc_post_filter(kiss_fft_cfg fft_fwd_cfg, COMP Pw[], float ak[], int order)
+{
+    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     */
+  
+    /* 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);
+
+    for(i=0; i<FFT_ENC/2; i++) {
+       Aw[i].real = 1.0/sqrt(Aw[i].real*Aw[i].real + Aw[i].imag*Aw[i].imag);
+    }
+
+    /* Determine weighting filter spectrum W(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] * pow(LPCPF_GAMMA, (float)i);
+    kiss_fft(fft_fwd_cfg, (kiss_fft_cpx *)x, (kiss_fft_cpx *)Ww);
+
+    for(i=0; i<FFT_ENC/2; i++) {
+       Ww[i].real = sqrt(Ww[i].real*Ww[i].real + Ww[i].imag*Ww[i].imag);
+    }
+
+    /* Determined combined filter R = WA ---------------------------*/
+
+    for(i=0; i<FFT_ENC/2; i++) {
+       Rw[i] = Ww[i].real * Aw[i].real;
+    }
+    /* create post filter mag spectrum and apply ------------------*/
+    
+    /* measure energy before post filtering */
+
+    e_before = 0.0;
+    for(i=0; i<FFT_ENC/2; i++)
+       e_before += Pw[i].real;
+
+    /* apply post filter and measure energy  */
+
+    e_after = 0.0;
+    for(i=0; i<FFT_ENC/2; i++) {
+       Pfw[i] = pow(Rw[i], LPCPF_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;
+    }
+
+}
+
+
 /*---------------------------------------------------------------------------*\
                                                                          
    aks_to_M2()                                                             
@@ -669,7 +778,8 @@ void aks_to_M2(
   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           sim_pf,      /* true to simulate a post filter */
+  int           pf           /* true to post filter */
 )
 {
   COMP pw[FFT_ENC];    /* input to FFT for power spectrum */
@@ -698,12 +808,16 @@ void aks_to_M2(
 
   for(i=0; i<FFT_ENC/2; i++)
     Pw[i].real = E/(Pw[i].real*Pw[i].real + Pw[i].imag*Pw[i].imag);
+
   #ifdef DUMP
   if (dump) 
       dump_Pw(Pw);
   #endif
 
-  /* Determine magnitudes by linear interpolation of P(w) -------------------*/
+  if (pf)
+      lpc_post_filter(fft_fwd_cfg, Pw, ak, order);
+
+  /* Determine magnitudes from P(w) ----------------------------------------*/
 
   /* when used just by decoded {A} might be all zeroes so init signal
      to prevent log(0) errors */
@@ -903,7 +1017,7 @@ float speech_to_uq_lsps(float lsp[],
  
     /* 15 Hz BW expansion as I can't hear the difference and it may help
        help occasional fails in the LSP root finding.  Important to do this
-       after energy caluclation to avoid -ve energy values.
+       after energy calculation to avoid -ve energy values.
     */
 
     for(i=0; i<=order; i++)
@@ -911,7 +1025,7 @@ float speech_to_uq_lsps(float lsp[],
 
     roots = lpc_to_lsp(ak, order, lsp, 5, LSP_DELTA1);
     if (roots != order) {
-       /* use some benign LSP values we can use instead */
+       /* if root finding fails use some benign LSP values instead */
        for(i=0; i<order; i++)
            lsp[i] = (PI/order)*(float)i;
     }
@@ -1456,7 +1570,7 @@ float decode_amplitudes(kiss_fft_cfg  fft_fwd_cfg,
     bw_expand_lsps(lsps, LPC_ORD);
     lsp_to_lpc(lsps, ak, LPC_ORD);
     *e = decode_energy(energy_index);
-    aks_to_M2(ak, LPC_ORD, model, *e, &snr, 1, 0); 
+    aks_to_M2(ak, LPC_ORD, model, *e, &snr, 1, 0, 0, 1); 
     apply_lpc_correction(model);
 
     return snr;
index fd434f789a1cf6a86d4c5aba4c291dd439d3a5ed..3c77c6adf38db090dc8e4aec8e7719c344761106 100644 (file)
@@ -52,7 +52,7 @@ void quantise_init();
 float lpc_model_amplitudes(float Sn[], float w[], MODEL *model, int order,
                           int lsp,float ak[]);
 void aks_to_M2(kiss_fft_cfg fft_fwd_cfg, float ak[], int order, MODEL *model, 
-              float E, float *snr, int dump, int sim_pf);
+              float E, float *snr, int dump, int sim_pf, int pf);
 
 int   encode_Wo(float Wo);
 float decode_Wo(int index);