Files
deckcraft/crates/av1/src/symbol.rs
T
Brandon ThomasandClaude Opus 5.5 4375fe7b89 Media playback: slidecraft-media (probe, audio decode, H.264/HEVC/VP9/AV1 video, mixer and clock), cpal output, editor control bar, slide show playback, media.* commands
- crates/media: probe (duration, rate/channels, size/fps; WMA/WMV recognised), audio decode to
  f32 PCM (MP3, AAC M4A/ADTS, ALAC, WAV, AIFF, CAF, FLAC, Vorbis via symphonia; Opus with our
  decoder in Ogg/WebM/MKV), video decode (MP4/MOV/WebM/MKV) with YUV->RGBA, poster frames,
  trim/fade/volume, Player mixer + per-voice clock (device-clocked or wall-clocked), VideoFeed.
- Codec crates ported from FilmCraft: bitstream, h264, hevc, vp9, av1, isobmff, matroska, ogg, opus.
- insert.audio/insert.video probe and store duration/size and a poster frame; unplayable media
  is embedded with a warning. New media.play/pause/toggle/stop/seek/info/posterFrame.
- UI: MediaHost (cpal output injected by the desktop app), control bar under media, video frames
  on canvas and in the show; autoplay, click to play, loop, rewind, across slides, hide, full screen.
- Tests with in-code WAV/AIFF and CC0 self-generated fixtures (make.sh, ATTRIBUTION.md rows).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:35:09 -04:00

139 lines
4.4 KiB
Rust

//! Symbol (arithmetic) decoder, spec 8.2.
//!
//! The state follows the specification literally: `SymbolValue` / `SymbolRange` are 15/16-bit
//! quantities renormalised by reading `bits` new bits at a time, and `SymbolMaxBits` counts the
//! bits still available (going negative while zero padding is consumed).
const EC_PROB_SHIFT: u32 = 6;
const EC_MIN_PROB: u32 = 4;
pub(crate) struct SymbolDecoder<'a> {
data: &'a [u8],
/// Next byte of `data` to load into the window.
next: usize,
/// SymbolValue followed by `k` look-ahead bits: the bitstream with every bit inverted
/// (the spec's renormalisation XORs the new bits with ones), zero bits past the end of
/// the data reading as ones. SymbolValue = win >> k.
win: u64,
k: u32,
range: u32,
pub disable_update: bool,
}
impl<'a> SymbolDecoder<'a> {
/// `init_symbol( sz )` over `data` (the tile's bytes).
pub fn new(data: &'a [u8], disable_update: bool) -> Self {
let mut d = SymbolDecoder { data, next: 0, win: 0, k: 0, range: 1 << 15, disable_update };
// SymbolValue = ((1 << 15) - 1) ^ (first 15 bits): 15 inverted bits.
let mut filled = 0;
while filled < 15 + 32 {
d.win = (d.win << 8) | d.inverted_byte() as u64;
filled += 8;
}
d.k = filled - 15;
d
}
#[inline(always)]
fn inverted_byte(&mut self) -> u8 {
let b = self.data.get(self.next).copied().unwrap_or(0);
self.next += 1;
!b
}
/// `read_symbol( cdf )`: `cdf` has N + 1 entries (the last is the adaptation counter).
#[inline]
pub fn read_symbol(&mut self, cdf: &mut [u16]) -> usize {
let n = cdf.len() - 1;
let symbol = self.decode(cdf, n);
if !self.disable_update {
update_cdf(cdf, n, symbol);
}
symbol
}
#[inline(always)]
fn decode(&mut self, cdf: &[u16], n: usize) -> usize {
let value = (self.win >> self.k) as u32;
let mut cur = self.range;
let mut symbol = 0usize;
let mut prev;
loop {
prev = cur;
let f = (1u32 << 15) - cdf[symbol] as u32;
cur = ((self.range >> 8) * (f >> EC_PROB_SHIFT)) >> (7 - EC_PROB_SHIFT);
cur += EC_MIN_PROB * (n - symbol - 1) as u32;
if value >= cur {
break;
}
symbol += 1;
}
self.range = prev - cur;
self.win -= (cur as u64) << self.k;
self.renormalize();
symbol
}
/// Renormalisation (8.2.6): SymbolValue takes `bits` more (inverted) bits, which are
/// already in the window.
#[inline(always)]
fn renormalize(&mut self) {
let bits = 15 - (31 - self.range.leading_zeros());
self.range <<= bits;
self.k -= bits;
if self.k < 16 {
// SymbolValue < 2^16, so 16 + k <= 64 bits stay in the window.
while self.k <= 40 {
self.win = (self.win << 8) | self.inverted_byte() as u64;
self.k += 8;
}
}
}
/// `read_bool()`
#[inline]
pub fn read_bool(&mut self) -> u32 {
let cdf = [1u16 << 14, 1 << 15, 0];
self.decode(&cdf, 2) as u32
}
/// `read_literal( n )` (L(n))
pub fn read_literal(&mut self, n: u32) -> u32 {
let mut x = 0;
for _ in 0..n {
x = (x << 1) | self.read_bool();
}
x
}
/// NS(n)
pub fn read_ns(&mut self, n: u32) -> u32 {
if n <= 1 {
return 0;
}
let w = 32 - n.leading_zeros();
let m = (1u32 << w) - n;
let v = self.read_literal(w - 1);
if v < m {
return v;
}
let extra = self.read_literal(1);
(v << 1) - m + extra
}
}
/// CDF adaptation (8.2.6).
#[inline(always)]
pub(crate) fn update_cdf(cdf: &mut [u16], n: usize, symbol: usize) {
let count = cdf[n];
let rate = 3 + (count > 15) as u32 + (count > 31) as u32 + (31 - (n as u32).leading_zeros()).min(2);
// Entries below `symbol` move towards 0, the others towards 1 << 15 (the specification's
// comparison with tmp, resolved per entry: c >= 0 and c <= 1 << 15), without branching on
// the decoded symbol.
for (i, e) in cdf[..n - 1].iter_mut().enumerate() {
let c = *e as u32;
*e = if i < symbol { c - (c >> rate) } else { c + (((1 << 15) - c) >> rate) } as u16;
}
cdf[n] += (count < 32) as u16;
}