FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/aacenc_tns.c
Date: 2026-09-27 18:38:33
Exec Total Coverage
Lines: 225 257 87.5%
Functions: 7 7 100.0%
Branches: 168 228 73.7%

Line Branch Exec Source
1 /*
2 * AAC encoder TNS
3 * Copyright (C) 2015 Rostislav Pehlivanov
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file
24 * AAC encoder temporal noise shaping
25 * @author Rostislav Pehlivanov ( atomnuker gmail com )
26 */
27
28 #include <math.h>
29
30 #include "aacenc.h"
31 #include <float.h>
32 #include "aacenc_tns.h"
33 #include "aactab.h"
34 #include "aacenc_utils.h"
35 #include "lpc_functions.h"
36
37 /* Could be set to 3 to save an additional bit at the cost of little quality */
38 #define TNS_Q_BITS 4
39
40 /* Coefficient resolution in short windows */
41 #define TNS_Q_BITS_IS8 4
42
43 /* We really need the bits we save here elsewhere */
44 #define TNS_ENABLE_COEF_COMPRESSION
45
46 /* Apple-derived TNS: weighted-spectrum predictor, accepted only if the measured
47 * post-quantization prediction gain clears a block-type-dependent bar (Apple RE). */
48 #define TNS_PREDGAIN_GATE 1.4f /* first gate: predicted LPC gain */
49 #define TNS_PG_C1_LONG 1.4f /* min measured gain, long blocks */
50 #define TNS_PG_C1_SHORT 3.2f /* min measured gain, short blocks */
51 #define TNS_PG_CLAMP 6.0f /* upper bound: poles near unit circle → noise blowup */
52 #define TNS_WEIGHT_FLOOR 0.01f /* per-bin masking floor for the weighted spectrum */
53
54 82 static inline int compress_coeffs(int *coef, int order, int c_bits)
55 {
56 int i;
57
1/2
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
82 const int low_idx = c_bits ? 4 : 2;
58
1/2
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
82 const int shift_val = c_bits ? 8 : 4;
59
1/2
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
82 const int high_idx = c_bits ? 11 : 5;
60 #ifndef TNS_ENABLE_COEF_COMPRESSION
61 return 0;
62 #endif /* TNS_ENABLE_COEF_COMPRESSION */
63
2/2
✓ Branch 0 taken 383 times.
✓ Branch 1 taken 57 times.
440 for (i = 0; i < order; i++)
64
4/4
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 239 times.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 119 times.
383 if (coef[i] >= low_idx && coef[i] <= high_idx)
65 25 return 0;
66
2/2
✓ Branch 0 taken 342 times.
✓ Branch 1 taken 57 times.
399 for (i = 0; i < order; i++)
67
2/2
✓ Branch 0 taken 107 times.
✓ Branch 1 taken 235 times.
342 coef[i] -= (coef[i] > high_idx) ? shift_val : 0;
68 57 return 1;
69 }
70
71 /** Encode TNS data. */
72 10196 void ff_aac_encode_tns_info(AACEncContext *s, SingleChannelElement *sce)
73 {
74 10196 TemporalNoiseShaping *tns = &sce->tns;
75 10196 int i, w, filt, coef_compress = 0, coef_len;
76 10196 const int is8 = sce->ics.window_sequence[0] == EIGHT_SHORT_SEQUENCE;
77 10196 const int c_bits = is8 ? TNS_Q_BITS_IS8 == 4 : TNS_Q_BITS == 4;
78
79
2/2
✓ Branch 0 taken 10133 times.
✓ Branch 1 taken 63 times.
10196 if (!sce->tns.present)
80 10133 return;
81
82
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 63 times.
126 for (i = 0; i < sce->ics.num_windows; i++) {
83 63 put_bits(&s->pb, 2 - is8, sce->tns.n_filt[i]);
84
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
63 if (!tns->n_filt[i])
85 ✗ continue;
86 63 put_bits(&s->pb, 1, c_bits);
87
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 63 times.
189 for (filt = 0; filt < tns->n_filt[i]; filt++) {
88 126 put_bits(&s->pb, 6 - 2 * is8, tns->length[i][filt]);
89 126 put_bits(&s->pb, 5 - 2 * is8, tns->order[i][filt]);
90
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 82 times.
126 if (!tns->order[i][filt])
91 44 continue;
92 82 put_bits(&s->pb, 1, tns->direction[i][filt]);
93 82 coef_compress = compress_coeffs(tns->coef_idx[i][filt],
94 tns->order[i][filt], c_bits);
95 82 put_bits(&s->pb, 1, coef_compress);
96 82 coef_len = c_bits + 3 - coef_compress;
97
2/2
✓ Branch 0 taken 492 times.
✓ Branch 1 taken 82 times.
574 for (w = 0; w < tns->order[i][filt]; w++)
98 492 put_bits(&s->pb, coef_len, tns->coef_idx[i][filt][w]);
99 }
100 }
101 }
102
103 /* Cap the TNS band range at the first PNS band to avoid TNS+PNS conflicts. */
104 5815 static int tns_max_nonpns(const SingleChannelElement *sce, int mmm)
105 {
106
2/2
✓ Branch 0 taken 6949 times.
✓ Branch 1 taken 5815 times.
12764 for (int w = 0; w < sce->ics.num_windows; w += sce->ics.group_len[w])
107
2/2
✓ Branch 0 taken 217776 times.
✓ Branch 1 taken 6949 times.
224725 for (int g = 0; g < mmm; g++)
108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 217776 times.
217776 if (sce->band_type[w*16+g] == NOISE_BT) { mmm = g; break; }
109 5815 return mmm;
110 }
111
112 /* Apply TNS filter */
113 2844 void ff_aac_apply_tns(AACEncContext *s, SingleChannelElement *sce)
114 {
115 2844 TemporalNoiseShaping *tns = &sce->tns;
116 2844 IndividualChannelStream *ics = &sce->ics;
117 int w, filt, m, i, top, order, bottom, start, end, size, inc;
118 2844 const int mmm = tns_max_nonpns(sce, FFMIN(ics->tns_max_bands, ics->max_sfb));
119 float lpc[TNS_MAX_ORDER];
120
121 /* TNS predicts from the post-M/S and post-I/S coefficients. */
122 float hist[1024];
123 2844 memcpy(hist, sce->coeffs, sizeof(hist));
124
125
2/2
✓ Branch 0 taken 3747 times.
✓ Branch 1 taken 2844 times.
6591 for (w = 0; w < ics->num_windows; w++) {
126 3747 bottom = ics->num_swb;
127
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 3747 times.
3873 for (filt = 0; filt < tns->n_filt[w]; filt++) {
128 int b0, e0;
129 126 top = bottom;
130 126 bottom = FFMAX(0, top - tns->length[w][filt]);
131 126 order = tns->order[w][filt];
132
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 82 times.
126 if (order == 0)
133 44 continue;
134
135 // tns_decode_coef
136 82 compute_lpc_coefs(tns->coef[w][filt], 0, order, lpc, 0, 0, 0, NULL);
137
138 82 b0 = FFMIN(bottom, mmm);
139 82 e0 = FFMIN( top, mmm);
140 82 start = ics->swb_offset[b0];
141 82 end = ics->swb_offset[e0];
142
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if ((size = end - start) <= 0)
143 ✗ continue;
144
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 52 times.
82 if (tns->direction[w][filt]) {
145 30 inc = -1;
146 30 start = end - 1;
147 } else {
148 52 inc = 1;
149 }
150 82 start += w * 128;
151
152 /* AR filter */
153
2/2
✓ Branch 0 taken 20656 times.
✓ Branch 1 taken 82 times.
20738 for (m = 0; m < size; m++, start += inc) {
154
2/2
✓ Branch 0 taken 122214 times.
✓ Branch 1 taken 20656 times.
142870 for (i = 1; i <= FFMIN(m, order); i++) {
155 122214 sce->coeffs[start] += lpc[i-1]*hist[start - i*inc];
156 }
157 }
158
159 }
160 }
161 2844 }
162
163 /*
164 * c_bits - 1 if 4 bit coefficients, 0 if 3 bit coefficients
165 */
166 235 static inline void quantize_coefs(double *coef, int *idx, float *lpc, int order,
167 int c_bits)
168 {
169 int i;
170 235 const float *quant_arr = ff_tns_tmp2_map[c_bits];
171
2/2
✓ Branch 0 taken 1510 times.
✓ Branch 1 taken 235 times.
1745 for (i = 0; i < order; i++) {
172
1/2
✓ Branch 0 taken 1510 times.
✗ Branch 1 not taken.
1510 idx[i] = quant_array_idx(coef[i], quant_arr, c_bits ? 16 : 8);
173 1510 lpc[i] = quant_arr[idx[i]];
174 }
175 235 }
176
177 /*
178 * 3 bits per coefficient with 8 short windows
179 */
180 /* Short blocks, pooled per group: one filter per scalefactor group so the
181 * shared sf sees uniform residuals (per-window filters caused silent
182 * sub-windows); all-or-none accept. */
183 127 static void search_for_tns_short_pooled(AACEncContext *s, SingleChannelElement *sce)
184 {
185 127 TemporalNoiseShaping *tns = &sce->tns;
186
1/2
✓ Branch 0 taken 127 times.
✗ Branch 1 not taken.
127 const int mmm = tns_max_nonpns(sce, FFMIN(sce->ics.tns_max_bands, sce->ics.max_sfb ? sce->ics.max_sfb : sce->ics.num_swb));
187 127 const int sfb_start = av_clip(tns_min_sfb[1][s->samplerate_index], 0, mmm);
188 127 const int sfb_end = av_clip(sce->ics.num_swb, 0, mmm);
189 127 const int c_bits = TNS_Q_BITS_IS8 == 4;
190 127 int count = 0;
191 127 FFPsyBand *const psy_bands = &s->psy.ch[s->cur_channel].psy_bands[0];
192
193 127 memset(tns, 0, sizeof(*tns));
194
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
127 if (sfb_end - sfb_start <= 0)
195 ✗ return;
196 127 const int c_lo = sce->ics.swb_offset[sfb_start];
197 127 const int c_hi = sce->ics.swb_offset[sfb_end];
198 127 const int clen = c_hi - c_lo;
199 127 const int ord_g = 7;
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
127 if (clen <= 2*ord_g)
201 ✗ return;
202
203
2/2
✓ Branch 0 taken 501 times.
✓ Branch 1 taken 127 times.
628 for (int wh = 0; wh < sce->ics.num_windows; wh += sce->ics.group_len[wh]) {
204 501 int gl = sce->ics.group_len[wh];
205 double coefs[MAX_LPC_ORDER];
206 float pooled[1024], lpc_q[TNS_MAX_ORDER];
207 float gain, gmin;
208 501 int ok = 1;
209
210 /* per-window weighted spectra, concatenated over the group */
211
2/2
✓ Branch 0 taken 1016 times.
✓ Branch 1 taken 501 times.
1517 for (int w2 = 0; w2 < gl; w2++) {
212 1016 int w = wh + w2;
213 1016 float maxrms = 0.0f, floorrms;
214
2/2
✓ Branch 0 taken 11144 times.
✓ Branch 1 taken 1016 times.
12160 for (int g = sfb_start; g < sfb_end; g++) {
215 11144 int s0 = sce->ics.swb_offset[g], s1 = sce->ics.swb_offset[g+1];
216
3/4
✓ Branch 0 taken 11143 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 11144 times.
✗ Branch 3 not taken.
11144 float rms = sqrtf(FFMAX(psy_bands[w*16 + g].threshold, 0.0f) / FFMAX(s1 - s0, 1));
217
2/2
✓ Branch 0 taken 9511 times.
✓ Branch 1 taken 1633 times.
11144 maxrms = FFMAX(maxrms, rms);
218 }
219
1/2
✓ Branch 0 taken 1016 times.
✗ Branch 1 not taken.
1016 floorrms = FFMAX(maxrms * TNS_WEIGHT_FLOOR, 1e-9f);
220
2/2
✓ Branch 0 taken 11144 times.
✓ Branch 1 taken 1016 times.
12160 for (int g = sfb_start; g < sfb_end; g++) {
221 11144 int s0 = sce->ics.swb_offset[g], s1 = sce->ics.swb_offset[g+1];
222
3/4
✓ Branch 0 taken 11143 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 11144 times.
✗ Branch 3 not taken.
11144 float rms = sqrtf(FFMAX(psy_bands[w*16 + g].threshold, 0.0f) / FFMAX(s1 - s0, 1));
223
2/2
✓ Branch 0 taken 10901 times.
✓ Branch 1 taken 243 times.
11144 float wgt = 1.0f / FFMAX(rms, floorrms);
224
2/2
✓ Branch 0 taken 117344 times.
✓ Branch 1 taken 11144 times.
128488 for (int k = s0; k < s1; k++)
225 117344 pooled[w2*clen + (k - c_lo)] = sce->coeffs[w*128 + k] * wgt;
226 }
227 }
228
229 501 gain = ff_lpc_calc_ref_coefs_f(&s->lpc, pooled, clen*gl, ord_g, coefs, 0);
230
6/6
✓ Branch 0 taken 499 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 317 times.
✓ Branch 3 taken 182 times.
✓ Branch 4 taken 217 times.
✓ Branch 5 taken 100 times.
501 if (!isfinite(gain) || gain < TNS_PREDGAIN_GATE || gain > TNS_PG_CLAMP)
231 401 continue;
232
2/2
✓ Branch 0 taken 700 times.
✓ Branch 1 taken 100 times.
800 for (int i = 0; i < ord_g; i++)
233 700 coefs[i] = -coefs[i];
234
235 100 quantize_coefs(coefs, tns->coef_idx[wh][0], tns->coef[wh][0], ord_g, c_bits);
236 100 compute_lpc_coefs(tns->coef[wh][0], 0, ord_g, lpc_q, 0, 0, 0, NULL);
237
238 /* every window must clear the measured post-quantization bar */
239 100 gmin = FLT_MAX;
240
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 100 times.
216 for (int w2 = 0; w2 < gl; w2++) {
241 116 const float *msrc = pooled + w2*clen;
242 116 float orig_e = 0.0f, filt_e = 0.0f;
243
2/2
✓ Branch 0 taken 13392 times.
✓ Branch 1 taken 116 times.
13508 for (int m = 0; m < clen; m++) {
244 13392 float acc = msrc[m];
245
2/2
✓ Branch 0 taken 90496 times.
✓ Branch 1 taken 13392 times.
103888 for (int i = 1; i <= FFMIN(m, ord_g); i++)
246 90496 acc += lpc_q[i-1] * msrc[m - i];
247 13392 orig_e += msrc[m]*msrc[m];
248 13392 filt_e += acc*acc;
249 }
250
4/6
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 101 times.
✓ Branch 3 taken 15 times.
✓ Branch 4 taken 101 times.
✗ Branch 5 not taken.
116 gmin = FFMIN(gmin, orig_e / FFMAX(filt_e, 1e-9f));
251 }
252 {
253 /* accept Schmitt, run-scoped: hard entry / easy hold inside
254 * short runs (anti-gravel); isolated frames use the base bar */
255
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 14 times.
100 int in_run = s->nmr ? s->nmr->prev_was_short : 0;
256
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 14 times.
100 int prev_on = s->nmr ? s->nmr->tns8_prev[s->cur_channel & 15] : 0;
257
3/4
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 72 times.
100 float bar = TNS_PG_C1_SHORT * (!in_run ? 1.0f : prev_on ? 0.5f : 1.8f);
258
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 if (gmin < bar)
259 100 ok = 0;
260 }
261
262
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
100 if (ok) {
263 ✗ for (int w2 = 0; w2 < gl; w2++) {
264 ✗ int w = wh + w2;
265 ✗ tns->n_filt[w] = 1;
266 ✗ tns->length[w][0] = sfb_end - sfb_start;
267 ✗ tns->order[w][0] = ord_g;
268 ✗ tns->direction[w][0] = 0;
269 ✗ if (w2) {
270 ✗ memcpy(tns->coef_idx[w][0], tns->coef_idx[wh][0], sizeof(tns->coef_idx[w][0]));
271 ✗ memcpy(tns->coef[w][0], tns->coef[wh][0], sizeof(tns->coef[w][0]));
272 }
273 ✗ count++;
274 }
275 }
276 }
277 127 sce->tns.present = !!count;
278
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 38 times.
127 if (s->nmr)
279 89 s->nmr->tns8_prev[s->cur_channel & 15] = !!count;
280 }
281
282 2844 void ff_aac_search_for_tns(AACEncContext *s, SingleChannelElement *sce)
283 {
284 2844 TemporalNoiseShaping *tns = &sce->tns;
285 2844 int w, count = 0;
286 2844 const int mmm = tns_max_nonpns(sce, FFMIN(sce->ics.tns_max_bands, sce->ics.max_sfb));
287 2844 const int is8 = sce->ics.window_sequence[0] == EIGHT_SHORT_SEQUENCE;
288 2844 const int c_bits = is8 ? TNS_Q_BITS_IS8 == 4 : TNS_Q_BITS == 4;
289 2844 const int sfb_start = av_clip(tns_min_sfb[is8][s->samplerate_index], 0, mmm);
290 2844 const int sfb_end = av_clip(sce->ics.num_swb, 0, mmm);
291
2/2
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 2715 times.
2844 const int order = is8 ? 7 : 12;
292
2/2
✓ Branch 0 taken 2714 times.
✓ Branch 1 taken 130 times.
5558 const int slant = sce->ics.window_sequence[0] == LONG_STOP_SEQUENCE ? 1 :
293
2/2
✓ Branch 0 taken 130 times.
✓ Branch 1 taken 2584 times.
2714 sce->ics.window_sequence[0] == LONG_START_SEQUENCE ? 0 : 2;
294 2844 const int sfb_len = sfb_end - sfb_start;
295 2844 const int coef_len = sce->ics.swb_offset[sfb_end] - sce->ics.swb_offset[sfb_start];
296
3/4
✓ Branch 0 taken 2715 times.
✓ Branch 1 taken 129 times.
✓ Branch 2 taken 2715 times.
✗ Branch 3 not taken.
2844 const int n_filt = is8 ? 1 : order != TNS_MAX_ORDER ? 2 : 3;
297 2844 const int ord_g = order / n_filt;
298
299 /* Apple's accept bar (minimum measured prediction gain): higher on short blocks,
300 * where a weak filter's shaped-noise tail spreads across the 50% overlap. */
301
2/2
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 2715 times.
2844 const float c1 = is8 ? TNS_PG_C1_SHORT : TNS_PG_C1_LONG;
302 2844 FFPsyBand *const psy_bands = &s->psy.ch[s->cur_channel].psy_bands[0];
303
304
3/4
✓ Branch 0 taken 2452 times.
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2452 times.
2844 if (coef_len <= 0 || sfb_len <= 0) {
305 392 sce->tns.present = 0;
306 519 return;
307 }
308
2/2
✓ Branch 0 taken 127 times.
✓ Branch 1 taken 2325 times.
2452 if (is8) {
309 127 search_for_tns_short_pooled(s, sce);
310 127 return;
311 }
312
313 /* time-domain window length backing one coding window: a long MDCT block is
314 * fed 2048 windowed samples (current 1024 + overlap), each short block 256. */
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2325 times.
2325 const int tlen = is8 ? 256 : 2048;
316
317 2325 float mgain[8] = {0};
318
319
2/2
✓ Branch 0 taken 2325 times.
✓ Branch 1 taken 2325 times.
4650 for (w = 0; w < sce->ics.num_windows; w++) {
320 2325 int filt, any = 0;
321
322 /* The filter gets ran in the direction of the signal's *temporal* energy,
323 * so the quantization noise stays in the loud masked part rather than spilling
324 * into the quiet part. */
325 2325 const float *tw = sce->ret_buf + w*tlen;
326 2325 float e_early = 0.0f, e_late = 0.0f;
327 int ti;
328
2/2
✓ Branch 0 taken 2380800 times.
✓ Branch 1 taken 2325 times.
2383125 for (ti = 0; ti < tlen/2; ti++)
329 2380800 e_early += tw[ti]*tw[ti];
330
2/2
✓ Branch 0 taken 2380800 times.
✓ Branch 1 taken 2325 times.
2383125 for (; ti < tlen; ti++)
331 2380800 e_late += tw[ti]*tw[ti];
332 2325 const int tdir = e_early > e_late;
333
334 /* Walk the frequency regions exactly as the decoder does: filter 0 is the
335 * topmost band region, each subsequent filter covers the next region down,
336 * clamped to mmm. Each filter gets its own LPC over its own region. */
337 2325 int top_sfb = sce->ics.num_swb;
338
2/2
✓ Branch 0 taken 4650 times.
✓ Branch 1 taken 2325 times.
6975 for (filt = 0; filt < n_filt; filt++) {
339 double coefs[MAX_LPC_ORDER];
340 float wspec[1024], tmp[1024], lpc_q[TNS_MAX_ORDER];
341 11625 int len_sfb = (filt == n_filt - 1) ? sfb_len - filt*(sfb_len/n_filt)
342
2/2
✓ Branch 0 taken 2325 times.
✓ Branch 1 taken 2325 times.
4650 : sfb_len/n_filt;
343 4650 int bot_sfb = FFMAX(0, top_sfb - len_sfb);
344 4650 int g_lo = FFMIN(bot_sfb, mmm), g_hi = FFMIN(top_sfb, mmm);
345 4650 int c_lo = sce->ics.swb_offset[g_lo];
346 4650 int c_hi = sce->ics.swb_offset[g_hi];
347 4650 int clen = c_hi - c_lo;
348
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 4572 times.
4650 const int dir = slant != 2 ? slant : tdir;
349 4650 float gain, orig_e = 0.0f, filt_e = 0.0f;
350 int m, i, g, inc, st;
351
352 4650 tns->length[w][filt] = len_sfb;
353 4650 tns->order[w][filt] = 0; /* default: region carries no filter */
354 4650 top_sfb = bot_sfb;
355
356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4650 times.
4650 if (clen <= 2*ord_g) /* too short for a stable order-ord_g LPC */
357 4568 continue;
358
359 /* Fit LPC on the perceptually-weighted spectrum X/sqrt(thr), floored
360 * to avoid a near-zero threshold blowing up a single bin (Apple). */
361 {
362 4650 float maxrms = 0.0f, floorrms;
363 int k;
364
2/2
✓ Branch 0 taken 40683 times.
✓ Branch 1 taken 4650 times.
45333 for (g = g_lo; g < g_hi; g++) {
365 40683 int s0 = sce->ics.swb_offset[g], s1 = sce->ics.swb_offset[g+1];
366
1/2
✓ Branch 0 taken 40683 times.
✗ Branch 1 not taken.
40683 float rms = sqrtf(FFMAX(psy_bands[w*16 + g].threshold, 0.0f) /
367
1/2
✓ Branch 0 taken 40683 times.
✗ Branch 1 not taken.
40683 FFMAX(s1 - s0, 1));
368
2/2
✓ Branch 0 taken 22382 times.
✓ Branch 1 taken 18301 times.
40683 maxrms = FFMAX(maxrms, rms);
369 }
370
1/2
✓ Branch 0 taken 4650 times.
✗ Branch 1 not taken.
4650 floorrms = FFMAX(maxrms * TNS_WEIGHT_FLOOR, 1e-9f);
371
2/2
✓ Branch 0 taken 40683 times.
✓ Branch 1 taken 4650 times.
45333 for (g = g_lo; g < g_hi; g++) {
372 40683 int s0 = sce->ics.swb_offset[g], s1 = sce->ics.swb_offset[g+1];
373
1/2
✓ Branch 0 taken 40683 times.
✗ Branch 1 not taken.
40683 float rms = sqrtf(FFMAX(psy_bands[w*16 + g].threshold, 0.0f) /
374
1/2
✓ Branch 0 taken 40683 times.
✗ Branch 1 not taken.
40683 FFMAX(s1 - s0, 1));
375
2/2
✓ Branch 0 taken 38190 times.
✓ Branch 1 taken 2493 times.
40683 float wgt = 1.0f / FFMAX(rms, floorrms);
376
2/2
✓ Branch 0 taken 1222824 times.
✓ Branch 1 taken 40683 times.
1263507 for (k = s0; k < s1; k++)
377 1222824 wspec[k - c_lo] = sce->coeffs[w*128 + k] * wgt;
378 }
379 /* Short blocks: unwindowed fit; Hann window zeros the edges of the
380 * tiny region, wrecking the LPC. Long blocks keep the window. */
381 4650 gain = ff_lpc_calc_ref_coefs_f(&s->lpc, wspec, clen, ord_g, coefs, !is8);
382 }
383 /* Reject below the first gate and above the clamp (poles near unit circle). */
384
5/6
✓ Branch 0 taken 4650 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 572 times.
✓ Branch 3 taken 4078 times.
✓ Branch 4 taken 437 times.
✓ Branch 5 taken 135 times.
4650 if (!isfinite(gain) || gain < TNS_PREDGAIN_GATE || gain > TNS_PG_CLAMP)
385 4515 continue;
386 /* Negate: ff_lpc_calc_ref_coefs_f sign convention is opposite to what
387 * ff_aac_apply_tns's MA filter needs; fed unnegated, it anti-whitens. */
388
2/2
✓ Branch 0 taken 810 times.
✓ Branch 1 taken 135 times.
945 for (i = 0; i < ord_g; i++)
389 810 coefs[i] = -coefs[i];
390
391 /* Quantize, then build the decoder's direct-form LPC. */
392 135 quantize_coefs(coefs, tns->coef_idx[w][filt], tns->coef[w][filt],
393 ord_g, c_bits);
394 135 compute_lpc_coefs(tns->coef[w][filt], 0, ord_g, lpc_q, 0, 0, 0, NULL);
395
396 /* Apply the quantized filter to the weighted spectrum and measure gain. */
397 135 const float *msrc = wspec;
398
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 82 times.
135 inc = dir ? -1 : 1;
399
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 82 times.
135 st = dir ? clen - 1 : 0;
400
2/2
✓ Branch 0 taken 34380 times.
✓ Branch 1 taken 135 times.
34515 for (m = 0; m < clen; m++) {
401 34380 int idx = st + m*inc;
402 34380 float acc = msrc[idx];
403
2/2
✓ Branch 0 taken 203445 times.
✓ Branch 1 taken 34380 times.
237825 for (i = 1; i <= FFMIN(m, ord_g); i++)
404 203445 acc += lpc_q[i-1] * msrc[idx - i*inc];
405 34380 tmp[idx] = acc;
406 }
407
2/2
✓ Branch 0 taken 34380 times.
✓ Branch 1 taken 135 times.
34515 for (m = 0; m < clen; m++) {
408 34380 orig_e += msrc[m]*msrc[m];
409 34380 filt_e += tmp[m]*tmp[m];
410 }
411
1/2
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
135 filt_e = FFMAX(filt_e, 1e-9f);
412
413 /* Keep only if measured post-quantization gain clears C1 (Apple's outcome gate). */
414
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 82 times.
135 if (orig_e < c1*filt_e)
415 53 continue;
416
417 82 tns->order[w][filt] = ord_g;
418 82 tns->direction[w][filt] = dir;
419 82 mgain[w] = orig_e / filt_e;
420 82 any = 1;
421 }
422
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 2262 times.
2325 tns->n_filt[w] = any ? n_filt : 0;
423
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 2262 times.
2325 if (any)
424 63 count++;
425 }
426
427 /* per-window path: group-uniformity gate (mismatched whitening within a
428 * shared-sf group silences sub-windows) */
429
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2325 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2325 if (is8 && count) {
430 ✗ const float gspread = 2.0f;
431 ✗ count = 0;
432 ✗ for (w = 0; w < sce->ics.num_windows; w += sce->ics.group_len[w]) {
433 ✗ int gl = sce->ics.group_len[w], drop = 0;
434 ✗ float gmin = FLT_MAX, gmax = 0.0f;
435 ✗ for (int w2 = w; w2 < w + gl; w2++) {
436 ✗ if (!tns->n_filt[w2] || mgain[w2] <= 0.0f) { drop = 1; break; }
437 ✗ gmin = FFMIN(gmin, mgain[w2]);
438 ✗ gmax = FFMAX(gmax, mgain[w2]);
439 }
440 ✗ if (!drop && gmax > gspread * gmin)
441 ✗ drop = 1;
442 ✗ for (int w2 = w; w2 < w + gl; w2++) {
443 ✗ if (drop) {
444 ✗ tns->n_filt[w2] = 0;
445 ✗ for (int f2 = 0; f2 < n_filt; f2++)
446 ✗ tns->order[w2][f2] = 0;
447 ✗ } else if (tns->n_filt[w2]) {
448 ✗ count++;
449 }
450 }
451 }
452 }
453 2325 sce->tns.present = !!count;
454 }
455