use super::bits::BitWriter;
use super::enc_frame::LcEncoder;
use super::error::{Error, Result};
use super::swb::LONG_WINDOW_LEN;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Kind {
Sce = 0,
Cpe = 1,
Lfe = 3,
}
type Route = (Kind, u8, [usize; 2]);
fn routes(planes: usize) -> Option<(u8, &'static [Route])> {
use Kind::{Cpe, Lfe, Sce};
Some(match planes {
3 => (3, &[(Sce, 0, [2, 2]), (Cpe, 0, [0, 1])]),
4 => (4, &[(Sce, 0, [2, 2]), (Cpe, 0, [0, 1]), (Sce, 1, [3, 3])]),
5 => (5, &[(Sce, 0, [2, 2]), (Cpe, 0, [0, 1]), (Cpe, 1, [3, 4])]),
6 => (
6,
&[
(Sce, 0, [2, 2]),
(Cpe, 0, [0, 1]),
(Cpe, 1, [4, 5]),
(Lfe, 0, [3, 3]),
],
),
8 => (
7,
&[
(Sce, 0, [2, 2]),
(Cpe, 0, [0, 1]),
(Cpe, 1, [6, 7]),
(Cpe, 2, [4, 5]),
(Lfe, 0, [3, 3]),
],
),
_ => return None,
})
}
fn weight(kind: Kind) -> u64 {
match kind {
Kind::Sce => 10,
Kind::Cpe => 20,
Kind::Lfe => 1,
}
}
#[must_use]
pub fn config_for(planes: usize) -> Option<u8> {
routes(planes).map(|r| r.0)
}
pub struct McEncoder {
config: u8,
routes: &'static [Route],
elems: Vec<LcEncoder>,
fs_index: u8,
budget: usize,
credit: i64,
pad_debt: i64,
rate_guess: i32,
fixed_split: bool,
specs: Vec<super::enc_frame::Specs>,
}
impl McEncoder {
#[cfg(test)]
pub fn new(sample_rate: u32, planes: usize, bitrate_bps: u32) -> Result<Self> {
Self::new_with(sample_rate, planes, bitrate_bps, LcEncoder::new)
}
pub fn new_with(
sample_rate: u32,
planes: usize,
bitrate_bps: u32,
make: impl Fn(u32, usize, u32) -> Result<LcEncoder>,
) -> Result<Self> {
let (config, routes) =
routes(planes).ok_or(Error::Format("LC encoder: planes must be 3, 4, 5, 6 or 8"))?;
let total: u64 = routes.iter().map(|r| weight(r.0)).sum();
let mut elems = Vec::with_capacity(routes.len());
for &(kind, _, _) in routes {
let ch = if kind == Kind::Cpe { 2 } else { 1 };
let share = (u64::from(bitrate_bps) * weight(kind) / total).max(1);
let cap = u64::from(super::enc_frame::max_bitrate_bps(sample_rate, ch));
let mut e = make(sample_rate, ch, share.min(cap) as u32)?;
if kind == Kind::Lfe {
e.set_lfe();
}
elems.push(e);
}
let fs_index = elems.first().map_or(0, LcEncoder::fs_index);
let cap: usize = routes
.iter()
.map(|r| if r.0 == Kind::Cpe { 2 } else { 1 })
.sum::<usize>()
* super::enc_frame::MAX_BITS_PER_CHANNEL;
let budget = (u64::from(bitrate_bps) * 1024 / u64::from(sample_rate)) as usize;
let fixed_split = elems.iter().any(LcEncoder::is_quality);
Ok(Self {
config,
routes,
specs: vec![[[0.0; LONG_WINDOW_LEN]; 2]; elems.len()],
elems,
fs_index,
budget: budget.min(cap).min(super::enc_frame::MAX_PAYLOAD_BYTES * 8),
credit: 0,
pad_debt: 0,
rate_guess: OFFSET_LO,
fixed_split,
})
}
#[must_use]
pub fn channel_configuration(&self) -> u8 {
self.config
}
#[must_use]
pub fn fs_index(&self) -> u8 {
self.fs_index
}
#[must_use]
pub fn planes(&self) -> usize {
self.routes.iter().map(|r| r.2[1]).max().unwrap_or(0) + 1
}
pub fn reset(&mut self) {
for e in &mut self.elems {
e.reset();
}
self.credit = 0;
self.pad_debt = 0;
self.rate_guess = OFFSET_LO;
}
#[cfg(test)]
pub(crate) fn pin_rate_guess(&mut self, v: i32) {
self.rate_guess = v;
}
#[cfg(test)]
pub fn set_fixed_split(&mut self, on: bool) {
self.fixed_split = on;
}
pub fn encode_into(&mut self, pcm: &[&[f32]], out: &mut Vec<u8>) -> Result<()> {
if pcm.len() != self.planes() || pcm.iter().any(|p| p.len() != LONG_WINDOW_LEN) {
return Err(Error::Format("LC encoder: bad frame shape"));
}
if !self.fixed_split {
return self.encode_common(pcm, out);
}
let mut w = BitWriter::from_vec(std::mem::take(out));
let mut padded = 0usize;
for (enc, &(kind, tag, src)) in self.elems.iter_mut().zip(self.routes.iter()) {
if kind == Kind::Cpe {
enc.encode_into(&[pcm[src[0]], pcm[src[1]]])?;
} else {
enc.encode_into(&[pcm[src[0]]])?;
}
let rdb = enc.payload();
padded += rdb.len();
let last = last_set_bit(rdb).ok_or(Error::Format("LC encoder: empty element"))?;
w.write(kind as u32, 3);
w.write(u32::from(tag), 4);
append_bits(&mut w, rdb, 7, last - 2);
}
let base = w.bit_len() as usize;
let target = padded.min(super::enc_frame::MAX_PAYLOAD_BYTES);
let room = target.saturating_mul(8).saturating_sub(base + 3);
super::enc_pad::write_pad_fill(&mut w, super::enc_pad::pad_fill_for_room(room));
w.write(7, 3); let bytes = w.finish();
*out = bytes;
Ok(())
}
}
impl McEncoder {
fn bits_at(&mut self, offset: i32) -> usize {
let each: usize = self
.elems
.iter_mut()
.zip(self.specs.iter())
.zip(self.routes.iter())
.map(|((e, s), r)| e.mc_bits(s, elem_offset(r.0, offset)))
.sum();
each - 10 * (self.elems.len() - 1)
}
fn encode_common(&mut self, pcm: &[&[f32]], out: &mut Vec<u8>) -> Result<()> {
for ((enc, specs), &(kind, _, src)) in self
.elems
.iter_mut()
.zip(self.specs.iter_mut())
.zip(self.routes.iter())
{
*specs = if kind == Kind::Cpe {
enc.mc_analyze(&[pcm[src[0]], pcm[src[1]]])?
} else {
enc.mc_analyze(&[pcm[src[0]]])?
};
}
let spend = self.budget + self.credit.min(self.budget as i64 / 2) as usize;
let limit = spend.saturating_sub(32);
let guess = self.rate_guess;
let offset = super::enc_frame::search_monotonic(OFFSET_LO, OFFSET_HI, guess, |off| {
self.bits_at(off) <= limit
});
self.rate_guess = offset;
let total = self.bits_at(offset);
let scale = (total > limit).then_some((limit, total));
let mut w = BitWriter::from_vec(std::mem::take(out));
let mut silent = true;
for ((enc, specs), &(kind, tag, _)) in self
.elems
.iter_mut()
.zip(self.specs.iter())
.zip(self.routes.iter())
{
let at = elem_offset(kind, offset);
let cap = scale.map_or(usize::MAX, |(limit, total)| {
enc.mc_bits(specs, at) * limit / total
});
let (_, zero) = enc.mc_emit(specs, at, cap);
silent &= zero;
let rdb = enc.payload();
let last = last_set_bit(rdb).ok_or(Error::Format("LC encoder: empty element"))?;
w.write(kind as u32, 3);
w.write(u32::from(tag), 4);
append_bits(&mut w, rdb, 7, last - 2);
}
let base = w.bit_len() as usize;
let coded = (base + 3).div_ceil(8) * 8;
let pad_to = self.budget.saturating_sub(self.pad_debt.max(0) as usize);
if !silent && coded < pad_to * 97 / 100 {
let room = (pad_to / 8).saturating_mul(8).saturating_sub(base + 3);
super::enc_pad::write_pad_fill(&mut w, super::enc_pad::pad_fill_for_room(room));
}
w.write(7, 3); let bytes = w.finish();
let emitted = (bytes.len() * 8) as i64;
self.pad_debt = (self.pad_debt + emitted - self.budget as i64).max(0);
self.credit =
(self.credit + self.budget as i64 - coded as i64).clamp(0, self.budget as i64);
*out = bytes;
Ok(())
}
}
const LFE_BIAS: i32 = -16;
fn elem_offset(kind: Kind, offset: i32) -> i32 {
if kind == Kind::Lfe {
(offset + LFE_BIAS).max(OFFSET_LO)
} else {
offset
}
}
const OFFSET_LO: i32 = -60;
const OFFSET_HI: i32 = 240;
fn last_set_bit(bytes: &[u8]) -> Option<usize> {
let i = bytes.iter().rposition(|&b| b != 0)?;
Some(i * 8 + 7 - bytes[i].trailing_zeros() as usize)
}
fn append_bits(w: &mut BitWriter, bytes: &[u8], from: usize, to: usize) {
let mut at = from;
while at < to {
let in_byte = 8 - at % 8;
let n = in_byte.min(to - at);
let v = (u32::from(bytes[at / 8]) >> (in_byte - n)) & ((1u32 << n) - 1);
w.write(v, n as u32);
at += n;
}
}
#[cfg(test)]
#[path = "enc_mc_tests.rs"]
mod enc_mc_tests;