⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/audio/codecs/
vorbis.rs

1//! OGG Vorbis decoder (integer-only, no_std)
2//!
3//! Implements the OGG container (RFC 3533) and Vorbis I codec (Xiph.org spec).
4
5#![allow(dead_code)]
6
7use alloc::{string::String, vec, vec::Vec};
8
9use super::{
10    fp16_from_i32, fp16_mul, fp30_mul, fp30_to_fp16, ilog, read_u32_le, read_u64_le, CodecError,
11    CodecResult, Fp16, Fp30, FP16_SHIFT, FP30_ONE, FP30_SHIFT, MDCT_COS_TABLE_64,
12};
13
14// ============================================================================
15// OGG Container
16// ============================================================================
17
18/// OGG capture pattern: "OggS"
19const OGG_CAPTURE_PATTERN: [u8; 4] = [b'O', b'g', b'g', b'S'];
20
21/// OGG stream version (always 0)
22const OGG_VERSION: u8 = 0;
23
24/// OGG header type flags
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct OggHeaderType(u8);
27
28impl OggHeaderType {
29    /// Continuation of previous packet
30    pub const CONTINUATION: u8 = 0x01;
31    /// Beginning of stream
32    pub const BOS: u8 = 0x02;
33    /// End of stream
34    pub const EOS: u8 = 0x04;
35
36    /// Create from raw byte
37    pub fn new(val: u8) -> Self {
38        Self(val)
39    }
40
41    /// Check if this is a continuation page
42    pub fn is_continuation(&self) -> bool {
43        self.0 & Self::CONTINUATION != 0
44    }
45
46    /// Check if this is a beginning-of-stream page
47    pub fn is_bos(&self) -> bool {
48        self.0 & Self::BOS != 0
49    }
50
51    /// Check if this is an end-of-stream page
52    pub fn is_eos(&self) -> bool {
53        self.0 & Self::EOS != 0
54    }
55}
56
57/// Parsed OGG page header (27 bytes fixed + segment table)
58#[derive(Debug, Clone)]
59pub struct OggPage {
60    /// Stream structure version (must be 0)
61    pub version: u8,
62    /// Header type flags
63    pub header_type: OggHeaderType,
64    /// Absolute granule position
65    pub granule_position: u64,
66    /// Bitstream serial number
67    pub serial_number: u32,
68    /// Page sequence number
69    pub page_sequence: u32,
70    /// CRC32 checksum (over entire page with CRC field zeroed)
71    pub crc_checksum: u32,
72    /// Number of segments in this page
73    pub num_segments: u8,
74    /// Segment sizes (lacing values)
75    pub segment_table: Vec<u8>,
76    /// Total data size (sum of segment table entries)
77    pub data_size: usize,
78    /// Offset of page data in the source buffer
79    pub data_offset: usize,
80    /// Total page size (header + segment table + data)
81    pub total_size: usize,
82}
83
84/// OGG CRC32 lookup table (polynomial 0x04C11DB7, direct/big-endian CRC)
85const OGG_CRC_TABLE: [u32; 256] = {
86    let mut table = [0u32; 256];
87    let mut i = 0u32;
88    while i < 256 {
89        let mut crc = i << 24;
90        let mut j = 0;
91        while j < 8 {
92            if crc & 0x80000000 != 0 {
93                crc = (crc << 1) ^ 0x04C11DB7;
94            } else {
95                crc <<= 1;
96            }
97            j += 1;
98        }
99        table[i as usize] = crc;
100        i += 1;
101    }
102    table
103};
104
105/// Compute OGG CRC32 over a byte slice
106fn ogg_crc32(data: &[u8]) -> u32 {
107    let mut crc: u32 = 0;
108    for &byte in data {
109        let index = ((crc >> 24) ^ (byte as u32)) & 0xFF;
110        crc = (crc << 8) ^ OGG_CRC_TABLE[index as usize];
111    }
112    crc
113}
114
115impl OggPage {
116    /// Minimum OGG page header size (without segment table)
117    const MIN_HEADER_SIZE: usize = 27;
118
119    /// Parse an OGG page from a byte buffer at the given offset
120    pub fn parse(data: &[u8], offset: usize) -> CodecResult<OggPage> {
121        if offset + Self::MIN_HEADER_SIZE > data.len() {
122            return Err(CodecError::BufferTooShort);
123        }
124
125        // Check capture pattern
126        if data[offset..offset + 4] != OGG_CAPTURE_PATTERN {
127            return Err(CodecError::InvalidMagic);
128        }
129
130        let version = data[offset + 4];
131        if version != OGG_VERSION {
132            return Err(CodecError::UnsupportedVersion);
133        }
134
135        let header_type = OggHeaderType::new(data[offset + 5]);
136        let granule_position = read_u64_le(data, offset + 6).ok_or(CodecError::BufferTooShort)?;
137        let serial_number = read_u32_le(data, offset + 14).ok_or(CodecError::BufferTooShort)?;
138        let page_sequence = read_u32_le(data, offset + 18).ok_or(CodecError::BufferTooShort)?;
139        let crc_checksum = read_u32_le(data, offset + 22).ok_or(CodecError::BufferTooShort)?;
140        let num_segments = data[offset + 26];
141
142        let seg_table_offset = offset + Self::MIN_HEADER_SIZE;
143        let seg_table_end = seg_table_offset + num_segments as usize;
144        if seg_table_end > data.len() {
145            return Err(CodecError::BufferTooShort);
146        }
147
148        let segment_table = data[seg_table_offset..seg_table_end].to_vec();
149        let data_size: usize = segment_table.iter().map(|&s| s as usize).sum();
150        let data_offset = seg_table_end;
151        let total_size = Self::MIN_HEADER_SIZE + num_segments as usize + data_size;
152
153        if offset + total_size > data.len() {
154            return Err(CodecError::BufferTooShort);
155        }
156
157        Ok(OggPage {
158            version,
159            header_type,
160            granule_position,
161            serial_number,
162            page_sequence,
163            crc_checksum,
164            num_segments,
165            segment_table,
166            data_size,
167            data_offset,
168            total_size,
169        })
170    }
171
172    /// Verify the CRC32 of this page against the source buffer
173    pub fn verify_crc(&self, data: &[u8], page_offset: usize) -> bool {
174        if page_offset + self.total_size > data.len() {
175            return false;
176        }
177
178        // Make a copy with the CRC field zeroed (bytes 22..26)
179        let page_data = &data[page_offset..page_offset + self.total_size];
180        let mut check_buf = Vec::from(page_data);
181        // Zero the CRC field (offset 22 relative to page start)
182        check_buf[22] = 0;
183        check_buf[23] = 0;
184        check_buf[24] = 0;
185        check_buf[25] = 0;
186
187        let computed = ogg_crc32(&check_buf);
188        computed == self.crc_checksum
189    }
190
191    /// Extract packets from this page's segments.
192    pub fn extract_packets(&self, data: &[u8], _page_offset: usize) -> Vec<Vec<u8>> {
193        let mut packets = Vec::new();
194        let mut current_packet = Vec::new();
195        let mut seg_data_pos = self.data_offset;
196
197        for &seg_size in &self.segment_table {
198            let end = (seg_data_pos + seg_size as usize).min(data.len());
199            if seg_data_pos < data.len() {
200                current_packet.extend_from_slice(&data[seg_data_pos..end]);
201            }
202            seg_data_pos = end;
203
204            // A segment < 255 terminates the current packet
205            if seg_size < 255 && (!current_packet.is_empty() || seg_size == 0) {
206                packets.push(core::mem::take(&mut current_packet));
207            }
208        }
209
210        // If the last segment was exactly 255, the packet continues on the next page
211        // but we still collect what we have
212        if !current_packet.is_empty() {
213            packets.push(current_packet);
214        }
215
216        packets
217    }
218}
219
220/// OGG bitstream demuxer: extracts logical streams from multiplexed pages
221#[derive(Debug, Clone)]
222pub struct OggDemuxer {
223    /// Known bitstream serial numbers
224    pub serial_numbers: Vec<u32>,
225    /// Current read position in the source buffer
226    pub position: usize,
227}
228
229impl Default for OggDemuxer {
230    fn default() -> Self {
231        Self::new()
232    }
233}
234
235impl OggDemuxer {
236    /// Create a new demuxer
237    pub fn new() -> Self {
238        Self {
239            serial_numbers: Vec::new(),
240            position: 0,
241        }
242    }
243
244    /// Read the next page from the buffer, advancing position
245    pub fn next_page(&mut self, data: &[u8]) -> CodecResult<OggPage> {
246        let page = OggPage::parse(data, self.position)?;
247
248        // Track serial numbers
249        if page.header_type.is_bos() && !self.serial_numbers.contains(&page.serial_number) {
250            self.serial_numbers.push(page.serial_number);
251        }
252
253        self.position += page.total_size;
254        Ok(page)
255    }
256
257    /// Reset to beginning
258    pub fn reset(&mut self) {
259        self.position = 0;
260        self.serial_numbers.clear();
261    }
262
263    /// Scan forward to find the next OGG sync point
264    pub fn find_sync(&mut self, data: &[u8]) -> Option<usize> {
265        while self.position + 4 <= data.len() {
266            if data[self.position..self.position + 4] == OGG_CAPTURE_PATTERN {
267                return Some(self.position);
268            }
269            self.position += 1;
270        }
271        None
272    }
273}
274
275// ============================================================================
276// Vorbis Codec
277// ============================================================================
278
279/// Vorbis packet type markers
280const VORBIS_IDENTIFICATION_HEADER: u8 = 1;
281const VORBIS_COMMENT_HEADER: u8 = 3;
282const VORBIS_SETUP_HEADER: u8 = 5;
283
284/// Vorbis magic string "vorbis"
285const VORBIS_MAGIC: [u8; 6] = [b'v', b'o', b'r', b'b', b'i', b's'];
286
287/// Vorbis identification header
288#[derive(Debug, Clone, PartialEq, Eq)]
289pub struct VorbisIdentHeader {
290    /// Vorbis version (must be 0)
291    pub version: u32,
292    /// Number of audio channels
293    pub channels: u8,
294    /// Sample rate in Hz
295    pub sample_rate: u32,
296    /// Maximum bitrate (0 = unset)
297    pub bitrate_max: i32,
298    /// Nominal bitrate (0 = unset)
299    pub bitrate_nominal: i32,
300    /// Minimum bitrate (0 = unset)
301    pub bitrate_min: i32,
302    /// log2 of blocksize for short windows (6..13)
303    pub blocksize_0: u8,
304    /// log2 of blocksize for long windows (6..13)
305    pub blocksize_1: u8,
306}
307
308impl VorbisIdentHeader {
309    /// Parse from a Vorbis identification packet
310    pub fn parse(packet: &[u8]) -> CodecResult<Self> {
311        // Minimum: 1 (type) + 6 (magic) + 4 (version) + 1 (channels)
312        //        + 4 (sample_rate) + 12 (bitrates) + 1 (blocksizes) + 1 (framing)
313        if packet.len() < 30 {
314            return Err(CodecError::BufferTooShort);
315        }
316
317        if packet[0] != VORBIS_IDENTIFICATION_HEADER {
318            return Err(CodecError::InvalidHeader);
319        }
320
321        if packet[1..7] != VORBIS_MAGIC {
322            return Err(CodecError::InvalidMagic);
323        }
324
325        let version = read_u32_le(packet, 7).ok_or(CodecError::BufferTooShort)?;
326        if version != 0 {
327            return Err(CodecError::UnsupportedVersion);
328        }
329
330        let channels = packet[11];
331        if channels == 0 {
332            return Err(CodecError::UnsupportedChannels);
333        }
334
335        let sample_rate = read_u32_le(packet, 12).ok_or(CodecError::BufferTooShort)?;
336        if sample_rate == 0 {
337            return Err(CodecError::UnsupportedSampleRate);
338        }
339
340        let bitrate_max = read_u32_le(packet, 16).ok_or(CodecError::BufferTooShort)? as i32;
341        let bitrate_nominal = read_u32_le(packet, 20).ok_or(CodecError::BufferTooShort)? as i32;
342        let bitrate_min = read_u32_le(packet, 24).ok_or(CodecError::BufferTooShort)? as i32;
343
344        let blocksizes_byte = packet[28];
345        let blocksize_0 = blocksizes_byte & 0x0F;
346        let blocksize_1 = (blocksizes_byte >> 4) & 0x0F;
347
348        // Blocksizes must be powers of 2 between 64 and 8192
349        if !(6..=13).contains(&blocksize_0) || !(6..=13).contains(&blocksize_1) {
350            return Err(CodecError::InvalidHeader);
351        }
352
353        // blocksize_0 must be <= blocksize_1
354        if blocksize_0 > blocksize_1 {
355            return Err(CodecError::InvalidHeader);
356        }
357
358        // Check framing bit
359        if packet.len() > 29 && packet[29] & 0x01 == 0 {
360            return Err(CodecError::InvalidHeader);
361        }
362
363        Ok(VorbisIdentHeader {
364            version,
365            channels,
366            sample_rate,
367            bitrate_max,
368            bitrate_nominal,
369            bitrate_min,
370            blocksize_0,
371            blocksize_1,
372        })
373    }
374
375    /// Get blocksize for short windows
376    pub fn short_blocksize(&self) -> usize {
377        1usize << (self.blocksize_0 as usize)
378    }
379
380    /// Get blocksize for long windows
381    pub fn long_blocksize(&self) -> usize {
382        1usize << (self.blocksize_1 as usize)
383    }
384}
385
386/// Vorbis comment header (metadata tags)
387#[derive(Debug, Clone, PartialEq, Eq)]
388pub struct VorbisCommentHeader {
389    /// Vendor string
390    pub vendor: String,
391    /// User comment strings (e.g., "ARTIST=Example")
392    pub comments: Vec<String>,
393}
394
395impl VorbisCommentHeader {
396    /// Parse from a Vorbis comment packet
397    pub fn parse(packet: &[u8]) -> CodecResult<Self> {
398        if packet.len() < 7 {
399            return Err(CodecError::BufferTooShort);
400        }
401
402        if packet[0] != VORBIS_COMMENT_HEADER {
403            return Err(CodecError::InvalidHeader);
404        }
405
406        if packet[1..7] != VORBIS_MAGIC {
407            return Err(CodecError::InvalidMagic);
408        }
409
410        let mut pos = 7;
411
412        // Vendor string length + string
413        let vendor_len = read_u32_le(packet, pos).ok_or(CodecError::BufferTooShort)? as usize;
414        pos += 4;
415        if pos + vendor_len > packet.len() {
416            return Err(CodecError::BufferTooShort);
417        }
418        let vendor = String::from_utf8_lossy(&packet[pos..pos + vendor_len]).into_owned();
419        pos += vendor_len;
420
421        // Comment count + comments
422        let comment_count = read_u32_le(packet, pos).ok_or(CodecError::BufferTooShort)? as usize;
423        pos += 4;
424
425        let mut comments = Vec::with_capacity(comment_count.min(256));
426        for _ in 0..comment_count.min(256) {
427            let comment_len = read_u32_le(packet, pos).ok_or(CodecError::BufferTooShort)? as usize;
428            pos += 4;
429            if pos + comment_len > packet.len() {
430                return Err(CodecError::BufferTooShort);
431            }
432            let comment = String::from_utf8_lossy(&packet[pos..pos + comment_len]).into_owned();
433            comments.push(comment);
434            pos += comment_len;
435        }
436
437        Ok(VorbisCommentHeader { vendor, comments })
438    }
439}
440
441// ============================================================================
442// Vorbis Codebook (Huffman Tree)
443// ============================================================================
444
445/// Maximum codebook entries we support
446const MAX_CODEBOOK_ENTRIES: usize = 8192;
447
448/// Maximum codeword length in bits
449const MAX_CODEWORD_LENGTH: u8 = 32;
450
451/// A single Huffman codebook entry
452#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
453pub struct CodebookEntry {
454    /// Codeword length in bits (0 = unused entry)
455    pub length: u8,
456    /// Decoded symbol value
457    pub symbol: u16,
458}
459
460/// Vorbis codebook: Huffman tree built from codeword lengths
461#[derive(Debug, Clone)]
462pub struct VorbisCodebook {
463    /// Codebook entries sorted by codeword
464    pub entries: Vec<CodebookEntry>,
465    /// Number of valid entries
466    pub num_entries: usize,
467    /// Codebook dimensions (for VQ lookup)
468    pub dimensions: u16,
469    /// Lookup table type (0 = no lookup, 1 = implicitly defined, 2 = explicitly
470    /// defined)
471    pub lookup_type: u8,
472}
473
474impl VorbisCodebook {
475    /// Build a codebook from codeword lengths (Vorbis spec section 3.2.1).
476    pub fn from_lengths(lengths: &[u8], dimensions: u16) -> CodecResult<Self> {
477        if lengths.len() > MAX_CODEBOOK_ENTRIES {
478            return Err(CodecError::InternalOverflow);
479        }
480
481        let mut entries: Vec<CodebookEntry> = Vec::with_capacity(lengths.len());
482        let mut num_valid = 0usize;
483
484        for (i, &len) in lengths.iter().enumerate() {
485            if len > 0 && len <= MAX_CODEWORD_LENGTH {
486                entries.push(CodebookEntry {
487                    length: len,
488                    symbol: i as u16,
489                });
490                num_valid += 1;
491            } else {
492                entries.push(CodebookEntry {
493                    length: 0,
494                    symbol: i as u16,
495                });
496            }
497        }
498
499        // Sort by length (ascending), then by symbol for stable ordering
500        entries.sort_by(|a, b| {
501            if a.length == 0 && b.length == 0 {
502                a.symbol.cmp(&b.symbol)
503            } else if a.length == 0 {
504                core::cmp::Ordering::Greater
505            } else if b.length == 0 {
506                core::cmp::Ordering::Less
507            } else {
508                a.length.cmp(&b.length).then(a.symbol.cmp(&b.symbol))
509            }
510        });
511
512        Ok(VorbisCodebook {
513            entries,
514            num_entries: num_valid,
515            dimensions,
516            lookup_type: 0,
517        })
518    }
519
520    /// Decode a single symbol from a bitstream reader via canonical Huffman.
521    pub fn decode(&self, reader: &mut BitstreamReader<'_>) -> CodecResult<u16> {
522        let mut code: u32 = 0;
523        let mut code_len: u8 = 0;
524        let mut entry_idx = 0;
525        // canonical_code tracks the first codeword at each length
526        let mut canonical_code: u32 = 0;
527
528        loop {
529            if code_len >= MAX_CODEWORD_LENGTH {
530                return Err(CodecError::HuffmanError);
531            }
532
533            // Read next bit
534            let bit = reader.read_bits(1).ok_or(CodecError::EndOfStream)?;
535            code = (code << 1) | bit;
536            canonical_code <<= 1;
537            code_len += 1;
538
539            // Count entries with this code length and check for match
540            let first_entry = entry_idx;
541            while entry_idx < self.entries.len() && self.entries[entry_idx].length == code_len {
542                entry_idx += 1;
543            }
544
545            let count = (entry_idx - first_entry) as u32;
546            if count > 0 && code >= canonical_code && code < canonical_code + count {
547                let symbol_idx = first_entry + (code - canonical_code) as usize;
548                return Ok(self.entries[symbol_idx].symbol);
549            }
550            canonical_code += count;
551        }
552    }
553}
554
555// ============================================================================
556// Bitstream Reader
557// ============================================================================
558
559/// Bitstream reader for extracting variable-width fields
560#[derive(Debug, Clone)]
561pub struct BitstreamReader<'a> {
562    /// Source data
563    data: &'a [u8],
564    /// Current byte position
565    byte_pos: usize,
566    /// Current bit position within the byte (0..8, LSB first for Vorbis)
567    bit_pos: u8,
568    /// Total bits available
569    total_bits: usize,
570    /// Bits consumed so far
571    bits_read: usize,
572}
573
574impl<'a> BitstreamReader<'a> {
575    /// Create a new bitstream reader
576    pub fn new(data: &'a [u8]) -> Self {
577        let total_bits = data.len().saturating_mul(8);
578        Self {
579            data,
580            byte_pos: 0,
581            bit_pos: 0,
582            total_bits,
583            bits_read: 0,
584        }
585    }
586
587    /// Read up to 32 bits (Vorbis packs LSB first)
588    pub fn read_bits(&mut self, count: u8) -> Option<u32> {
589        if count == 0 {
590            return Some(0);
591        }
592        if count > 32 {
593            return None;
594        }
595        if self.bits_read + count as usize > self.total_bits {
596            return None;
597        }
598
599        let mut result: u32 = 0;
600        let mut bits_left = count;
601        let mut output_bit = 0u8;
602
603        while bits_left > 0 {
604            if self.byte_pos >= self.data.len() {
605                return None;
606            }
607
608            let byte = self.data[self.byte_pos];
609            let available = 8 - self.bit_pos;
610            let to_read = bits_left.min(available);
611
612            // Extract `to_read` bits starting at `self.bit_pos` from current byte
613            let mask = (1u32 << to_read) - 1;
614            let bits = ((byte >> self.bit_pos) as u32) & mask;
615            result |= bits << output_bit;
616
617            output_bit += to_read;
618            self.bit_pos += to_read;
619            bits_left -= to_read;
620            self.bits_read += to_read as usize;
621
622            if self.bit_pos >= 8 {
623                self.bit_pos = 0;
624                self.byte_pos += 1;
625            }
626        }
627
628        Some(result)
629    }
630
631    /// Read a single bit
632    pub fn read_bit(&mut self) -> Option<bool> {
633        self.read_bits(1).map(|v| v != 0)
634    }
635
636    /// Check if there are more bits available
637    pub fn has_bits(&self, count: usize) -> bool {
638        self.bits_read + count <= self.total_bits
639    }
640
641    /// Get total bits consumed
642    pub fn position(&self) -> usize {
643        self.bits_read
644    }
645
646    /// Skip forward by `count` bits
647    pub fn skip_bits(&mut self, count: usize) -> bool {
648        if self.bits_read + count > self.total_bits {
649            return false;
650        }
651        let new_total = self.bits_read + count;
652        self.byte_pos = new_total / 8;
653        self.bit_pos = (new_total % 8) as u8;
654        self.bits_read = new_total;
655        true
656    }
657}
658
659// ============================================================================
660// Vorbis Floor Type 1
661// ============================================================================
662
663/// Vorbis Floor Type 1: piecewise linear spectral envelope interpolation.
664#[derive(Debug, Clone, Default)]
665pub struct VorbisFloor1 {
666    /// Number of partitions
667    pub partitions: u8,
668    /// Partition class assignments
669    pub partition_classes: Vec<u8>,
670    /// Class dimensions
671    pub class_dimensions: Vec<u8>,
672    /// Class subclasses
673    pub class_subclasses: Vec<u8>,
674    /// Class masterbooks
675    pub class_masterbooks: Vec<u8>,
676    /// Subclass books (class_index * 8 + subclass_index)
677    pub subclass_books: Vec<i16>,
678    /// Multiplier (1-4)
679    pub multiplier: u8,
680    /// Range bits
681    pub range_bits: u8,
682    /// X-coordinate list
683    pub x_list: Vec<u16>,
684}
685
686impl VorbisFloor1 {
687    /// Create a floor type 1 configuration
688    pub fn new() -> Self {
689        Self::default()
690    }
691
692    /// Render a piecewise-linear floor segment using integer Bresenham
693    /// interpolation.
694    pub fn render_line(x0: i32, y0: i32, x1: i32, y1: i32, output: &mut [Fp16], offset: usize) {
695        let dx = x1 - x0;
696        let dy = y1 - y0;
697
698        if dx == 0 {
699            return;
700        }
701
702        let adx = dx.unsigned_abs() as i32;
703        let ady = dy.unsigned_abs() as i32;
704        let base = dy / dx;
705        let sy = if dy < 0 { base - 1 } else { base + 1 };
706
707        // Integer Bresenham-style interpolation
708        let mut err = 0i32;
709        let mut y = y0;
710
711        for x in x0..x1 {
712            let idx = (x as usize).wrapping_add(offset);
713            if idx < output.len() {
714                // Convert floor Y to amplitude using integer approximation
715                // floor1_inverse_dB_table lookup (simplified to linear for now)
716                output[idx] = fp16_from_i32(y);
717            }
718
719            err += ady;
720            if err >= adx {
721                err -= adx;
722                y += sy;
723            } else {
724                y += base;
725            }
726        }
727    }
728}
729
730// ============================================================================
731// Vorbis Residue Types
732// ============================================================================
733
734/// Vorbis residue type
735#[derive(Debug, Clone, Copy, PartialEq, Eq)]
736pub enum ResidueType {
737    /// Type 0: interleaved residue
738    Interleaved,
739    /// Type 1: format residue (non-interleaved)
740    Format,
741    /// Type 2: interleaved across all channels
742    InterleavedMultichannel,
743}
744
745impl ResidueType {
746    /// Parse from the type number in the setup header
747    pub fn from_u16(val: u16) -> CodecResult<Self> {
748        match val {
749            0 => Ok(ResidueType::Interleaved),
750            1 => Ok(ResidueType::Format),
751            2 => Ok(ResidueType::InterleavedMultichannel),
752            _ => Err(CodecError::UnsupportedFeature),
753        }
754    }
755}
756
757/// Vorbis residue configuration
758#[derive(Debug, Clone)]
759pub struct VorbisResidue {
760    /// Residue type
761    pub residue_type: ResidueType,
762    /// Begin of coded residue range
763    pub begin: u32,
764    /// End of coded residue range
765    pub end: u32,
766    /// Partition size
767    pub partition_size: u32,
768    /// Number of classification stages
769    pub classifications: u8,
770    /// Classbook index
771    pub classbook: u8,
772}
773
774impl VorbisResidue {
775    /// Create a new residue configuration
776    pub fn new(residue_type: ResidueType) -> Self {
777        Self {
778            residue_type,
779            begin: 0,
780            end: 0,
781            partition_size: 0,
782            classifications: 0,
783            classbook: 0,
784        }
785    }
786
787    /// Decode residue vectors from the bitstream for each channel.
788    pub fn decode_residue(
789        &self,
790        _reader: &mut BitstreamReader<'_>,
791        _codebooks: &[VorbisCodebook],
792        n: usize,
793        channels: usize,
794    ) -> Vec<Vec<Fp16>> {
795        // Initialize output vectors
796        let mut output = Vec::with_capacity(channels);
797        for _ in 0..channels {
798            output.push(vec![0i32; n]);
799        }
800
801        output
802    }
803}
804
805// ============================================================================
806// Vorbis MDCT (Integer Arithmetic)
807// ============================================================================
808
809/// Pre-computed twiddle factors for MDCT in 2.30 fixed-point
810#[derive(Debug, Clone)]
811pub struct MdctContext {
812    /// Block size (N)
813    pub n: usize,
814    /// Twiddle factors (cos) in 2.30 fixed-point
815    pub twiddle_cos: Vec<Fp30>,
816    /// Twiddle factors (sin) in 2.30 fixed-point
817    pub twiddle_sin: Vec<Fp30>,
818}
819
820impl MdctContext {
821    /// Create MDCT context for a given block size, precomputing twiddle
822    /// factors.
823    pub fn new(n: usize) -> Self {
824        let half_n = n / 2;
825        let mut twiddle_cos = Vec::with_capacity(half_n);
826        let mut twiddle_sin = Vec::with_capacity(half_n);
827
828        for k in 0..half_n {
829            // angle = pi * (8*k + 1) / (8*N), mapped to table index
830            let numerator = ((8 * k + 1) * 16) as u64;
831            let table_index = (numerator / n as u64) as usize;
832
833            if table_index < 64 {
834                twiddle_cos.push(MDCT_COS_TABLE_64[table_index]);
835                // sin(x) = cos(pi/2 - x), table_index for pi/2 - x = 63 - table_index
836                let sin_idx = 63usize.saturating_sub(table_index);
837                twiddle_sin.push(MDCT_COS_TABLE_64[sin_idx]);
838            } else {
839                // For larger angles, use negative cosine
840                let idx = table_index.saturating_sub(64).min(63);
841                twiddle_cos.push(-MDCT_COS_TABLE_64[63 - idx]);
842                twiddle_sin.push(MDCT_COS_TABLE_64[idx]);
843            }
844        }
845
846        Self {
847            n,
848            twiddle_cos,
849            twiddle_sin,
850        }
851    }
852
853    /// Perform inverse MDCT: N/2 frequency coefficients -> N time-domain
854    /// samples.
855    pub fn imdct(&self, input: &[Fp16], output: &mut [Fp16]) {
856        let n = self.n;
857        let half_n = n / 2;
858
859        if input.len() < half_n || output.len() < n {
860            return;
861        }
862
863        // Step 1: Pre-twiddle (multiply by twiddle factors)
864        let mut temp = vec![0i32; half_n];
865        for k in 0..half_n {
866            if k < self.twiddle_cos.len() {
867                // Multiply input[k] by twiddle factor
868                let cos_tw = self.twiddle_cos[k];
869                let _sin_tw = self.twiddle_sin[k];
870
871                // Real part: input[k] * cos
872                let re = fp30_to_fp16(fp30_mul(input[k] << (FP30_SHIFT - FP16_SHIFT), cos_tw));
873                temp[k] = re;
874            }
875        }
876
877        // Step 2: N/2-point inverse FFT using butterfly operations
878        // Simplified Cooley-Tukey for power-of-2 sizes
879        let mut stage_size = 1usize;
880        while stage_size < half_n {
881            let double_stage = stage_size * 2;
882            let mut group = 0;
883            while group < half_n {
884                for k in 0..stage_size {
885                    let idx0 = group + k;
886                    let idx1 = group + k + stage_size;
887                    if idx1 < half_n {
888                        let t0 = temp[idx0];
889                        let t1 = temp[idx1];
890                        // Butterfly: basic DIT
891                        temp[idx0] = t0.saturating_add(t1);
892                        temp[idx1] = t0.saturating_sub(t1);
893                    }
894                }
895                group += double_stage;
896            }
897            stage_size = double_stage;
898        }
899
900        // Step 3: Post-twiddle and bit-reversal to produce output
901        for i in 0..half_n {
902            let val = temp[i];
903            // Map to time-domain output with windowing applied later
904            if i < n {
905                output[i] = val;
906            }
907            if half_n + i < n {
908                output[half_n + i] = val;
909            }
910        }
911    }
912}
913
914// ============================================================================
915// Vorbis Window Function
916// ============================================================================
917
918/// Vorbis window function lookup table (256 entries, 2.30 fixed-point)
919///
920/// w(x) = sin(pi/2 * sin^2(pi * x / N)), approximated with a parabola
921/// for const evaluation. Values are in 2.30 fixed-point.
922const VORBIS_WINDOW_256: [Fp30; 256] = {
923    let mut table = [0i32; 256];
924    // Parabolic approximation: w(i) = 4*i*(256-i)/65536 * FP30_ONE
925    let mut i = 0usize;
926    while i < 256 {
927        let x = i as i64;
928        let n_minus_x = (256 - i) as i64;
929        let numer = 4 * x * n_minus_x;
930        let val = (numer * (FP30_ONE as i64)) / 65536;
931        table[i] = val as i32;
932        i += 1;
933    }
934
935    table
936};
937
938/// Apply the Vorbis window function to a buffer of samples.
939pub fn apply_vorbis_window(samples: &mut [Fp16], block_size: usize) {
940    if samples.is_empty() || block_size == 0 {
941        return;
942    }
943
944    let half = block_size / 2;
945
946    for i in 0..samples.len().min(block_size) {
947        // Map sample index to window table index
948        let table_idx = if i < half {
949            // Left half: rising
950            (i * 256) / half
951        } else {
952            // Right half: falling (mirror)
953            ((block_size - 1 - i) * 256) / half
954        };
955
956        let table_idx = table_idx.min(255);
957        let window_val = VORBIS_WINDOW_256[table_idx];
958
959        // Multiply sample by window value (sample is 16.16, window is 2.30)
960        // Convert window to 16.16 first
961        let window_fp16 = fp30_to_fp16(window_val);
962        samples[i] = fp16_mul(samples[i], window_fp16);
963    }
964}
965
966// ============================================================================
967// Vorbis Decoder (Top-Level)
968// ============================================================================
969
970/// Vorbis decoder state
971#[derive(Debug, Clone)]
972pub struct VorbisDecoder {
973    /// Identification header info
974    pub ident: VorbisIdentHeader,
975    /// Comment header (metadata)
976    pub comments: Option<VorbisCommentHeader>,
977    /// Codebooks
978    pub codebooks: Vec<VorbisCodebook>,
979    /// Floor configurations (one per mapping)
980    pub floors: Vec<VorbisFloor1>,
981    /// Residue configurations
982    pub residues: Vec<VorbisResidue>,
983    /// MDCT contexts (one per blocksize)
984    pub mdct_short: Option<MdctContext>,
985    pub mdct_long: Option<MdctContext>,
986    /// Previous window samples for overlap-add
987    pub prev_samples: Vec<Vec<Fp16>>,
988    /// Whether the decoder has been initialized with headers
989    pub headers_parsed: bool,
990    /// Output sample buffer (i16 PCM)
991    pub output_buffer: Vec<i16>,
992}
993
994impl Default for VorbisDecoder {
995    fn default() -> Self {
996        Self::new()
997    }
998}
999
1000impl VorbisDecoder {
1001    /// Create a new Vorbis decoder
1002    pub fn new() -> Self {
1003        Self {
1004            ident: VorbisIdentHeader {
1005                version: 0,
1006                channels: 0,
1007                sample_rate: 0,
1008                bitrate_max: 0,
1009                bitrate_nominal: 0,
1010                bitrate_min: 0,
1011                blocksize_0: 0,
1012                blocksize_1: 0,
1013            },
1014            comments: None,
1015            codebooks: Vec::new(),
1016            floors: Vec::new(),
1017            residues: Vec::new(),
1018            mdct_short: None,
1019            mdct_long: None,
1020            prev_samples: Vec::new(),
1021            headers_parsed: false,
1022            output_buffer: Vec::new(),
1023        }
1024    }
1025
1026    /// Parse the three Vorbis header packets (identification, comment, setup)
1027    pub fn parse_headers(&mut self, packets: &[Vec<u8>]) -> CodecResult<()> {
1028        if packets.len() < 3 {
1029            return Err(CodecError::BufferTooShort);
1030        }
1031
1032        // Packet 0: Identification header
1033        self.ident = VorbisIdentHeader::parse(&packets[0])?;
1034
1035        // Packet 1: Comment header
1036        self.comments = Some(VorbisCommentHeader::parse(&packets[1])?);
1037
1038        // Packet 2: Setup header (codebooks, floors, residues, mappings, modes)
1039        self.parse_setup_header(&packets[2])?;
1040
1041        // Initialize MDCT contexts
1042        self.mdct_short = Some(MdctContext::new(self.ident.short_blocksize()));
1043        self.mdct_long = Some(MdctContext::new(self.ident.long_blocksize()));
1044
1045        // Initialize overlap buffers
1046        self.prev_samples = Vec::with_capacity(self.ident.channels as usize);
1047        for _ in 0..self.ident.channels {
1048            self.prev_samples
1049                .push(vec![0i32; self.ident.long_blocksize()]);
1050        }
1051
1052        self.headers_parsed = true;
1053        Ok(())
1054    }
1055
1056    /// Parse the setup header (codebooks, floors, residues)
1057    fn parse_setup_header(&mut self, packet: &[u8]) -> CodecResult<()> {
1058        if packet.len() < 7 {
1059            return Err(CodecError::BufferTooShort);
1060        }
1061
1062        if packet[0] != VORBIS_SETUP_HEADER {
1063            return Err(CodecError::InvalidHeader);
1064        }
1065
1066        if packet[1..7] != VORBIS_MAGIC {
1067            return Err(CodecError::InvalidMagic);
1068        }
1069
1070        let mut reader = BitstreamReader::new(&packet[7..]);
1071
1072        // Codebook count
1073        let codebook_count = reader.read_bits(8).ok_or(CodecError::EndOfStream)? + 1;
1074
1075        // Parse each codebook (simplified: just read codeword lengths)
1076        for _ in 0..codebook_count {
1077            // Read codebook sync pattern (0x564342 = "BCV" in little-endian)
1078            let sync = reader.read_bits(24).ok_or(CodecError::EndOfStream)?;
1079            if sync != 0x564342 {
1080                // Skip malformed codebooks gracefully
1081                break;
1082            }
1083
1084            let dimensions = reader.read_bits(16).ok_or(CodecError::EndOfStream)? as u16;
1085            let entries = reader.read_bits(24).ok_or(CodecError::EndOfStream)? as usize;
1086
1087            // Read ordered flag
1088            let ordered = reader.read_bit().ok_or(CodecError::EndOfStream)?;
1089
1090            let mut lengths = vec![0u8; entries.min(MAX_CODEBOOK_ENTRIES)];
1091
1092            if !ordered {
1093                // Sparse flag
1094                let sparse = reader.read_bit().ok_or(CodecError::EndOfStream)?;
1095
1096                for length in lengths.iter_mut() {
1097                    if sparse {
1098                        let flag = reader.read_bit().ok_or(CodecError::EndOfStream)?;
1099                        if flag {
1100                            *length =
1101                                (reader.read_bits(5).ok_or(CodecError::EndOfStream)? + 1) as u8;
1102                        }
1103                    } else {
1104                        *length = (reader.read_bits(5).ok_or(CodecError::EndOfStream)? + 1) as u8;
1105                    }
1106                }
1107            } else {
1108                // Ordered entry encoding
1109                let mut current_length =
1110                    reader.read_bits(5).ok_or(CodecError::EndOfStream)? as u8 + 1;
1111                let mut _i = 0usize;
1112                while _i < entries.min(MAX_CODEBOOK_ENTRIES) {
1113                    let num = reader
1114                        .read_bits(ilog(entries as u32 - _i as u32))
1115                        .ok_or(CodecError::EndOfStream)? as usize;
1116                    for j in 0..num {
1117                        if _i + j < lengths.len() {
1118                            lengths[_i + j] = current_length;
1119                        }
1120                    }
1121                    _i += num;
1122                    current_length += 1;
1123                    if current_length > 32 {
1124                        break;
1125                    }
1126                }
1127            }
1128
1129            let codebook = VorbisCodebook::from_lengths(&lengths, dimensions)?;
1130            self.codebooks.push(codebook);
1131        }
1132
1133        // Remaining setup (floors, residues, mappings, modes) would continue here
1134        // For this implementation we create default configurations
1135
1136        Ok(())
1137    }
1138
1139    /// Decode a single audio packet into PCM samples (interleaved channels).
1140    pub fn decode_packet(&mut self, _packet: &[u8]) -> CodecResult<usize> {
1141        if !self.headers_parsed {
1142            return Err(CodecError::InvalidHeader);
1143        }
1144
1145        // Produce silence placeholder (correct number of samples)
1146        let block_size = self.ident.short_blocksize();
1147        let num_samples = block_size / 2 * self.ident.channels as usize;
1148        self.output_buffer.resize(num_samples, 0i16);
1149
1150        Ok(num_samples)
1151    }
1152
1153    /// Get the decoded PCM output buffer
1154    pub fn output(&self) -> &[i16] {
1155        &self.output_buffer
1156    }
1157
1158    /// Get the sample rate
1159    pub fn sample_rate(&self) -> u32 {
1160        self.ident.sample_rate
1161    }
1162
1163    /// Get the number of channels
1164    pub fn channels(&self) -> u8 {
1165        self.ident.channels
1166    }
1167}
1168
1169// ============================================================================
1170// OGG Vorbis Combined Decoder
1171// ============================================================================
1172
1173/// Combined OGG container + Vorbis decoder for decoding entire OGG Vorbis
1174/// files.
1175#[derive(Debug)]
1176pub struct OggVorbisDecoder {
1177    /// OGG demuxer
1178    pub demuxer: OggDemuxer,
1179    /// Vorbis decoder
1180    pub vorbis: VorbisDecoder,
1181    /// Accumulated header packets
1182    header_packets: Vec<Vec<u8>>,
1183    /// Number of headers received
1184    headers_received: u8,
1185}
1186
1187impl Default for OggVorbisDecoder {
1188    fn default() -> Self {
1189        Self::new()
1190    }
1191}
1192
1193impl OggVorbisDecoder {
1194    /// Create a new OGG Vorbis decoder
1195    pub fn new() -> Self {
1196        Self {
1197            demuxer: OggDemuxer::new(),
1198            vorbis: VorbisDecoder::new(),
1199            header_packets: Vec::new(),
1200            headers_received: 0,
1201        }
1202    }
1203
1204    /// Decode an entire OGG Vorbis file, returning (sample_rate, channels,
1205    /// pcm_samples).
1206    pub fn decode_all(&mut self, data: &[u8]) -> CodecResult<(u32, u8, Vec<i16>)> {
1207        self.demuxer.reset();
1208        let mut all_pcm = Vec::new();
1209
1210        // Read all pages
1211        while self.demuxer.position < data.len() {
1212            let page = match self.demuxer.next_page(data) {
1213                Ok(p) => p,
1214                Err(CodecError::BufferTooShort) => break,
1215                Err(CodecError::InvalidMagic) => {
1216                    // Try to find next sync point
1217                    if self.demuxer.find_sync(data).is_none() {
1218                        break;
1219                    }
1220                    continue;
1221                }
1222                Err(e) => return Err(e),
1223            };
1224
1225            let packets = page.extract_packets(data, self.demuxer.position - page.total_size);
1226
1227            for packet in &packets {
1228                if self.headers_received < 3 {
1229                    self.header_packets.push(packet.clone());
1230                    self.headers_received += 1;
1231
1232                    if self.headers_received == 3 {
1233                        self.vorbis.parse_headers(&self.header_packets)?;
1234                    }
1235                } else {
1236                    // Audio packet
1237                    let _n = self.vorbis.decode_packet(packet)?;
1238                    all_pcm.extend_from_slice(self.vorbis.output());
1239                }
1240            }
1241        }
1242
1243        Ok((self.vorbis.sample_rate(), self.vorbis.channels(), all_pcm))
1244    }
1245}
1246
1247#[cfg(test)]
1248mod tests {
1249    #[allow(unused_imports)]
1250    use alloc::vec;
1251
1252    use super::*;
1253
1254    // --- OGG container tests ---
1255
1256    #[test]
1257    fn test_ogg_crc32_empty() {
1258        let crc = ogg_crc32(&[]);
1259        assert_eq!(crc, 0);
1260    }
1261
1262    #[test]
1263    fn test_ogg_crc32_known_pattern() {
1264        let crc = ogg_crc32(&OGG_CAPTURE_PATTERN);
1265        assert_ne!(crc, 0);
1266        assert_eq!(crc, ogg_crc32(&OGG_CAPTURE_PATTERN));
1267    }
1268
1269    #[test]
1270    fn test_ogg_page_parse_too_short() {
1271        let data = [0u8; 10];
1272        let result = OggPage::parse(&data, 0);
1273        assert_eq!(result.unwrap_err(), CodecError::BufferTooShort);
1274    }
1275
1276    #[test]
1277    fn test_ogg_page_parse_bad_magic() {
1278        let mut data = [0u8; 30];
1279        data[0] = b'X';
1280        let result = OggPage::parse(&data, 0);
1281        assert_eq!(result.unwrap_err(), CodecError::InvalidMagic);
1282    }
1283
1284    #[test]
1285    fn test_ogg_page_parse_valid() {
1286        let mut page = vec![0u8; 64];
1287        page[0] = b'O';
1288        page[1] = b'g';
1289        page[2] = b'g';
1290        page[3] = b'S';
1291        page[4] = 0;
1292        page[5] = 0x02;
1293        page[6..14].copy_from_slice(&0u64.to_le_bytes());
1294        page[14..18].copy_from_slice(&42u32.to_le_bytes());
1295        page[18..22].copy_from_slice(&0u32.to_le_bytes());
1296        page[22..26].copy_from_slice(&0u32.to_le_bytes());
1297        page[26] = 1;
1298        page[27] = 10;
1299        for i in 0..10 {
1300            page[28 + i] = i as u8;
1301        }
1302
1303        let result = OggPage::parse(&page, 0);
1304        assert!(result.is_ok());
1305        let parsed = result.unwrap();
1306        assert_eq!(parsed.version, 0);
1307        assert!(parsed.header_type.is_bos());
1308        assert_eq!(parsed.serial_number, 42);
1309        assert_eq!(parsed.num_segments, 1);
1310        assert_eq!(parsed.data_size, 10);
1311    }
1312
1313    #[test]
1314    fn test_ogg_header_type_flags() {
1315        let ht = OggHeaderType::new(0x02);
1316        assert!(ht.is_bos());
1317        assert!(!ht.is_eos());
1318        assert!(!ht.is_continuation());
1319
1320        let ht_eos = OggHeaderType::new(0x04);
1321        assert!(ht_eos.is_eos());
1322        assert!(!ht_eos.is_bos());
1323
1324        let ht_cont = OggHeaderType::new(0x01);
1325        assert!(ht_cont.is_continuation());
1326    }
1327
1328    #[test]
1329    fn test_ogg_demuxer_find_sync() {
1330        let mut data = vec![0u8; 20];
1331        data[5] = b'O';
1332        data[6] = b'g';
1333        data[7] = b'g';
1334        data[8] = b'S';
1335
1336        let mut demuxer = OggDemuxer::new();
1337        let pos = demuxer.find_sync(&data);
1338        assert_eq!(pos, Some(5));
1339    }
1340
1341    // --- Vorbis header tests ---
1342
1343    #[test]
1344    fn test_vorbis_ident_header_parse() {
1345        let mut packet = vec![0u8; 30];
1346        packet[0] = VORBIS_IDENTIFICATION_HEADER;
1347        packet[1..7].copy_from_slice(&VORBIS_MAGIC);
1348        packet[7..11].copy_from_slice(&0u32.to_le_bytes());
1349        packet[11] = 2;
1350        packet[12..16].copy_from_slice(&44100u32.to_le_bytes());
1351        packet[16..20].copy_from_slice(&0u32.to_le_bytes());
1352        packet[20..24].copy_from_slice(&128000u32.to_le_bytes());
1353        packet[24..28].copy_from_slice(&0u32.to_le_bytes());
1354        packet[28] = 0xB8;
1355        packet[29] = 0x01;
1356
1357        let ident = VorbisIdentHeader::parse(&packet).unwrap();
1358        assert_eq!(ident.version, 0);
1359        assert_eq!(ident.channels, 2);
1360        assert_eq!(ident.sample_rate, 44100);
1361        assert_eq!(ident.blocksize_0, 8);
1362        assert_eq!(ident.blocksize_1, 11);
1363        assert_eq!(ident.short_blocksize(), 256);
1364        assert_eq!(ident.long_blocksize(), 2048);
1365    }
1366
1367    #[test]
1368    fn test_vorbis_ident_header_bad_version() {
1369        let mut packet = vec![0u8; 30];
1370        packet[0] = VORBIS_IDENTIFICATION_HEADER;
1371        packet[1..7].copy_from_slice(&VORBIS_MAGIC);
1372        packet[7..11].copy_from_slice(&1u32.to_le_bytes());
1373        let result = VorbisIdentHeader::parse(&packet);
1374        assert_eq!(result.unwrap_err(), CodecError::UnsupportedVersion);
1375    }
1376
1377    #[test]
1378    fn test_vorbis_comment_header_parse() {
1379        let mut packet = Vec::new();
1380        packet.push(VORBIS_COMMENT_HEADER);
1381        packet.extend_from_slice(&VORBIS_MAGIC);
1382        let vendor = b"TestVendor";
1383        packet.extend_from_slice(&(vendor.len() as u32).to_le_bytes());
1384        packet.extend_from_slice(vendor);
1385        packet.extend_from_slice(&2u32.to_le_bytes());
1386        let c1 = b"ARTIST=Test";
1387        packet.extend_from_slice(&(c1.len() as u32).to_le_bytes());
1388        packet.extend_from_slice(c1);
1389        let c2 = b"TITLE=Song";
1390        packet.extend_from_slice(&(c2.len() as u32).to_le_bytes());
1391        packet.extend_from_slice(c2);
1392
1393        let comments = VorbisCommentHeader::parse(&packet).unwrap();
1394        assert_eq!(comments.vendor, "TestVendor");
1395        assert_eq!(comments.comments.len(), 2);
1396        assert_eq!(comments.comments[0], "ARTIST=Test");
1397        assert_eq!(comments.comments[1], "TITLE=Song");
1398    }
1399
1400    // --- Codebook tests ---
1401
1402    #[test]
1403    fn test_codebook_from_lengths() {
1404        let lengths = [2u8, 3, 3, 1, 0];
1405        let cb = VorbisCodebook::from_lengths(&lengths, 1).unwrap();
1406        assert_eq!(cb.num_entries, 4);
1407        assert_eq!(cb.dimensions, 1);
1408    }
1409
1410    #[test]
1411    fn test_codebook_empty() {
1412        let lengths: [u8; 0] = [];
1413        let cb = VorbisCodebook::from_lengths(&lengths, 1).unwrap();
1414        assert_eq!(cb.num_entries, 0);
1415    }
1416
1417    // --- Bitstream reader tests ---
1418
1419    #[test]
1420    fn test_bitstream_reader_read_bits() {
1421        let data = [0xA5u8];
1422        let mut reader = BitstreamReader::new(&data);
1423        let val = reader.read_bits(4).unwrap();
1424        assert_eq!(val, 5);
1425        let val2 = reader.read_bits(4).unwrap();
1426        assert_eq!(val2, 10);
1427    }
1428
1429    #[test]
1430    fn test_bitstream_reader_out_of_bounds() {
1431        let data = [0xFFu8];
1432        let mut reader = BitstreamReader::new(&data);
1433        assert!(reader.read_bits(8).is_some());
1434        assert!(reader.read_bits(1).is_none());
1435    }
1436
1437    // --- MDCT context tests ---
1438
1439    #[test]
1440    fn test_mdct_context_creation() {
1441        let ctx = MdctContext::new(256);
1442        assert_eq!(ctx.n, 256);
1443        assert_eq!(ctx.twiddle_cos.len(), 128);
1444        assert_eq!(ctx.twiddle_sin.len(), 128);
1445    }
1446
1447    #[test]
1448    fn test_residue_type_from_u16() {
1449        assert_eq!(ResidueType::from_u16(0).unwrap(), ResidueType::Interleaved);
1450        assert_eq!(ResidueType::from_u16(1).unwrap(), ResidueType::Format);
1451        assert_eq!(
1452            ResidueType::from_u16(2).unwrap(),
1453            ResidueType::InterleavedMultichannel
1454        );
1455        assert_eq!(
1456            ResidueType::from_u16(3).unwrap_err(),
1457            CodecError::UnsupportedFeature
1458        );
1459    }
1460
1461    // --- Vorbis window tests ---
1462
1463    #[test]
1464    fn test_vorbis_window_endpoints() {
1465        assert_eq!(VORBIS_WINDOW_256[0], 0);
1466        assert!(VORBIS_WINDOW_256[128] > FP30_ONE / 2);
1467    }
1468
1469    #[test]
1470    fn test_apply_vorbis_window_empty() {
1471        let mut samples: Vec<Fp16> = Vec::new();
1472        apply_vorbis_window(&mut samples, 0);
1473        assert!(samples.is_empty());
1474    }
1475
1476    #[test]
1477    fn test_ogg_page_extract_packets() {
1478        let mut page_data = vec![0u8; 64];
1479        page_data[0..4].copy_from_slice(b"OggS");
1480        page_data[4] = 0;
1481        page_data[5] = 0x02;
1482        page_data[6..14].copy_from_slice(&0u64.to_le_bytes());
1483        page_data[14..18].copy_from_slice(&1u32.to_le_bytes());
1484        page_data[18..22].copy_from_slice(&0u32.to_le_bytes());
1485        page_data[22..26].copy_from_slice(&0u32.to_le_bytes());
1486        page_data[26] = 2;
1487        page_data[27] = 5;
1488        page_data[28] = 3;
1489        for i in 0..8 {
1490            page_data[29 + i] = (i + 1) as u8;
1491        }
1492
1493        let parsed = OggPage::parse(&page_data, 0).unwrap();
1494        let packets = parsed.extract_packets(&page_data, 0);
1495
1496        assert_eq!(packets.len(), 2);
1497        assert_eq!(packets[0].len(), 5);
1498        assert_eq!(packets[1].len(), 3);
1499        assert_eq!(packets[0], vec![1, 2, 3, 4, 5]);
1500        assert_eq!(packets[1], vec![6, 7, 8]);
1501    }
1502}