Skip to main content

g729_sys/g729/
utils.rs

1#[cfg(target_arch = "aarch64")]
2use core::arch::aarch64::*;
3#[cfg(target_arch = "x86_64")]
4use core::arch::x86_64::*;
5
6use crate::g729::basic_operations::*;
7use crate::g729::codebooks::MA_PREDICTION_COEFFICIENTS;
8use crate::g729::fixed_point_math::*;
9use crate::g729::ld8k::*;
10
11pub fn insertion_sort(x: &mut [Word16], length: usize) {
12    for i in 1..length {
13        let current_value = x[i];
14        let mut j = (i as i32) - 1;
15        while j >= 0 && x[j as usize] > current_value {
16            x[(j + 1) as usize] = x[j as usize];
17            j -= 1;
18        }
19        x[(j + 1) as usize] = current_value;
20    }
21}
22
23pub fn get_min_in_array(x: &[Word16], length: usize) -> Word16 {
24    let mut min = MAX_16;
25    for i in 0..length {
26        if x[i] < min {
27            min = x[i];
28        }
29    }
30    min
31}
32
33pub fn compute_parity(mut adaptative_codebook_index: UWord16) -> UWord16 {
34    let mut parity = 1;
35    adaptative_codebook_index = adaptative_codebook_index >> 2; /* ignore the two LSB */
36
37    for _ in 0..6 {
38        parity ^= adaptative_codebook_index & 1; /* XOR with the LSB */
39        adaptative_codebook_index = adaptative_codebook_index >> 1;
40    }
41    parity
42}
43
44pub fn rearrange_coefficients(q_lsp: &mut [Word16], j: Word16) {
45    /* qLSP in Q2.13 and J in Q0.13(fitting on 4 bits: possible values 10 and 5) */
46    for i in 1..NB_LSP_COEFF {
47        let delta = (add16(sub16(q_lsp[i - 1], q_lsp[i]), j)) / 2; /* delta = (l[i-1] - l[i] +J)/2 */
48        if delta > 0 {
49            q_lsp[i - 1] = sub16(q_lsp[i - 1], delta); /* qLSP still in Q2.13 */
50            q_lsp[i] = add16(q_lsp[i], delta);
51        }
52    }
53}
54
55pub fn synthesis_filter(
56    input_signal: &[Word16],
57    filter_coefficients: &[Word16],
58    filtered_signal: &mut [Word16],
59) {
60    // filtered_signal is accessed in ranges [-10, -1] as input and [0, 39] as output.
61    // In Rust, we pass the slice starting at index 0 of the output buffer.
62    // But we need access to previous 10 elements.
63    // So the caller should pass a slice that includes the memory.
64    // The C function signature: void synthesisFilter(word16_t inputSignal[], word16_t filterCoefficients[], word16_t filteredSignal[]);
65    // In C, filteredSignal points to the start of the output buffer (index 0).
66    // But the loop accesses filteredSignal[i-j-1]. When i=0, j=0, it accesses filteredSignal[-1].
67    // So in Rust, we must pass the slice such that index 10 corresponds to "0".
68    // Or we pass the full buffer and an offset.
69    // Let's assume the passed `filtered_signal` slice starts at the memory (index -10 in C terms).
70    // So `filtered_signal[10]` corresponds to `filteredSignal[0]` in C.
71    // Wait, the C comment says: "filteredSignal: 50 values in Q0 accessed in ranges [-10,-1] as input and [0, 39] as output."
72    // So the pointer passed to C function is likely pointing to the 11th element of the allocated array.
73    // In Rust, let's change the signature to take the full buffer and the start index?
74    // Or just assume the slice passed starts at "0" and we can't access negative indices.
75    // If I follow C pointer arithmetic, I should pass the slice starting at "0". But Rust slices don't support negative indexing.
76    // So I will change the signature to take the whole buffer and assume the "current" frame starts at index `NB_LSP_COEFF`.
77    // But `inputSignal` is just the input.
78
79    // Let's look at how it's called in C.
80    // synthesisFilter(&(encoderChannelContext->targetSignal[NB_LSP_COEFF]), ...)
81    // So it passes a pointer to the "current" part.
82
83    // In Rust, I'll implement it taking `filtered_signal` as the slice starting at the "memory" (index -NB_LSP_COEFF).
84    // So `filtered_signal` has length `NB_LSP_COEFF + L_SUBFRAME`.
85    // The output is written to `filtered_signal[NB_LSP_COEFF..]`.
86
87    for i in 0..L_SUBFRAME {
88        let mut acc = sshl(input_signal[i] as Word32, 12); /* acc get the first term of the sum, in Q12 (inputSignal is in Q0)*/
89        for j in 0..NB_LSP_COEFF {
90            // filteredSignal[i-j-1] in C.
91            // In Rust slice starting at -10:
92            // index 0 corresponds to C index -10.
93            // index 10 corresponds to C index 0.
94            // C index k maps to Rust index k + 10.
95            // C: i-j-1. Rust: i-j-1 + 10 = i - j + 9.
96            acc = msu16_16(
97                acc,
98                filter_coefficients[j],
99                filtered_signal[i + NB_LSP_COEFF - j - 1],
100            );
101        }
102        filtered_signal[i + NB_LSP_COEFF] = saturate(pshr(acc, 12), MAX_16 as Word32) as Word16;
103    }
104}
105
106pub fn dot_product(x: &[Word16], y: &[Word16]) -> Word32 {
107    let len = x.len().min(y.len());
108    #[allow(unused)]
109    let mut sum: Word32 = 0;
110    let mut i = 0;
111
112    #[cfg(target_arch = "aarch64")]
113    unsafe {
114        let mut sum_vec = vdupq_n_s32(0);
115        while i + 8 <= len {
116            let a = vld1q_s16(x.as_ptr().add(i));
117            let b = vld1q_s16(y.as_ptr().add(i));
118            let a_low = vget_low_s16(a);
119            let b_low = vget_low_s16(b);
120            let a_high = vget_high_s16(a);
121            let b_high = vget_high_s16(b);
122
123            sum_vec = vmlal_s16(sum_vec, a_low, b_low);
124            sum_vec = vmlal_s16(sum_vec, a_high, b_high);
125
126            i += 8;
127        }
128        sum = vaddvq_s32(sum_vec);
129    }
130
131    #[cfg(target_arch = "x86_64")]
132    unsafe {
133        let mut sum_vec = _mm_setzero_si128();
134        while i + 8 <= len {
135            let a = _mm_loadu_si128(x.as_ptr().add(i) as *const _);
136            let b = _mm_loadu_si128(y.as_ptr().add(i) as *const _);
137            let prod = _mm_madd_epi16(a, b);
138            sum_vec = _mm_add_epi32(sum_vec, prod);
139            i += 8;
140        }
141        let high = _mm_unpackhi_epi64(sum_vec, sum_vec);
142        let sum_vec = _mm_add_epi32(sum_vec, high);
143        let high = _mm_shuffle_epi32(sum_vec, 1);
144        let sum_vec = _mm_add_epi32(sum_vec, high);
145        sum = _mm_cvtsi128_si32(sum_vec);
146    }
147
148    while i < len {
149        sum = mac16_16(sum, x[i], y[i]);
150        i += 1;
151    }
152    sum
153}
154
155pub fn dot_product_16_32_q12(x: &[Word16], y: &[Word32]) -> Word32 {
156    let len = x.len().min(y.len());
157    #[allow(unused)]
158    let mut sum: Word32 = 0;
159    let mut i = 0;
160
161    #[cfg(target_arch = "aarch64")]
162    unsafe {
163        let mut sum_vec = vdupq_n_s32(0);
164        while i + 4 <= len {
165            let a = vld1_s16(x.as_ptr().add(i)); // Load 4 i16
166            let b = vld1q_s32(y.as_ptr().add(i)); // Load 4 i32
167
168            let a_32 = vmovl_s16(a); // Extend to 4 i32
169
170            // Multiply -> 4 i64 (low and high)
171            let prod_low = vmull_s32(vget_low_s32(a_32), vget_low_s32(b));
172            let prod_high = vmull_high_s32(a_32, b);
173
174            // Shift right by 12
175            let prod_low_shifted = vshrq_n_s64(prod_low, 12);
176            let prod_high_shifted = vshrq_n_s64(prod_high, 12);
177
178            // Narrow back to i32
179            let res_low = vmovn_s64(prod_low_shifted); // 2 i32
180            let res_high = vmovn_s64(prod_high_shifted); // 2 i32
181
182            // Combine
183            let res = vcombine_s32(res_low, res_high);
184
185            // Accumulate
186            sum_vec = vaddq_s32(sum_vec, res);
187
188            i += 4;
189        }
190        sum = vaddvq_s32(sum_vec);
191    }
192
193    while i < len {
194        sum = mac16_32_q12(sum, x[i], y[i]);
195        i += 1;
196    }
197    sum
198}
199
200pub fn vec_mult_16_16(x: &[Word16], y: &[Word16], out: &mut [Word32]) {
201    let len = x.len().min(y.len()).min(out.len());
202    let mut i = 0;
203
204    #[cfg(target_arch = "aarch64")]
205    unsafe {
206        while i + 8 <= len {
207            let a = vld1q_s16(x.as_ptr().add(i));
208            let b = vld1q_s16(y.as_ptr().add(i));
209
210            let prod_low = vmull_s16(vget_low_s16(a), vget_low_s16(b));
211            let prod_high = vmull_high_s16(a, b);
212
213            vst1q_s32(out.as_mut_ptr().add(i), prod_low);
214            vst1q_s32(out.as_mut_ptr().add(i + 4), prod_high);
215
216            i += 8;
217        }
218    }
219
220    #[cfg(target_arch = "x86_64")]
221    unsafe {
222        while i + 8 <= len {
223            let a = _mm_loadu_si128(x.as_ptr().add(i) as *const _);
224            let b = _mm_loadu_si128(y.as_ptr().add(i) as *const _);
225
226            let lo = _mm_mullo_epi16(a, b);
227            let hi = _mm_mulhi_epi16(a, b);
228
229            let res_lo = _mm_unpacklo_epi16(lo, hi);
230            let res_hi = _mm_unpackhi_epi16(lo, hi);
231
232            _mm_storeu_si128(out.as_mut_ptr().add(i) as *mut _, res_lo);
233            _mm_storeu_si128(out.as_mut_ptr().add(i + 4) as *mut _, res_hi);
234
235            i += 8;
236        }
237    }
238
239    while i < len {
240        out[i] = mult16_16(x[i], y[i]);
241        i += 1;
242    }
243}
244
245pub fn correlate_vectors(x: &[Word16], y: &[Word16], c: &mut [Word32]) {
246    for i in 0..L_SUBFRAME {
247        c[i] = dot_product(&x[i..L_SUBFRAME], &y[0..L_SUBFRAME - i]);
248    }
249}
250
251#[inline]
252pub fn count_leading_zeros(x: Word32) -> UWord16 {
253    if x == 0 {
254        31
255    } else {
256        (x as u32).leading_zeros() as UWord16 - 1
257    }
258}
259
260#[inline]
261pub fn unsigned_count_leading_zeros(x: UWord32) -> UWord16 {
262    x.leading_zeros() as UWord16
263}
264
265pub fn ma_code_gain_prediction(
266    previous_gain_prediction_error: &[Word16],
267    fixed_codebook_vector: &[Word16],
268) -> Word32 {
269    /* compute the sum of squares of fixedCodebookVector in Q26 */
270    let mut fixed_codebook_vector_squares_sum: Word32 = 0;
271
272    for i in 0..L_SUBFRAME {
273        if fixed_codebook_vector[i] != 0 {
274            fixed_codebook_vector_squares_sum = mac16_16(
275                fixed_codebook_vector_squares_sum,
276                fixed_codebook_vector[i],
277                fixed_codebook_vector[i],
278            );
279        }
280    }
281
282    /* compute E| - E as in eq71, result in Q16 */
283    let mut acc = mac16_32_q13(
284        8145364,
285        -24660,
286        g729_log2_q0q16(fixed_codebook_vector_squares_sum),
287    ); /* acc in Q16 */
288
289    /* accumulate the MA prediction described in eq69 to the previous Sum, result will be in E~(m) + E| -E as used in eq71 */
290    acc = shl(acc, 8); /* acc in Q24 to match the fixed point of next accumulations */
291    for i in 0..4 {
292        acc = mac16_16(
293            acc,
294            previous_gain_prediction_error[i],
295            MA_PREDICTION_COEFFICIENTS[i],
296        );
297    }
298
299    /* compute eq71, we already have the exposant in acc so */
300    /* g'c = 10^(acc/20)                                    */
301    /*     = 2^((acc*ln(10))/(20*ln(2)))                    */
302    /*     = 2^(0,1661*acc)                                 */
303    acc = shr(acc, 2); /* Q24->Q22 */
304    acc = mult16_32_q15(5442, acc); /* 5442 is 0.1661 in Q15 -> acc now in Q4.22 range [1.6, 10.8] */
305    acc = pshr(acc, 11); /* get acc in Q4.11 */
306
307    g729_exp2_q11q16(acc as Word16)
308}
309
310pub fn compute_gain_prediction_error(
311    fixed_codebook_gain_correction_factor: Word16,
312    previous_gain_prediction_error: &mut [Word16],
313) {
314    /* need to compute eq72: 20log10(fixedCodebookGainCorrectionFactor) */
315    /*  = (20/log2(10))*log2(fixedCodebookGainCorrectionFactor) */
316    /*  = 6.0206*log2(fixedCodebookGainCorrectionFactor) */
317    /* log2 input in Q0, output in Q16,fixedCodebookGainCorrectionFactor being in Q12, we shall substract 12 in Q16(786432) to the result of log2 function -> final result in Q2.16 */
318    let mut current_gain_prediction_error = sub32(
319        g729_log2_q0q16(fixed_codebook_gain_correction_factor as Word32),
320        786432,
321    );
322    current_gain_prediction_error = pshr(mult16_32_q12(24660, current_gain_prediction_error), 6); /* 24660 = 6.0206 in Q3.12 -> mult result in Q16, precise shift right to get it in Q4.10 */
323
324    /* shift the array and insert the current Prediction Error */
325    previous_gain_prediction_error[3] = previous_gain_prediction_error[2];
326    previous_gain_prediction_error[2] = previous_gain_prediction_error[1];
327    previous_gain_prediction_error[1] = previous_gain_prediction_error[0];
328    previous_gain_prediction_error[0] = current_gain_prediction_error as Word16;
329}
330
331pub fn parameters_array_2_bit_stream(parameters: &[UWord16], bit_stream: &mut [u8]) {
332    bit_stream[0] = (((parameters[0] & 0x1) << 7) | (parameters[1] & 0x7f)) as u8;
333
334    bit_stream[1] = (((parameters[2] & 0x1f) << 3) | ((parameters[3] >> 2) & 0x7)) as u8;
335
336    bit_stream[2] = (((parameters[3] & 0x3) << 6) | ((parameters[4] >> 2) & 0x3f)) as u8;
337
338    bit_stream[3] = (((parameters[4] & 0x3) << 6)
339        | ((parameters[5] & 0x1) << 5)
340        | ((parameters[6] >> 8) & 0x1f)) as u8;
341
342    bit_stream[4] = (parameters[6] & 0xff) as u8;
343
344    bit_stream[5] = (((parameters[7] & 0xf) << 4)
345        | ((parameters[8] & 0x7) << 1)
346        | ((parameters[9] >> 3) & 0x1)) as u8;
347
348    bit_stream[6] = (((parameters[9] & 0x7) << 5) | (parameters[10] & 0x1f)) as u8;
349
350    bit_stream[7] = ((parameters[11] >> 5) & 0xff) as u8;
351
352    bit_stream[8] = (((parameters[11] & 0x1f) << 3) | ((parameters[12] >> 1) & 0x7)) as u8;
353
354    bit_stream[9] = (((parameters[12] & 0x1) << 7)
355        | ((parameters[13] & 0x7) << 4)
356        | (parameters[14] & 0xf)) as u8;
357}
358
359pub fn cng_parameters_array_2_bit_stream(parameters: &[UWord16], bit_stream: &mut [u8]) {
360    bit_stream[0] = (((parameters[0] & 0x1) << 7)
361        | ((parameters[1] & 0x1f) << 2)
362        | ((parameters[2] >> 2) & 0x3)) as u8;
363
364    bit_stream[1] = (((parameters[2] & 0x03) << 6) | ((parameters[3] & 0x1f) << 1)) as u8;
365}
366
367pub fn parameters_bit_stream_2_array(bit_stream: &[u8], parameters: &mut [UWord16]) {
368    parameters[0] = ((bit_stream[0] >> 7) & 0x1) as UWord16;
369    parameters[1] = (bit_stream[0] & 0x7f) as UWord16;
370    parameters[2] = ((bit_stream[1] >> 3) & 0x1f) as UWord16;
371    parameters[3] =
372        (((bit_stream[1] & 0x7) as UWord16) << 2) | ((bit_stream[2] >> 6) & 0x3) as UWord16;
373    parameters[4] =
374        (((bit_stream[2] & 0x3f) as UWord16) << 2) | ((bit_stream[3] >> 6) & 0x3) as UWord16;
375    parameters[5] = ((bit_stream[3] >> 5) & 0x1) as UWord16;
376    parameters[6] = (((bit_stream[3] & 0x1f) as UWord16) << 8) | bit_stream[4] as UWord16;
377    parameters[7] = ((bit_stream[5] >> 4) & 0xf) as UWord16;
378    parameters[8] = ((bit_stream[5] >> 1) & 0x7) as UWord16;
379    parameters[9] =
380        (((bit_stream[5] & 0x1) as UWord16) << 3) | ((bit_stream[6] >> 5) & 0x7) as UWord16;
381    parameters[10] = (bit_stream[6] & 0x1f) as UWord16;
382    parameters[11] = ((bit_stream[7] as UWord16) << 5) | ((bit_stream[8] >> 3) & 0x1f) as UWord16;
383    parameters[12] =
384        (((bit_stream[8] & 0x7) as UWord16) << 1) | ((bit_stream[9] >> 7) & 0x1) as UWord16;
385    parameters[13] = ((bit_stream[9] >> 4) & 0x7) as UWord16;
386    parameters[14] = (bit_stream[9] & 0xf) as UWord16;
387}
388
389pub fn pseudo_random(random_generator_seed: &mut UWord16) -> UWord16 {
390    /* pseudoRandomSeed is stored in an uint16_t var, we shall not worry about overflow here */
391    /* pseudoRandomSeed*31821 + 13849; */
392    // MAC16_16(13849, (*randomGeneratorSeed), 31821);
393    // MAC16_16 returns Word32.
394    // In C: *randomGeneratorSeed = MAC16_16(...)
395    // It implicitly casts Word32 to uint16_t (truncates).
396    let res = mac16_16(13849, *random_generator_seed as Word16, 31821);
397    *random_generator_seed = res as UWord16;
398    *random_generator_seed
399}
400
401#[cfg(test)]
402mod tests {
403    use super::*;
404
405    #[test]
406    fn test_dot_product() {
407        let x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
408        let y = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
409        // Sum = 55
410        assert_eq!(dot_product(&x, &y), 55);
411
412        let x = [MAX_16, 1];
413        let y = [1, 1];
414        assert_eq!(dot_product(&x, &y), MAX_16 as Word32 + 1);
415
416        // Test negative
417        let x = [-1, -2];
418        let y = [1, 1];
419        assert_eq!(dot_product(&x, &y), -3);
420    }
421}