[FFmpeg-devel] [PATCH] QCELP decoder

Kenan Gillet kenan.gillet
Mon Nov 10 06:49:20 CET 2008


Hi,
On Nov 8, 2008, at 3:57 PM, Michael Niedermayer wrote:

> On Fri, Nov 07, 2008 at 09:24:25AM -0800, Kenan Gillet wrote:
>> Hi everybody,
>>
>> sorry for the delay,
>>
>> And here is another updated set of patches which makes it round 8.
>> and a small summary:
>
>> - optimizes qcelp_lspf2lpc which is now 60% faster.
>> 	should it still be in it own file or merging qcelp_lsp.c and  
>> qcelpdec.c is
>> ok?
>
> i need to look at this a little more but i think the final code  
> should be
> in some file seperate from th qcelpp decoder
>
>
>> - simplifies interpolate_lpc and determine_framerate after Michael  
>> comment.
>> - removes useless wrapper function.
>> - added minor version bump in libavcodec/avcodec.h
>> - it fixes the FIR filter to behave like in the reference code
>> - some cosmetics
>>
>
> [...]
>> +typedef struct {
>> +    GetBitContext     gb;
>> +    qcelp_packet_rate framerate;
>> +
>
>> +// beginning of unpacked data
>> +    uint8_t           cbsign[16];
>> +    uint8_t           cbgain[16];
>> +    uint8_t           cindex[16];
>> +    uint8_t           plag[4];
>> +    uint8_t           pfrac[4];
>> +    uint8_t           pgain[4];
>> +    uint8_t           lspv[10];               /*!< LSP for  
>> RATE_OCTAVE, LSPV for other rates */
>> +    uint8_t           reserved;               /*!< on all but rate  
>> 1/2 packets */
>> +// end of unpacked data
>
> doxygen has some tags to mark groups of things
> something like ///@{ or so

done


[...]


>> +/**
>> + * 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,
>> +                            float *gain,
>> +                            float *cdn_vector) {
>> +    int      i, j, k;
>> +    uint16_t cbseed, cindex;
>> +    float    *rnd;
>> +
>> +    switch (q->framerate) {
>> +    case RATE_FULL:
>> +        for (i = 0; i < 16; i++) {
>
>> +            gain[i] *= QCELP_RATE_FULL_CODEBOOK_RATIO;
>
> it is very hackish to misuse gain as a temporary variable
> gain should be const

done


>> +            cindex = -q->cindex[i];
>> +            for (j = 0; j < 10; j++)
>> +                *cdn_vector++ = gain[i] *  
>> qcelp_rate_full_codebook[cindex++ & 127];
>> +        }
>> +        break;
>> +    case RATE_HALF:
>> +        for (i = 0; i < 4; i++) {
>> +            gain[i] *= QCELP_RATE_HALF_CODEBOOK_RATIO;
>> +            cindex = -q->cindex[i];
>> +            for (j = 0; j < 40; j++)
>> +                *cdn_vector++ = gain[i] *  
>> 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->fir_filter_mem + 20;
>> +        for (i = 0; i < 8; i++) {
>> +            gain[i] *= QCELP_SQRT1887 / 32768.0;
>> +            for (k = 0; k < 20; k++) {
>> +                cbseed = 521 * cbseed + 259;
>> +                *rnd = (int16_t)cbseed;
>> +
>
>> +                // FIR filter
>> +                *cdn_vector = 0.0;
>> +                for (j = 0; j < 10; j++)
>> +                    *cdn_vector += qcelp_rnd_fir_coefs[j ] * (rnd[- 
>> j ] + rnd[-20+j]);
>> +                *cdn_vector     += qcelp_rnd_fir_coefs[10] *   
>> rnd[-10];
>> +
>> +                *cdn_vector++ *= gain[i];
>
> i think this would be clearer with a temporary variable insteda of  
> using
> *cdn_vector, also it should help gcc in terms of optimization  
> simplicity
> as gcc can not exclude that cdn_vector is pointing to gain or another
> array.

done


> [...]
>> +/**
>> + * Apply generic gain control.
>> + *
>> + * @param v_out output vector
>> + * @param v_in vector to control gain of
>> + * @param v_gain gain-controlled vector
>> + *
>> + * 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_in,
>> +                            const float *v_gain) {
>> +    int   i, j, len;
>> +    float scalefactor;
>> +
>> +    for (i = 0, j = 0; i < 4; i++) {
>> +        scalefactor = ff_dot_productf(v_gain + j, v_gain + j, 40);
>> +        if (scalefactor) {
>> +            scalefactor = sqrt(ff_dot_productf(v_in + j, v_in + j,  
>> 40) / scalefactor);
>> +            for (len = j + 40; j < len; j++)
>> +                v_out[j] = scalefactor * v_gain[j];
>
> somehow the variable naming seems exchanged
> one would have expected vin->vout and controlled by a vref

done


>> +        } else {
>> +            memset(v_out + j, 0,  40 * sizeof(float));
>> +            j += 40;
>> +        }
>> +    }
>> +}
>> +
>> +/**
>>  * Apply filter in pitch-subframe steps.
>>  *
>>  * @param memory buffer for the previous state of the filter
>> @@ -90,7 +453,260 @@
>>     return memory + 143;
>> }
>>
>> +/**
>> + * 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;
>> +            }
>> +            memcpy(q->prev_pitch_lag, q->plag, 4 * sizeof(*q- 
>> >plag));
>> +        } else {
>> +            gain[3] = q->erasure_count < 3 ? 0.9 - 0.3 * (q- 
>> >erasure_count - 1)
>> +                                           : 0.0;
>> +            for (i = 0; i < 4; i++)
>> +                gain[i] = FFMIN(q->prev_pitch_gain[i], gain[3]);
>> +
>
>> +            memset(q->pfrac, 0, 4 *sizeof(*q->pfrac));
>> +            memcpy(q->plag, q->prev_pitch_lag, 4 * sizeof(*q- 
>> >plag));
>
> s/4*sizeof(*)/ sizeof()/

done


>> +        }
>> +
>> +        // pitch synthesis filter
>> +        v_synthesis_filtered = do_pitchfilter(q- 
>> >pitch_synthesis_filter_mem, cdn_vector,
>> +                                              gain, q->plag, q- 
>> >pfrac);
>> +
>> +        // pitch prefilter update
>> +        for (i = 0; i < 4; i++)
>> +            gain[i] = 0.5 * FFMIN(gain[i], 1.0);
>> +
>> +        v_pre_filtered = do_pitchfilter(q->pitch_pre_filter_mem,  
>> v_synthesis_filtered,
>> +                                        gain, q->plag, q->pfrac);
>> +
>> +        apply_gain_ctrl(cdn_vector, v_synthesis_filtered,  
>> v_pre_filtered);
>> +
>> +        memcpy(q->prev_pitch_gain, gain, sizeof(q- 
>> >prev_pitch_gain));
>> +
>> +    } else {
>> +        memcpy(q->pitch_synthesis_filter_mem, cdn_vector + 17, 143  
>> * sizeof(float));
>> +        memcpy(q->pitch_pre_filter_mem,       cdn_vector + 17, 143  
>> * sizeof(float));
>> +        memset(q->prev_pitch_gain, 0, sizeof(q->prev_pitch_gain));
>
>> +        memset(q->prev_pitch_lag,  0, 4 * sizeof(*q->plag));
>
> same

done


>> +    }
>> +    return 0;
>> +}
>> +
>
>> +/**
>> + * Interpolates LSP frequencies and computes LPC coefficients
>> + * for a given framerate & pitch subframe.
>> + *
>> + * TIA/EIA/IS-733 2.4.3.3.4
>> + *
>> + * @param q the context
>> + * @param curr_lspf LSP frequencies vector of the current frame
>> + * @param lpc float vector for the resulting LPC
>> + * @param subframe_num frame number in decoded stream
>> + */
>> +void interpolate_lpc(QCELPContext *q,
>> +                     const float *curr_lspf,
>> +                     float *lpc,
>> +                     const int subframe_num) {
>> +    float interpolated_lspf[10];
>> +    float weight;
>> +
>> +    if (q->framerate >= RATE_QUARTER) {
>> +        weight = 0.25 * (subframe_num + 1);
>> +    } else if (q->framerate == RATE_OCTAVE && !subframe_num) {
>> +        weight = 0.625;
>> +    } else {
>> +        weight = 1.0;
>> +    }
>> +
>> +    if (weight != 1.0) {
>> +        weighted_vector_sumf(interpolated_lspf, curr_lspf, q- 
>> >prev_lspf, weight, 1.0 - weight, 10);
>
>> +        if (q->framerate >= RATE_QUARTER || !subframe_num)
>> +            lspf2lpc(q, interpolated_lspf, lpc);
>
> this if is unneeded

done


>> +    } else if (q->framerate >= RATE_QUARTER || (q->framerate ==  
>> I_F_Q && !subframe_num))
>> +        lspf2lpc(q, curr_lspf, lpc);
>> +}
>> +
>
>> +static int buf_size2framerate(const int buf_size) {
>> +    switch (buf_size) {
>> +    case 35:
>> +        return RATE_FULL;
>> +    case 17:
>> +        return RATE_HALF;
>> +    case  8:
>> +        return RATE_QUARTER;
>> +    case  4:
>> +        return RATE_OCTAVE;
>> +    case  1:
>> +        return SILENCE;
>> +    }
>> +    return -1;
>> +}
>
> ok
>
>
>> +/*
>> + * Determine the framerate from the frame size and/or the first  
>> byte of the frame.
>> + *
>> + * @param avctx the AV codec context
>> + * @param q the QCELP context
>> + * @param buf_size length of the buffer
>> + * @param buf the bufffer
>> + *
>> + * @return 0 on success, negative error number otherwise.
>> + */
>> +static int determine_framerate(AVCodecContext *avctx,
>> +                               QCELPContext *q,
>> +                               const int buf_size,
>> +                               uint8_t **buf) {
>> +    if ((q->framerate = buf_size2framerate(buf_size)) >= 0) {
>> +        if (q->framerate != **buf) {
>> +            av_log(avctx, AV_LOG_WARNING, "Claimed framerate and  
>> buffer size mismatch.\n");
>> +            q->framerate = **buf;
>> +        }
>> +        (*buf)++;
>> +    } else if ((q->framerate = buf_size2framerate(buf_size + 1))  
>> >= 0)
>> +        av_log(avctx, AV_LOG_WARNING,
>> +               "Framerate byte is missing, guessing the framerate  
>> from packet size.\n");
>> +    else
>> +        return -1;
>> +
>> +    if (q->framerate == SILENCE) {
>> +        // FIXME: the decoder should not handle SILENCE frames as  
>> I_F_Q frames
>> +        av_log_missing_feature(avctx, "Blank frame", 1);
>> +        q->framerate = I_F_Q;
>> +    }
>> +    return 0;
>> +}
>
> IMHO this should return the "frame rate" instead of setting q- 
> >framerate
> behind the curtain.

done

New patch attached which makes it round 9:
- above changes
- the qcelp_g12ga changed back to float because it seems
          see other email for benchmark

Kenan


-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: qcelp-round9-decoder.patch.txt
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20081109/b0889ca8/attachment.txt>
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: qcelp-round9-doc-glue.patch.txt
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20081109/b0889ca8/attachment-0001.txt>
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: qcelp-round9-lsp.patch.txt
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20081109/b0889ca8/attachment-0002.txt>



More information about the ffmpeg-devel mailing list