pub const SILK_LOG_GAIN_MULTIPLIER: u32 = 0x001D_1C71;
pub const SILK_LOG_GAIN_BIAS: u32 = 2090;
pub const SILK_GAIN_Q16_MIN: u32 = 81_920;
pub const SILK_GAIN_Q16_MAX: u32 = 1_686_110_208;
pub fn silk_log2lin(in_log_q7: u32) -> u32 {
let in_q7 = in_log_q7 as i32;
let i = in_q7 >> 7;
let f = in_q7 & 127;
let base = 1_i32 << i;
let bowed = (-174_i32 * f * (128 - f)) >> 16;
let scaled = (bowed + f) * (base >> 7);
let result = base + scaled;
result as u32
}
pub fn silk_gains_dequant(log_gain: u8) -> u32 {
debug_assert!(log_gain <= 63, "log_gain must be in 0..=63 per §4.2.7.4");
let scaled = (SILK_LOG_GAIN_MULTIPLIER * log_gain as u32) >> 16;
silk_log2lin(scaled + SILK_LOG_GAIN_BIAS)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn log_gain_zero_dequantises_to_min() {
assert_eq!(silk_gains_dequant(0), SILK_GAIN_Q16_MIN);
assert_eq!(SILK_GAIN_Q16_MIN, 81_920);
}
#[test]
fn log_gain_max_dequantises_to_max() {
assert_eq!(silk_gains_dequant(63), SILK_GAIN_Q16_MAX);
assert_eq!(SILK_GAIN_Q16_MAX, 1_686_110_208);
}
#[test]
fn dequant_is_strictly_monotone_in_log_gain() {
let mut prev = 0_u32;
for g in 0..=63u8 {
let q16 = silk_gains_dequant(g);
assert!(
q16 > prev,
"log_gain {g} => gain_Q16 {q16} not greater than prev {prev}"
);
prev = q16;
}
}
#[test]
fn dequant_all_in_documented_range() {
for g in 0..=63u8 {
let q16 = silk_gains_dequant(g);
assert!(
(SILK_GAIN_Q16_MIN..=SILK_GAIN_Q16_MAX).contains(&q16),
"log_gain {g} => gain_Q16 {q16} outside RFC-documented \
[{SILK_GAIN_Q16_MIN}, {SILK_GAIN_Q16_MAX}] range"
);
}
}
#[test]
fn log2lin_integer_exponent_is_pure_power_of_two() {
for i in 0..=30u32 {
let in_q7 = 128 * i;
assert_eq!(silk_log2lin(in_q7), 1u32 << i, "i = {i}");
}
}
#[test]
fn log2lin_zero_is_one() {
assert_eq!(silk_log2lin(0), 1);
}
#[test]
fn log2lin_one_q7_is_just_above_unity() {
assert_eq!(silk_log2lin(1), 1);
}
#[test]
fn log2lin_at_q7_seven_doubles() {
assert_eq!(silk_log2lin(7), 1);
assert_eq!(silk_log2lin(7 << 7), 128);
assert_eq!(silk_log2lin((7 << 7) | 64), 181);
}
#[test]
fn rescaled_log_gain_at_zero() {
let log_gain: u32 = 0;
let in_q7 = ((SILK_LOG_GAIN_MULTIPLIER * log_gain) >> 16) + SILK_LOG_GAIN_BIAS;
assert_eq!(in_q7, 2090);
assert_eq!(silk_log2lin(in_q7), 81_920);
}
#[test]
fn rescaled_log_gain_at_max() {
let in_q7 = (SILK_LOG_GAIN_MULTIPLIER * 63) >> 16;
let in_q7 = in_q7 + SILK_LOG_GAIN_BIAS;
assert_eq!(in_q7, 3923);
assert_eq!(silk_log2lin(in_q7), 1_686_110_208);
}
fn log2lin_oracle(in_log_q7: u32) -> u32 {
let in_q = in_log_q7 as i64;
let i = in_q >> 7;
let f = in_q & 127;
let base: i64 = 1_i64 << i;
let bowed = (-174_i64 * f * (128 - f)) >> 16;
let scaled = (bowed + f) * (base >> 7);
(base + scaled) as u32
}
#[test]
fn log2lin_matches_oracle_on_silk_dequant_domain() {
for log_gain in 0..=63u8 {
let in_q7 = ((SILK_LOG_GAIN_MULTIPLIER * log_gain as u32) >> 16) + SILK_LOG_GAIN_BIAS;
assert_eq!(
silk_log2lin(in_q7),
log2lin_oracle(in_q7),
"production / oracle disagree at log_gain = {log_gain}, in_log_q7 = {in_q7}"
);
}
}
#[test]
fn log2lin_matches_oracle_on_q7_domain_sweep() {
for i in 0..=30u32 {
for f in 0..=127u32 {
let in_q7 = (i << 7) | f;
assert_eq!(
silk_log2lin(in_q7),
log2lin_oracle(in_q7),
"production / oracle disagree at in_log_q7 = {in_q7}"
);
}
}
}
fn gains_dequant_oracle(log_gain: u8) -> u32 {
let scaled = (SILK_LOG_GAIN_MULTIPLIER as u64 * log_gain as u64) >> 16;
log2lin_oracle(scaled as u32 + SILK_LOG_GAIN_BIAS)
}
#[test]
fn dequant_matches_oracle_for_full_log_gain_domain() {
for g in 0..=63u8 {
assert_eq!(
silk_gains_dequant(g),
gains_dequant_oracle(g),
"production / oracle disagree at log_gain = {g}"
);
}
}
#[test]
fn min_gain_q16_is_one_point_two_five() {
assert_eq!(SILK_GAIN_Q16_MIN as f64 / 65536.0, 1.25);
}
#[test]
fn max_gain_q16_is_approximately_twenty_five_thousand() {
let linear = SILK_GAIN_Q16_MAX as f64 / 65536.0;
assert!(
(25_727.5..25_728.5).contains(&linear),
"expected ~25728, got {linear}"
);
}
#[test]
fn dequant_step_is_positive_at_each_boundary() {
for g in 0..=62u8 {
let here = silk_gains_dequant(g);
let next = silk_gains_dequant(g + 1);
assert!(
next > here,
"log_gain {g} -> {here}; log_gain {} -> {next} not strictly greater",
g + 1
);
}
}
#[test]
fn dequant_table_pin() {
let mut table: Vec<(u8, u32)> = (0..=63u8).map(|g| (g, gains_dequant_oracle(g))).collect();
assert_eq!(table.first().unwrap().1, SILK_GAIN_Q16_MIN);
assert_eq!(table.last().unwrap().1, SILK_GAIN_Q16_MAX);
for (g, expected) in table.drain(..) {
assert_eq!(silk_gains_dequant(g), expected, "log_gain = {g}");
}
}
}