[FFmpeg-devel] [PATCH] Common ACELP routines (2/3) - filters

Vladimir Voroshilov voroshil
Fri Apr 25 09:40:08 CEST 2008


Michael Niedermayer wrote: 
> On Fri, Apr 25, 2008 at 08:22:15AM +0700, Vladimir Voroshilov wrote:
> > On Fri, Apr 25, 2008 at 1:17 AM, Michael Niedermayer <michaelni at gmx.at> wrote:
> > > On Fri, Apr 25, 2008 at 12:07:15AM +0700, Vladimir Voroshilov wrote:
> > >  > On Thu, Apr 24, 2008 at 10:13 AM, Michael Niedermayer <michaelni at gmx.at> wrote:
> > >  [...]
> > >
> > >
> > > > >  > > > +        filter_data[10+n] = out[n] = sum;
> > >  > >  > >
> > >  > >  > > This duplicated storeage is unacceptable.
> > >  > >  >
> > >  > >  > First for all assigned to filter data values will be used in loop later.
> > >  > >  > Thus filter_data can not be eliminated.
> > >  > >  > I can't use "out" instead of it due to necessary 10 items
> > >  > >  > with data from previous subframe at top).
> > >  > >  > Extending out with 10 items at top will require another temporary buffer
> > >  > >  > one memcpy somewhere later (because i will not be able to use output buffer
> > >  > >  > directly).
> > >  > >
> > >  > >  The double write is definitly useless after the first 10 iterations as
> > >  > >  after that you can just work in the out buffer.
> > >  > >
> > >  > >  foobar_filter(filter_data+10, 10);
> > >  > >  memcpy(out, filter_data+10, 10);
> > >  > >  foobar_filter(out+10, N-10);
> > >  > >
> > >  > >  should work fine and will for large N (dunno how large it is, so maybe
> > >  > >  this isnt worth it ...) be faster. Also it allows filter_data to be smaller.
> > >  >
> > >  > ... and code will look like :(
> > >  >
> > >  > if(foobar_filter(filter_data+10, 10)!=OVERFLOW)
> > >  > {
> > >  >   memcpy(out, filter_data+10, 10);
> > >  >   if(foobar_filter(out+10, N-10)==OVERFLOW)
> > >  >   {
> > >  >      for(i=0;i<len;i++) out>>=2;
> > >  >      foobar_filter(filter_data+10, 10);
> > >  >      memcpy(out, filter_data+10, 10);
> > >  >      foobar_filter(out+10, N-10);
> > >  >   }
> > >  > }
> > >  > else
> > >  > {
> > >  >      for(i=0;i<len;i++) out>>=2;
> > >  >      foobar_filter(filter_data+10, 10);
> > >  >      memcpy(out, filter_data+10, 10);
> > >  >      foobar_filter(out+10, N-10);
> > >  > }
> > >
> > >  for(;;){
> > >     overflow= foobar_filter(filter_data+10, 10);
> > >
> > >     memcpy(out, filter_data+10, 10);
> > >     overflow|= foobar_filter(out+10, N-10);
> > >     if(!overflow)
> > >         break;
> > >
> > >     for(i=0;i<len;i++) out>>=2;
> > >  }
> > 
> > This will change filter_data even if overflow occuried.
> > Which cause wrong synthesis result on second iteration.
> > Current code on overflow case just downscales
> > excitation signal (without touching filter data).
> 
> well its a matter of adding if(!overflow)
> 
> Also note that a memcpy is likely faster than the one by one element writing
> in the loop. So if you dislike spliting it in 2 like above, then a seperate
> memcpy is definitly prefered over the double writing.

I've split it in two routines.
first is alike your's foobar_filter.

Second routine calls previous one 
twice - for first 10 samples and the remaining.

I've also moved output parameters to top.

> 
> 
> [...]
> -- 
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
> 
> It is not what we do, but why we do it that matters.



-- 
Regards,
Vladimir Voroshilov mailto:voroshil at gmail.com
Omsk State University
JID: voroshil at jabber.ru
ICQ: 95587719
-------------- next part --------------
diff --git a/libavcodec/acelp_filters.c b/libavcodec/acelp_filters.c
new file mode 100644
index 0000000..1751dd5
--- /dev/null
+++ b/libavcodec/acelp_filters.c
@@ -0,0 +1,222 @@
+/*
+ * Various filters for ACELP-based codecs
+ *
+ * Copyright (c) 2008 Vladimir Voroshilov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <inttypes.h>
+
+#include "avcodec.h"
+#include "acelp_filters.h"
+
+/**
+ * Low-pass FIR (Finite Impulse Response) filter coefficients
+ *
+ *   Similar filter is named b30 in G.729.
+ *
+ *   G.729 specification says:
+ *     b30 is based on Hamming windowed sinc functions, truncated at +/-29 and
+ *     padded with zeros at +/-30 b30[30]=0
+ *     The filter has a cut-off frequency (-3 dB) at 3600 Hz in the oversampled domain.
+ *
+ *   After some analysis, I found this aproximation:
+ *
+ *                                    PI * x
+ *   Hamm(x,N) = 0.53836-0.46164*cos(--------)
+ *                                      N-1
+ *                                      ---
+ *                                       2
+ *
+ *                                                             PI * x
+ *   Hamm'(x,k) = Hamm(x - k, 2*k+1) =  0.53836 + 0.46164*cos(--------)
+ *                                                                k
+ *
+ *             sin(PI * x)
+ *   Sinc(x) = ----------- (normalized sinc function)
+ *               PI * x
+ *
+ *   h(t,B) = 2 * B * Sinc(2 * B * t) (impulse response of sinc low-pass filter)
+ *
+ *   b(k,B, n) = Hamm'(n, k) * h(n, B)
+ *
+ *
+ *       3600
+ *   B = ----
+ *       8000
+ *
+ *   3600 - cut-off frequency
+ *   8000 - sampliny rate
+ *   k    - filter's order
+ *   
+ *   ff_acelp_interp_filter[i][j] = b(10, 3600/8000, i+j/6)
+ *
+ */
+const int16_t ff_acelp_interp_filter[11][6] =
+{ /* Q15 */
+  { 29443, 28346, 25207, 20449, 14701,  8693},
+  {  3143, -1352, -4402, -5865, -5850, -4673},
+  { -2783,  -672,  1211,  2536,  3130,  2991},
+  {  2259,  1170,     0, -1001, -1652, -1868},
+  { -1666, -1147,  -464,   218,   756,  1060},
+  {  1099,   904,   550,   135,  -245,  -514},
+  {  -634,  -602,  -451,  -231,     0,   191},
+  {   308,   340,   296,   198,    78,   -36},
+  {  -120,  -163,  -165,  -132,   -79,   -19},
+  {    34,    73,    91,    89,    70,    38},
+  {     0,     0,     0,     0,     0,     0},
+};
+
+void ff_acelp_interpolate_excitation(int16_t* ac_v, int pitch_delay_6x, int subframe_size)
+{
+    int n, i;
+    int v;
+    // TODO: clarify why used such expression (hint: -1/3 , 0 ,1/3 order in interpol_filter)
+    int pitch_delay_frac = 2 - (pitch_delay_6x%6);
+    int pitch_delay_int  = pitch_delay_6x / 6;
+
+    //Make sure that pitch_delay_frac will be always positive
+    if(pitch_delay_frac < 0)
+    {
+        pitch_delay_frac += 6;
+        pitch_delay_int++;
+    }
+
+    //pitch_delay_frac [0; 5]
+    //pitch_delay_int  [PITCH_LAG_MIN-1; PITCH_LAG_MAX]
+    for(n=0; n<subframe_size; n++)
+    {
+        /* 3.7.1 of G.729, Equation 40 */
+        v=0;
+        for(i=0; i<10; i++)
+        {
+            /*  R(x):=ac_v[-k+x] */
+            v += ac_v[n - pitch_delay_int - i    ] * ff_acelp_interp_filter[i][    pitch_delay_frac];
+            v = av_clip(v, -0x40000000, 0x3fffffff); //v += R(n-i)*ff_acelp_interp_filter(t+3i)
+            v += ac_v[n - pitch_delay_int + i + 1] * ff_acelp_interp_filter[i][6 - pitch_delay_frac];
+            v = av_clip(v, -0x40000000, 0x3fffffff); //v += R(n+i+1)*ff_acelp_interp_filter(3-t+3i)
+        }
+        ac_v[n] = (v + 0x4000) >> 15;
+    }
+}
+
+void ff_acelp_convolve_circ(int16_t* fc_out, const int16_t* fc_in, const int16_t* filter, int subframe_size)
+{
+    int i, k;
+
+    memset(fc_out, 0, subframe_size * sizeof(int16_t));
+
+    for(i=0; i<subframe_size; i++)
+    {
+        if(fc_in[i])
+        {
+            for(k=0; k<i; k++)
+                fc_out[k] += (fc_in[i] * filter[subframe_size + k - i]) >> 15;
+
+            for(k=i; k<subframe_size; k++)
+                fc_out[k] += (fc_in[i] * filter[k - i]) >> 15;
+        }
+    }
+}
+
+int ff_acelp_lp_synthesis_filter_inplace(
+        int16_t *out,
+        const int16_t* filter_coeffs,
+        const int16_t* in,
+        int length,
+        int stop_on_overflow)
+{
+    int i,n;
+    int sum;
+
+    for(n=0; n<length; n++)
+    {
+        sum = in[n] << 12;
+        for(i=1; i<11; i++)
+            sum -= filter_coeffs[i] * out[n-i];
+
+        sum = (sum + 0x800) >> 12;
+
+        //hecking for overflow
+        if(sum + 0x8000 > 0xFFFFU)
+        {
+            if(stop_on_overflow)
+                return 1;
+            sum = (sum >> 31) ^ 32767;
+        }
+        out[n] = sum;
+    }
+
+    return 0;
+}
+
+int ff_acelp_lp_synthesis_filter(
+        int16_t *out,
+        int16_t *filter_data,
+        const int16_t* filter_coeffs,
+        const int16_t *in,
+        int subframe_size,
+        int update_filter_data)
+{
+    int overflow;
+
+    // Processing first 10 samples using filter_data as past data
+    overflow = ff_acelp_lp_synthesis_filter_inplace(
+            filter_data+10,
+            filter_coeffs,
+            in,
+            10,
+            !update_filter_data);
+    if(overflow && !update_filter_data)
+        return 1;
+
+    memcpy(out, filter_data+10, 10*sizeof(int16_t));
+
+    /*
+      Processing remaining samples using previously computed
+      samples in the same (output) buffer as past data
+    */
+    overflow = ff_acelp_lp_synthesis_filter_inplace(
+            out+10,
+            filter_coeffs,
+            in+10,
+            subframe_size-10,
+            !update_filter_data);
+    if(overflow && !update_filter_data)
+        return 1;
+
+    // Saving data for using in next subframe
+    if(update_filter_data)
+        memcpy(filter_data, out + subframe_size - 10, 10 * sizeof(int16_t));
+
+    return overflow;
+}
+
+void ff_acelp_weighted_filter(int16_t *out, const int16_t* in, int16_t weight)
+{
+    int weight_pow = 1 << 15;
+    int n;
+
+    for(n=0; n<11; n++)
+    {
+        // Q15 * Q12 -> Q12 with rounding
+        out[n] = (in[n] * weight_pow + 0x4000) >> 15;
+        // Q15 * Q12 -> Q12 with rounding
+        weight_pow = (weight_pow * weight + 0x4000) >> 15;
+    }
+}
diff --git a/libavcodec/acelp_filters.h b/libavcodec/acelp_filters.h
new file mode 100644
index 0000000..7224136
--- /dev/null
+++ b/libavcodec/acelp_filters.h
@@ -0,0 +1,110 @@
+/*
+ * Various filters for ACELP-based codecs
+ *
+ * Copyright (c) 2008 Vladimir Voroshilov
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FFMPEG_ACELP_FILT_H
+#define FFMPEG_ACELP_FILT_H
+
+/**
+ * \brief Decoding of the adaptive-codebook vector (4.1.3 of G.729)
+ * \param ac_v [out] (Q0) buffer to store decoded vector into
+ * \param pitch_delay pitch delay with 1/6 precision
+ * \param subframe_size length of subframe
+ *
+ * Routine assumes following fractions order (X - integer delay):
+ *
+ * 1/3 precision: X     1/3     -1/3      X     1/3     -1/3      X
+ * 1/6 precision: X 1/6 2/6 3/6 -2/6 -1/6 X 1/6 2/6 3/6 -2/6 -1/6 X
+ *
+ * Second parameter should contain 6*int+frac+2
+ *
+ * Routine can be used for 1/3 precision too, by passing 2*pitch_delay as second parameter
+ */
+void ff_acelp_interpolate_excitation(int16_t* ac_v, int pitch_delay_6x, int subframe_size);
+
+
+/**
+ * \brief Circularly convolve fixed vector with a phase dispersion impulse response filter
+ * \param fc_out vector with filter applied
+ * \param fc_in source vector
+ * \param filter impulse response of phase filter to apply
+ *
+ * \note fc_in and fc_out should not overlap!
+ */
+void ff_acelp_convolve_circ(int16_t* fc_new, const int16_t* fc_cur, const int16_t* filter, int subframe_size);
+
+/**
+ * \brief LP synthesis filter
+ * \param out [out] (Q0) pointer to output buffer
+ * \param filter_coeffs (Q12) filter coefficients
+ * \param in (Q0) input signal
+ * \param length amount of data to proceed
+ * \param stop_on_overflow   1 - return immediately if overflow ocures
+ *                           0 - ignore overflows
+ *
+ * \return 1 if overflow occured, 0 - otherwise
+ *
+ * \note output buffer must contains 10 samples of past
+ *       speech data before pointer
+ *
+ * Routine applies 1/A(z) filter to given speech data
+ */
+int ff_acelp_lp_synthesis_filter_inplace(
+        int16_t *out,
+        const int16_t* filter_coeffs,
+        const int16_t* in,
+        int length,
+        int stop_on_overflow);
+
+/**
+ * \brief LP synthesis filter
+ * \param out [out] (Q0) output (filtered) signal
+ * \param filter_data [in/out] (Q0) filter data array (previous synthesis data)
+ * \param filter_coeffs (Q12) filter coefficients
+ * \param in (Q0) input signal
+ * \param subframe_size length of subframe
+ * \param update_filter_data 1 - update past filter data arrays
+ *                           0 - don't update
+ *
+ * \return 1 if overflow occured, 0 - otherwise
+ *
+ * \note filter_data should be at least subframe_size+10 size
+ * Routine applies 1/A(z) filter to given speech data
+ */
+int ff_acelp_lp_synthesis_filter(
+	int16_t *out,
+	int16_t *filter_data,
+        const int16_t* filter_coeffs,
+	const int16_t *in,
+	int subframe_size,
+	int update_filter_data);
+
+/**
+ * \brief Calculates coefficients of weighted A(z/weight) filter
+ * \param out [out] (Q12) resulted weighted A(z/weight) filter
+ * \param in (Q12) source filter
+ * \param weight (Q15) weight factor of postfiltering
+ *
+ * out[i]=weight^i*in[i] , i=0..9
+ */
+void ff_acelp_weighted_filter(int16_t *out, const int16_t* in, int16_t weight);
+
+#endif // FFMPEG_ACELP_FILT_H



More information about the ffmpeg-devel mailing list