Line data Source code
1 : /******************************************************************************
2 : * ETSI TS 103 634 V1.5.1 *
3 : * Low Complexity Communication Codec Plus (LC3plus) *
4 : * *
5 : * Copyright licence is solely granted through ETSI Intellectual Property *
6 : * Rights Policy, 3rd April 2019. No patent licence is granted by implication, *
7 : * estoppel or otherwise. *
8 : ******************************************************************************/
9 :
10 : #include "defines.h"
11 : #include "functions.h"
12 :
13 0 : static Word32 change_bit_at_position(Word32 value, Word8 bit_position, UWord8 bit)
14 : {
15 0 : Word32 helper_mask = ~L_shl_pos(1, bit_position);
16 0 : Word32 tmp = L_and(value, helper_mask);
17 0 : tmp = L_or(tmp, L_shl_pos(bit, bit_position));
18 0 : return tmp;
19 : }
20 :
21 0 : static void update_bit_and_byte_positions(Word16 longterm_analysis_counter_max_bytebuffer, Word8 *byte_position, Word8 *bit_position)
22 : {
23 0 : IF (sub(*bit_position, 29) == 0)
24 : {
25 0 : *bit_position = 0; move16();
26 :
27 0 : if (sub(*byte_position, longterm_analysis_counter_max_bytebuffer) < -1)
28 : {
29 0 : *byte_position = add(*byte_position, 1);
30 : } else {
31 0 : *byte_position = 0; move16();
32 : }
33 : } ELSE {
34 0 : *bit_position = add(*bit_position, 1);
35 : }
36 0 : }
37 :
38 0 : static void array_insert_and_shift(Word32 *array, UWord8 value, Word16 longterm_analysis_counter_max, Word16 *overall_counter, Word8 *byte_position, Word8 *bit_position)
39 : {
40 0 : Word32 current_byte = 0;
41 :
42 : #ifdef WMOPS
43 : push_wmops("PLC::array_insert_and_shift");
44 : #endif
45 :
46 0 : IF( overall_counter != NULL)
47 : {
48 0 : *overall_counter = s_min(add(*overall_counter, 1), longterm_analysis_counter_max);
49 : }
50 :
51 0 : current_byte = array[*byte_position]; move16();
52 0 : current_byte = change_bit_at_position(current_byte, *bit_position, value);
53 0 : array[*byte_position] = current_byte; move16();
54 :
55 : #ifdef WMOPS
56 : pop_wmops();
57 : #endif
58 0 : }
59 :
60 0 : static void array_calculate(Word32 *array_tdc, Word32 *array_ns, int length, Word16 *counter_tdc, Word16 *counter_ns, Word16 longterm_analysis_counter_max)
61 : {
62 : int i, k;
63 0 : Word32 current_byte_tdc = 0, current_byte_ns = 0;
64 0 : Word16 counter_loc_tdc = 0, counter_loc_ns = 0, counter_tmp = 0;
65 :
66 : #ifdef WMOPS
67 : push_wmops("PLC::array_calculate");
68 : #endif
69 :
70 0 : for (i = length - 1; i >= 0; i--)
71 : {
72 0 : current_byte_tdc = array_tdc[i];
73 0 : current_byte_ns = array_ns[i];
74 :
75 0 : for (k = 0; k < 30; k++)
76 : {
77 0 : counter_loc_tdc += ((current_byte_tdc >> k) & 1);
78 0 : counter_loc_ns += ((current_byte_ns >> k) & 1);
79 0 : counter_tmp++;
80 :
81 : /* Break from both loops if full 2s buffer has been evaluated */
82 0 : if (counter_tmp >= longterm_analysis_counter_max)
83 : {
84 0 : i = -1;
85 0 : k = 30;
86 0 : break;
87 : }
88 : }
89 : }
90 :
91 0 : *counter_tdc = counter_loc_tdc;
92 0 : *counter_ns = counter_loc_ns;
93 :
94 : #ifdef WMOPS
95 : pop_wmops();
96 : #endif
97 0 : }
98 :
99 : static Word16 spectral_centroid_fx_lc(Word16 old_scf_q[], const Word16 *band_offsets, Word16 bands_number, Word16 frame_length,
100 : Word16 fs_idx, Word8 *scratchBuffer
101 : #ifdef ENABLE_HR_MODE
102 : , Word16 hrmode
103 : #endif
104 : );
105 :
106 0 : void processPLCclassify_fx(Word16 plcMeth, Word16 *concealMethod, Word16 *nbLostFramesInRow, Word16 bfi,
107 : Word16 ltpf_mem_pitch_int, Word16 frame_length, Word16 frame_dms, Word16 fs_idx, Word16 yLen,
108 : Word16 q_old_d_fx[], const Word16 *band_offsets, Word16 bands_number, AplcSetup *plcAd, Word8 *scratchBuffer
109 : #ifdef ENABLE_HR_MODE
110 : , Word16 hrmode
111 : #endif
112 : )
113 : {
114 : Dyn_Mem_Deluxe_In(
115 : Word16 scQ15;
116 : Word32 class;
117 : );
118 :
119 : #ifdef WMOPS
120 : push_wmops("PLC::processPLCclassify_fx");
121 : #endif
122 :
123 : UNUSED(yLen);
124 : UNUSED(q_old_d_fx);
125 :
126 0 : if (plcAd)
127 : {
128 0 : plcAd->norm_corrQ15_fx = 0; move16();
129 : }
130 :
131 : /* assert(bfi != 2 && "Error bfi flag value, state of fadeout cntr is affected by PartialConcealment here "); */
132 : /* Save statistics for 24 kHz, 48 kHz and 96 kHz */
133 0 : IF((sub(bfi, 1) == 0) || (((bfi >= 0) && (sub(bfi, 2) <= 0)) && ((sub(fs_idx, 2) == 0) || (sub(fs_idx, 4) == 0) || (sub(fs_idx, 5) == 0)))) /* note for PC bfi==2 is possible */
134 : {
135 : /* increase counter of lost-frames-in-a-row */
136 0 : IF (sub(bfi, 1) == 0)
137 : {
138 0 : *nbLostFramesInRow = add(*nbLostFramesInRow, 1);
139 0 : *nbLostFramesInRow = s_min(*nbLostFramesInRow, 0x100);
140 : }
141 :
142 : /*assert((bfi != 2) && "PartialConcealment checked vs bfi==0 can cause issues "); */
143 0 : IF ((sub(*nbLostFramesInRow, 1) == 0) || (bfi != 1) ) /* was "|| (bfi==0)" , NB only test bfi vs "1" as bfi can have the states [0(good),1(bad),2(good,partialConcealment) } */
144 : {
145 0 : *concealMethod = plcMeth; move16();
146 :
147 0 : IF(sub(plcMeth, 1) == 0)
148 : {
149 0 : IF(ltpf_mem_pitch_int > 0)
150 : {
151 0 : *concealMethod = LC3_CON_TEC_TDPLC; move16(); /* TD-PLC */
152 : /* Calculate Features */
153 :
154 0 : plcAd->norm_corrQ15_fx = plc_xcorr_lc_fx(plcAd->x_old_tot_fx, plcAd->max_len_pcm_plc, ltpf_mem_pitch_int, fs_idx);
155 0 : scQ15 = spectral_centroid_fx_lc(plcAd->old_scf_q, band_offsets, bands_number, frame_length,
156 : fs_idx, scratchBuffer
157 : #ifdef ENABLE_HR_MODE
158 : , hrmode
159 : #endif
160 : );
161 :
162 : /* Classify */
163 0 : class = L_mult(plcAd->norm_corrQ15_fx, 7640);
164 0 : class = L_mac(class, scQ15, -32768);
165 0 : class = L_add_sat(class, -335020208);
166 :
167 0 : IF(class <= 0)
168 : {
169 : #ifdef ENABLE_HR_MODE
170 0 : IF((frame_dms == 100) && (hrmode == 0))
171 : #else
172 : IF(frame_dms == 100)
173 : #endif
174 : {
175 0 : *concealMethod = LC3_CON_TEC_PHASE_ECU; move16(); /* Phase ECU selected */
176 0 : array_insert_and_shift(plcAd->plc_longterm_advc_tdc, 0, plcAd->longterm_analysis_counter_max, &plcAd->overall_counter, &plcAd->longterm_counter_byte_position, &plcAd->longterm_counter_bit_position);
177 0 : array_insert_and_shift(plcAd->plc_longterm_advc_ns, 0, plcAd->longterm_analysis_counter_max, NULL, &plcAd->longterm_counter_byte_position, &plcAd->longterm_counter_bit_position);
178 : }
179 : ELSE
180 : {
181 0 : array_insert_and_shift(plcAd->plc_longterm_advc_tdc, 0, plcAd->longterm_analysis_counter_max, &plcAd->overall_counter, &plcAd->longterm_counter_byte_position, &plcAd->longterm_counter_bit_position);
182 0 : array_insert_and_shift(plcAd->plc_longterm_advc_ns, 0, plcAd->longterm_analysis_counter_max, NULL, &plcAd->longterm_counter_byte_position, &plcAd->longterm_counter_bit_position);
183 : }
184 : }
185 : ELSE {
186 0 : array_insert_and_shift(plcAd->plc_longterm_advc_tdc, 1, plcAd->longterm_analysis_counter_max, &plcAd->overall_counter, &plcAd->longterm_counter_byte_position, &plcAd->longterm_counter_bit_position);
187 0 : array_insert_and_shift(plcAd->plc_longterm_advc_ns, 0, plcAd->longterm_analysis_counter_max, NULL, &plcAd->longterm_counter_byte_position, &plcAd->longterm_counter_bit_position);
188 : }
189 : }
190 : ELSE
191 : {
192 0 : *concealMethod = LC3_CON_TEC_NS_ADV; move16(); /* Noise Substitution */
193 0 : array_insert_and_shift(plcAd->plc_longterm_advc_tdc, 0, plcAd->longterm_analysis_counter_max, &plcAd->overall_counter, &plcAd->longterm_counter_byte_position, &plcAd->longterm_counter_bit_position);
194 0 : array_insert_and_shift(plcAd->plc_longterm_advc_ns, 1, plcAd->longterm_analysis_counter_max, NULL, &plcAd->longterm_counter_byte_position, &plcAd->longterm_counter_bit_position);
195 : }
196 :
197 0 : array_calculate(plcAd->plc_longterm_advc_tdc, plcAd->plc_longterm_advc_ns, plcAd->longterm_analysis_counter_max_bytebuffer, &plcAd->longterm_counter_plcTdc, &plcAd->longterm_counter_plcNsAdv, plcAd->longterm_analysis_counter_max);
198 0 : update_bit_and_byte_positions(plcAd->longterm_analysis_counter_max_bytebuffer, &plcAd->longterm_counter_byte_position, &plcAd->longterm_counter_bit_position);
199 : }
200 :
201 : }
202 : }
203 :
204 : Dyn_Mem_Deluxe_Out();
205 : #ifdef WMOPS
206 : pop_wmops();
207 : #endif
208 0 : }
209 :
210 :
211 0 : Word16 spectral_centroid_fx_lc(Word16 old_scf_q[], const Word16 *band_offsets, Word16 bands_number, Word16 frame_length,
212 : Word16 fs_idx, Word8 *scratchBuffer
213 : #ifdef ENABLE_HR_MODE
214 : , Word16 hrmode
215 : #endif
216 : )
217 : {
218 : Dyn_Mem_Deluxe_In(
219 : Counter i, j;
220 : Word32 den32, num32, tmp32;
221 : Word16 s, sc, fac, freq, inv, startfreq, stopfreq;
222 : Word16 s2;
223 : Word16 *old_scf_q_mod;
224 : Word16 *old_scf_q_mod_exp;
225 : Word16 *band_offsets_local;
226 : );
227 : #ifdef WMOPS
228 : push_wmops("PLC::spectral_centroid_fx_lc");
229 : #endif
230 :
231 : #ifdef ENABLE_HR_MODE
232 0 : s2 = 0;
233 : #else
234 : UNUSED(s2);
235 : #endif
236 :
237 :
238 0 : old_scf_q_mod = (Word16 *)scratchAlign(scratchBuffer, 0); /* Size = 2 * M */
239 0 : old_scf_q_mod_exp = (Word16 *)scratchAlign(old_scf_q_mod, sizeof(*old_scf_q_mod) * M); /* Size = 2 * M */
240 0 : band_offsets_local = (Word16 *)scratchAlign(old_scf_q_mod_exp, sizeof(*old_scf_q_mod_exp) * (M)); /* Size = 2 * bands_number */
241 :
242 : /* Linear Domain */
243 0 : FOR (i = 0; i < M; i++)
244 : {
245 0 : old_scf_q_mod[i] = BASOP_Util_InvLog2_16(old_scf_q[i], &old_scf_q_mod_exp[i]);
246 : }
247 :
248 : /* De-emphasis */
249 0 : FOR (i = 0; i < M; i++)
250 : {
251 0 : old_scf_q_mod[i] = mult(old_scf_q_mod[i], lpc_warp_dee_emphasis[fs_idx][i]); move16();
252 0 : old_scf_q_mod_exp[i] = add(old_scf_q_mod_exp[i], lpc_warp_dee_emphasis_e[fs_idx][i]); move16();
253 : }
254 :
255 0 : IF (sub(bands_number, 64) == 0)
256 : {
257 0 : basop_memmove(band_offsets_local, band_offsets, (bands_number + 1) * sizeof(Word16));
258 : }
259 0 : IF (sub(bands_number, 32) < 0)
260 : {
261 0 : band_offsets_local[0] = 0; move16();
262 0 : s = sub(32, bands_number);
263 0 : FOR (i = sub(bands_number, 1); i >= s; i--)
264 : {
265 0 : band_offsets_local[(i + s) * 2 + 1 + 1] = band_offsets[i + 1]; move16();
266 0 : band_offsets_local[(i + s) * 2 + 0 + 1] = band_offsets[i + 1]; move16();
267 : }
268 0 : FOR (i = sub(s, 1); i >= 0; i--)
269 : {
270 0 : band_offsets_local[i * 4 + 3 + 1] = band_offsets[i + 1]; move16();
271 0 : band_offsets_local[i * 4 + 2 + 1] = band_offsets[i + 1]; move16();
272 0 : band_offsets_local[i * 4 + 1 + 1] = band_offsets[i + 1]; move16();
273 0 : band_offsets_local[i * 4 + 0 + 1] = band_offsets[i + 1]; move16();
274 : }
275 : }
276 : ELSE
277 0 : IF (sub(bands_number, 64) < 0)
278 : {
279 0 : band_offsets_local[0] = 0; move16();
280 0 : s = sub(64, bands_number);
281 0 : FOR (i = sub(bands_number, 1); i >= s; i--)
282 : {
283 0 : band_offsets_local[i + s + 1] = band_offsets[i + 1]; move16();
284 : }
285 0 : FOR (i = sub(s, 1); i >= 0; i--)
286 : {
287 0 : band_offsets_local[i * 2 + 1 + 1] = band_offsets[i + 1]; move16();
288 0 : band_offsets_local[i * 2 + 0 + 1] = band_offsets[i + 1]; move16();
289 : }
290 : }
291 :
292 0 : den32 = 1; move16();
293 0 : num32 = 0; move16();
294 0 : inv = div_s(1, frame_length);
295 :
296 0 : FOR (i = 0; i < M; i++)
297 : {
298 0 : freq = 0; move16();
299 0 : startfreq = add(band_offsets_local[i * 4], 1);
300 0 : stopfreq = band_offsets_local[i * 4 + 4];
301 :
302 : #ifdef ENABLE_HR_MODE
303 0 : IF (hrmode != 0)
304 : {
305 0 : tmp32 = 0; move32();
306 0 : FOR (j = startfreq; j <= stopfreq; j++)
307 : {
308 0 : tmp32 = L_add(tmp32, j);
309 : }
310 :
311 0 : s2 = norm_l(tmp32);
312 0 : freq = extract_h(L_shl(tmp32, s2));
313 0 : s2 = sub(add(15, s2), 31);
314 0 : tmp32 = L_mult(inv, freq);
315 0 : s = norm_l(tmp32);
316 : }
317 : ELSE
318 : #endif
319 : {
320 0 : FOR (j = startfreq; j <= stopfreq; j++)
321 : {
322 0 : freq = add(freq, j);
323 : }
324 :
325 0 : tmp32 = L_mult(inv, freq);
326 0 : s = norm_l(tmp32);
327 : }
328 :
329 0 : tmp32 = L_mult(old_scf_q_mod[i], extract_h(L_shl(tmp32, s)));
330 :
331 : #ifdef ENABLE_HR_MODE
332 0 : if (hrmode != 0)
333 : {
334 0 : s = add(s, s2);
335 : }
336 : #endif
337 :
338 0 : num32 = L_add(num32, L_shl(tmp32, add(add(-15, old_scf_q_mod_exp[i]), sub(15, s))));
339 0 : den32 = L_add(den32, L_shl(L_mult(old_scf_q_mod[i], stopfreq - startfreq + 1), old_scf_q_mod_exp[i]));
340 : }
341 :
342 0 : s = norm_l(den32);
343 0 : s = sub(16, s);
344 :
345 0 : sc = div_s(extract_l(L_shr(num32, s)), extract_l(L_shr(den32, s)));
346 :
347 0 : SWITCH (fs_idx)
348 : {
349 0 : case 0:
350 0 : fac = 5461; move16();
351 0 : BREAK;
352 0 : case 1:
353 0 : fac = 10922; move16();
354 0 : BREAK;
355 0 : case 2:
356 0 : fac = 16384; move16();
357 0 : BREAK;
358 0 : case 3:
359 0 : fac = 21845; move16();
360 0 : BREAK;
361 0 : default: /* case 4: */
362 0 : fac = 32767; move16();
363 0 : BREAK;
364 : }
365 0 : sc = round_fx(L_mult(sc, fac));
366 : #ifdef ENABLE_HR_MODE
367 0 : if (sub(fs_idx, 5) == 0)
368 : {
369 0 : sc = shl_pos(sc, 1);
370 : }
371 : #endif
372 :
373 : Dyn_Mem_Deluxe_Out();
374 : #ifdef WMOPS
375 : pop_wmops();
376 : #endif
377 0 : return sc;
378 : }
379 :
380 :
|