[FFmpeg-devel] [PATCH] QCELP decoder
Michael Niedermayer
michaelni
Sun Nov 9 00:57:59 CET 2008
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
> +
> + uint8_t erasure_count;
> + uint8_t octave_count; /*!< count the consecutive RATE_OCTAVE frames */
> + float prev_lspf[10];
> + float predictor_lspf[10]; /*!< LSP predictor,
> + only use for RATE_OCTAVE and I_F_Q */
> + float pitch_synthesis_filter_mem[303];
> + float pitch_pre_filter_mem[303];
> + float formant_mem[170];
> + float fir_filter_mem[180];
> + float last_codebook_gain;
> + int prev_g1[2];
> + int prev_framerate;
> + float prev_pitch_gain[4];
> + uint8_t prev_pitch_lag[4];
> + uint16_t first16bits;
> + float bandwith_expansion_coeff[10];
> +#define QCELP_BANDWITH_EXPANSION_COEFF 0.9883
> +
> + float g12ga[61]; /*!< unpacked qcelp_g12ga table */
hmm, constant tables must not be in the context as that wastes memory.
depending on how often it is accessed, qcelp_g12ga should either be
changed back to float or the int16->float be done as it is accessed.
(int16->float is prefered due to smaller memory if there is no speed
differeence otherwise the faster one should be used)
[...]
> +/**
> + * circular codebook for rate 1 frames in x*100 form
> + *
> + * TIA/EIA/IS-733 2.4.6.1-2
> + */
> +static const int16_t qcelp_rate_full_codebook[128] = {
> + 10, -65, -59, 12, 110, 34, -134, 157,
> + 104, -84, -34, -115, 23, -101, 3, 45,
> + -101, -16, -59, 28, -45, 134, -67, 22,
> + 61, -29, 226, -26, -55, -179, 157, -51,
> + -220, -93, -37, 60, 118, 74, -48, -95,
> + -181, 111, 36, -52, -215, 78, -112, 39,
> + -17, -47, -223, 19, 12, -98, -142, 130,
> + 54, -127, 21, -12, 39, -48, 12, 128,
> + 6, -167, 82, -102, -79, 55, -44, 48,
> + -20, -53, 8, -61, 11, -70, -157, -168,
> + 20, -56, -74, 78, 33, -63, -173, -2,
> + -75, -53, -146, 77, 66, -29, 9, -75,
> + 65, 119, -43, 76, 233, 98, 125, -156,
> + -27, 78, -9, 170, 176, 143, -148, -7,
> + 27, -136, 5, 27, 18, 139, 204, 7,
> + -184, -197, 52, -3, 78, -189, 8, -65
> +};
> +#define QCELP_RATE_FULL_CODEBOOK_RATIO .01
> +
> +/**
> + * circular codebook for rate 1/2 frames in x*2 form
> + *
> + * TIA/EIA/IS-733 2.4.6.1-1
> + */
> +static const int8_t qcelp_rate_half_codebook[128] = {
> + 0, -4, 0, -3, 0, 0, 0, 0,
> + 0, 0, 0, 0, 0, 0, 0, 0,
> + 0, -3, -2, 0, 0, 0, 0, 0,
> + 0, 0, 0, 0, 0, 0, 0, 5,
> + 0, 0, 0, 0, 0, 0, 4, 0,
> + 0, 3, 2, 0, 3, 4, 0, 0,
> + 0, 0, 0, 0, 0, 0, 0, 0,
> + 0, 0, 0, 0, 0, 3, 0, 0,
> + -3, 3, 0, 0, -2, 0, 3, 0,
> + 0, 0, 0, 0, 0, 0, -5, 0,
> + 0, 0, 0, 3, 0, 0, 0, 3,
> + 0, 0, 0, 0, 0, 0, 0, 4,
> + 0, 0, 0, 0, 0, 0, 0, 0,
> + 0, 3, 6, -3, -4, 0, -3, -3,
> + 3, -3, 0, 0, 0, 0, 0, 0,
> + 0, 0, 0, 0, 0, 0, 0, 0
> +};
> +#define QCELP_RATE_HALF_CODEBOOK_RATIO 0.5
ok
[...]
> Index: libavcodec/qcelpdec.c
> ===================================================================
> --- libavcodec/qcelpdec.c (revision 15785)
> +++ libavcodec/qcelpdec.c (working copy)
> @@ -38,7 +38,370 @@
> #undef NDEBUG
> #include <assert.h>
>
> +static void weighted_vector_sumf(float *out,
> + const float *in_a,
> + const float *in_b,
> + float weight_coeff_a,
> + float weight_coeff_b,
> + int length) {
> + int i;
> +
> + for (i = 0; i < length; i++)
> + out[i] = weight_coeff_a * in_a[i]
> + + weight_coeff_b * in_b[i];
> +}
> +
ok
[...]
> +/**
> + * 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
> + 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.
[...]
> +/**
> + * 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
> + } 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()/
> + }
> +
> + // 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
> + }
> + 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
> + } 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.
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
If you really think that XML is the answer, then you definitly missunderstood
the question -- Attila Kinali
-------------- 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/20081109/d0b5c0c8/attachment.pgp>
More information about the ffmpeg-devel
mailing list