[FFmpeg-devel] [PATCH] QCELP decoder postfilter & final gain control

Kenan Gillet kenan.gillet
Sat Apr 4 02:01:37 CEST 2009


Hi,

here is a series of patch implementing the postfiltering and final gain control
for the QCELP decoder which should make the decoder features complete.

- patches 1, 2 and 3 are splitting and cleanup the current code.
  They give the same output on all my test samples
- patch 4 adds a LP zero filter
- patch 5 implements the postfiltering and final gain control.

thanks for the review

Kenan
-------------- next part --------------
Index: libavcodec/qcelpdec.c
===================================================================
--- libavcodec/qcelpdec.c	(revision 18292)
+++ libavcodec/qcelpdec.c	(working copy)
@@ -412,18 +417,39 @@
 }
 
 /**
- * Apply generic gain control.
+ * Compute the gain control
  *
- * @param v_out output vector
  * @param v_in gain-controlled vector
  * @param v_ref vector to control gain of
  *
+ * @return gain control
+ *
  * FIXME: If v_ref is a zero vector, it energy is zero
  *        and the behavior of the gain control is
  *        undefined in the specs.
  *
  * TIA/EIA/IS-733 2.4.8.3-2/3/4/5, 2.4.8.6
  */
+static float compute_gain_ctrl(const float *v_ref, const float *v_in, const int len)
+{
+    float scalefactor = ff_dot_productf(v_in, v_in, len);
+
+    if(scalefactor)
+        scalefactor = sqrt(ff_dot_productf(v_ref, v_ref, len) / scalefactor);
+    else
+        ff_log_missing_feature(NULL, "Zero energy for gain control", 1);
+    return scalefactor;
+}
+
+/**
+ * Apply generic gain control.
+ *
+ * @param v_out output vector
+ * @param v_in gain-controlled vector
+ * @param v_ref vector to control gain of
+ *
+ * TIA/EIA/IS-733 2.4.8.3, 2.4.8.6
+ */
 static void apply_gain_ctrl(float *v_out, const float *v_ref,
                             const float *v_in)
 {
@@ -432,12 +458,7 @@
 
     for(i=0, j=0; i<4; i++)
     {
-        scalefactor = ff_dot_productf(v_in + j, v_in + j, 40);
-        if(scalefactor)
-            scalefactor = sqrt(ff_dot_productf(v_ref + j, v_ref + j, 40)
-                        / scalefactor);
-        else
-            ff_log_missing_feature(NULL, "Zero energy for gain control", 1);
+        scalefactor = compute_gain_ctrl(v_ref + j, v_in + j, 40);
         for(len=j+40; j<len; j++)
             v_out[j] = scalefactor * v_in[j];
     }
-------------- next part --------------
Index: libavcodec/qcelpdec.c
===================================================================
--- libavcodec/qcelpdec.c	(revision 18292)
+++ libavcodec/qcelpdec.c	(working copy)
@@ -819,8 +819,8 @@
     for(i=0; i<4; i++)
     {
         interpolate_lpc(q, quantized_lspf, lpc, i);
-        ff_celp_lp_synthesis_filterf(formant_mem, lpc, outbuffer + i * 40, 40,
-                                     10);
+        ff_celp_lp_synthesis_filterf(formant_mem, lpc, outbuffer, 40, 10);
+        outbuffer   += 40;
         formant_mem += 40;
     }
     memcpy(q->formant_mem, q->formant_mem + 160, 10 * sizeof(float));
@@ -828,6 +828,7 @@
     // FIXME: postfilter and final gain control should be here.
     // TIA/EIA/IS-733 2.4.8.6
 
+    outbuffer  -= 160;
     formant_mem = q->formant_mem + 10;
     for(i=0; i<160; i++)
         *outbuffer++ = av_clipf(*formant_mem++, QCELP_CLIP_LOWER_BOUND,
-------------- next part --------------
--- libavcodec/qcelpdec.c	2009-04-01 22:05:11.000000000 -0700
+++ libavcodec/qcelpdec-w-postfilter.c	2009-04-01 22:16:37.000000000 -0700
@@ -733,7 +774,7 @@ static int qcelp_decode_frame(AVCodecCon
 {
     QCELPContext *q = avctx->priv_data;
     float *outbuffer = data;
-    int   i;
+    int   i, j;
     float quantized_lspf[10], lpc[10];
     float gain[16];
     float *formant_mem;
@@ -804,6 +861,9 @@ erasure:
     {
         interpolate_lpc(q, quantized_lspf, lpc, i);
         ff_celp_lp_synthesis_filterf(formant_mem, lpc, outbuffer, 40, 10);
+        for(j=0; j<40; j++)
+            outbuffer[j] = av_clipf(formant_mem[j], QCELP_CLIP_LOWER_BOUND,
+                                    QCELP_CLIP_UPPER_BOUND);
         outbuffer   += 40;
         formant_mem += 40;
     }
@@ -812,12 +872,6 @@ erasure:
     // FIXME: postfilter and final gain control should be here.
     // TIA/EIA/IS-733 2.4.8.6
 
-    outbuffer  -= 160;
-    formant_mem = q->formant_mem + 10;
-    for(i=0; i<160; i++)
-        *outbuffer++ = av_clipf(*formant_mem++, QCELP_CLIP_LOWER_BOUND,
-                                QCELP_CLIP_UPPER_BOUND);
-
     memcpy(q->prev_lspf, quantized_lspf, sizeof(q->prev_lspf));
     q->prev_bitrate = q->bitrate;
 
-------------- next part --------------
Index: libavcodec/celp_filters.c
===================================================================
--- libavcodec/celp_filters.c	(revision 18292)
+++ libavcodec/celp_filters.c	(working copy)
@@ -105,3 +105,24 @@
             out[n] -= filter_coeffs[i] * out[n-i];
     }
 }
+
+void ff_celp_lp_zero_synthesis_filterf(
+        float *out,
+        const float* filter_coeffs,
+        const float* in,
+        int buffer_length,
+        int filter_length)
+{
+    int i,n;
+
+    // These two lines are to avoid a -1 subtraction in the main loop
+    filter_length++;
+    filter_coeffs--;
+
+    for(n=0; n<buffer_length; n++)
+    {
+        out[n] = in[n];
+        for(i=1; i<filter_length; i++)
+            out[n] -= filter_coeffs[i] * in[n-i];
+    }
+}
Index: libavcodec/celp_filters.h
===================================================================
--- libavcodec/celp_filters.h	(revision 18292)
+++ libavcodec/celp_filters.h	(working copy)
@@ -91,4 +91,26 @@
         int buffer_length,
         int filter_length);
 
+/**
+ * LP zero synthesis filter.
+ * @param out [out] pointer to output buffer
+ * @param filter_coeffs filter coefficients.
+ * @param in input signal
+ *        - the array in[-filter_length, -1] must
+ *        contain the previous input of this filter
+ * @param buffer_length amount of data to process
+ * @param filter_length filter length (10 for 10th order LP filter)
+ *
+ * @note Output buffer must contain filter_length samples of past
+ *       speech data before pointer.
+ *
+ * Routine applies A(z) filter to given speech data.
+ */
+void ff_celp_lp_zero_synthesis_filterf(
+        float *out,
+        const float* filter_coeffs,
+        const float* in,
+        int buffer_length,
+        int filter_length);
+
 #endif /* AVCODEC_CELP_FILTERS_H */
-------------- next part --------------
--- libavcodec/qcelpdec.c	2009-04-01 23:26:19.000000000 -0700
+++ libavcodec/qcelpdec-w-postfilter.c	2009-04-01 23:28:23.000000000 -0700
@@ -66,6 +66,9 @@ typedef struct
     float    pitch_pre_filter_mem[303];
     float    rnd_fir_filter_mem[180];
     float    formant_mem[170];
+    float    postfilter_pole_mem[170];
+    float    prev_anti_tilt_filter_mem;
+    float    final_gain;
     float    last_codebook_gain;
     int      prev_g1[2];
     int      prev_bitrate;
@@ -97,6 +100,8 @@ static av_cold int qcelp_decode_init(AVC
     for(i=0; i<10; i++)
         q->prev_lspf[i] = (i+1)/11.;
 
+    q->final_gain = 1.;
+
     return 0;
 }
 
@@ -656,6 +661,48 @@ void interpolate_lpc(QCELPContext *q, co
         lspf2lpc(q->prev_lspf, lpc);
 }
 
+/**
+ * Apply postfilter in subframe steps.
+ *
+ * @param q the context
+ * @param outbuffer the ouput buffer,
+ *        it must be at least 40 float large
+ * @param lpc linear predictive coding coefficients
+ * @param formant_mem the formant filter memory,
+ *        it must contain 40 samples and 10 previous samples
+ * @param pole_mem the postfilter pole filter memory,
+ *        it must contain 40 samples and 10 previous samples
+ *
+ * TIA/EIA/IS-733 2.4.8.6
+ */
+static void do_postfilter(QCELPContext *q, float *outbuffer, const float *lpc,
+                          const float *formant_mem, float *pole_mem)
+{
+    float weighted_lpc[10];
+    float weight = -0.625;
+    int i;
+
+    for(i=0; i<10; i++)
+    {
+        weighted_lpc[i] = lpc[i] * weight;
+        weight *= 0.625;
+    }
+    ff_celp_lp_zero_synthesis_filterf(outbuffer, weighted_lpc, formant_mem, 40, 10);
+
+    weight = 0.775;
+    for(i=0; i<10; i++)
+    {
+        weighted_lpc[i] = lpc[i] * weight;
+        weight *= 0.775;
+    }
+    ff_celp_lp_synthesis_filterf(pole_mem, weighted_lpc, outbuffer, 40, 10);
+
+    outbuffer[0] = pole_mem[0] - 0.3 * q->prev_anti_tilt_filter_mem;
+    for(i=1; i<40; i++)
+        outbuffer[i] = pole_mem[i] - 0.3 * outbuffer[i-1];
+    q->prev_anti_tilt_filter_mem = outbuffer[39];
+}
+
 static qcelp_packet_rate buf_size2bitrate(const int buf_size)
 {
     switch(buf_size)
@@ -820,16 +867,19 @@ erasure:
     {
         interpolate_lpc(q, quantized_lspf, lpc, i);
         ff_celp_lp_synthesis_filterf(formant_mem, lpc, outbuffer, 40, 10);
+        do_postfilter(q, outbuffer, lpc, formant_mem, q->postfilter_pole_mem + 10 + 40*i);
+
+        q->final_gain = 0.9375 * q->final_gain
+                      + 0.0625 * compute_gain_ctrl(formant_mem, outbuffer, 40);
+
         for(j=0; j<40; j++)
-            outbuffer[j] = av_clipf(formant_mem[j], QCELP_CLIP_LOWER_BOUND,
+            outbuffer[j] = av_clipf(q->final_gain * outbuffer[j], QCELP_CLIP_LOWER_BOUND,
                                     QCELP_CLIP_UPPER_BOUND);
         outbuffer   += 40;
         formant_mem += 40;
     }
     memcpy(q->formant_mem, q->formant_mem + 160, 10 * sizeof(float));
-
-    // FIXME: postfilter and final gain control should be here.
-    // TIA/EIA/IS-733 2.4.8.6
+    memcpy(q->postfilter_pole_mem,  q->postfilter_pole_mem  + 160, 10 * sizeof(float));
 
     memcpy(q->prev_lspf, quantized_lspf, sizeof(q->prev_lspf));
     q->prev_bitrate = q->bitrate;



More information about the ffmpeg-devel mailing list