use super::bits::BitWriter;
use super::enc_huff::{sf_delta_bits, sf_emit_delta, spectral_emit};
use super::enc_ms::MsBands;
use super::enc_quant::{BOOKS, MAX_BANDS, QuantChannel, UNREPRESENTABLE};
use super::enc_tns::EncTns;
use super::ics::WindowSequence;
use super::section::has_spectral;
fn section_header_bits(len: usize) -> usize {
4 + 5 * (len / 31 + 1)
}
fn range_book_bits(coded: &[bool], bits: &[[u32; BOOKS]], lo: usize, hi: usize, cb: usize) -> u32 {
let mut total = 0u32;
for b in lo..hi {
if !coded[b] {
continue;
}
let bits = bits[b][cb];
if bits == UNREPRESENTABLE {
return UNREPRESENTABLE;
}
total = total.saturating_add(bits);
}
total
}
fn best_book(coded: &[bool], bits: &[[u32; BOOKS]], lo: usize, hi: usize) -> Option<(u8, u32)> {
let mut best: Option<(u8, u32)> = None;
for cb in 1..BOOKS {
let bits = range_book_bits(coded, bits, lo, hi, cb);
if bits == UNREPRESENTABLE {
continue;
}
if best.is_none_or(|(_, b)| bits < b) {
best = Some((cb as u8, bits));
}
}
best
}
pub(super) fn plan_books_into(
coded: &[bool],
bits: &[[u32; BOOKS]],
n: usize,
sfb_cb: &mut [u8],
header_bits: fn(usize) -> usize,
) {
for b in 0..n {
if coded[b] {
sfb_cb[b] = best_book(coded, bits, b, b + 1)
.map(|(cb, _)| cb)
.unwrap_or(11);
} else {
sfb_cb[b] = 0;
}
}
let mut improved = true;
while improved {
improved = false;
let mut lo = 0usize;
while lo < n {
if !coded[lo] {
lo += 1;
continue;
}
let mut hi = lo + 1;
while hi < n && coded[hi] {
hi += 1;
}
if merge_run(coded, bits, sfb_cb, lo, hi, header_bits) {
improved = true;
}
lo = hi;
}
}
}
fn merge_run(
coded: &[bool],
bits: &[[u32; BOOKS]],
sfb_cb: &mut [u8],
lo: usize,
hi: usize,
header_bits: fn(usize) -> usize,
) -> bool {
let mut merged = false;
let mut i = lo;
while i < hi {
let mut j = i + 1;
while j < hi && sfb_cb[j] == sfb_cb[i] {
j += 1;
}
if j < hi {
let mut k = j + 1;
while k < hi && sfb_cb[k] == sfb_cb[j] {
k += 1;
}
let now = header_bits(j - i) + header_bits(k - j);
let one = header_bits(k - i);
let saved_headers = now as i64 - one as i64;
if let Some((cb, bits_merged)) = best_book(coded, bits, i, k) {
let cur = range_book_bits(coded, bits, i, j, usize::from(sfb_cb[i]))
.saturating_add(range_book_bits(coded, bits, j, k, usize::from(sfb_cb[j])));
if i64::from(bits_merged) - i64::from(cur) < saved_headers {
sfb_cb[i..k].fill(cb);
merged = true;
}
}
}
i = j;
}
merged
}
pub fn plan_books(q: &QuantChannel) -> [u8; MAX_BANDS] {
let mut sfb_cb = [0u8; MAX_BANDS];
plan_books_into(
&q.coded,
&q.bits,
q.n_bands,
&mut sfb_cb,
section_header_bits,
);
sfb_cb
}
pub fn emit_ics_info(w: &mut BitWriter, seq: WindowSequence, max_sfb: u8) {
w.write_bit(false); w.write(u32::from(seq as u8), 2);
w.write_bit(true); if seq.is_eight_short() {
w.write(u32::from(max_sfb), 4);
w.write(0, 7); } else {
w.write(u32::from(max_sfb), 6);
w.write_bit(false); }
}
pub fn emit_section_data(w: &mut BitWriter, sfb_cb: &[u8; MAX_BANDS], max_sfb: usize) {
let mut k = 0usize;
while k < max_sfb {
let cb = sfb_cb[k];
let mut len = 1usize;
while k + len < max_sfb && sfb_cb[k + len] == cb {
len += 1;
}
w.write(u32::from(cb), 4);
let mut rem = len;
while rem >= 31 {
w.write(31, 5);
rem -= 31;
}
w.write(rem as u32, 5);
k += len;
}
}
pub fn emit_scale_factors(
w: &mut BitWriter,
sfb_cb: &[u8; MAX_BANDS],
q: &QuantChannel,
global_gain: u8,
) {
let mut prev = i32::from(global_gain);
let bands = sfb_cb.iter().zip(q.sf.iter()).take(q.n_bands);
for (&cb, &sf) in bands {
if !has_spectral(cb) {
continue;
}
sf_emit_delta(w, sf - prev);
prev = sf;
}
}
pub fn emit_spectral(
w: &mut BitWriter,
offsets: &[u16],
sfb_cb: &[u8; MAX_BANDS],
q: &QuantChannel,
) {
let bands = sfb_cb.iter().zip(offsets.windows(2)).take(q.n_bands);
for (&cb, win) in bands {
if !has_spectral(cb) {
continue;
}
let lo = usize::from(win[0]);
let hi = usize::from(win[1]);
let step = if cb <= 4 { 4 } else { 2 };
let mut i = lo;
while i < hi {
spectral_emit(cb, &q.quant[i..i + step], w);
i += step;
}
}
}
pub fn channel_body_bits(
sfb_cb: &[u8; MAX_BANDS],
q: &QuantChannel,
global_gain: u8,
standalone: bool,
tns: &EncTns,
) -> usize {
let mut bits = 8; if standalone {
bits += 11; }
let mut k = 0usize;
while k < q.n_bands {
let mut len = 1usize;
while k + len < q.n_bands && sfb_cb[k + len] == sfb_cb[k] {
len += 1;
}
bits += section_header_bits(len);
k += len;
}
let mut prev = i32::from(global_gain);
let bands = sfb_cb
.iter()
.zip(q.sf.iter())
.zip(q.bits.iter())
.take(q.n_bands);
for ((&cb, &sf), row) in bands {
if !has_spectral(cb) {
continue;
}
bits += sf_delta_bits(sf - prev);
prev = sf;
bits += row[usize::from(cb)] as usize;
}
bits += 2 + tns.bits(); bits
}
#[allow(clippy::too_many_arguments)]
pub fn emit_channel_body(
w: &mut BitWriter,
offsets: &[u16],
seq: WindowSequence,
sfb_cb: &[u8; MAX_BANDS],
q: &QuantChannel,
global_gain: u8,
standalone: bool,
tns: &EncTns,
) {
w.write(u32::from(global_gain), 8);
if standalone {
emit_ics_info(w, seq, q.n_bands as u8);
}
emit_section_data(w, sfb_cb, q.n_bands);
emit_scale_factors(w, sfb_cb, q, global_gain);
w.write_bit(false); tns.emit(w); w.write_bit(false); emit_spectral(w, offsets, sfb_cb, q);
}
#[allow(clippy::too_many_arguments)]
pub fn emit_frame(
offsets: &[u16],
seq: WindowSequence,
chans: &[QuantChannel],
books: &[[u8; MAX_BANDS]],
gains: &[u8],
ms: &MsBands,
tns: &[EncTns],
channels: usize,
) -> Vec<u8> {
let mut w = BitWriter::new();
if channels == 1 {
w.write(0, 3); w.write(0, 4); emit_channel_body(
&mut w, offsets, seq, &books[0], &chans[0], gains[0], true, &tns[0],
);
} else {
w.write(1, 3); w.write(0, 4); w.write_bit(true); emit_ics_info(&mut w, seq, chans[0].n_bands as u8);
ms.emit(&mut w); for ch in 0..channels {
emit_channel_body(
&mut w, offsets, seq, &books[ch], &chans[ch], gains[ch], false, &tns[ch],
);
}
}
w.write(7, 3); w.finish()
}
#[cfg(test)]
#[path = "enc_section_tests.rs"]
mod enc_section_tests;