pub const LIM_GAIN: f32 = 1.41254;
pub const MAX_SIG_GAIN: f32 = 1.0e5;
pub const EPSILON0: f32 = 1.0e-12;
pub const MAX_BOOST_FACT: f32 = 1.584_893_2;
pub fn derive_sbg_lim(sbg_sig_lowres: &[u32], sbg_patches: &[u32]) -> Vec<u32> {
if sbg_sig_lowres.is_empty() {
return Vec::new();
}
let mut sbg_lim: Vec<u32> = sbg_sig_lowres.to_vec();
if sbg_patches.len() >= 2 {
for &p in &sbg_patches[1..sbg_patches.len() - 1] {
sbg_lim.push(p);
}
}
sbg_lim.sort_unstable();
let is_in_patches = |v: u32| sbg_patches.contains(&v);
let mut sbg = 1usize;
while sbg < sbg_lim.len() {
let prev = sbg_lim[sbg - 1] as f32;
let cur = sbg_lim[sbg] as f32;
if prev <= 0.0 {
sbg += 1;
continue;
}
let num_octaves = (cur / prev).log2();
if num_octaves < 0.245 {
if sbg_lim[sbg] == sbg_lim[sbg - 1] {
sbg_lim.remove(sbg); continue;
}
let cur_in_patches = is_in_patches(sbg_lim[sbg]);
let prev_in_patches = is_in_patches(sbg_lim[sbg - 1]);
if cur_in_patches {
if prev_in_patches {
sbg += 1;
continue;
} else {
sbg_lim.remove(sbg - 1);
continue;
}
} else {
sbg_lim.remove(sbg);
continue;
}
} else {
sbg += 1;
continue;
}
}
sbg_lim
}
#[allow(clippy::too_many_arguments)]
pub fn compute_sig_gains_full(
est_sig_sb: &[Vec<f32>],
scf_sig_sb: &[Vec<f32>],
scf_noise_sb: &[Vec<f32>],
sine_area_sb: &[Vec<u8>],
aspx_tsg_ptr: u32,
p_sine_at_end: Option<u32>,
) -> Vec<Vec<f32>> {
const EPSILON: f32 = 1.0;
let num_sb = est_sig_sb.len();
if num_sb == 0 {
return Vec::new();
}
let num_atsg = est_sig_sb[0].len();
let mut out: Vec<Vec<f32>> = vec![vec![0.0_f32; num_atsg]; num_sb];
for sb in 0..num_sb {
for atsg in 0..num_atsg {
let est = est_sig_sb[sb][atsg];
let sig = scf_sig_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
let noise = scf_noise_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
let area = sine_area_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0);
let exception =
atsg as u32 == aspx_tsg_ptr || matches!(p_sine_at_end, Some(p) if atsg as u32 == p);
let denom_base = EPSILON + est;
let (numer, denom) = if area == 0 {
let denom = if exception {
denom_base
} else {
denom_base * (1.0 + noise)
};
(sig, denom)
} else {
(sig * noise, denom_base * (1.0 + noise))
};
let ratio = if denom > 0.0 { numer / denom } else { 0.0 };
out[sb][atsg] = ratio.max(0.0).sqrt();
}
}
out
}
pub fn max_sig_gain(
est_sig_sb: &[Vec<f32>],
scf_sig_sb: &[Vec<f32>],
sbg_lim: &[u32],
sbx: u32,
num_sb_aspx: u32,
) -> Vec<Vec<f32>> {
let num_sb = num_sb_aspx as usize;
if num_sb == 0 || est_sig_sb.is_empty() {
return Vec::new();
}
let num_atsg = est_sig_sb[0].len();
let num_sbg_lim = sbg_lim.len().saturating_sub(1);
if num_sbg_lim == 0 {
return vec![vec![MAX_SIG_GAIN; num_atsg]; num_sb];
}
let mut max_sb: Vec<Vec<f32>> = vec![vec![0.0_f32; num_atsg]; num_sb];
let mut sbg_max: Vec<Vec<f32>> = vec![vec![0.0_f32; num_atsg]; num_sbg_lim];
for atsg in 0..num_atsg {
for sbg in 0..num_sbg_lim {
let mut nom = 0.0_f32;
let mut denom = EPSILON0;
let lo = sbg_lim[sbg].saturating_sub(sbx) as usize;
let hi = sbg_lim[sbg + 1].saturating_sub(sbx) as usize;
let lo = lo.min(num_sb);
let hi = hi.min(num_sb);
for sb in lo..hi {
let sig = scf_sig_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
let est = est_sig_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
nom += sig;
denom += est;
}
let g = if denom > 0.0 {
(nom / denom).max(0.0).sqrt() * LIM_GAIN
} else {
0.0
};
sbg_max[sbg][atsg] = g;
}
let mut sbg = 0usize;
#[allow(clippy::needless_range_loop)]
for sb in 0..num_sb {
while sbg + 1 < num_sbg_lim && sb >= sbg_lim[sbg + 1].saturating_sub(sbx) as usize {
sbg += 1;
}
max_sb[sb][atsg] = sbg_max[sbg][atsg].min(MAX_SIG_GAIN);
}
}
max_sb
}
pub fn limit_noise_level(
noise_lev_sb: &[Vec<f32>],
max_sig_gain_sb: &[Vec<f32>],
sig_gain_sb: &[Vec<f32>],
) -> Vec<Vec<f32>> {
let num_sb = noise_lev_sb.len();
if num_sb == 0 {
return Vec::new();
}
let num_atsg = noise_lev_sb[0].len();
let mut out: Vec<Vec<f32>> = vec![vec![0.0_f32; num_atsg]; num_sb];
for sb in 0..num_sb {
for atsg in 0..num_atsg {
let n = noise_lev_sb[sb][atsg];
let mx = max_sig_gain_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
let g = sig_gain_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
let ratio = if g > 0.0 { mx / g } else { 1.0 };
let tmp = n * ratio;
out[sb][atsg] = n.min(tmp);
}
}
out
}
pub fn limit_sig_gain(sig_gain_sb: &[Vec<f32>], max_sig_gain_sb: &[Vec<f32>]) -> Vec<Vec<f32>> {
let num_sb = sig_gain_sb.len();
if num_sb == 0 {
return Vec::new();
}
let num_atsg = sig_gain_sb[0].len();
let mut out: Vec<Vec<f32>> = vec![vec![0.0_f32; num_atsg]; num_sb];
for sb in 0..num_sb {
for atsg in 0..num_atsg {
let g = sig_gain_sb[sb][atsg];
let mx = max_sig_gain_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(g);
out[sb][atsg] = g.min(mx);
}
}
out
}
#[allow(clippy::too_many_arguments)]
pub fn boost_factor(
est_sig_sb: &[Vec<f32>],
scf_sig_sb: &[Vec<f32>],
sig_gain_sb_lim: &[Vec<f32>],
sine_lev_sb: &[Vec<f32>],
noise_lev_sb_lim: &[Vec<f32>],
sbg_lim: &[u32],
sbx: u32,
num_sb_aspx: u32,
aspx_tsg_ptr: u32,
p_sine_at_end: Option<u32>,
) -> Vec<Vec<f32>> {
let num_sb = num_sb_aspx as usize;
if num_sb == 0 || est_sig_sb.is_empty() {
return Vec::new();
}
let num_atsg = est_sig_sb[0].len();
let num_sbg_lim = sbg_lim.len().saturating_sub(1);
if num_sbg_lim == 0 {
return vec![vec![1.0; num_atsg]; num_sb];
}
let mut boost_sb: Vec<Vec<f32>> = vec![vec![1.0_f32; num_atsg]; num_sb];
let mut sbg_boost: Vec<Vec<f32>> = vec![vec![1.0_f32; num_atsg]; num_sbg_lim];
for atsg in 0..num_atsg {
let exception =
atsg as u32 == aspx_tsg_ptr || matches!(p_sine_at_end, Some(p) if atsg as u32 == p);
for sbg in 0..num_sbg_lim {
let mut nom = EPSILON0;
let mut denom = EPSILON0;
let lo = sbg_lim[sbg].saturating_sub(sbx) as usize;
let hi = sbg_lim[sbg + 1].saturating_sub(sbx) as usize;
let lo = lo.min(num_sb);
let hi = hi.min(num_sb);
for sb in lo..hi {
let sig = scf_sig_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
let est = est_sig_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
let g = sig_gain_sb_lim
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
let s = sine_lev_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
let n = noise_lev_sb_lim
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
nom += sig;
denom += est * g * g;
denom += s * s;
if s == 0.0 && !exception {
denom += n * n;
}
}
let g = if denom > 0.0 {
(nom / denom).max(0.0).sqrt()
} else {
1.0
};
sbg_boost[sbg][atsg] = g;
}
let mut sbg = 0usize;
#[allow(clippy::needless_range_loop)]
for sb in 0..num_sb {
while sbg + 1 < num_sbg_lim && sb >= sbg_lim[sbg + 1].saturating_sub(sbx) as usize {
sbg += 1;
}
boost_sb[sb][atsg] = sbg_boost[sbg][atsg].min(MAX_BOOST_FACT);
}
}
boost_sb
}
pub struct LimitedAdjustments {
pub sig_gain_sb_adj: Vec<Vec<f32>>,
pub noise_lev_sb_adj: Vec<Vec<f32>>,
pub sine_lev_sb_adj: Vec<Vec<f32>>,
}
pub fn apply_boost(
sig_gain_sb_lim: &[Vec<f32>],
noise_lev_sb_lim: &[Vec<f32>],
sine_lev_sb: &[Vec<f32>],
boost_fact_sb: &[Vec<f32>],
) -> LimitedAdjustments {
let num_sb = sig_gain_sb_lim.len();
if num_sb == 0 {
return LimitedAdjustments {
sig_gain_sb_adj: Vec::new(),
noise_lev_sb_adj: Vec::new(),
sine_lev_sb_adj: Vec::new(),
};
}
let num_atsg = sig_gain_sb_lim[0].len();
let mut sig: Vec<Vec<f32>> = vec![vec![0.0_f32; num_atsg]; num_sb];
let mut noise: Vec<Vec<f32>> = vec![vec![0.0_f32; num_atsg]; num_sb];
let mut sine: Vec<Vec<f32>> = vec![vec![0.0_f32; num_atsg]; num_sb];
for sb in 0..num_sb {
for atsg in 0..num_atsg {
let bf = boost_fact_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(1.0);
sig[sb][atsg] = sig_gain_sb_lim[sb][atsg] * bf;
let n = noise_lev_sb_lim
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
noise[sb][atsg] = n * bf;
let s = sine_lev_sb
.get(sb)
.and_then(|row| row.get(atsg))
.copied()
.unwrap_or(0.0);
sine[sb][atsg] = s * bf;
}
}
LimitedAdjustments {
sig_gain_sb_adj: sig,
noise_lev_sb_adj: noise,
sine_lev_sb_adj: sine,
}
}
#[allow(clippy::too_many_arguments)]
pub fn run(
est_sig_sb: &[Vec<f32>],
scf_sig_sb: &[Vec<f32>],
sig_gain_sb: &[Vec<f32>],
sine_lev_sb: &[Vec<f32>],
noise_lev_sb: &[Vec<f32>],
sbg_lim: &[u32],
sbx: u32,
num_sb_aspx: u32,
aspx_tsg_ptr: u32,
p_sine_at_end: Option<u32>,
) -> LimitedAdjustments {
let max_sig_gain_sb = max_sig_gain(est_sig_sb, scf_sig_sb, sbg_lim, sbx, num_sb_aspx);
let noise_lev_sb_lim = limit_noise_level(noise_lev_sb, &max_sig_gain_sb, sig_gain_sb);
let sig_gain_sb_lim = limit_sig_gain(sig_gain_sb, &max_sig_gain_sb);
let boost_fact_sb = boost_factor(
est_sig_sb,
scf_sig_sb,
&sig_gain_sb_lim,
sine_lev_sb,
&noise_lev_sb_lim,
sbg_lim,
sbx,
num_sb_aspx,
aspx_tsg_ptr,
p_sine_at_end,
);
apply_boost(
&sig_gain_sb_lim,
&noise_lev_sb_lim,
sine_lev_sb,
&boost_fact_sb,
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn derive_sbg_lim_empty_when_no_lowres() {
let out = derive_sbg_lim(&[], &[]);
assert!(out.is_empty());
}
#[test]
fn derive_sbg_lim_only_lowres_when_no_patches() {
let out = derive_sbg_lim(&[4, 8, 16], &[]);
assert_eq!(out, vec![4, 8, 16]);
}
#[test]
fn derive_sbg_lim_drops_pure_duplicates() {
let out = derive_sbg_lim(&[4, 8, 16], &[8, 8]);
assert_eq!(out, vec![4, 8, 16]);
}
#[test]
fn derive_sbg_lim_merges_subnoctave_pairs() {
let out = derive_sbg_lim(&[8, 16, 32], &[7, 9, 16, 17]);
assert_eq!(out, vec![9, 16, 32]);
}
#[test]
fn max_sig_gain_flat_input_yields_lim_gain() {
let sbg_lim = vec![4u32, 8u32]; let sbx = 4u32;
let num_sb_aspx = 4u32;
let est_sig: Vec<Vec<f32>> = vec![vec![1.0]; 4];
let scf_sig: Vec<Vec<f32>> = vec![vec![1.0]; 4];
let m = max_sig_gain(&est_sig, &scf_sig, &sbg_lim, sbx, num_sb_aspx);
for row in m.iter().take(4) {
assert!((row[0] - LIM_GAIN).abs() < 1e-3);
}
}
#[test]
fn max_sig_gain_clamped_at_max_when_signal_dwarfs_estimate() {
let sbg_lim = vec![0u32, 1u32];
let est_sig: Vec<Vec<f32>> = vec![vec![0.0]];
let scf_sig: Vec<Vec<f32>> = vec![vec![1.0]];
let m = max_sig_gain(&est_sig, &scf_sig, &sbg_lim, 0, 1);
assert!((m[0][0] - MAX_SIG_GAIN).abs() < 1e-3);
}
#[test]
fn limit_noise_level_caps_when_max_below_gain() {
let noise = vec![vec![10.0_f32]];
let max_sig = vec![vec![2.0_f32]];
let sig_gain = vec![vec![4.0_f32]];
let out = limit_noise_level(&noise, &max_sig, &sig_gain);
assert!((out[0][0] - 5.0).abs() < 1e-5);
}
#[test]
fn limit_noise_level_keeps_when_max_above_gain() {
let noise = vec![vec![10.0_f32]];
let max_sig = vec![vec![4.0_f32]];
let sig_gain = vec![vec![1.0_f32]];
let out = limit_noise_level(&noise, &max_sig, &sig_gain);
assert!((out[0][0] - 10.0).abs() < 1e-5);
}
#[test]
fn limit_sig_gain_takes_min() {
let g = vec![vec![3.0_f32, 1.0]];
let m = vec![vec![2.0_f32, 5.0]];
let out = limit_sig_gain(&g, &m);
assert_eq!(out[0][0], 2.0);
assert_eq!(out[0][1], 1.0);
}
#[test]
fn boost_factor_unity_when_input_balanced() {
let sbg_lim = vec![0u32, 2u32];
let est = vec![vec![1.0_f32]; 2];
let scf_sig = vec![vec![1.0_f32]; 2];
let sig_gain_lim = vec![vec![1.0_f32]; 2];
let sine = vec![vec![0.0_f32]; 2];
let noise = vec![vec![0.0_f32]; 2];
let bf = boost_factor(
&est,
&scf_sig,
&sig_gain_lim,
&sine,
&noise,
&sbg_lim,
0,
2,
0,
Some(0), );
for row in bf.iter().take(2) {
assert!((row[0] - 1.0).abs() < 1e-3);
}
}
#[test]
fn boost_factor_clamped_at_max() {
let sbg_lim = vec![0u32, 1u32];
let est = vec![vec![0.0_f32]];
let scf_sig = vec![vec![100.0_f32]];
let sig_gain_lim = vec![vec![0.0_f32]];
let sine = vec![vec![0.0_f32]];
let noise = vec![vec![0.0_f32]];
let bf = boost_factor(
&est,
&scf_sig,
&sig_gain_lim,
&sine,
&noise,
&sbg_lim,
0,
1,
0,
None,
);
assert!((bf[0][0] - MAX_BOOST_FACT).abs() < 1e-5);
}
#[test]
fn apply_boost_scales_each_array() {
let sig = vec![vec![2.0_f32]];
let noise = vec![vec![3.0_f32]];
let sine = vec![vec![5.0_f32]];
let bf = vec![vec![1.5_f32]];
let out = apply_boost(&sig, &noise, &sine, &bf);
assert!((out.sig_gain_sb_adj[0][0] - 3.0).abs() < 1e-5);
assert!((out.noise_lev_sb_adj[0][0] - 4.5).abs() < 1e-5);
assert!((out.sine_lev_sb_adj[0][0] - 7.5).abs() < 1e-5);
}
#[test]
fn run_no_op_when_signal_already_within_limit() {
let sbg_lim = vec![0u32, 1u32];
let est = vec![vec![0.0_f32]];
let scf_sig = vec![vec![1.0_f32]];
let sig_gain = vec![vec![1.0_f32]];
let sine = vec![vec![0.0_f32]];
let noise = vec![vec![0.0_f32]];
let out = run(
&est, &scf_sig, &sig_gain, &sine, &noise, &sbg_lim, 0, 1, 0, None,
);
assert!(out.sig_gain_sb_adj[0][0] > 0.0);
assert!(out.sig_gain_sb_adj[0][0] <= MAX_BOOST_FACT * 1.0 + 1e-5);
}
#[test]
fn run_caps_runaway_gain() {
let sbg_lim = vec![0u32, 2u32];
let est = vec![vec![1.0_f32]; 2];
let scf_sig = vec![vec![1.0_f32]; 2];
let sig_gain = vec![vec![1000.0_f32]; 2]; let sine = vec![vec![0.0_f32]; 2];
let noise = vec![vec![0.5_f32]; 2];
let out = run(
&est, &scf_sig, &sig_gain, &sine, &noise, &sbg_lim, 0, 2, 99, None,
);
for (sb, row) in out.sig_gain_sb_adj.iter().take(2).enumerate() {
assert!(
(row[0] - 1.0).abs() < 1e-3,
"sb {sb}: got {} want ~1.0",
row[0]
);
}
}
#[test]
fn compute_sig_gains_full_no_sine_no_exception_matches_simple_path() {
let est = vec![vec![3.0_f32]; 1];
let scf = vec![vec![16.0_f32]; 1];
let n = vec![vec![1.0_f32]; 1];
let area = vec![vec![0_u8]; 1];
let g = compute_sig_gains_full(&est, &scf, &n, &area, 99, None);
let expected = (16.0_f32 / ((1.0 + 3.0) * (1.0 + 1.0))).sqrt();
assert!((g[0][0] - expected).abs() < 1e-5);
}
#[test]
fn compute_sig_gains_full_no_sine_with_exception_drops_noise_term() {
let est = vec![vec![3.0_f32]; 1];
let scf = vec![vec![16.0_f32]; 1];
let n = vec![vec![1.0_f32]; 1];
let area = vec![vec![0_u8]; 1];
let g = compute_sig_gains_full(&est, &scf, &n, &area, 0, None);
assert!((g[0][0] - 2.0).abs() < 1e-5);
}
#[test]
fn compute_sig_gains_full_with_sine_uses_noise_in_numerator() {
let est = vec![vec![3.0_f32]; 1];
let scf = vec![vec![16.0_f32]; 1];
let n = vec![vec![2.0_f32]; 1];
let area = vec![vec![1_u8]; 1];
let g = compute_sig_gains_full(&est, &scf, &n, &area, 99, None);
let expected = (32.0_f32 / 12.0).sqrt();
assert!((g[0][0] - expected).abs() < 1e-5);
}
}