use crate::bands::BANDS;
use crate::fixed::{DB_PER_OCTAVE, acc, hi, low, norm, round, sat, scale32, shift};
const DEEPEST: i16 = -10240;
const DEPTH_STEPS: [i16; 3] = [2048, 4096, 3072];
pub fn depths(sum: i16) -> ([i64; BANDS], [i16; BANDS]) {
let mut remaining = sum as i64 - DEPTH_STEPS[0] as i64;
let mut depth = [0i64; BANDS];
if remaining <= 0 {
depth = [shift(acc((DEEPEST as i64) << 16), -2); BANDS];
} else {
remaining -= DEPTH_STEPS[1] as i64;
let set = if remaining <= 0 {
0
} else {
remaining -= DEPTH_STEPS[2] as i64;
if remaining <= 0 { 1 } else { 2 }
};
for (band, d) in depth.iter_mut().enumerate() {
let v = crate::tables::DEPTH_TABLES[BANDS * set + band] as i64;
*d = shift(acc(v << 16), -2);
}
}
let mut weight = [0i16; BANDS];
for (band, v) in weight.iter_mut().enumerate() {
*v = low(shift(
acc((crate::tables::BAND_WEIGHT[band] as i64) << 16),
-21,
));
}
(depth, weight)
}
const SUM_WEIGHT: i16 = 26214;
const LOG_OFFSET: i16 = 6;
pub fn overall_level(energy: &[i64; BANDS]) -> i16 {
let mut sum = 0i64;
for &e in energy.iter() {
sum = sat(acc(sum + e));
}
let scaled = shift(sat((SUM_WEIGHT as i64) * (hi(sum) as i64) * 2), -4);
let log = crate::bands::log2(scaled);
let level = acc(shift(log, -1) - ((LOG_OFFSET as i64) << 16));
hi(sat(shift(scale32(level, DB_PER_OCTAVE), 12)))
}
fn mul32r(x: i64, y: i16) -> i64 {
round(scale32(x, y))
}
fn pow2(integer: i16, fraction: i16) -> i64 {
let scaled = sat((fraction as i64) * 32 * 2);
let index = shift(scaled, -16) as usize;
let frac = low(shift(scaled, -1)) & 32767;
let base = (crate::tables::POW2_CURVE[index] as i64) << 16;
let slope = acc(base - ((crate::tables::POW2_CURVE[index + 1] as i64) << 16));
let interpolated = sat(acc(base - (frac as i64) * (hi(slope) as i64) * 2));
let shifted = norm(interpolated, integer - 29);
shift(acc(shifted + 1), -1)
}
fn split(value: i64) -> (i16, i16) {
let magnitude = sat(if value < 0 { -value } else { value });
let integer = low(acc(shift(shift(magnitude, -16), -12) + 15));
let fraction = low(shift(magnitude, -13)) & 32767;
(integer, fraction)
}
const EXP_SCALE: i16 = 5443;
const TRIM: i16 = 19661;
const HALF_SUM_WEIGHT: i16 = 3277;
const IDLE_MIX: i64 = 1024 << 16;
const MIX_UNITY: i16 = 16384;
const LOUD_NOISE: i16 = 15360;
fn half_sums(level: &[i16; BANDS]) -> (i64, i64) {
let mut low = 0i64;
let mut high = 0i64;
for band in 0..BANDS / 2 {
low = sat(acc(
low + (level[band] as i64) * (HALF_SUM_WEIGHT as i64) * 2
));
high =
sat(acc(high
+ (level[BANDS / 2 + band] as i64)
* (HALF_SUM_WEIGHT as i64)
* 2));
}
(low, high)
}
fn loud_mix_parameters(change: i64, sum: i16, score: i16) -> (i64, i16) {
let bias = if (score as i64) - 1 > 0 {
3072i64 << 16
} else {
sat(shift(3072i64 << 16, 1))
};
let scaled = shift(sat(shift(acc(((sum as i64) << 16) - bias), 2)), -16);
(change.min(24576i64 << 16), low(scaled.min(12288)))
}
fn quiet_mix_parameters(change: i64, sum: i16) -> (i64, i16) {
let x = acc(sum as i64 - 2048);
let amount = if (sum as i64) - 12288 < 0 {
if acc(x - 1024) > 0 {
4096
} else {
low(sat(shift(x, 4)))
}
} else if acc(x - 12288) > 0 {
8192
} else {
hi(sat(shift((low(x) as i64) << 16, 2)))
};
(change.min(8192i64 << 16), amount)
}
fn mix_parameters(
low_sum: i64,
high_sum: i64,
sum: i16,
noise_level: i16,
score: i16,
) -> (i64, i16) {
let change = sat(shift(sat(abs(acc(low_sum - high_sum))), 2));
if (noise_level as i64) - LOUD_NOISE as i64 >= 0 {
loud_mix_parameters(change, sum, score)
} else {
quiet_mix_parameters(change, sum)
}
}
fn mix_bands(change: i64, amount: i16, low_sum: i64, high_sum: i64) -> [i64; BANDS] {
let (i, f) = split(scale32(change, EXP_SCALE));
let first = sat(shift(pow2(i, f), 12));
let (i, f) = split(sat((amount as i64) * (EXP_SCALE as i64) * 2));
let second = sat(shift(pow2(i, f), 12));
let product = sat(shift(scale32(first, hi(mul32r(second, TRIM))), 4));
let (a, b) = if acc(high_sum - low_sum) < 0 {
(second, product)
} else {
(product, second)
};
let mut out = [0i64; BANDS];
for (band, mixed_out) in out.iter_mut().enumerate() {
let c = crate::tables::MIX_COEFFS[band];
let weight = hi(acc(((MIX_UNITY as i64) << 16) - ((c as i64) << 16)));
let mixed = sat(acc(scale32(a, weight) + scale32(b, c)));
*mixed_out = shift(mixed, -1);
}
out
}
pub struct Mix;
impl Mix {
pub fn run(
level: &[i16; BANDS],
sum: i16,
noise_level: i16,
score: i16,
active: bool,
) -> ([i64; BANDS], i16) {
if !active {
return ([IDLE_MIX; BANDS], 0);
}
let (low_sum, high_sum) = half_sums(level);
let (change, amount) = mix_parameters(low_sum, high_sum, sum, noise_level, score);
(mix_bands(change, amount, low_sum, high_sum), amount)
}
}
fn abs(v: i64) -> i64 {
if v < 0 { -v } else { v }
}
const CARRY: [(i16, i16); 2] = [(31130, 1638), (26214, 6554)];
const LEVEL_OFFSET: i16 = 6;
#[derive(Clone, Copy)]
pub struct Depths {
previous: [i64; BANDS],
}
impl Default for Depths {
fn default() -> Self {
Depths {
previous: [((DEEPEST as i64) << 16) >> 2; BANDS],
}
}
}
fn carry_depths(depth: &mut [i64; BANDS], previous: &mut [i64; BANDS], active: bool) {
let (keep, fresh) = CARRY[!active as usize];
for (current, previous) in depth.iter_mut().zip(previous.iter_mut()) {
let carried = sat(acc(scale32(*current, keep) + scale32(*previous, fresh)));
*current = carried;
*previous = carried;
}
}
fn clamp_depths(depth: &mut [i64; BANDS], energy: &[i64; BANDS]) {
for band in 0..BANDS {
let log = crate::bands::log2(energy[band]);
let scaled = acc(shift(log, -1) - ((LEVEL_OFFSET as i64) << 16));
let ceiling = sat(shift(sat(-scale32(scaled, DB_PER_OCTAVE)), 12));
if acc(ceiling - depth[band]) >= 0 {
depth[band] = ceiling.min(0);
}
}
}
fn fold_depths(
depth: &[i64; BANDS],
mix: &mut [i64; BANDS],
level: &[i16; BANDS],
weight: &[i16; BANDS],
) {
for band in 0..BANDS {
let floor = (weight[band] as i64) << 16;
let excess = hi(acc(((level[band] as i64) << 16).max(floor) - floor));
let folded = sat(shift(scale32(mix[band], excess), 3));
mix[band] = sat(acc(folded + depth[band])).min(0);
}
}
impl Depths {
pub fn refine(
&mut self,
depth: &mut [i64; BANDS],
mix: &mut [i64; BANDS],
energy: &[i64; BANDS],
level: &[i16; BANDS],
weight: &[i16; BANDS],
active: bool,
) {
carry_depths(depth, &mut self.previous, active);
clamp_depths(depth, energy);
fold_depths(depth, mix, level, weight);
}
}
const SMOOTH_UNITY: i16 = 16384;
const BAND_FLOOR: i16 = 6144;
const FULL_SCALE: i64 = 0x7fffffff;
fn curve(value: i64) -> i64 {
let scaled = sat(32 * (hi(value) as i64) * 2);
let integer = low(acc(shift(scaled, -16) + 1));
let fraction = low(shift(acc((scaled & 0xffff) + ((-1i64) << 16)), -1));
let square = sat(acc(
sat((fraction as i64) * (fraction as i64) * 2) + (1 << 15)
));
let mut b = sat((crate::tables::CURVE[0] as i64) * (hi(square) as i64) * 2);
b = sat(acc(
b + (fraction as i64) * (crate::tables::CURVE[1] as i64) * 2
));
let out = acc(b + ((crate::tables::CURVE[2] as i64) << 16));
if integer >= 0 {
out
} else {
norm(out, integer)
}
}
struct Slope {
offset: i16,
gain: i16,
bias: i16,
}
fn suppression_slope(noise_level: i16, sum: i16) -> Slope {
if (noise_level as i64) - 15360 >= 0 {
Slope {
offset: -10240,
gain: 0,
bias: 0,
}
} else if acc(sum as i64 - 6144) < 0 {
Slope {
offset: -8192,
gain: 0,
bias: 0,
}
} else if acc(sum as i64 - 12288) < 0 {
Slope {
offset: -8192,
gain: 29491,
bias: 0,
}
} else {
Slope {
offset: -8192,
gain: 29491,
bias: 3072,
}
}
}
fn band_scales(mix: &mut [i64; BANDS], level: &[i16; BANDS], slope: &Slope) -> [i16; BANDS] {
let mut scale = [0i16; BANDS];
for band in 0..BANDS {
let above = acc(((level[band] as i64) << 16) - ((BAND_FLOOR as i64) << 16));
let mut a = (slope.offset as i64) << 16;
if above > 0 {
let mut b = shift(above, -1);
if slope.gain != 0 {
b = sat(shift(b, 1));
let lifted = acc(b + ((slope.bias as i64) << 16));
b = sat((slope.gain as i64) * (hi(lifted) as i64) * 2);
}
a = acc(b + ((slope.offset as i64) << 16)) & !0xffff;
}
a = a.min(0);
a = acc(a - sat(shift(mix[band], 2))).min(0);
a = shift(a, -2);
mix[band] = ((hi(a) as i64) << 16) | (a & 0xffff);
let w = curve(nonzero(sat(shift(scale32(mix[band], EXP_SCALE), 2))));
scale[band] = hi(acc(FULL_SCALE - w));
}
scale
}
#[derive(Clone, Copy)]
pub struct Shaping {
previous: [i16; BANDS],
}
impl Default for Shaping {
fn default() -> Self {
Shaping {
previous: [5194; BANDS],
}
}
}
impl Shaping {
fn smooth_weights(&mut self, mix: &[i64; BANDS], active: bool) -> [i16; BANDS] {
let table = BANDS * !active as usize;
let mut weight = [0i16; BANDS];
for band in 0..BANDS {
let w = curve(nonzero(sat(shift(scale32(mix[band], EXP_SCALE), 2))));
let c = crate::tables::BAND_SMOOTH[table + band];
let mut b = sat((c as i64) * (hi(w) as i64) * 2);
b = sat(acc(b
+ (self.previous[band] as i64)
* ((SMOOTH_UNITY - c) as i64)
* 2));
let rounded = hi(acc(sat(shift(b, 1)) + (1 << 15)));
weight[band] = rounded;
self.previous[band] = rounded;
}
weight
}
pub fn finish(
&mut self,
mix: &mut [i64; BANDS],
level: &[i16; BANDS],
noise_level: i16,
sum: i16,
active: bool,
) -> ([i16; BANDS], [i16; BANDS]) {
let weight = self.smooth_weights(mix, active);
let slope = suppression_slope(noise_level, sum);
(weight, band_scales(mix, level, &slope))
}
}
fn nonzero(v: i64) -> i64 {
if v == 0 { -1 } else { v }
}