[PR] avcodec/amrwbdec: implement SID/comfort noise support (PR #24003)
ssenthilnathan3 via ffmpeg-devel <[email protected]> Tue, 04 Aug 2026 11:30:43 -0000
| Newsgroups | gmane.comp.video.ffmpeg.devel |
|---|---|
| Message-ID | <178584304456.59.3901172017361706203@29965ddac10e> |
PR #24003 opened by ssenthilnathan3 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24003 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24003.patch Add DTX comfort noise generation for AMR-WB SID frames. Supports SID_FIRST, SID_UPDATE, and continues comfort noise during NO_DATA frames in DTX mode. Replaces the 'patchwelcome' stub with a full implementation based on 3GPP TS 26.190, 26.192, and 26.201. >From aa312d0e75e710806cd39ff2b7df0348efaee10d Mon Sep 17 00:00:00 2001 From: Senthilnathan <[email protected]> Date: Tue, 4 Aug 2026 16:56:09 +0530 Subject: [PATCH] avcodec/amrwbdec: implement SID/comfort noise support Add DTX comfort noise generation for AMR-WB SID frames. Supports SID_FIRST, SID_UPDATE, and continues comfort noise during NO_DATA frames in DTX mode. Replaces the 'patchwelcome' stub with a full implementation based on 3GPP TS 26.190, 26.192, and 26.201. --- libavcodec/amrwbdata.h | 132 +++++++++++++++++++++- libavcodec/amrwbdec.c | 246 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 366 insertions(+), 12 deletions(-) diff --git a/libavcodec/amrwbdata.h b/libavcodec/amrwbdata.h index 95c0aaa37b..21d2535fda 100644 --- a/libavcodec/amrwbdata.h +++ b/libavcodec/amrwbdata.h @@ -669,6 +669,19 @@ static const uint16_t order_MODE_23k85[] = { 402, 437, 459, 7, AMR_OF(3, vq_gain), 1, 16, 42, 24, 36, 46, 28, 4, AMR_OF(3, hb_gain), 83, 82, 81, 80, + 0 +}; + +static const uint16_t order_MODE_SID[] = { + 6, AMR_BIT(isp_id[0]), 0, 1, 2, 3, 4, 5, + 6, AMR_BIT(isp_id[1]), 6, 7, 8, 9, 10, 11, + 6, AMR_BIT(isp_id[2]), 12, 13, 14, 15, 16, 17, + 5, AMR_BIT(isp_id[3]), 18, 19, 20, 21, 22, + 5, AMR_BIT(isp_id[4]), 23, 24, 25, 26, 27, + 6, AMR_BIT(isp_id[5]), 28, 29, 30, 31, 32, 33, + 1, AMR_BIT(isp_id[6]), 34, + 1, AMR_BIT(vad), 35, + 4, AMR_OF(0, adap), 36, 37, 38, 39, 0 }; @@ -682,7 +695,8 @@ static const uint16_t * const amr_bit_orderings_by_mode[] = { order_MODE_18k25, order_MODE_19k85, order_MODE_23k05, - order_MODE_23k85 + order_MODE_23k85, + order_MODE_SID }; // Extracted from 3GPP TS 26.173 V9.0.0 (qpisf_2s.tab) @@ -1887,4 +1901,120 @@ static const uint16_t cf_sizes_wb[] = { 40, 0, 0, 0, 0, 0, 0 }; +// SID (comfort noise) ISF codebook tables from 3GPP TS 26.192 / reference code +// All values in Q15 + +static const int16_t dicoSID_isf_0[64 * 2] = { + -269, -673, -222, -537, -233, -430, -138, -451, + -212, -331, -192, -241, -87, -231, -191, -128, + -70, -106, -164, -6, 74, -179, 27, -33, + -102, 74, -162, 115, -94, 172, -6, 130, + -143, 234, 14, 218, -65, 270, 88, 182, + -124, 341, -44, 381, 38, 335, 117, 274, + -112, 454, 74, 431, -5, 488, 175, 384, + -83, 561, 122, 529, 21, 601, 229, 481, + 231, 303, 226, 608, 300, 372, 210, 187, + 306, 265, 328, 473, 382, 331, 371, 132, + 139, 58, 365, 21, 250, -82, 443, 218, + 483, 110, 426, 415, 579, 222, 518, 333, + 573, 448, 455, 529, 685, 329, 332, 580, + 595, 593, 468, 645, 762, 517, 326, 709, + 485, 793, 130, 684, 671, 737, 354, 876, + 88, 806, -65, 706, -35, 1016, 266, 1123 +}; + +static const int16_t dicoSID_isf_1[64 * 3] = { + -824, -884, -949, -805, -456, -418, -442, -438, -541, + -217, -578, -793, -168, -444, -582, -287, -492, -274, + -552, -297, -300, -163, -333, -358, -370, -232, -232, + -175, -358, -159, -381, -21, -357, -184, -159, -162, + -53, -191, -280, 18, -267, -215, -138, 61, -283, + 71, -95, -294, 13, -156, -546, 0, -83, -79, + 44, 97, -316, 178, -52, -213, 222, -261, -422, + 237, -118, -44, 141, 145, -132, 363, 81, -287, + 213, 65, 34, -107, 94, -5, 91, -29, 126, + -355, 51, -41, -219, -76, 145, -63, 100, 244, + -719, 44, 27, -572, -124, 155, -423, 133, 315, + -917, 71, 224, -268, 318, 131, -93, -190, 420, + -97, 122, 491, -79, 317, 355, 130, 100, 325, + 86, -293, 210, 133, 258, 161, 176, -73, 465, + 195, 300, 384, 348, 22, 221, 376, 183, 409, + 377, 286, 202, 242, 213, 659, 257, 565, 248, + 344, 408, -76, 405, 440, 509, 612, 385, 379, + 536, 607, 216, -56, 582, 192, 100, 517, 567, + -365, 448, 445, 728, 347, 10, 505, 357, 759, + 636, 582, 658, 335, 517, 852, 378, 809, 572, + -195, 878, 829, 529, 707, 987, 918, 726, 392, + 1250, 997, 1063 +}; + +static const int16_t dicoSID_isf_2[64 * 3] = { + -805, -838, -774, -522, -627, -828, -477, -486, -603, + -295, -481, -634, -366, -384, -393, -186, -414, -396, + -237, -394, -106, -252, -202, -275, -61, -177, -442, + -84, -198, -199, -179, -125, -31, -72, -47, -163, + -298, -220, 215, -64, -168, 251, -133, 156, -59, + -30, -2, 127, 54, 66, -61, -233, 21, 251, + 209, -50, 32, 33, 194, 136, -117, -18, 475, + 202, 46, 309, 256, 185, 53, 35, 200, 390, + 200, 263, 242, -216, 302, 294, 128, 358, 0, + 19, 431, 287, 224, 447, 280, 367, 165, 213, + 397, 314, 319, 383, 379, 75, 277, 325, 462, + 394, 505, 334, 251, 98, -213, 450, 153, 448, + 565, 226, 76, 470, 383, 502, 635, 390, 278, + 237, 135, 620, 342, 401, 649, 331, 551, 518, + 130, 418, 592, 531, 306, 737, 729, 389, 580, + 497, 557, 699, 296, 383, 874, 283, 624, 759, + 126, 622, 476, 559, 595, 472, 382, 770, 616, + 719, 613, 745, 540, 639, 928, 517, 826, 801, + 684, 811, 604, 752, 786, 857, 933, 661, 350, + 694, 450, 1061, 562, 911, 1051, 824, 813, 1104, + 758, 1047, 882, 1140, 917, 889, 1039, 1246, 1426, + 1483, 1666, 1876 +}; + +static const int16_t dicoSID_isf_3[32 * 4] = { + -776, -854, -891, -920, -552, -610, -663, -741, + -321, -370, -476, -565, 274, -160, -456, 201, + 265, 67, -160, -306, -8, -210, 79, 272, + 163, 236, 307, 308, 578, 317, 64, 298, + -9, 197, 342, 620, 343, 232, 314, 622, + 173, 149, 548, 527, 356, 370, 481, 376, + 135, 444, 488, 556, 391, 471, 487, 653, + 228, 424, 576, 835, 422, 372, 722, 682, + 295, 673, 693, 635, 539, 596, 590, 449, + 475, 618, 659, 818, 735, 517, 491, 673, + 602, 346, 257, 877, 625, 635, 849, 720, + 727, 818, 698, 595, 653, 481, 690, 1139, + 814, 762, 704, 908, 507, 747, 898, 936, + 848, 855, 924, 785, 646, 1037, 882, 795, + 772, 845, 1024, 1151, 1133, 983, 818, 921, + 940, 1068, 1252, 1302, 1588, 1767, 1718, 1513 +}; + +static const int16_t dicoSID_isf_4[32 * 4] = { + -810, -879, -945, -254, 248, 184, 671, 128, + 288, 703, 918, 99, 658, 558, 662, 219, + 552, 585, 910, 208, 559, 804, 759, 119, + 606, 774, 921, -139, 782, 761, 748, 208, + 756, 708, 983, 56, 544, 864, 1010, 152, + 737, 698, 987, 299, 771, 924, 879, 103, + 536, 785, 961, 405, 667, 916, 801, 328, + 738, 705, 773, 439, 823, 871, 992, 355, + 640, 1004, 1052, 369, 724, 822, 949, 597, + 415, 655, 729, 482, 1009, 896, 793, 363, + 908, 803, 687, -25, 1016, 838, 1011, 189, + 947, 1112, 942, 222, 914, 1049, 981, 527, + 956, 987, 1011, -120, 781, 1049, 1121, 92, + 1178, 1053, 884, 47, 1123, 1059, 1182, 118, + 933, 972, 1277, 357, 1109, 918, 1101, 503, + 1039, 1286, 1220, 317, 1351, 1207, 1010, 326 +}; + +/** Mean ISF vector for comfort noise in Q15 */ +static const int16_t isf_noise_mean[LP_ORDER] = { + 478, 1100, 2213, 3267, 4219, 5222, 6198, 7240, + 8229, 9153, 10098, 11108, 12144, 13184, 14165, 3803 +}; + #endif /* AVCODEC_AMRWBDATA_H */ diff --git a/libavcodec/amrwbdec.c b/libavcodec/amrwbdec.c index 3557b4709d..3fbecfa368 100644 --- a/libavcodec/amrwbdec.c +++ b/libavcodec/amrwbdec.c @@ -96,6 +96,15 @@ typedef struct AMRWBContext { CELPFContext celpf_ctx; ///< context for filters for CELP-based codecs CELPMContext celpm_ctx; ///< context for fixed point math operations + float sid_isf[LP_ORDER]; ///< decoded SID ISF for comfort noise + float sid_isf_prev[LP_ORDER]; ///< previous SID ISF + float sid_log_en; ///< SID log energy (log2) + float sid_log_en_prev; ///< previous SID log energy + int sid_valid_data; ///< whether valid SID parameters exist + int sid_dith_flag; ///< CN dithering flag + int dtx_since_last_sid; ///< frames since last SID update + int dtx_state; ///< 0=SPEECH, 1=DTX active + } AMRWBContext; typedef struct AMRWBChannelsContext { @@ -138,6 +147,14 @@ static av_cold int amrwb_decode_init(AVCodecContext *avctx) ff_acelp_vectors_init(&ctx->acelpv_ctx); ff_celp_filter_init(&ctx->celpf_ctx); ff_celp_math_init(&ctx->celpm_ctx); + + for (i = 0; i < LP_ORDER; i++) + ctx->sid_isf[i] = ctx->sid_isf_prev[i] = isf_init[i] * (1.0f / (1 << 15)); + ctx->sid_log_en = ctx->sid_log_en_prev = -14.0f; + ctx->sid_valid_data = 0; + ctx->sid_dith_flag = 0; + ctx->dtx_since_last_sid = 0; + ctx->dtx_state = 0; } return 0; @@ -1109,6 +1126,35 @@ static void update_sub_state(AMRWBContext *ctx) LP_ORDER_16k * sizeof(float)); } +/** + * Decode ISF parameters from SID comfort noise codebook. + * + * @param[in] indices Array of 5 codebook indices + * @param[out] isf_q Decoded ISF vector (Q15, not normalized) + */ +static void decode_sid_isf(const uint16_t *indices, float *isf_q) +{ + int i; + + for (i = 0; i < 2; i++) + isf_q[i] = dicoSID_isf_0[indices[0] * 2 + i] * (1.0f / (1 << 15)); + + for (i = 0; i < 3; i++) + isf_q[i + 2] = dicoSID_isf_1[indices[1] * 3 + i] * (1.0f / (1 << 15)); + + for (i = 0; i < 3; i++) + isf_q[i + 5] = dicoSID_isf_2[indices[2] * 3 + i] * (1.0f / (1 << 15)); + + for (i = 0; i < 4; i++) + isf_q[i + 8] = dicoSID_isf_3[indices[3] * 4 + i] * (1.0f / (1 << 15)); + + for (i = 0; i < 4; i++) + isf_q[i + 12] = dicoSID_isf_4[indices[4] * 4 + i] * (1.0f / (1 << 15)); + + for (i = 0; i < LP_ORDER; i++) + isf_q[i] += isf_noise_mean[i] * (1.0f / (1 << 15)); +} + static int amrwb_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, AVPacket *avpkt) { @@ -1143,14 +1189,6 @@ static int amrwb_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (!ctx->fr_quality) av_log(avctx, AV_LOG_ERROR, "Encountered a bad or corrupted frame\n"); - if (ctx->fr_cur_mode == NO_DATA || !ctx->fr_quality) { - /* The specification suggests a "random signal" and - "a muting technique" to "gradually decrease the output level". */ - av_samples_set_silence(&frame->extended_data[ch], 0, frame->nb_samples, 1, AV_SAMPLE_FMT_FLT); - buf += expected_fr_size; - buf_size -= expected_fr_size; - continue; - } if (ctx->fr_cur_mode > MODE_SID) { av_log(avctx, AV_LOG_ERROR, "Invalid mode %d\n", ctx->fr_cur_mode); @@ -1164,9 +1202,195 @@ static int amrwb_decode_frame(AVCodecContext *avctx, AVFrame *frame, return AVERROR_INVALIDDATA; } - if (ctx->fr_cur_mode == MODE_SID) { /* Comfort noise frame */ - avpriv_request_sample(avctx, "SID mode"); - return AVERROR_PATCHWELCOME; + if (ctx->fr_cur_mode == NO_DATA || !ctx->fr_quality) { + if (ctx->sid_valid_data) { + /* In DTX mode: continue comfort noise from last SID params */ + ctx->dtx_state = 1; + ctx->dtx_since_last_sid++; + } else { + av_samples_set_silence(&frame->extended_data[ch], 0, + frame->nb_samples, 1, AV_SAMPLE_FMT_FLT); + } + if (ctx->sid_valid_data) { + float *sub_buf; + float sid_lpc[LP_ORDER]; + double sid_isp[LP_ORDER]; + float sid_isf[LP_ORDER]; + int fi; + + memcpy(sid_isf, ctx->sid_isf, LP_ORDER * sizeof(float)); + sid_isf[LP_ORDER - 1] *= 2.0; + ff_acelp_lsf2lspd(sid_isp, sid_isf, LP_ORDER); + ff_amrwb_lsp2lpc(sid_isp, sid_lpc, LP_ORDER); + + for (fi = 0; fi < 4; fi++) { + float noise_exc[AMRWB_SFR_SIZE]; + float synth_exc[AMRWB_SFR_SIZE]; + float *samples_az_ptr = &ctx->samples_az[LP_ORDER]; + float hb_exc[AMRWB_SFR_SIZE_16k]; + float hb_samples_buf[AMRWB_SFR_SIZE_16k]; + float hb_gain; + float cn_gain; + float exc_scale; + + for (i = 0; i < AMRWB_SFR_SIZE; i++) + noise_exc[i] = 32768.0 - (uint16_t) av_lfg_get(&ctx->prng); + + exc_scale = powf(2.0, ctx->sid_log_en - 2.0); + cn_gain = sqrtf(exc_scale / (AMRWB_SFR_SIZE)); + for (i = 0; i < AMRWB_SFR_SIZE; i++) + noise_exc[i] *= cn_gain; + + ctx->celpf_ctx.celp_lp_synthesis_filterf( + samples_az_ptr, sid_lpc, noise_exc, AMRWB_SFR_SIZE, LP_ORDER); + + de_emphasis(&ctx->samples_up[UPS_MEM_SIZE], + samples_az_ptr, PREEMPH_FAC, ctx->demph_mem); + + ctx->acelpf_ctx.acelp_apply_order_2_transfer_function( + &ctx->samples_up[UPS_MEM_SIZE], + &ctx->samples_up[UPS_MEM_SIZE], + hpf_zeros, hpf_31_poles, hpf_31_gain, + ctx->hpf_31_mem, AMRWB_SFR_SIZE); + + sub_buf = buf_out + fi * AMRWB_SFR_SIZE_16k; + upsample_5_4(sub_buf, &ctx->samples_up[UPS_FIR_SIZE], + AMRWB_SFR_SIZE_16k, &ctx->celpm_ctx); + + memcpy(synth_exc, noise_exc, AMRWB_SFR_SIZE * sizeof(float)); + + ctx->acelpf_ctx.acelp_apply_order_2_transfer_function( + hb_samples_buf, &ctx->samples_up[UPS_MEM_SIZE], + hpf_zeros, hpf_400_poles, hpf_400_gain, + ctx->hpf_400_mem, AMRWB_SFR_SIZE); + + hb_gain = find_hb_gain(ctx, hb_samples_buf, 0, 1); + + scaled_hb_excitation(ctx, hb_exc, synth_exc, hb_gain); + + memcpy(ctx->lp_coef[fi], sid_lpc, LP_ORDER * sizeof(float)); + + hb_synthesis(ctx, fi, &ctx->samples_hb[LP_ORDER_16k], + hb_exc, ctx->isf_cur, ctx->isf_past_final); + + hb_fir_filter(hb_samples_buf, bpf_6_7_coef, + ctx->bpf_6_7_mem, + &ctx->samples_hb[LP_ORDER_16k]); + + /* Add the low and high frequency bands */ + for (i = 0; i < AMRWB_SFR_SIZE_16k; i++) + sub_buf[i] = (sub_buf[i] + hb_samples_buf[i]) * (1.0f / (1 << 15)); + + update_sub_state(ctx); + } + } + buf += expected_fr_size; + buf_size -= expected_fr_size; + continue; + } + + if (ctx->fr_cur_mode == MODE_SID) { + AMRWBFrame *cf = &ctx->frame; + float sid_isf_dec[LP_ORDER]; + double sid_isp[LP_ORDER]; + int is_sid_update; + int fi; + float *sub_buf; + + ff_amr_bit_reorder((uint16_t *) cf, sizeof(AMRWBFrame), + buf + header_size, amr_bit_orderings_by_mode[ctx->fr_cur_mode]); + + decode_sid_isf(cf->isp_id, sid_isf_dec); + ff_set_min_dist_lsf(sid_isf_dec, MIN_ISF_SPACING, LP_ORDER - 1); + + is_sid_update = cf->vad != 0; + + if (is_sid_update) { + ctx->sid_log_en = ((int)cf->isp_id[5]) * (1.0f / 2.625f) * + (1 << (15 - 6)) / (1 << 15); + ctx->sid_dith_flag = cf->isp_id[6]; + ctx->sid_valid_data = 1; + ctx->dtx_since_last_sid = 0; + + memcpy(ctx->sid_isf, sid_isf_dec, LP_ORDER * sizeof(float)); + } else { + memcpy(ctx->sid_isf, sid_isf_dec, LP_ORDER * sizeof(float)); + ctx->sid_valid_data = 1; + ctx->dtx_since_last_sid = 0; + } + + ctx->dtx_state = 1; + + memcpy(ctx->isf_cur, ctx->sid_isf, LP_ORDER * sizeof(float)); + + sid_isf_dec[LP_ORDER - 1] *= 2.0; + ff_acelp_lsf2lspd(sid_isp, sid_isf_dec, LP_ORDER); + + for (fi = 0; fi < 4; fi++) + ff_amrwb_lsp2lpc(sid_isp, ctx->lp_coef[fi], LP_ORDER); + + for (fi = 0; fi < 4; fi++) { + float noise_exc[AMRWB_SFR_SIZE]; + float synth_exc[AMRWB_SFR_SIZE]; + float *samples_az_ptr = &ctx->samples_az[LP_ORDER]; + float hb_exc[AMRWB_SFR_SIZE_16k]; + float hb_samples_buf[AMRWB_SFR_SIZE_16k]; + float hb_gain; + float exc_scale, cn_gain; + + for (i = 0; i < AMRWB_SFR_SIZE; i++) + noise_exc[i] = 32768.0 - (uint16_t) av_lfg_get(&ctx->prng); + + exc_scale = powf(2.0, ctx->sid_log_en - 2.0); + cn_gain = sqrtf(exc_scale / AMRWB_SFR_SIZE); + for (i = 0; i < AMRWB_SFR_SIZE; i++) + noise_exc[i] *= cn_gain; + + ctx->celpf_ctx.celp_lp_synthesis_filterf( + samples_az_ptr, ctx->lp_coef[fi], noise_exc, + AMRWB_SFR_SIZE, LP_ORDER); + + de_emphasis(&ctx->samples_up[UPS_MEM_SIZE], + samples_az_ptr, PREEMPH_FAC, ctx->demph_mem); + + ctx->acelpf_ctx.acelp_apply_order_2_transfer_function( + &ctx->samples_up[UPS_MEM_SIZE], + &ctx->samples_up[UPS_MEM_SIZE], + hpf_zeros, hpf_31_poles, hpf_31_gain, + ctx->hpf_31_mem, AMRWB_SFR_SIZE); + + sub_buf = buf_out + fi * AMRWB_SFR_SIZE_16k; + upsample_5_4(sub_buf, &ctx->samples_up[UPS_FIR_SIZE], + AMRWB_SFR_SIZE_16k, &ctx->celpm_ctx); + + memcpy(synth_exc, noise_exc, AMRWB_SFR_SIZE * sizeof(float)); + + ctx->acelpf_ctx.acelp_apply_order_2_transfer_function( + hb_samples_buf, &ctx->samples_up[UPS_MEM_SIZE], + hpf_zeros, hpf_400_poles, hpf_400_gain, + ctx->hpf_400_mem, AMRWB_SFR_SIZE); + + hb_gain = find_hb_gain(ctx, hb_samples_buf, 0, 1); + + scaled_hb_excitation(ctx, hb_exc, synth_exc, hb_gain); + + hb_synthesis(ctx, fi, &ctx->samples_hb[LP_ORDER_16k], + hb_exc, ctx->isf_cur, ctx->isf_past_final); + + hb_fir_filter(hb_samples_buf, bpf_6_7_coef, + ctx->bpf_6_7_mem, + &ctx->samples_hb[LP_ORDER_16k]); + + /* Add the low and high frequency bands */ + for (i = 0; i < AMRWB_SFR_SIZE_16k; i++) + sub_buf[i] = (sub_buf[i] + hb_samples_buf[i]) * (1.0f / (1 << 15)); + + update_sub_state(ctx); + } + + buf += expected_fr_size; + buf_size -= expected_fr_size; + continue; } ff_amr_bit_reorder((uint16_t *) &ctx->frame, sizeof(AMRWBFrame), -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]