[FFmpeg-devel] [PATCH] QCELP decoder

Kenan Gillet kenan.gillet
Wed Oct 29 00:26:36 CET 2008


On Oct 24, 2008, at 6:00 PM, Michael Niedermayer wrote:

> On Fri, Oct 17, 2008 at 09:46:31AM -0700, Kenan Gillet wrote:
>> Hi,
>>
>> On Thu, Oct 16, 2008 at 11:51 PM, Diego Biurrun <diego at biurrun.de>  
>> wrote:
>>> On Thu, Oct 16, 2008 at 03:38:03PM -0700, Kenan Gillet wrote:
>>>> On Oct 15, 2008, at 3:09 PM, Michael Niedermayer wrote:
>>>>
>>>>> cOn Tue, Oct 14, 2008 at 06:59:34PM -0700, Kenan Gillet wrote:
>>>>>>
>>>>>> --- libavcodec/qcelp.h     (revision 0)
>>>>>> +++ libavcodec/qcelp.h     (revision 0)
>>>>>> +static float compute_subframe_energy(const float *vector, const
>>>>>> int subframeno) {
>>>>>> +    int   i;
>>>>>> +    float energy = 0;
>>>>>> +
>>>>>> +    vector += 40 * subframeno;
>>>>>> +
>>>>>> +    for (i = 0; i < 40; i++)
>>>>>> +        energy += vector[i] * vector[i];
>>>>>> +
>>>>>> +    return energy;
>>>>>> +}
>>>>>
>>>>> IMHO its cleaner not to add subframeno inside the function.
>>>>> a simple dot_product(vector, len) should be more readable
>>>>> also such a function has a much better chance to be shareable
>>>>> between codec, and especially when things like that are  
>>>>> optimized its
>>>>> better if it isnt needed to be redone for each len and other  
>>>>> obscure
>>>>> variant
>>>>
>>>> done, I svn cp libavcodec/acelp_math.c into libavcodec/qcelp_math.c
>>>> and made it ff_dot_product.
>>>> or is there a better place for that ?
>>>
>>> If the file is used for both acelp and qcelp, it should not be  
>>> called
>>> qcelp, but celp instead.
>>
>> done
>>
>>>
>>>> Should it be in a different patch ?
>>>
>>> Smaller patches get reviewed and applied faster.
>>
>> round 6 summary:
>> - no code change from round 5
>> - replace svn cp by svn add to make the patch smaller
>> - rename qcelp_math.[ch] into celp_math.[ch]
>> - the patch is now broken into:
>>    - qcelp-round6-decoder.patch.txt
>>    core decoder code
>>
>>    - qcelp-round6-doc-glue.patch.txt
>>    doc and glue code
>>
>>    - qcelp-round6-filters.patch.txt
>>    filters functions
>>
>>    - qcelp-round6-lsp.patch.txt
>>    lsp functions
>>
>>    - qcelp-round6-math.patch.txt
>>    do_product
>>
>>
>> Let me know if I can do anything to ease you reviewing effort.
> [...]
>
>> +
>> +static const uint16_t qcelp_bits_per_rate[6] = {
>> +    0, ///!< for SILENCE rate
>> +    sizeof(qcelp_rate_octave_bitmap)  /  
>> sizeof(*qcelp_rate_octave_bitmap),
>> +    sizeof(qcelp_rate_quarter_bitmap) /  
>> sizeof(*qcelp_rate_quarter_bitmap),
>> +    sizeof(qcelp_rate_half_bitmap)    /  
>> sizeof(*qcelp_rate_half_bitmap),
>> +    sizeof(qcelp_rate_full_bitmap)    /  
>> sizeof(*qcelp_rate_full_bitmap),
>> +    0  ///!< for I_F_Q rate
>> +};
>
> FF_ARRAY_ELEMS

done



>> +
>> +typedef uint16_t qcelp_vector[2];
>> +
>> +/**
>> + * LSP vector quantization tables in x*10000 form
>> + *
>> + * TIA/EIA/IS-733 tables 2.4.3.2.6.3-1 through 2.4.3.2.6.3-5
>> + */
>> +
>> +static const qcelp_vector qcelp_lspvq1[64]= {
>> +{ 327, 118},{ 919, 111},{ 427, 440},{1327, 185},
>> +{ 469,  50},{1272,  91},{ 892,  59},{1771, 193},
>> +{ 222, 158},{1100, 127},{ 827,  55},{ 978, 791},
>> +{ 665,  47},{ 700,1401},{ 670, 859},{1913,1048},
>> +{ 471, 215},{1046, 125},{ 645, 298},{1599, 160},
>> +{ 593,  39},{1187, 462},{ 749, 341},{1520, 511},
>> +{ 290, 792},{ 909, 362},{ 753,  81},{1111,1058},
>> +{ 519, 253},{ 828, 839},{ 685, 541},{1421,1258},
>> +{ 386, 130},{ 962, 119},{ 542, 387},{1431, 185},
>> +{ 526,  51},{1175, 260},{ 831, 167},{1728, 510},
>> +{ 273, 437},{1172, 113},{ 771, 144},{1122, 751},
>> +{ 619, 119},{ 492,1276},{ 658, 695},{1882, 615},
>> +{ 415, 200},{1018,  88},{ 681, 339},{1436, 325},
>> +{ 555, 122},{1042, 485},{ 826, 345},{1374, 743},
>> +{ 383,1018},{1005, 358},{ 704,  86},{1301, 586},
>> +{ 597, 241},{ 832, 621},{ 555, 573},{1504, 839}};
>> +
>> +static const qcelp_vector qcelp_lspvq2[128]= {
>> +{ 255, 293},{ 904, 219},{ 151,1211},{1447, 498},
>> +{ 470, 253},{1559, 177},{1547, 994},{2394, 242},
>> +{  91, 813},{ 857, 590},{ 934,1326},{1889, 282},
>> +{ 813, 472},{1057,1494},{ 450,3315},{2163,1895},
>> +{ 538, 532},{1399, 218},{ 146,1552},{1755, 626},
>> +{ 822, 202},{1299, 663},{ 706,1732},{2656, 401},
>> +{ 418, 745},{ 762,1038},{ 583,1748},{1746,1285},
>> +{ 527,1169},{1314, 830},{ 556,2116},{1073,2321},
>> +{ 297, 570},{ 981, 403},{ 468,1103},{1740, 243},
>> +{ 725, 179},{1255, 474},{1374,1362},{1922, 912},
>> +{ 285, 947},{ 930, 700},{ 593,1372},{1909, 576},
>> +{ 588, 916},{1110,1116},{ 224,2719},{1633,2220},
>> +{ 402, 520},{1061, 448},{ 402,1352},{1499, 775},
>> +{ 664, 589},{1081, 727},{ 801,2206},{2165,1157},
>> +{ 566, 802},{ 911,1116},{ 306,1703},{1792, 836},
>> +{ 655, 999},{1061,1038},{ 298,2089},{1110,1753},
>> +{ 361, 311},{ 970, 239},{ 265,1231},{1495, 573},
>> +{ 566, 262},{1569, 293},{1341,1144},{2271, 544},
>> +{ 214, 877},{ 847, 719},{ 794,1384},{2067, 274},
>> +{ 703, 688},{1099,1306},{ 391,2947},{2024,1670},
>> +{ 471, 525},{1245, 290},{ 264,1557},{1568, 807},
>> +{ 718, 399},{1193, 685},{ 883,1594},{2729, 764},
>> +{ 500, 754},{ 809,1108},{ 541,1648},{1523,1385},
>> +{ 614,1196},{1209, 847},{ 345,2242},{1442,1747},
>> +{ 199, 560},{1092, 194},{ 349,1253},{1653, 507},
>> +{ 625, 354},{1376, 431},{1187,1465},{2164, 872},
>> +{ 360, 974},{1008, 698},{ 704,1346},{2114, 452},
>> +{ 720, 816},{1240,1089},{ 439,2475},{1498,2040},
>> +{ 336, 718},{1213, 187},{ 451,1450},{1368, 885},
>> +{ 592, 578},{1131, 531},{ 861,1855},{1764,1500},
>> +{ 444, 970},{ 935, 903},{ 424,1687},{1633,1102},
>> +{ 793, 897},{1060, 897},{ 185,2011},{1205,1855}};
>> +
>> +static const qcelp_vector qcelp_lspvq3[128]= {
>> +{ 225, 283},{1296, 355},{ 543, 343},{2073, 274},
>> +{ 204,1099},{1562, 523},{1388, 161},{2784, 274},
>> +{ 112, 849},{1870, 175},{1189, 160},{1490,1088},
>> +{ 969,1115},{ 659,3322},{1158,1073},{3183,1363},
>> +{ 517, 223},{1740, 223},{ 704, 387},{2637, 234},
>> +{ 692,1005},{1287,1610},{ 952, 532},{2393, 646},
>> +{ 490, 552},{1619, 657},{ 845, 670},{1784,2280},
>> +{ 191,1775},{ 272,2868},{ 942, 952},{2628,1479},
>> +{ 278, 579},{1565, 218},{ 814, 180},{2379, 187},
>> +{ 276,1444},{1199,1223},{1200, 349},{3009, 307},
>> +{ 312, 844},{1898, 306},{ 863, 470},{1685,1241},
>> +{ 513,1727},{ 711,2233},{1085, 864},{3398, 527},
>> +{ 414, 440},{1356, 612},{ 964, 147},{2173, 738},
>> +{ 465,1292},{ 877,1749},{1104, 689},{2105,1311},
>> +{ 580, 864},{1895, 752},{ 652, 609},{1485,1699},
>> +{ 514,1400},{ 386,2131},{ 933, 798},{2473, 986},
>> +{ 334, 360},{1375, 398},{ 621, 276},{2183, 280},
>> +{ 311,1114},{1382, 807},{1284, 175},{2605, 636},
>> +{ 230, 816},{1739, 408},{1074, 176},{1619,1120},
>> +{ 784,1371},{ 448,3050},{1189, 880},{3039,1165},
>> +{ 424, 241},{1672, 186},{ 815, 333},{2432, 324},
>> +{ 584,1029},{1137,1546},{1015, 585},{2198, 995},
>> +{ 574, 581},{1746, 647},{ 733, 740},{1938,1737},
>> +{ 347,1710},{ 373,2429},{ 787,1061},{2439,1438},
>> +{ 185, 536},{1489, 178},{ 703, 216},{2178, 487},
>> +{ 154,1421},{1414, 994},{1103, 352},{3072, 473},
>> +{ 408, 819},{2055, 168},{ 998, 354},{1917,1140},
>> +{ 665,1799},{ 993,2213},{1234, 631},{3003, 762},
>> +{ 373, 620},{1518, 425},{ 913, 300},{1966, 836},
>> +{ 402,1185},{ 948,1385},{1121, 555},{1802,1509},
>> +{ 474, 886},{1888, 610},{ 739, 585},{1231,2379},
>> +{ 661,1335},{ 205,2211},{ 823, 822},{2480,1179}};
>> +
>> +static const qcelp_vector qcelp_lspvq4[64]= {
>> +{ 348, 311},{ 812,1145},{ 552, 461},{1826, 263},
>> +{ 601, 675},{1730, 172},{1523, 193},{2449, 277},
>> +{ 334, 668},{ 805,1441},{1319, 207},{1684, 910},
>> +{ 582,1318},{1403,1098},{ 979, 832},{2700,1359},
>> +{ 624, 228},{1292, 979},{ 800, 195},{2226, 285},
>> +{ 730, 862},{1537, 601},{1115, 509},{2720, 354},
>> +{ 218,1167},{1212,1538},{1074, 247},{1674,1710},
>> +{ 322,2142},{1263, 777},{ 981, 556},{2119,1710},
>> +{ 193, 596},{1035, 957},{ 694, 397},{1997, 253},
>> +{ 743, 603},{1584, 321},{1346, 346},{2221, 708},
>> +{ 451, 732},{1040,1415},{1184, 230},{1853, 919},
>> +{ 310,1661},{1625, 706},{ 856, 843},{2902, 702},
>> +{ 467, 348},{1108,1048},{ 859, 306},{1964, 463},
>> +{ 560,1013},{1425, 533},{1142, 634},{2391, 879},
>> +{ 397,1084},{1345,1700},{ 976, 248},{1887,1189},
>> +{ 644,2087},{1262, 603},{ 877, 550},{2203,1307}};
>> +
>> +static const qcelp_vector qcelp_lspvq5[64]= {
>> +{ 360, 222},{ 820,1097},{ 601, 319},{1656, 198},
>> +{ 604, 513},{1552, 141},{1391, 155},{2474, 261},
>> +{ 269, 785},{1463, 646},{1123, 191},{2015, 223},
>> +{ 785, 844},{1202,1011},{ 980, 807},{3014, 793},
>> +{ 570, 180},{1135,1382},{ 778, 256},{1901, 179},
>> +{ 807, 622},{1461, 458},{1231, 178},{2028, 821},
>> +{ 387, 927},{1496,1004},{ 888, 392},{2246, 341},
>> +{ 295,1462},{1156, 694},{1022, 473},{2226,1364},
>> +{ 210, 478},{1029,1020},{ 722, 181},{1730, 251},
>> +{ 730, 488},{1465, 293},{1303, 326},{2595, 387},
>> +{ 458, 584},{1569, 742},{1029, 173},{1910, 495},
>> +{ 605,1159},{1268, 719},{ 973, 646},{2872, 428},
>> +{ 443, 334},{ 835,1465},{ 912, 138},{1716, 442},
>> +{ 620, 778},{1316, 450},{1186, 335},{1446,1665},
>> +{ 486,1050},{1675,1019},{ 880, 278},{2214, 202},
>> +{ 539,1564},{1142, 533},{ 984, 391},{2130,1089}};
>> +
>> +static const qcelp_vector * const qcelp_lspvq[5] = {
>> +    qcelp_lspvq1,
>> +    qcelp_lspvq2,
>> +    qcelp_lspvq3,
>> +    qcelp_lspvq4,
>> +    qcelp_lspvq5
>> +};
>
> ok

should i remove it in the next patches, leave it, or put it in a oked  
file ?


>> +
>> +
>> +/**
>> + * the final gain scalefactor before clipping into a usable output  
>> float
>> + */
>> +#define QCELP_SCALE 8192.
>> +
>
>> +/**
>> + * table for computing Ga (decoded linear codebook gain magnitude)
>> + *
>> + * TIA/EIA/IS-733 2.4.6.2.1-3
>> + */
>> +
>> +static const float qcelp_g12ga[61] = {
>> +    1.000/QCELP_SCALE,   1.125/QCELP_SCALE,   1.250/QCELP_SCALE,    
>> 1.375/QCELP_SCALE,
>> +    1.625/QCELP_SCALE,   1.750/QCELP_SCALE,   2.000/QCELP_SCALE,    
>> 2.250/QCELP_SCALE,
>> +    2.500/QCELP_SCALE,   2.875/QCELP_SCALE,   3.125/QCELP_SCALE,    
>> 3.500/QCELP_SCALE,
>> +    4.000/QCELP_SCALE,   4.500/QCELP_SCALE,   5.000/QCELP_SCALE,    
>> 5.625/QCELP_SCALE,
>> +    6.250/QCELP_SCALE,   7.125/QCELP_SCALE,   8.000/QCELP_SCALE,    
>> 8.875/QCELP_SCALE,
>> +   10.000/QCELP_SCALE,  11.250/QCELP_SCALE,  12.625/QCELP_SCALE,   
>> 14.125/QCELP_SCALE,
>> +   15.875/QCELP_SCALE,  17.750/QCELP_SCALE,  20.000/QCELP_SCALE,   
>> 22.375/QCELP_SCALE,
>> +   25.125/QCELP_SCALE,  28.125/QCELP_SCALE,  31.625/QCELP_SCALE,   
>> 35.500/QCELP_SCALE,
>> +   39.750/QCELP_SCALE,  44.625/QCELP_SCALE,  50.125/QCELP_SCALE,   
>> 56.250/QCELP_SCALE,
>> +   63.125/QCELP_SCALE,  70.750/QCELP_SCALE,  79.375/QCELP_SCALE,   
>> 89.125/QCELP_SCALE,
>> +  100.000/QCELP_SCALE, 112.250/QCELP_SCALE, 125.875/QCELP_SCALE,  
>> 141.250/QCELP_SCALE,
>> +  158.500/QCELP_SCALE, 177.875/QCELP_SCALE, 199.500/QCELP_SCALE,  
>> 223.875/QCELP_SCALE,
>> +  251.250/QCELP_SCALE, 281.875/QCELP_SCALE, 316.250/QCELP_SCALE,  
>> 354.875/QCELP_SCALE,
>> +  398.125/QCELP_SCALE, 446.625/QCELP_SCALE, 501.125/QCELP_SCALE,  
>> 563.375/QCELP_SCALE,
>> +  631.000/QCELP_SCALE, 708.000/QCELP_SCALE, 794.375/QCELP_SCALE,  
>> 891.250/QCELP_SCALE,
>> + 1000.000/QCELP_SCALE};
>
> this fits in a uint16_t when the numbers are multiplied by 8

done, also corrected wrong value 562.275 instead of 563.275


>> +/**
>> + * circular codebook for rate 1 frames
>> + * 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,
>
> the space before 59 looks odd

done


> [...]
>> +/**
>> + * Verify the samplerate and the number of channels.
>> + * Initialize the speech codec according to the specification.
>> + *
>> + * TIA/EIA/IS-733 2.4.9
>> + */
>> +static 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;
>> +}
>
> "Verify the samplerate and the number of channels." ?
> i cant see what this comment means in relation to this function

removed, code related to the comment is now in mov.c


>> +
>> +/**
>> + * Decodes the 10 quantized LSP frequencies from the LSPV/LSP
>> + * transmission codes of any framerate and check for badly  
>> received packets.
>> + *
>> + * @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;
>> +        float erasure_coeff = 1.0;
>> +        const float *predictors = (q->prev_framerate !=  
>> RATE_OCTAVE ||
>> +                                   q->prev_framerate != I_F_Q ? q- 
>> >prev_lspf
>> +                                                              : q- 
>> >predictor_lspf);
>> +
>
>> +        if (q->framerate == RATE_OCTAVE) {
>> +            q->octave_count++;
>> +            smooth = (q->octave_count < 10 ? .875 : 0.1);
>> +        } else {
>> +            if (q->erasure_count > 1)
>> +                erasure_coeff = (q->erasure_count < 4 ? 0.9 : 0.7);
>> +            smooth = 0.125;
>> +        }
>
> i think it would be more readable if erasure_coeff would be set to 1  
> in a
> else here instead of above, its not used in the RATE_OCTAVE case  
> anyway

done


>> +
>> +        for (i = 0; i < 10; i++) {
>> +            lspf[i] = (i + 1) / 11.;
>> +            if (q->framerate == RATE_OCTAVE) {
>> +                q->predictor_lspf[i]  =
>> +                             lspf[i] += (q->lspv[i] ?   
>> QCELP_LSP_SPREAD_FACTOR
>> +                                                    : - 
>> QCELP_LSP_SPREAD_FACTOR)
>> +                                      + (predictors[i] - lspf[i])  
>> * QCELP_LSP_OCTAVE_PREDICTOR;
>> +            } else {
>> +                q->predictor_lspf[i] = (predictors[i] - lspf[i]) *  
>> erasure_coeff;
>> +                lspf[i] += QCELP_LSP_OCTAVE_PREDICTOR * q- 
>> >predictor_lspf[i];
>> +            }
>> +        }
>
> the for () can be split and the 2 put under te if/else above
> i belive this would be more readable and faster

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
>> + * other than RATE_FULL or RATE_HALF
>> + * @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;
>> +    uint16_t cbseed;
>> +    float    rnd[160];
>> +
>> +    switch (q->framerate) {
>> +    case RATE_FULL:
>> +        for (i = 0; i < 16; i++)
>> +            for (j = 0; j < 10; j++)
>> +                *cdn_vector++ = gain[i] *  
>> qcelp_rate_full_codebook[(j - q->cindex[i]) & 127]
>> +                              * QCELP_RATE_FULL_CODEBOOK_RATIO;
>> +        break;
>> +    case RATE_HALF:
>> +        for (i = 0; i < 4; i++)
>> +            for (j = 0; j < 40; j++)
>> +                *cdn_vector++ = gain[i] *  
>> qcelp_rate_half_codebook[(j - q->cindex[i]) & 127]
>> +                              * QCELP_RATE_HALF_CODEBOOK_RATIO;
>> +        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 ;
>
>> +        for (i = 0; i < 160; i++) {
>> +            cbseed = 521 * cbseed + 259;
>> +            rnd[i] = (QCELP_SQRT1887 / 32768.0) * (int16_t)cbseed;
>> +
>
>> +            // FIR filter
>> +            cdn_vector[i] = qcelp_rnd_fir_coefs[1] * rnd[i];
>> +            for (j = 1; j < 22 && !(i-j+1); j++)
>> +                cdn_vector[i] += qcelp_rnd_fir_coefs[j]*rnd[i-j];
>
> the rnd array can be made larger by ~22 entries to avoid !(i-j+1)  
> check
> it can also be optimized to
> for()
>    cdn_vector[i] += qcelp_rnd_fir_coefs[j]*(rnd[i-j] + rnd[i+j])

I checked this FIR filter with the reference code, and it seems not to  
match it.
working on it, and will post it in a future round if it is ok with you.


>> +
>> +            cdn_vector[i] *= gain[i/20];
>
> division is slow, the loop can be split to avoid that

done


>> +        }
>> +        break;
>> +    case RATE_OCTAVE:
>> +        cbseed = q->first16bits;
>> +        for (i = 0; i < 8; i++)
>> +            for (j = 0; j < 20; j++) {
>> +                cbseed = 521 * cbseed + 259;
>> +                cdn_vector[20*i+j] = gain[i]
>> +                                   * (QCELP_SQRT1887 / 32768.0) *  
>> (int16_t)cbseed;
>> +            }
>
> gain[i] * (QCELP_SQRT1887 / 32768.0)
> can be calculated outside of the loop

done, it gives slightly different outputs though, probably due to  
float rounding error.



>> +        break;
>> +    case I_F_Q:
>
>> +        cbseed = 44; // random codebook index
>> +        for (i = 0; i < 160; i++)
>> +            cdn_vector[i] = gain[i/40] *  
>> qcelp_rate_full_codebook[(i-cbseed) & 127]
>> +                          * QCELP_RATE_FULL_CODEBOOK_RATIO;
>
> the loop can be split to avoid the /40

done


>> +        break;
>> +    }
>> +}
>> +
>
>> +/**
>> + * 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 = sqrt(ff_dot_product(v_in  + j, v_in  + j,  
>> 40) /
>> +                           ff_dot_product(v_gain + j, v_gain + j,  
>> 40));
>
> verical align
> and a 1/0 issue (no i am not sure if it can happen but you said
> IIRC it can be 0)

done, I believe that a SILENCE frame as the first frame would trigger  
a /0.



>> +        if (scalefactor)
>> +            for (len = j + 40; j < len; j++)
>> +                v_out[j] = scalefactor * v_gain[j];
>> +    }
>> +}
>> +
>> +/**
>> + * Apply filter in pitch-subframe steps.
>> + *
>> + * @param memory buffer for the previous state of the filter
>> + *        - must be able to contain 303 elements
>> + *        - the 143 fist elements are from the previous state
>> + *        - the next 160 are for output
>> + * @param v_in input filter vector
>> + * @param gain per-subframe gain array, each element is between  
>> 0.0 and 2.0
>> + * @param lag per-subframe lag array, each element is
>> + *        - between 16 and 143 if its corresponding pfrac is 0,
>> + *        - between 16 and 139 otherwise
>> + * @param pfrac per-subframe boolean array, 1 if the lag is  
>> fractional, 0 otherwise
>> + *
>> + * @return filter output vector
>> + */
>> +static const float *do_pitchfilter(float memory[303], const float  
>> v_in[160],
>> +                                   const float gain[4], const  
>> uint8_t *lag, const uint8_t pfrac[4]) {
>> +    int         i, j;
>> +    float       *v_lag, *v_out;
>> +    const float *v_len;
>> +
>> +    v_out = memory + 143; // output vector start at memory[143]
>> +
>> +    for (i = 0; i < 4; i++)
>> +        if (gain[i]) {
>> +            v_lag = memory + 143 + 40 * i - lag[i];
>> +            for (v_len = v_in + 40; v_in < v_len; v_in++) {
>> +                if (pfrac[i]) { // If is a fractional lag...
>
>> +                    for (j = -4, *v_out = 0.; j < 4; j++)
>> +                        *v_out += qcelp_hammsinc_table[j + 4] *  
>> v_lag[j];
>
> for(j=0; j<4; j++)
>    *v_out += qcelp_hammsinc_table[j] * (v_lag[foo] + v_lag[bar]);

done, it gives slightly different outputs though, probably due to  
float rounding error.


> [...]
>> +/**
>> + * 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];
>> +
>> +    switch (q->framerate) {
>> +    case RATE_FULL:
>> +    case RATE_HALF:
>> +    case RATE_QUARTER:
>> +        interpolate_lspf(interpolated_lspf, 0.25 * (subframe_num +  
>> 1),
>> +                         curr_lspf, q->prev_lspf);
>> +        qcelp_lspf2lpc(interpolated_lspf, lpc);
>> +        break;
>> +    case RATE_OCTAVE:
>> +        if (!subframe_num) {
>> +            interpolate_lspf(interpolated_lspf, 0.625, curr_lspf,  
>> q->prev_lspf);
>> +            qcelp_lspf2lpc(interpolated_lspf, lpc);
>> +        }
>> +        break;
>> +    case I_F_Q:
>> +        if (!subframe_num)
>> +            qcelp_lspf2lpc(curr_lspf, lpc);
>> +        break;
>> +    }
>> +}
>
> somehow iam thinking code like
>
> if(q->framerate >= RATE_QUARTER)
>
> would be nice ...

much nicer indeed
changed I_F_Q to be -1, and added some asserts.




> [...]
>> +static int qcelp_decode_frame(AVCodecContext *avctx,
>> +                              void *data, int *data_size,
>> +                              uint8_t *buf, const int buf_size) {
>> +    QCELPContext      *q = avctx->priv_data;
>> +    const QCELPBitmap *bitmaps = NULL;
>> +    float             *outbuffer = data;
>> +    int               i;
>> +    float             qtzd_lspf[10], lpc[10];
>> +    float             *formant_mem;
>> +
>> +    if (determine_framerate(avctx, q, buf_size, &buf) < 0) {
>> +        av_log(avctx, AV_LOG_ERROR, "Frame #%d: Unknown framerate,  
>> unsupported size: %d.\n",
>> +               avctx->frame_number, buf_size);
>> +        return -1;
>> +    }
>> +
>
>> +    if (q->framerate == RATE_OCTAVE &&
>> +       (q->first16bits = (buf[0] << 8) + buf[1]) == 0xFFFF) {
>
> AV_RB16()

done


>> +        warn_insufficient_frame_quality(avctx, "Framerate is 1/8  
>> and first 16 bits are on.");
>> +        goto erasure;
>> +    }
>> +
>> +    bitmaps = qcelp_unpacking_bitmaps_per_rate[q->framerate];
>> +    if (bitmaps) {
>> +        uint8_t *unpacked_data = (uint8_t *)q;
>> +        const QCELPBitmap *bitmaps_end = bitmaps +  
>> qcelp_bits_per_rate[q->framerate];
>> +
>> +        init_get_bits(&q->gb, buf, 8 * buf_size);
>> +
>
>> +        memset(q->cbsign, 0, 71 * sizeof(uint8_t));
>
> q->reserved - q->cbsign + 1
> 71 is fragile ...

done


>
>> +        for (; bitmaps < bitmaps_end; bitmaps++)
>> +            unpacked_data[bitmaps[0].index] |= get_bits1(&q->gb)  
>> << bitmaps[0].bitpos;
>
> maybe
> for (; bitmaps < bitmaps_end; bitmaps++)
>    unpacked_data[bitmaps[0].index] |= get_bits(&q->gb,  
> bitmaps[0].len) << bitmaps[0].bitpos;
>
> would lead to shorter tables and faster reading ...

done


> [...]
>> +    for (i = 0; i < 160; i++)
>> +        *outbuffer++ = av_clipf(*formant_mem++, -8192., 8191.75);
>
> is this cliping needed?

the clipping was wrong, and I corrected it to be [-1, 1[
I was under the impression that SAMPLE_FMT_FLT ouput had to be in this  
range.


> [...]
>> Index: libavcodec/qcelp_lsp.c
>> ===================================================================
>> --- libavcodec/qcelp_lsp.c	(revision 0)
>> +++ libavcodec/qcelp_lsp.c	(revision 0)
>> @@ -0,0 +1,98 @@
>> +/*
>> + * QCELP decoder
>> + * Copyright (c) 2007 Reynaldo H. Verdejo Pinochet
>> + *
>> + * 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
>> + */
>> +/**
>> + * @file qcelp_lsp.c
>> + * QCELP decoder
>> + * @author Reynaldo H. Verdejo Pinochet
>> + */
>> +
>> +#include <math.h>
>> +#include <string.h>
>> +
>> +#include "dsputil.h"
>> +
>> +/**
>> + * Linear convolution of two vectors v1 and [1, cos, 1]
>> + *
>> + * @param v_out the result of the convolution,
>> + *              needs to be able to hold v_in_len + 2 elements
>> + * @param v_in the input vector
>> + * @param cos
>> + * @param v_in_len the dimension of v_in assumed to be in   
>> {2,4,6,8,10}
>> + */
>> +static void convolve(float *v_out, const float *v_in, const float  
>> cos, const int v_in_len) {
>> +    int   i;
>> +
>> +    v_out[0] = v_in[0];
>> +    v_out[1] = v_in[1] + v_in[0] * cos;
>> +    for (i = 2; i < v_in_len; i++) {
>> +        v_out[i] = v_in[i]
>> +                 + v_in[i - 1] * cos
>> +                 + v_in[i - 2];
>> +    }
>> +    v_out[v_in_len    ] = v_in[v_in_len - 1] * cos + v_in[v_in_len  
>> - 2];
>> +    v_out[v_in_len + 1] = v_in[v_in_len - 1];
>> +}
>> +
>> +/**
>> + * Computes the Pa and Qa coefficients needed for LSP to LPC  
>> conversion.
>> + *
>> + * TIA/EIA/IS-733 2.4.3.3.5-1/2
>> + */
>> +static void lsp2poly(float *poly, const float *lspf, float *v_in,  
>> float *v_buf, const int odd) {
>> +    int   i;
>> +    float *v1 = v_in, *v2 = v_buf;
>> +
>> +    for (i = 0; i < 10; i += 2) {
>> +        convolve(v2, v1, -2 * cos(M_PI * lspf[i + odd]), i + 2);
>> +        FFSWAP(float *, v1, v2);
>> +    }
>> +    memcpy(poly, v1 + 1, 5 * sizeof(float));
>> +}
>> +
>> +/**
>> + * Reconstructs LPC coefficients from the line spectral pair  
>> frequencies.
>> + *
>> + * TIA/EIA/IS-733 2.4.3.3.5
>> + */
>> +void qcelp_lspf2lpc(const float *lspf, float *lpc) {
>> +    float pa[5],qa[5];
>> +    float v1[12], v2[12];
>> +    float beta = 0.9883;
>> +    int   i;
>> +
>> +    v1[0] = 0.5;
>> +    v1[1] = 0.5;
>> +    lsp2poly(pa, lspf, v1, v2, 0);
>> +
>> +    v1[0] =  0.5;
>> +    v1[1] = -0.5;
>> +    lsp2poly(qa, lspf, v1, v2, 1);
>> +
>> +    for (i = 0; i < 5; i++) {
>> +        lpc[i] = -(pa[i] + qa[i]) * beta;
>> +        beta *= 0.9883;
>> +    }
>> +    for (i = 5; i < 10; i++) {
>> +        lpc[i] = -(pa[9-i] - qa[9-i]) * beta;
>> +        beta *= 0.9883;
>> +    }
>> +}
>
> we have some float lsp code in ffmpeg already (in wma&vorbis), i would
> prefer to avoid adding more duplicated code.
> If there are problems with merging the different implementations  
> then this
> should be discussed, maybe there is a problem that would leave us
> no choice than adding this but until such problem is found this is  
> not good.

After looking at the vorbis code, and the vorbis specification, at
http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#vorbis-spec-floor0-synth
and comparing it to the QCELP specification (TIA/EIA/IS-733 2.4.3.3.5),
I don't see any way of merging the vorbis/wma code with the qcelp code.
But again I am no audio guru, and any help would be appreciated to  
confirm.

I've reworked the qcelp_lsp.c though to extract a more general  
convolve function.




>
>> Index: libavcodec/celp_math.c
>> ===================================================================
>> --- libavcodec/celp_math.c	(revision 0)
>> +++ libavcodec/celp_math.c	(revision 0)
>> @@ -0,0 +1,40 @@
>> +/*
> [...]
>> + */
>> +
>> +/**
>> + * returns the dot product.
>> + * @param a input data array
>> + * @param b input data array
>> + * @param length number of elements
>> + *
>> + * @return dot product = sum of elementwise products
>> + */
>> +float ff_dot_product(const float* a, const float* b, int length)
>> +{
>> +    float sum = 0;
>> +    int i;
>> +
>> +    for(i=0; i<length; i++)
>> +        sum += (a[i] * b[i]);
>> +
>> +    return sum;
>> +}
>
> superflous () and a dot product is not a celp specific thing
> the doxy belongs in the header not the c file

done,
should it go in libavutil/mathematic.[ch] or somewhere else ?


> [...]
>> Index: libavformat/mov.c
>> ===================================================================
>> --- libavformat/mov.c	(revision 15630)
>> +++ libavformat/mov.c	(working copy)
>> @@ -987,6 +987,10 @@
>> #endif
>>     /* no ifdef since parameters are always those */
>>     case CODEC_ID_QCELP:
>> +        st->need_parsing = AVSTREAM_PARSE_FULL;
>> +        st->codec->frame_size= 160;
>> +        st->codec->channels= 1; /* really needed */
>> +        break;
>>     case CODEC_ID_AMR_NB:
>>     case CODEC_ID_AMR_WB:
>>         st->codec->frame_size= sc->samples_per_frame;
>
> ok





More information about the ffmpeg-devel mailing list