#[cfg(target_arch = "aarch64")]
use core::arch::aarch64::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use crate::g729::basic_operations::*;
use crate::g729::codebooks::MA_PREDICTION_COEFFICIENTS;
use crate::g729::fixed_point_math::*;
use crate::g729::ld8k::*;
pub fn insertion_sort(x: &mut [Word16], length: usize) {
for i in 1..length {
let current_value = x[i];
let mut j = (i as i32) - 1;
while j >= 0 && x[j as usize] > current_value {
x[(j + 1) as usize] = x[j as usize];
j -= 1;
}
x[(j + 1) as usize] = current_value;
}
}
pub fn get_min_in_array(x: &[Word16], length: usize) -> Word16 {
let mut min = MAX_16;
for i in 0..length {
if x[i] < min {
min = x[i];
}
}
min
}
pub fn compute_parity(mut adaptative_codebook_index: UWord16) -> UWord16 {
let mut parity = 1;
adaptative_codebook_index = adaptative_codebook_index >> 2;
for _ in 0..6 {
parity ^= adaptative_codebook_index & 1;
adaptative_codebook_index = adaptative_codebook_index >> 1;
}
parity
}
pub fn rearrange_coefficients(q_lsp: &mut [Word16], j: Word16) {
for i in 1..NB_LSP_COEFF {
let delta = (add16(sub16(q_lsp[i - 1], q_lsp[i]), j)) / 2;
if delta > 0 {
q_lsp[i - 1] = sub16(q_lsp[i - 1], delta);
q_lsp[i] = add16(q_lsp[i], delta);
}
}
}
pub fn synthesis_filter(
input_signal: &[Word16],
filter_coefficients: &[Word16],
filtered_signal: &mut [Word16],
) {
for i in 0..L_SUBFRAME {
let mut acc = sshl(input_signal[i] as Word32, 12);
for j in 0..NB_LSP_COEFF {
acc = msu16_16(
acc,
filter_coefficients[j],
filtered_signal[i + NB_LSP_COEFF - j - 1],
);
}
filtered_signal[i + NB_LSP_COEFF] = saturate(pshr(acc, 12), MAX_16 as Word32) as Word16;
}
}
pub fn dot_product(x: &[Word16], y: &[Word16]) -> Word32 {
let len = x.len().min(y.len());
#[allow(unused)]
let mut sum: Word32 = 0;
let mut i = 0;
#[cfg(target_arch = "aarch64")]
unsafe {
let mut sum_vec = vdupq_n_s32(0);
while i + 8 <= len {
let a = vld1q_s16(x.as_ptr().add(i));
let b = vld1q_s16(y.as_ptr().add(i));
let a_low = vget_low_s16(a);
let b_low = vget_low_s16(b);
let a_high = vget_high_s16(a);
let b_high = vget_high_s16(b);
sum_vec = vmlal_s16(sum_vec, a_low, b_low);
sum_vec = vmlal_s16(sum_vec, a_high, b_high);
i += 8;
}
sum = vaddvq_s32(sum_vec);
}
#[cfg(target_arch = "x86_64")]
unsafe {
let mut sum_vec = _mm_setzero_si128();
while i + 8 <= len {
let a = _mm_loadu_si128(x.as_ptr().add(i) as *const _);
let b = _mm_loadu_si128(y.as_ptr().add(i) as *const _);
let prod = _mm_madd_epi16(a, b);
sum_vec = _mm_add_epi32(sum_vec, prod);
i += 8;
}
let high = _mm_unpackhi_epi64(sum_vec, sum_vec);
let sum_vec = _mm_add_epi32(sum_vec, high);
let high = _mm_shuffle_epi32(sum_vec, 1);
let sum_vec = _mm_add_epi32(sum_vec, high);
sum = _mm_cvtsi128_si32(sum_vec);
}
while i < len {
sum = mac16_16(sum, x[i], y[i]);
i += 1;
}
sum
}
pub fn dot_product_16_32_q12(x: &[Word16], y: &[Word32]) -> Word32 {
let len = x.len().min(y.len());
#[allow(unused)]
let mut sum: Word32 = 0;
let mut i = 0;
#[cfg(target_arch = "aarch64")]
unsafe {
let mut sum_vec = vdupq_n_s32(0);
while i + 4 <= len {
let a = vld1_s16(x.as_ptr().add(i)); let b = vld1q_s32(y.as_ptr().add(i));
let a_32 = vmovl_s16(a);
let prod_low = vmull_s32(vget_low_s32(a_32), vget_low_s32(b));
let prod_high = vmull_high_s32(a_32, b);
let prod_low_shifted = vshrq_n_s64(prod_low, 12);
let prod_high_shifted = vshrq_n_s64(prod_high, 12);
let res_low = vmovn_s64(prod_low_shifted); let res_high = vmovn_s64(prod_high_shifted);
let res = vcombine_s32(res_low, res_high);
sum_vec = vaddq_s32(sum_vec, res);
i += 4;
}
sum = vaddvq_s32(sum_vec);
}
while i < len {
sum = mac16_32_q12(sum, x[i], y[i]);
i += 1;
}
sum
}
pub fn vec_mult_16_16(x: &[Word16], y: &[Word16], out: &mut [Word32]) {
let len = x.len().min(y.len()).min(out.len());
let mut i = 0;
#[cfg(target_arch = "aarch64")]
unsafe {
while i + 8 <= len {
let a = vld1q_s16(x.as_ptr().add(i));
let b = vld1q_s16(y.as_ptr().add(i));
let prod_low = vmull_s16(vget_low_s16(a), vget_low_s16(b));
let prod_high = vmull_high_s16(a, b);
vst1q_s32(out.as_mut_ptr().add(i), prod_low);
vst1q_s32(out.as_mut_ptr().add(i + 4), prod_high);
i += 8;
}
}
#[cfg(target_arch = "x86_64")]
unsafe {
while i + 8 <= len {
let a = _mm_loadu_si128(x.as_ptr().add(i) as *const _);
let b = _mm_loadu_si128(y.as_ptr().add(i) as *const _);
let lo = _mm_mullo_epi16(a, b);
let hi = _mm_mulhi_epi16(a, b);
let res_lo = _mm_unpacklo_epi16(lo, hi);
let res_hi = _mm_unpackhi_epi16(lo, hi);
_mm_storeu_si128(out.as_mut_ptr().add(i) as *mut _, res_lo);
_mm_storeu_si128(out.as_mut_ptr().add(i + 4) as *mut _, res_hi);
i += 8;
}
}
while i < len {
out[i] = mult16_16(x[i], y[i]);
i += 1;
}
}
pub fn correlate_vectors(x: &[Word16], y: &[Word16], c: &mut [Word32]) {
for i in 0..L_SUBFRAME {
c[i] = dot_product(&x[i..L_SUBFRAME], &y[0..L_SUBFRAME - i]);
}
}
#[inline]
pub fn count_leading_zeros(x: Word32) -> UWord16 {
if x == 0 {
31
} else {
(x as u32).leading_zeros() as UWord16 - 1
}
}
#[inline]
pub fn unsigned_count_leading_zeros(x: UWord32) -> UWord16 {
x.leading_zeros() as UWord16
}
pub fn ma_code_gain_prediction(
previous_gain_prediction_error: &[Word16],
fixed_codebook_vector: &[Word16],
) -> Word32 {
let mut fixed_codebook_vector_squares_sum: Word32 = 0;
for i in 0..L_SUBFRAME {
if fixed_codebook_vector[i] != 0 {
fixed_codebook_vector_squares_sum = mac16_16(
fixed_codebook_vector_squares_sum,
fixed_codebook_vector[i],
fixed_codebook_vector[i],
);
}
}
let mut acc = mac16_32_q13(
8145364,
-24660,
g729_log2_q0q16(fixed_codebook_vector_squares_sum),
);
acc = shl(acc, 8);
for i in 0..4 {
acc = mac16_16(
acc,
previous_gain_prediction_error[i],
MA_PREDICTION_COEFFICIENTS[i],
);
}
acc = shr(acc, 2);
acc = mult16_32_q15(5442, acc);
acc = pshr(acc, 11);
g729_exp2_q11q16(acc as Word16)
}
pub fn compute_gain_prediction_error(
fixed_codebook_gain_correction_factor: Word16,
previous_gain_prediction_error: &mut [Word16],
) {
let mut current_gain_prediction_error = sub32(
g729_log2_q0q16(fixed_codebook_gain_correction_factor as Word32),
786432,
);
current_gain_prediction_error = pshr(mult16_32_q12(24660, current_gain_prediction_error), 6);
previous_gain_prediction_error[3] = previous_gain_prediction_error[2];
previous_gain_prediction_error[2] = previous_gain_prediction_error[1];
previous_gain_prediction_error[1] = previous_gain_prediction_error[0];
previous_gain_prediction_error[0] = current_gain_prediction_error as Word16;
}
pub fn parameters_array_2_bit_stream(parameters: &[UWord16], bit_stream: &mut [u8]) {
bit_stream[0] = (((parameters[0] & 0x1) << 7) | (parameters[1] & 0x7f)) as u8;
bit_stream[1] = (((parameters[2] & 0x1f) << 3) | ((parameters[3] >> 2) & 0x7)) as u8;
bit_stream[2] = (((parameters[3] & 0x3) << 6) | ((parameters[4] >> 2) & 0x3f)) as u8;
bit_stream[3] = (((parameters[4] & 0x3) << 6)
| ((parameters[5] & 0x1) << 5)
| ((parameters[6] >> 8) & 0x1f)) as u8;
bit_stream[4] = (parameters[6] & 0xff) as u8;
bit_stream[5] = (((parameters[7] & 0xf) << 4)
| ((parameters[8] & 0x7) << 1)
| ((parameters[9] >> 3) & 0x1)) as u8;
bit_stream[6] = (((parameters[9] & 0x7) << 5) | (parameters[10] & 0x1f)) as u8;
bit_stream[7] = ((parameters[11] >> 5) & 0xff) as u8;
bit_stream[8] = (((parameters[11] & 0x1f) << 3) | ((parameters[12] >> 1) & 0x7)) as u8;
bit_stream[9] = (((parameters[12] & 0x1) << 7)
| ((parameters[13] & 0x7) << 4)
| (parameters[14] & 0xf)) as u8;
}
pub fn cng_parameters_array_2_bit_stream(parameters: &[UWord16], bit_stream: &mut [u8]) {
bit_stream[0] = (((parameters[0] & 0x1) << 7)
| ((parameters[1] & 0x1f) << 2)
| ((parameters[2] >> 2) & 0x3)) as u8;
bit_stream[1] = (((parameters[2] & 0x03) << 6) | ((parameters[3] & 0x1f) << 1)) as u8;
}
pub fn parameters_bit_stream_2_array(bit_stream: &[u8], parameters: &mut [UWord16]) {
parameters[0] = ((bit_stream[0] >> 7) & 0x1) as UWord16;
parameters[1] = (bit_stream[0] & 0x7f) as UWord16;
parameters[2] = ((bit_stream[1] >> 3) & 0x1f) as UWord16;
parameters[3] =
(((bit_stream[1] & 0x7) as UWord16) << 2) | ((bit_stream[2] >> 6) & 0x3) as UWord16;
parameters[4] =
(((bit_stream[2] & 0x3f) as UWord16) << 2) | ((bit_stream[3] >> 6) & 0x3) as UWord16;
parameters[5] = ((bit_stream[3] >> 5) & 0x1) as UWord16;
parameters[6] = (((bit_stream[3] & 0x1f) as UWord16) << 8) | bit_stream[4] as UWord16;
parameters[7] = ((bit_stream[5] >> 4) & 0xf) as UWord16;
parameters[8] = ((bit_stream[5] >> 1) & 0x7) as UWord16;
parameters[9] =
(((bit_stream[5] & 0x1) as UWord16) << 3) | ((bit_stream[6] >> 5) & 0x7) as UWord16;
parameters[10] = (bit_stream[6] & 0x1f) as UWord16;
parameters[11] = ((bit_stream[7] as UWord16) << 5) | ((bit_stream[8] >> 3) & 0x1f) as UWord16;
parameters[12] =
(((bit_stream[8] & 0x7) as UWord16) << 1) | ((bit_stream[9] >> 7) & 0x1) as UWord16;
parameters[13] = ((bit_stream[9] >> 4) & 0x7) as UWord16;
parameters[14] = (bit_stream[9] & 0xf) as UWord16;
}
pub fn pseudo_random(random_generator_seed: &mut UWord16) -> UWord16 {
let res = mac16_16(13849, *random_generator_seed as Word16, 31821);
*random_generator_seed = res as UWord16;
*random_generator_seed
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dot_product() {
let x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let y = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
assert_eq!(dot_product(&x, &y), 55);
let x = [MAX_16, 1];
let y = [1, 1];
assert_eq!(dot_product(&x, &y), MAX_16 as Word32 + 1);
let x = [-1, -2];
let y = [1, 1];
assert_eq!(dot_product(&x, &y), -3);
}
}