use super::{common::clamp_i16, dqt::SeaDequantTab, lms::SeaLMS};
const MAX_RESIDUAL_BEAM_WIDTH: usize = 6;
const MAX_PERIOD_FRAMES: usize = 20;
const ADAPTIVE_SECOND_FACTOR_GAP_PERCENT: u64 = 25;
#[derive(Clone)]
struct ResidualPath {
error: u64,
lms: SeaLMS,
trace: u16,
symbol: usize,
}
#[derive(Clone, Copy)]
struct TraceNode {
parent: u16,
symbol: u8,
}
pub(super) struct BeamPeriod<'a> {
pub(super) input: &'a [i16],
pub(super) start: usize,
pub(super) frames: usize,
pub(super) channel: usize,
pub(super) initial_lms: &'a SeaLMS,
pub(super) fallback_lms: &'a SeaLMS,
pub(super) fallback_residuals: &'a [u8],
}
pub(super) struct ResidualBeamSearch {
channels: usize,
residual_beam_width: usize,
dequant: SeaDequantTab,
}
impl ResidualBeamSearch {
pub(super) fn new(channels: usize, scale_factor_bits: usize, beam_width: usize) -> Self {
Self {
channels,
residual_beam_width: beam_width.clamp(1, MAX_RESIDUAL_BEAM_WIDTH),
dequant: SeaDequantTab::init(scale_factor_bits),
}
}
pub(super) fn refine_period(
&self,
period: &BeamPeriod,
factor: usize,
width: usize,
) -> (u64, SeaLMS, [u8; 20]) {
let levels = self.dequant.get_dqt(width);
let mut paths: [Option<ResidualPath>; MAX_RESIDUAL_BEAM_WIDTH] =
core::array::from_fn(|_| None);
paths[0] = Some(ResidualPath {
error: 0,
lms: period.initial_lms.clone(),
trace: u16::MAX,
symbol: 0,
});
let mut path_count = 1;
let mut trace = [TraceNode {
parent: u16::MAX,
symbol: 0,
}; MAX_RESIDUAL_BEAM_WIDTH * 2 * MAX_PERIOD_FRAMES];
let mut trace_count = 0;
for frame in 0..period.frames {
let sample = period.input[period.start + frame * self.channels + period.channel];
let mut candidates: [Option<ResidualPath>; MAX_RESIDUAL_BEAM_WIDTH * 2] =
core::array::from_fn(|_| None);
let mut candidate_count = 0;
for path in paths[..path_count].iter().flatten() {
let Some(prediction) = Self::safe_predict(&path.lms) else {
continue;
};
debug_assert_eq!(prediction, path.lms.predict());
let nearest = Self::nearest_two_symbols(&levels[factor], prediction, sample);
for (error, symbol, reconstructed, decoded) in nearest.into_iter().flatten() {
let mut next = path.clone();
next.error = next.error.saturating_add(error);
if !Self::safe_update(&mut next.lms, reconstructed, decoded) {
continue;
}
let mut decoder_lms = path.lms.clone();
decoder_lms.update(reconstructed, decoded);
debug_assert_eq!(next.lms.history, decoder_lms.history);
debug_assert_eq!(next.lms.weights, decoder_lms.weights);
trace[trace_count] = TraceNode {
parent: path.trace,
symbol: symbol as u8,
};
next.trace = trace_count as u16;
trace_count += 1;
next.symbol = symbol;
candidates[candidate_count] = Some(next);
candidate_count += 1;
}
}
paths.fill(None);
path_count = 0;
for _ in 0..self.residual_beam_width {
let mut best: Option<usize> = None;
for (index, candidate) in candidates[..candidate_count].iter().enumerate() {
if candidate.as_ref().is_some_and(|value| {
best.is_none_or(|old| {
let old = candidates[old].as_ref().unwrap();
(value.error, value.symbol) < (old.error, old.symbol)
})
}) {
best = Some(index);
}
}
let Some(index) = best else {
break;
};
paths[path_count] = candidates[index].take();
path_count += 1;
}
}
if let Some(winner) = paths.iter_mut().find_map(|path| {
path.take()
.filter(|path| Self::safe_predict(&path.lms).is_some())
}) {
let mut residuals = [0u8; MAX_PERIOD_FRAMES];
let mut node_index = winner.trace as usize;
for residual in residuals[..period.frames].iter_mut().rev() {
let node = trace[node_index];
*residual = node.symbol;
node_index = node.parent as usize;
}
return (winner.error, winner.lms, residuals);
}
let mut residuals = [0u8; MAX_PERIOD_FRAMES];
for (frame, residual) in residuals.iter_mut().take(period.frames).enumerate() {
*residual = period.fallback_residuals[frame * self.channels + period.channel];
}
(u64::MAX, period.fallback_lms.clone(), residuals)
}
fn factor_sse(
&self,
input: &[i16],
frames: usize,
channel: usize,
factor: usize,
width: usize,
initial_lms: &SeaLMS,
) -> Option<u64> {
let levels = self.dequant.get_dqt(width);
let mut lms = initial_lms.clone();
let mut error = 0u64;
for frame in 0..frames {
let prediction = Self::safe_predict(&lms)?;
let sample = input[frame * self.channels + channel];
let nearest =
Self::nearest_two_symbols(&levels[factor], prediction, sample)[0].unwrap();
error = error.saturating_add(nearest.0);
if !Self::safe_update(&mut lms, nearest.2, nearest.3) {
return None;
}
}
Self::safe_predict(&lms)?;
Some(error)
}
pub(super) fn ambiguous_neighbor_factor(
&self,
input: &[i16],
frames: usize,
channel: usize,
factor: usize,
width: usize,
initial_lms: &SeaLMS,
) -> Option<usize> {
let current = self.factor_sse(input, frames, channel, factor, width, initial_lms)?;
let mut second: Option<(usize, u64)> = None;
for candidate in [
factor.checked_sub(1),
(factor + 1 < self.dequant.get_dqt(width).len()).then_some(factor + 1),
]
.into_iter()
.flatten()
{
if let Some(error) =
self.factor_sse(input, frames, channel, candidate, width, initial_lms)
{
if second.is_none_or(|(_, best)| error < best) {
second = Some((candidate, error));
}
}
}
second.and_then(|(candidate, error)| {
(error.saturating_sub(current).saturating_mul(100)
<= current.saturating_mul(ADAPTIVE_SECOND_FACTOR_GAP_PERCENT))
.then_some(candidate)
})
}
#[inline(always)]
fn safe_predict(lms: &SeaLMS) -> Option<i32> {
let prediction = lms.weights[0] as i64 * lms.history[0] as i64
+ lms.weights[1] as i64 * lms.history[1] as i64
+ lms.weights[2] as i64 * lms.history[2] as i64
+ lms.weights[3] as i64 * lms.history[3] as i64;
i32::try_from(prediction).ok().map(|value| value >> 13)
}
#[inline(always)]
fn safe_update(lms: &mut SeaLMS, sample: i16, residual: i32) -> bool {
let delta = residual >> 4;
let mut weights = lms.weights;
for (weight, &history) in weights.iter_mut().zip(&lms.history) {
let adjustment = if history < 0 { -delta } else { delta };
let next = *weight + adjustment;
if !(i16::MIN as i32..=i16::MAX as i32).contains(&next) {
return false;
}
*weight = next;
}
lms.weights = weights;
lms.history.copy_within(1.., 0);
lms.history[3] = sample as i32;
true
}
#[inline(always)]
pub(super) fn nearest_two_symbols(
levels: &[i32],
prediction: i32,
sample: i16,
) -> [Option<(u64, usize, i16, i32)>; 2] {
let max_magnitude = levels[levels.len() - 2];
let unclipped = prediction - max_magnitude >= i16::MIN as i32
&& prediction + max_magnitude <= i16::MAX as i32;
let target = (sample as i32 - prediction).unsigned_abs() as i32;
let pair_count = levels.len() / 2;
let mut first = 0usize;
let mut last = pair_count;
while first < last {
let middle = (first + last) / 2;
if levels[middle * 2] < target {
first = middle + 1;
} else {
last = middle;
}
}
let (start, end) = if unclipped {
(first.saturating_sub(2), (first + 2).min(pair_count))
} else {
(0, pair_count)
};
let mut nearest: [Option<(u64, usize, i16, i32)>; 2] = [None, None];
for pair in start..end {
for symbol in [pair * 2, pair * 2 + 1] {
let decoded = levels[symbol];
let reconstructed = clamp_i16(prediction + decoded);
let difference = sample as i64 - reconstructed as i64;
let candidate = (
(difference * difference) as u64,
symbol,
reconstructed,
decoded,
);
if nearest[0]
.as_ref()
.is_none_or(|best| (candidate.0, candidate.1) < (best.0, best.1))
{
nearest[1] = nearest[0].take();
nearest[0] = Some(candidate);
} else if nearest[1]
.as_ref()
.is_none_or(|best| (candidate.0, candidate.1) < (best.0, best.1))
{
nearest[1] = Some(candidate);
}
}
}
nearest
}
}