extern crate alloc;
use super::super::gains::gains_quant;
use super::super::indices::{CondCoding, MAX_NB_SUBFR, TYPE_VOICED};
use super::super::tables::QUANTIZATION_OFFSETS_Q10;
const LAMBDA_OFFSET: f32 = 1.2;
const LAMBDA_SPEECH_ACT: f32 = -0.2;
const LAMBDA_DELAYED_DECISIONS: f32 = -0.05;
const LAMBDA_INPUT_QUALITY: f32 = -0.1;
const LAMBDA_CODING_QUALITY: f32 = -0.2;
const LAMBDA_QUANT_OFFSET: f32 = 0.8;
fn sigmoid(x: f32) -> f32 {
1.0 / (1.0 + (-x).exp())
}
pub(crate) struct GainsResult {
pub gains_q16: [i32; MAX_NB_SUBFR],
pub gains_unq_q16: [i32; MAX_NB_SUBFR],
pub gains_indices: [i8; MAX_NB_SUBFR],
pub lambda: f32,
pub quant_offset_type: i32,
}
#[allow(clippy::too_many_arguments, reason = "mirrors the reference encoder state inputs")]
pub(crate) fn process_gains(
gains: &mut [f32; MAX_NB_SUBFR],
res_nrg: &[f32; MAX_NB_SUBFR],
signal_type: i32,
quant_offset_type: i32,
nb_subfr: usize,
subfr_length: usize,
snr_db_q7: i32,
ltp_pred_cod_gain: f32,
input_tilt_q15: i32,
n_states_delayed_decision: i32,
speech_activity_q8: i32,
input_quality: f32,
coding_quality: f32,
last_gain_index: &mut i8,
cond_coding: CondCoding,
) -> GainsResult {
let mut quant_offset_type = quant_offset_type;
if signal_type == TYPE_VOICED {
let s = 1.0 - 0.5 * sigmoid(0.25 * (ltp_pred_cod_gain - 12.0));
for g in gains.iter_mut().take(nb_subfr) {
*g *= s;
}
}
let inv_max_sqr_val = 2.0f32.powf(0.33 * (21.0 - snr_db_q7 as f32 / 128.0)) / subfr_length as f32;
for k in 0..nb_subfr {
let g = gains[k];
gains[k] = (g * g + res_nrg[k] * inv_max_sqr_val).sqrt().min(32767.0);
}
let mut gains_q16 = [0i32; MAX_NB_SUBFR];
for k in 0..nb_subfr {
gains_q16[k] = (gains[k] * 65536.0) as i32;
}
let gains_unq_q16 = gains_q16;
let mut gains_indices = [0i8; MAX_NB_SUBFR];
gains_quant(
&mut gains_indices,
&mut gains_q16,
last_gain_index,
cond_coding == CondCoding::Conditionally,
nb_subfr,
);
for k in 0..nb_subfr {
gains[k] = gains_q16[k] as f32 / 65536.0;
}
if signal_type == TYPE_VOICED {
quant_offset_type = i32::from(ltp_pred_cod_gain + input_tilt_q15 as f32 / 32768.0 <= 1.0);
}
let quant_offset =
f32::from(QUANTIZATION_OFFSETS_Q10[(signal_type >> 1) as usize][quant_offset_type as usize]) / 1024.0;
let lambda = LAMBDA_OFFSET
+ LAMBDA_DELAYED_DECISIONS * n_states_delayed_decision as f32
+ LAMBDA_SPEECH_ACT * speech_activity_q8 as f32 / 256.0
+ LAMBDA_INPUT_QUALITY * input_quality
+ LAMBDA_CODING_QUALITY * coding_quality
+ LAMBDA_QUANT_OFFSET * quant_offset;
GainsResult {
gains_q16,
gains_unq_q16,
gains_indices,
lambda,
quant_offset_type,
}
}
#[cfg(test)]
mod tests {
use super::super::super::gains::gains_dequant;
use super::*;
#[test]
fn process_gains_limits_and_quantises() {
let mut gains = [2000.0f32, 8000.0, 500.0, 30000.0];
let res_nrg = [1.0e7f32, 5.0e6, 2.0e7, 1.0e6];
let mut last_idx = 20i8;
let out = process_gains(
&mut gains,
&res_nrg,
1, 0,
4,
80,
18 << 7, 0.0,
0,
0,
128,
0.5,
0.5,
&mut last_idx,
CondCoding::Independently,
);
for (k, &g) in gains.iter().enumerate() {
assert!(g > 0.0 && g <= 32767.0, "gain {k} = {g}");
assert_eq!(out.gains_q16[k], (g * 65536.0) as i32);
}
let mut dec_idx = 20i8;
let dec = gains_dequant(&out.gains_indices, &mut dec_idx, false, 4);
assert_eq!(dec, out.gains_q16, "gains must round-trip through gains_dequant");
assert!(out.lambda > 0.0 && out.lambda < 4.0, "lambda {}", out.lambda);
}
}