oxideav-aac 0.1.7

Pure-Rust AAC-LC decoder and encoder for oxideav — ADTS framing, Huffman books 1-11, IMDCT, M/S stereo, TNS, PNS
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
//! `sbr_envelope()` / `sbr_noise()` raw decode — ISO/IEC 14496-3
//! §4.4.2.8, Tables 4.72–4.73.
//!
//! These two elements carry the SBR spectral-envelope scalefactors and
//! noise-floor scalefactors as **delta values** (`bs_data_env` /
//! `bs_data_noise`). For each envelope (resp. noise floor) the delta
//! direction comes from `sbr_dtdf()` ([`crate::sbr_grid::SbrDtdf`]):
//!
//! * delta-in-**frequency** (`bs_df_* == 0`): the first band carries an
//!   absolute *start value* read as a fixed-width field, and the
//!   remaining bands are frequency-direction Huffman deltas (`f_huff`).
//! * delta-in-**time** (`bs_df_* == 1`): every band is a
//!   time-direction Huffman delta (`t_huff`) relative to the
//!   corresponding band of the previous envelope / noise floor.
//!
//! The start-value field widths (Table 4.72 / 4.73) depend on the
//! coupling / channel / amplitude-resolution context:
//!
//! | element | context | width |
//! |---------|---------|-------|
//! | envelope | coupling && ch, amp_res   | 5 |
//! | envelope | coupling && ch, !amp_res  | 6 |
//! | envelope | level, amp_res            | 6 |
//! | envelope | level, !amp_res           | 7 |
//! | noise    | (any)                     | 5 |
//!
//! The per-envelope band count is `num_env_bands[bs_freq_res]` — the
//! high-resolution band count `NHigh` when the envelope's freq-res flag
//! is set, otherwise the low-resolution count `NLow`
//! ([`crate::sbr_freq_bands::HiLoTables`]). The noise band count is
//! `NQ` for every noise floor.
//!
//! This module produces the **raw** delta arrays exactly as written on
//! the wire; the §4.6.18.3.5 DPCM accumulation across bands / time and
//! the §4.6.18 dequantization to linear energies are downstream.

use crate::sbr_grid::{SbrDtdf, SbrGrid};
use crate::sbr_huffman::{env_tables, noise_tables, sbr_huff_dec, SbrHuffContext};
use crate::{Error, Result};
use oxideav_core::bits::BitReader;

/// Raw `bs_data_env` for one channel: one delta vector per envelope.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SbrEnvelopeData {
    /// `bs_data_env[env][band]` — the raw delta (or, at band 0 of a
    /// frequency-coded envelope, the absolute start value). One inner
    /// vector per envelope; its length is the envelope's band count.
    pub data: Vec<Vec<i32>>,
}

/// Raw `bs_data_noise` for one channel: one delta vector per noise
/// floor.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SbrNoiseData {
    /// `bs_data_noise[noise][band]` — raw delta / start value. One
    /// inner vector per noise floor; each has `NQ` entries.
    pub data: Vec<Vec<i32>>,
}

/// The number of envelope bands for a given freq-resolution flag:
/// `NHigh` (high res) or `NLow` (low res).
fn num_env_bands(bands: &crate::sbr_freq_bands::HiLoTables, high_res: bool) -> usize {
    if high_res {
        bands.n_high()
    } else {
        bands.n_low()
    }
}

impl SbrEnvelopeData {
    /// Parse `sbr_envelope()` (Table 4.72) for one channel.
    ///
    /// * `grid` / `dtdf` are this channel's already-parsed grid and
    ///   delta-direction flags.
    /// * `bands` supplies `NHigh` / `NLow` for the per-envelope band
    ///   counts.
    /// * `coupling` is the element `bs_coupling`; `ch` is the channel
    ///   index within the element; `amp_res` is the *effective*
    ///   amplitude resolution (after any single-envelope FIXFIX
    ///   override).
    pub fn parse(
        reader: &mut BitReader<'_>,
        grid: &SbrGrid,
        dtdf: &SbrDtdf,
        bands: &crate::sbr_freq_bands::HiLoTables,
        coupling: bool,
        ch: bool,
        amp_res: bool,
    ) -> Result<Self> {
        let ctx = SbrHuffContext {
            coupling,
            ch,
            amp_res,
        };
        let ((t_huff, t_lav), (f_huff, f_lav)) = env_tables(ctx);

        // Start-value width per Table 4.72.
        let start_bits = if coupling && ch {
            if amp_res {
                5
            } else {
                6
            }
        } else if amp_res {
            6
        } else {
            7
        };

        let mut data = Vec::with_capacity(grid.num_env);
        for env in 0..grid.num_env {
            let n = num_env_bands(bands, grid.freq_res[env]);
            let mut row = Vec::with_capacity(n);
            if !dtdf.df_env[env] {
                // Delta in frequency: band 0 is the absolute start
                // value, bands 1.. are f_huff deltas.
                let start = read(reader, start_bits)? as i32;
                row.push(start);
                for _ in 1..n {
                    row.push(sbr_huff_dec(reader, f_huff, f_lav)?);
                }
            } else {
                // Delta in time: every band is a t_huff delta.
                for _ in 0..n {
                    row.push(sbr_huff_dec(reader, t_huff, t_lav)?);
                }
            }
            data.push(row);
        }
        Ok(SbrEnvelopeData { data })
    }
}

impl SbrNoiseData {
    /// Parse `sbr_noise()` (Table 4.73) for one channel.
    ///
    /// `num_noise_bands` is `NQ`
    /// ([`crate::sbr_freq_bands::HiLoTables::n_q`]). The other
    /// arguments mirror [`SbrEnvelopeData::parse`]; the noise start
    /// value is always a 5-bit field (Table 4.73), regardless of
    /// `amp_res`.
    pub fn parse(
        reader: &mut BitReader<'_>,
        grid: &SbrGrid,
        dtdf: &SbrDtdf,
        num_noise_bands: usize,
        coupling: bool,
        ch: bool,
        amp_res: bool,
    ) -> Result<Self> {
        let ctx = SbrHuffContext {
            coupling,
            ch,
            amp_res,
        };
        let ((t_huff, t_lav), (f_huff, f_lav)) = noise_tables(ctx);

        let mut data = Vec::with_capacity(grid.num_noise);
        for noise in 0..grid.num_noise {
            let mut row = Vec::with_capacity(num_noise_bands);
            if !dtdf.df_noise[noise] {
                // Delta in frequency: band 0 is a 5-bit absolute start
                // value, bands 1.. are f_huff deltas.
                let start = read(reader, 5)? as i32;
                row.push(start);
                for _ in 1..num_noise_bands {
                    row.push(sbr_huff_dec(reader, f_huff, f_lav)?);
                }
            } else {
                for _ in 0..num_noise_bands {
                    row.push(sbr_huff_dec(reader, t_huff, t_lav)?);
                }
            }
            data.push(row);
        }
        Ok(SbrNoiseData { data })
    }
}

#[inline]
fn read(reader: &mut BitReader<'_>, n: u32) -> Result<u32> {
    reader.read_u32(n).map_err(|_| Error::SbrHuffInvalid)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::sbr_freq_bands::{k0, k2, master_table, HiLoTables};
    use crate::sbr_grid::FrameClass;
    use oxideav_core::bits::{BitReader, BitWriter};

    fn bands_44100() -> HiLoTables {
        // The known-good 44.1 kHz linear geometry from sbr_freq_bands.
        let k0v = k0(88_200, 5).unwrap();
        let k2v = k2(88_200, 5, k0v).unwrap();
        let fm = master_table(k0v, k2v, 0, false).unwrap();
        HiLoTables::derive(&fm, 1, 2).unwrap()
    }

    /// Push the MSB-first codeword for one table entry into a writer.
    fn push_code(w: &mut BitWriter, table: &[(u8, u32)], idx: usize) {
        let (len, code) = table[idx];
        w.write_u32(code, len as u32);
    }

    #[test]
    fn envelope_freq_direction_one_env() {
        let bands = bands_44100();
        // FIXFIX, single env, high-res freq, delta-in-frequency.
        let grid = SbrGrid {
            frame_class: FrameClass::FixFix,
            num_env: 1,
            num_noise: 1,
            freq_res: vec![true],
            var_bord_0: 0,
            var_bord_1: 0,
            rel_bord_0: vec![],
            rel_bord_1: vec![],
            pointer: 0,
            amp_res_override: true,
        };
        let dtdf = SbrDtdf {
            df_env: vec![false], // frequency direction
            df_noise: vec![false],
        };
        // amp_res = false → level start width 7 bits.
        let n = bands.n_high();
        let ((_t, _tl), (f_huff, f_lav)) = env_tables(SbrHuffContext {
            coupling: false,
            ch: false,
            amp_res: false,
        });
        let mut w = BitWriter::new();
        w.write_u32(40, 7); // start value
                            // remaining n-1 bands: pick a few known indices.
        let chosen: Vec<usize> = (1..n)
            .map(|i| (i + f_lav as usize) % f_huff.len())
            .collect();
        for &idx in &chosen {
            push_code(&mut w, f_huff, idx);
        }
        let bytes = w.finish();
        let mut r = BitReader::new(&bytes);
        let ev = SbrEnvelopeData::parse(&mut r, &grid, &dtdf, &bands, false, false, false).unwrap();
        assert_eq!(ev.data.len(), 1);
        assert_eq!(ev.data[0].len(), n);
        assert_eq!(ev.data[0][0], 40);
        for (band, &idx) in chosen.iter().enumerate() {
            assert_eq!(ev.data[0][band + 1], idx as i32 - f_lav);
        }
    }

    #[test]
    fn envelope_time_direction() {
        let bands = bands_44100();
        let grid = SbrGrid {
            frame_class: FrameClass::FixFix,
            num_env: 1,
            num_noise: 1,
            freq_res: vec![false], // low res
            var_bord_0: 0,
            var_bord_1: 0,
            rel_bord_0: vec![],
            rel_bord_1: vec![],
            pointer: 0,
            amp_res_override: false,
        };
        let dtdf = SbrDtdf {
            df_env: vec![true], // time direction → no start value
            df_noise: vec![false],
        };
        let n = bands.n_low();
        let ((t_huff, t_lav), (_f, _fl)) = env_tables(SbrHuffContext {
            coupling: false,
            ch: false,
            amp_res: false,
        });
        let mut w = BitWriter::new();
        let chosen: Vec<usize> = (0..n).map(|i| (i * 2 + 1) % t_huff.len()).collect();
        for &idx in &chosen {
            push_code(&mut w, t_huff, idx);
        }
        let bytes = w.finish();
        let mut r = BitReader::new(&bytes);
        let ev = SbrEnvelopeData::parse(&mut r, &grid, &dtdf, &bands, false, false, false).unwrap();
        assert_eq!(ev.data[0].len(), n);
        for (band, &idx) in chosen.iter().enumerate() {
            assert_eq!(ev.data[0][band], idx as i32 - t_lav);
        }
    }

    #[test]
    fn noise_freq_and_time() {
        let bands = bands_44100();
        let nq = bands.n_q();
        let grid = SbrGrid {
            frame_class: FrameClass::FixVar,
            num_env: 2,
            num_noise: 2,
            freq_res: vec![true, true],
            var_bord_0: 0,
            var_bord_1: 0,
            rel_bord_0: vec![],
            rel_bord_1: vec![],
            pointer: 0,
            amp_res_override: false,
        };
        let dtdf = SbrDtdf {
            df_env: vec![false, false],
            df_noise: vec![false, true], // floor 0 freq, floor 1 time
        };
        let ((t_huff, t_lav), (f_huff, f_lav)) = noise_tables(SbrHuffContext {
            coupling: false,
            ch: false,
            amp_res: false,
        });
        let mut w = BitWriter::new();
        // Floor 0: 5-bit start + (nq-1) f deltas.
        w.write_u32(12, 5);
        let f_chosen: Vec<usize> = (1..nq)
            .map(|i| (i + f_lav as usize) % f_huff.len())
            .collect();
        for &idx in &f_chosen {
            push_code(&mut w, f_huff, idx);
        }
        // Floor 1: nq t deltas.
        let t_chosen: Vec<usize> = (0..nq)
            .map(|i| (i + t_lav as usize) % t_huff.len())
            .collect();
        for &idx in &t_chosen {
            push_code(&mut w, t_huff, idx);
        }
        let bytes = w.finish();
        let mut r = BitReader::new(&bytes);
        let nd = SbrNoiseData::parse(&mut r, &grid, &dtdf, nq, false, false, false).unwrap();
        assert_eq!(nd.data.len(), 2);
        assert_eq!(nd.data[0].len(), nq);
        assert_eq!(nd.data[0][0], 12);
        for (band, &idx) in f_chosen.iter().enumerate() {
            assert_eq!(nd.data[0][band + 1], idx as i32 - f_lav);
        }
        for (band, &idx) in t_chosen.iter().enumerate() {
            assert_eq!(nd.data[1][band], idx as i32 - t_lav);
        }
    }

    #[test]
    fn coupling_balance_start_width() {
        // Coupled second channel at 3.0 dB → balance start width 5.
        let bands = bands_44100();
        let grid = SbrGrid {
            frame_class: FrameClass::FixFix,
            num_env: 1,
            num_noise: 1,
            freq_res: vec![true],
            var_bord_0: 0,
            var_bord_1: 0,
            rel_bord_0: vec![],
            rel_bord_1: vec![],
            pointer: 0,
            amp_res_override: false,
        };
        let dtdf = SbrDtdf {
            df_env: vec![false],
            df_noise: vec![false],
        };
        let n = bands.n_high();
        let ((_t, _tl), (f_huff, f_lav)) = env_tables(SbrHuffContext {
            coupling: true,
            ch: true,
            amp_res: true,
        });
        let mut w = BitWriter::new();
        w.write_u32(7, 5); // 5-bit balance start
        for i in 1..n {
            push_code(&mut w, f_huff, (i + f_lav as usize) % f_huff.len());
        }
        let bytes = w.finish();
        let mut r = BitReader::new(&bytes);
        let ev = SbrEnvelopeData::parse(&mut r, &grid, &dtdf, &bands, true, true, true).unwrap();
        assert_eq!(ev.data[0][0], 7);
    }

    #[test]
    fn truncated_envelope_errors() {
        let bands = bands_44100();
        let grid = SbrGrid {
            frame_class: FrameClass::FixFix,
            num_env: 1,
            num_noise: 1,
            freq_res: vec![true],
            var_bord_0: 0,
            var_bord_1: 0,
            rel_bord_0: vec![],
            rel_bord_1: vec![],
            pointer: 0,
            amp_res_override: false,
        };
        let dtdf = SbrDtdf {
            df_env: vec![false],
            df_noise: vec![false],
        };
        let bytes = [0u8; 0];
        let mut r = BitReader::new(&bytes);
        assert!(matches!(
            SbrEnvelopeData::parse(&mut r, &grid, &dtdf, &bands, false, false, false),
            Err(Error::SbrHuffInvalid)
        ));
    }
}