[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