use super::adts::ADTS_SAMPLE_RATES_HZ;
use super::bits::BitWriter;
use super::enc_psy::Psy;
use super::enc_quant::{self, MAX_BANDS, QuantChannel};
use super::enc_section;
use super::error::{Error, Result};
use super::filterbank::window_left;
use super::ics::WindowShape;
use super::mdct::mdct_into_f32;
use super::swb::{LONG_WINDOW_LEN, long_offsets};
pub const MAX_PAYLOAD_BYTES: usize = 8184;
const TARGET_Q: f32 = 2048.0;
const OFFSET_LO: i32 = -8;
const OFFSET_HI: i32 = 80;
pub struct LcEncoder {
fs_index: u8,
sample_rate: u32,
channels: usize,
bitrate_bps: u32,
offsets: &'static [u16],
window: Box<[f32; 2 * LONG_WINDOW_LEN]>,
prev: [[f32; LONG_WINDOW_LEN]; 2],
chans: Box<[QuantChannel; 2]>,
books: [[u8; MAX_BANDS]; 2],
gains: [u8; 2],
psy: Psy,
target_q: [[f32; MAX_BANDS]; 2],
credit: i64,
ms_used: bool,
}
impl LcEncoder {
pub fn new(sample_rate: u32, channels: usize, bitrate_bps: u32) -> Result<Self> {
let fs_index = ADTS_SAMPLE_RATES_HZ
.iter()
.position(|&r| r == sample_rate)
.ok_or(Error::UnsupportedSampleRateIndex(0xFF))? as u8;
if !(1..=2).contains(&channels) {
return Err(Error::Format("LC encoder: channels must be 1 or 2"));
}
if bitrate_bps == 0 {
return Err(Error::Format("LC encoder: bitrate must be > 0"));
}
let offsets = long_offsets(fs_index)?;
let half = window_left(2 * LONG_WINDOW_LEN, WindowShape::Kbd);
let mut window = Box::new([0.0f32; 2 * LONG_WINDOW_LEN]);
for i in 0..LONG_WINDOW_LEN {
window[i] = half[i] * 32768.0;
window[2 * LONG_WINDOW_LEN - 1 - i] = half[i] * 32768.0;
}
let n_bands = offsets.len() - 1;
Ok(Self {
fs_index,
sample_rate,
channels,
bitrate_bps,
offsets,
window,
prev: [[0.0; LONG_WINDOW_LEN]; 2],
chans: Box::new([QuantChannel::new(n_bands), QuantChannel::new(n_bands)]),
books: [[0; MAX_BANDS]; 2],
gains: [100; 2],
psy: Psy::new(offsets, sample_rate),
target_q: [[0.0; MAX_BANDS]; 2],
credit: 0,
ms_used: false,
})
}
#[must_use]
pub fn fs_index(&self) -> u8 {
self.fs_index
}
fn budget_bits(&self) -> usize {
let bits = u64::from(self.bitrate_bps) * 1024 / u64::from(self.sample_rate);
(bits as usize).min(MAX_PAYLOAD_BYTES * 8)
}
pub fn encode_frame(&mut self, pcm: &[&[f32]]) -> Result<Vec<u8>> {
if pcm.len() != self.channels || pcm.iter().any(|c| c.len() != LONG_WINDOW_LEN) {
return Err(Error::Format("LC encoder: bad frame shape"));
}
let mut specs = [[0.0f32; LONG_WINDOW_LEN]; 2];
for ((spec, prev), plane) in specs.iter_mut().zip(self.prev.iter_mut()).zip(pcm.iter()) {
let mut buf = [0.0f32; 2 * LONG_WINDOW_LEN];
buf[..LONG_WINDOW_LEN].copy_from_slice(prev);
buf[LONG_WINDOW_LEN..].copy_from_slice(plane);
for (b, &w) in buf.iter_mut().zip(self.window.iter()) {
*b *= w;
}
mdct_into_f32(&buf, spec);
prev.copy_from_slice(plane);
}
self.ms_used = self.channels == 2 && self.decide_ms(&mut specs);
let budget = self.budget_bits();
let spend = budget + (self.credit.min(budget as i64 / 2)) as usize;
let offset = self.search_offset(&specs, spend);
let bits = self.build(&specs, offset);
if bits > spend {
self.drop_bands_until(spend);
}
let out = self.emit();
self.credit =
(self.credit + budget as i64 - (out.len() * 8) as i64).clamp(0, budget as i64);
Ok(out)
}
fn decide_ms(&self, specs: &mut [[f32; LONG_WINDOW_LEN]; 2]) -> bool {
let mut e_side = 0.0f64;
let mut e_right = 0.0f64;
{
let [l, r] = specs;
for (&lv, &rv) in l.iter().zip(r.iter()) {
let s = f64::from((lv - rv) * 0.5);
e_side += s * s;
e_right += f64::from(rv) * f64::from(rv);
}
}
if e_side >= e_right {
return false;
}
let [l, r] = specs;
for (lv, rv) in l.iter_mut().zip(r.iter_mut()) {
let (a, b) = (*lv, *rv);
*lv = (a + b) * 0.5;
*rv = (a - b) * 0.5;
}
true
}
fn search_offset(&mut self, specs: &[[f32; LONG_WINDOW_LEN]; 2], spend: usize) -> i32 {
let budget = spend.saturating_sub(32);
if self.build(specs, OFFSET_LO) <= budget {
return OFFSET_LO; }
if self.build(specs, OFFSET_HI) > budget {
return OFFSET_HI; }
let (mut lo, mut hi) = (OFFSET_LO, OFFSET_HI);
while hi - lo > 1 {
let mid = lo + (hi - lo) / 2;
if self.build(specs, mid) <= budget {
hi = mid;
} else {
lo = mid;
}
}
hi
}
fn build(&mut self, specs: &[[f32; LONG_WINDOW_LEN]; 2], offset: i32) -> usize {
let standalone = self.channels == 1;
let mut total = 3 + 4 + 3; if self.channels == 2 {
total += 1 + 11 + 2; }
let channels = self
.chans
.iter_mut()
.zip(self.target_q.iter_mut())
.zip(specs.iter())
.zip(self.books.iter_mut())
.zip(self.gains.iter_mut())
.take(self.channels);
for ((((q, tq), spec), books), gain) in channels {
self.psy
.analyze(spec, self.offsets, TARGET_Q, &mut q.coded, tq);
let mut peaks = [0.0f32; MAX_BANDS];
enc_quant::band_peaks(spec, self.offsets, &mut peaks);
enc_quant::raw_scalefactors(&peaks, tq, offset, q);
*gain = enc_quant::normalize_sf(q);
enc_quant::quantize(spec, self.offsets, q);
*books = enc_section::plan_books(q);
total += enc_section::channel_body_bits(books, q, *gain, standalone);
}
total + 7 }
fn drop_bands_until(&mut self, limit_bits: usize) {
let standalone = self.channels == 1;
let cap = limit_bits.min(MAX_PAYLOAD_BYTES * 8);
loop {
let mut total = 3 + 4 + 3 + 7;
if self.channels == 2 {
total += 1 + 11 + 2;
}
let channels = self
.chans
.iter()
.zip(self.books.iter())
.zip(self.gains.iter())
.take(self.channels);
for ((q, books), gain) in channels {
total += enc_section::channel_body_bits(books, q, *gain, standalone);
}
if total <= cap {
return;
}
let mut hit: Option<(usize, usize)> = None;
for (ch, q) in self.chans.iter().enumerate().take(self.channels) {
if let Some(b) = q.coded[..q.n_bands].iter().rposition(|&c| c) {
hit = Some((ch, b));
break;
}
}
let Some((ch, b)) = hit else { return };
self.chans[ch].coded[b] = false;
self.books[ch][b] = 0;
self.books[ch] = enc_section::plan_books(&self.chans[ch]);
}
}
fn emit(&self) -> Vec<u8> {
let max_sfb = self.chans[0].n_bands as u8;
let mut w = BitWriter::new();
if self.channels == 1 {
w.write(0, 3); w.write(0, 4); enc_section::emit_channel_body(
&mut w,
self.offsets,
&self.books[0],
&self.chans[0],
self.gains[0],
true,
);
} else {
w.write(1, 3); w.write(0, 4); w.write_bit(true); enc_section::emit_ics_info(&mut w, max_sfb);
w.write(u32::from(self.ms_used) * 2, 2); let channels = self
.chans
.iter()
.zip(self.books.iter())
.zip(self.gains.iter());
for ((q, books), gain) in channels {
enc_section::emit_channel_body(&mut w, self.offsets, books, q, *gain, false);
}
}
w.write(7, 3); w.finish()
}
}
#[cfg(test)]
#[path = "enc_frame_tests.rs"]
mod enc_frame_tests;