[FFmpeg-devel] [PATCH] QCELP decoder

Michael Niedermayer michaelni
Fri Nov 14 11:14:39 CET 2008


On Thu, Nov 13, 2008 at 11:46:40AM -0800, Kenan Gillet wrote:
> Thanks for your reviews,
>
> here is round 10 of the QCELP decoder patch:
> - it simplifies and optimizes qcelp_lspf2lpc and lps2poly
> - it removes bandwith_expansion_coeff from QCELPContext, and replaces it
>   by a systematic recalculation as the benchmarks show it is faster
> - simplifies apply_gain_control,
> - adds FIXME comment an av_log_missing_features in apply_gain_control
>   if the vector to control gain of is a zero vector.
> - various cosmetics
>
> Kenan
>

[...]

> @@ -461,6 +517,15 @@
>  #define QCELP_RATE_HALF_CODEBOOK_RATIO 0.5
>  
>  /**
> + * sqrt(1.887) is the maximum of the pseudorandom
> + * white sequence used to generate the scaled codebook
> + * vector for framerate 1/4.
> + *
> + * TIA/EIA/IS-733 2.4.8.1.2
> + */
> +#define QCELP_SQRT1887 1.373681186
> +
> +/**
>   * table for impulse response of BPF used to filter
>   * the white excitation for framerate 1/4 synthesis
>   *

ok

[...]


> Index: libavcodec/qcelpdec.c
> ===================================================================
> --- libavcodec/qcelpdec.c	(revision 15816)
> +++ libavcodec/qcelpdec.c	(working copy)

> @@ -52,6 +52,327 @@
>  }
>  
>  /**
> + * Initialize the speech codec according to the specification.
> + *
> + * TIA/EIA/IS-733 2.4.9
> + */
> +static av_cold int qcelp_decode_init(AVCodecContext *avctx) {
> +    QCELPContext *q = avctx->priv_data;
> +    int i;
> +
> +    avctx->sample_fmt = SAMPLE_FMT_FLT;
> +
> +    for (i = 0; i < 10; i++)
> +        q->prev_lspf[i] = (i + 1) / 11.;
> +
> +    return 0;
> +}

ok


> +
> +/**
> + * Decodes the 10 quantized LSP frequencies from the LSPV/LSP
> + * transmission codes of any framerate and checks for badly received packets.
> + *
> + * @param q the context
> + * @param lspf line spectral pair frequencies
> + *
> + * @return 0 on success, -1 if the packet is badly received
> + *
> + * TIA/EIA/IS-733 2.4.3.2.6.2-2, 2.4.8.7.3
> + */
> +static int decode_lspf(QCELPContext *q,
> +                       float *lspf) {
> +    int i;
> +    float tmp_lspf;
> +
> +    if (q->framerate == RATE_OCTAVE ||
> +        q->framerate == I_F_Q) {
> +        float smooth;

> +        const float *predictors = (q->prev_framerate != RATE_OCTAVE ||
> +                                   q->prev_framerate != I_F_Q ? q->prev_lspf
> +                                                              : q->predictor_lspf);

3rd try.
This is a constant, if you disagree tell us for which value of prev_framerate
q->predictor_lspf is selected


[...]

> +/**
> + * Converts codebook transmission codes to GAIN and INDEX.
> + *
> + * @param q the context
> + * @param gain array holding the decoded gain
> + *
> + * @return 0 on success, -1 if the gain is out of range for RATE_QUARTER
> + *
> + * TIA/EIA/IS-733 2.4.6.2
> + */
> +static int decode_gain_and_index(QCELPContext  *q,
> +                                 float *gain) {
> +    int   i, subframes_count, g1[16];
> +    float ga[16], gain_memory, smooth_coef;
> +
> +    if (q->framerate >= RATE_QUARTER) {
> +        subframes_count = q->framerate == RATE_FULL ? 16
> +                                                    : q->framerate == RATE_HALF ? 4
> +                                                                                : 5;
> +        for (i = 0; i < subframes_count; i++) {
> +            g1[i] = 4 * q->cbgain[i];
> +            if (q->framerate == RATE_FULL && !((i+1) & 3)) {

> +                g1[i] += av_clip((g1[i-1] + g1[i-2] + g1[i-3]) / 3, 6, 38) - 6;

g1[i] += av_clip((g1[i-1] + g1[i-2] + g1[i-3]) / 3 - 6, 0, 32);


> +                if (g1[i] > 60)
> +                    g1[i] = 60;

i dont see how this can be true


> +            } else if (q->framerate == RATE_QUARTER) {

> +                if (i > 0  && FFABS(g1[i] -     g1[i-1]) > 40)
> +                    return -1;
> +                if (i >= 2 && FFABS(g1[i] - 2 * g1[i-1] + g1[i-2]) > 48)
> +                    return -1;

iam not sure, but maybe this check should be seperate and closer
to where the bits are read


> +            }
> +
> +            gain[i] = qcelp_g12ga[g1[i]];
> +
> +            if (q->cbsign[i]) {
> +                gain[i] = -gain[i];
> +                q->cindex[i] = (q->cindex[i]-89) & 127;
> +            }
> +        }
> +
> +        q->prev_g1[0] = g1[i-2];
> +        q->prev_g1[1] = g1[i-1];

> +        q->last_codebook_gain = gain[i-1];

q->last_codebook_gain = qcelp_g12ga[g1[i-1]];
would avoid the FFABS() later


> +
> +        if (q->framerate == RATE_QUARTER) {
> +            // Provide smoothing of the unvoiced excitation energy.
> +            gain[7] =     gain[4];
> +            gain[6] = 0.4*gain[3] + 0.6*gain[4];
> +            gain[5] =     gain[3];
> +            gain[4] = 0.8*gain[2] + 0.2*gain[3];
> +            gain[3] = 0.2*gain[1] + 0.8*gain[2];
> +            gain[2] =     gain[1];
> +            gain[1] = 0.6*gain[0] + 0.4*gain[1];
> +        }
> +    } else {
> +        if (q->framerate == RATE_OCTAVE) {

> +            g1[0] = -4 + 2 * q->cbgain[0]
> +                  + av_clip((q->prev_g1[0] + q->prev_g1[1]) / 2 - 1, 4, 58);

g1[0] = 2 * q->cbgain[0] + av_clip((q->prev_g1[0] + q->prev_g1[1]) / 2 - 5, 0, 54);



> +            smooth_coef = 0.125;
> +            i = 7;
> +        } else {
> +            assert(q->framerate == I_F_Q);
> +
> +            g1[0] = q->prev_g1[1];
> +            switch (q->erasure_count) {
> +            case 1 : break;
> +            case 2 : g1[0] -= 1; break;
> +            case 3 : g1[0] -= 2; break;
> +            default: g1[0] -= 6;
> +            }
> +            if (g1[0] < 0)
> +                g1[0] = 0;
> +            smooth_coef = 0.25;
> +            i = 3;
> +        }
> +        ga[0] = qcelp_g12ga[g1[0]];

> +        gain_memory = FFABS(q->last_codebook_gain);

this almost is qcelp_g12ga[q->prev_g1[1]]
i assume the "almost" is not just a bug and it was intended to be always?


> +
> +        q->last_codebook_gain =
> +                      gain[i] =
> +                        ga[0] = 0.5 * (gain_memory + ga[0]);
> +
> +        smooth_coef *= (ga[0] - gain_memory);

storing in ga[0] is redundant


> +        // This interpolation is done to produce smoother background noise.
> +        for (; i > 0; i--)
> +            gain[i-1] = gain_memory + smooth_coef * i;
> +
> +        q->prev_g1[0] = q->prev_g1[1];
> +        q->prev_g1[1] = g1[0];
> +    }
> +    return 0;
> +}
> +



> +/**
> + * Computes the scaled codebook vector Cdn From INDEX and GAIN
> + * for all rates.
> + *
> + * The specification lacks some information here.
> + *
> + * TIA/EIA/IS-733 has an omission on the codebook index determination
> + * formula for RATE_FULL and RATE_HALF frames at section 2.4.8.1.1. It says
> + * you have to subtract the decoded index parameter from the given scaled
> + * codebook vector index 'n' to get the desired circular codebook index, but
> + * it does not mention that you have to clamp 'n' to [0-9] in order to get
> + * RI-compliant results.
> + *
> + * The reason for this mistake seems to be the fact they forgot to mention you
> + * have to do these calculations per codebook subframe and adjust given
> + * equation values accordingly.
> + *
> + * @param q the context
> + * @param gain array holding the 4 pitch subframe gain values
> + * @param cdn_vector array for the generated scaled codebook vector
> + */
> +static void compute_svector(const QCELPContext *q,
> +                            const float *gain,
> +                            float *cdn_vector) {
> +    int      i, j, k;
> +    uint16_t cbseed, cindex;
> +    float    *rnd, tmp_gain, fir_filter_value;
> +
> +    switch (q->framerate) {
> +    case RATE_FULL:
> +        for (i = 0; i < 16; i++) {
> +            tmp_gain = gain[i] * QCELP_RATE_FULL_CODEBOOK_RATIO;
> +            cindex = -q->cindex[i];
> +            for (j = 0; j < 10; j++)
> +                *cdn_vector++ = tmp_gain * qcelp_rate_full_codebook[cindex++ & 127];
> +        }
> +        break;
> +    case RATE_HALF:
> +        for (i = 0; i < 4; i++) {
> +            tmp_gain = gain[i] * QCELP_RATE_HALF_CODEBOOK_RATIO;
> +            cindex = -q->cindex[i];
> +            for (j = 0; j < 40; j++)
> +                *cdn_vector++ = tmp_gain * qcelp_rate_half_codebook[cindex++ & 127];
> +        }
> +        break;
> +    case RATE_QUARTER:
> +        cbseed = (0x0003 & q->lspv[4])<<14 |
> +                 (0x003F & q->lspv[3])<< 8 |
> +                 (0x0060 & q->lspv[2])<< 1 |
> +                 (0x0007 & q->lspv[1])<< 3 |
> +                 (0x0038 & q->lspv[0])>> 3 ;
> +        rnd = q->rnd_fir_filter_mem + 20;
> +        for (i = 0; i < 8; i++) {
> +            tmp_gain = gain[i] * (QCELP_SQRT1887 / 32768.0);
> +            for (k = 0; k < 20; k++) {
> +                cbseed = 521 * cbseed + 259;
> +                *rnd = (int16_t)cbseed;
> +
> +                // FIR filter
> +                fir_filter_value = 0.0;
> +                for (j = 0; j < 10; j++)
> +                    fir_filter_value += qcelp_rnd_fir_coefs[j ] * (rnd[-j ] + rnd[-20+j]);
> +                fir_filter_value     += qcelp_rnd_fir_coefs[10] *  rnd[-10];
> +
> +                *cdn_vector++ = tmp_gain * fir_filter_value;
> +                rnd++;
> +            }
> +        }
> +        memcpy(q->rnd_fir_filter_mem, q->rnd_fir_filter_mem + 160, 20 * sizeof(float));
> +        break;
> +    case RATE_OCTAVE:
> +        cbseed = q->first16bits;
> +        for (i = 0; i < 8; i++) {
> +            tmp_gain = gain[i] * (QCELP_SQRT1887 / 32768.0);
> +            for (j = 0; j < 20; j++) {
> +                cbseed = 521 * cbseed + 259;
> +                *cdn_vector++ = tmp_gain * (int16_t)cbseed;
> +            }
> +        }
> +        break;
> +    case I_F_Q:
> +        cbseed = -44; // random codebook index
> +        for (i = 0; i < 4; i++) {
> +            tmp_gain = gain[i] * QCELP_RATE_FULL_CODEBOOK_RATIO;
> +            for (j = 0; j < 40; j++)
> +                *cdn_vector++ = tmp_gain * qcelp_rate_full_codebook[cbseed++ & 127];
> +        }
> +        break;
> +    }
> +}
> +
> +/**
> + * Apply generic gain control.
> + *
> + * @param v_out output vector
> + * @param v_in gain-controlled vector
> + * @param v_ref vector to control gain of
> + *
> + * 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 void apply_gain_ctrl(float *v_out,
> +                            const float *v_ref,
> +                            const float *v_in) {
> +    int   i, j, len;
> +    float scalefactor;
> +
> +    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
> +            av_log_missing_feature(NULL, "Zero energy for gain control", 1);
> +        for (len = j + 40; j < len; j++)
> +            v_out[j] = scalefactor * v_in[j];
> +    }
> +}

ok


> +
> +/**
>   * Apply filter in pitch-subframe steps.
>   *
>   * @param memory buffer for the previous state of the filter
> @@ -104,6 +425,70 @@
>  }
>  

>  /**
> + * Apply pitch synthesis filter and pitch prefilter to the scaled codebook vector.
> + * TIA/EIA/IS-733 2.4.5.2
> + *
> + * @param q the context
> + * @param cdn_vector the scaled codebook vector
> + *
> + * @return 0 on success, -1 if the lag is out of range
> + */
> +static int apply_pitch_filters(QCELPContext *q,
> +                               float *cdn_vector) {
> +    int         i;
> +    float       gain[4];
> +    const float *v_synthesis_filtered, *v_pre_filtered;
> +
> +    if (q->framerate >= RATE_HALF ||
> +       (q->framerate == I_F_Q && (q->prev_framerate >= RATE_HALF))) {
> +
> +        if (q->framerate >= RATE_HALF) {
> +
> +            // Compute gain & lag for the whole frame.
> +            for (i = 0; i < 4; i++) {
> +                gain[i] = q->plag[i] ? (q->pgain[i] + 1) / 4.0 : 0.0;
> +
> +                q->plag[i] += 16;
> +

> +                if (q->pfrac[i] && q->plag[i] >= 140)
> +                    return -1;

iam thinking that such bitstream checks should be closer to the bitstream
decoding.
this also would allow this function to have no return value

[...]

> @@ -131,9 +516,9 @@
>  
>      if (weight != 1.0) {
>          weighted_vector_sumf(interpolated_lspf, curr_lspf, q->prev_lspf, weight, 1.0 - weight, 10);
> -        lspf2lpc(q, interpolated_lspf, lpc);
> +        qcelp_lspf2lpc(interpolated_lspf, lpc);
>      } else if (q->framerate >= RATE_QUARTER || (q->framerate == I_F_Q && !subframe_num))
> -        lspf2lpc(q, curr_lspf, lpc);
> +        qcelp_lspf2lpc(curr_lspf, lpc);
>  }
>  
>  static int buf_size2framerate(const int buf_size) {

ok


> @@ -152,11 +537,140 @@
>      return -1;
>  }
>  
> +/*
> + * Determine the framerate from the frame size and/or the first byte of the frame.
> + *
> + * @param avctx the AV codec context
> + * @param buf_size length of the buffer
> + * @param buf the bufffer
> + *
> + * @return the framerate on success, RATE_UNKNOWN otherwise.
> + */
> +static int determine_framerate(AVCodecContext *avctx,
> +                               const int buf_size,
> +                               uint8_t **buf) {
> +    qcelp_packet_rate framerate;
> +
> +    if ((framerate = buf_size2framerate(buf_size)) >= 0) {
> +        if (framerate != **buf) {

iam not sure but didnt you at some point reorder the enum?
if so how can this code be correct before and afterwards?

[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

If a bugfix only changes things apparently unrelated to the bug with no
further explanation, that is a good sign that the bugfix is wrong.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20081114/2a5a56c2/attachment.pgp>



More information about the ffmpeg-devel mailing list