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

veridian_kernel/audio/codecs/
mp3.rs

1//! MP3 (MPEG-1 Layer III) decoder (integer-only, no_std)
2//!
3//! Implements ISO 11172-3 / ISO 13818-3 Layer III decoding.
4
5#![allow(dead_code)]
6
7use alloc::{vec, vec::Vec};
8
9use super::{
10    fp16_mul, get_cos_from_table, read_u16_be, CodecError, CodecResult, Fp16, FP16_ONE, FP16_SHIFT,
11    FP30_SHIFT,
12};
13
14// ============================================================================
15// MP3 Bitstream Reader (MSB first)
16// ============================================================================
17
18/// Bitstream reader for MP3 (MSB first, big-endian bit ordering)
19#[derive(Debug, Clone)]
20pub struct Mp3BitstreamReader<'a> {
21    data: &'a [u8],
22    byte_pos: usize,
23    bit_pos: u8,
24    total_bits: usize,
25    bits_read: usize,
26}
27
28impl<'a> Mp3BitstreamReader<'a> {
29    /// Create a new MSB-first bitstream reader
30    pub fn new(data: &'a [u8]) -> Self {
31        let total_bits = data.len().saturating_mul(8);
32        Self {
33            data,
34            byte_pos: 0,
35            bit_pos: 0,
36            total_bits,
37            bits_read: 0,
38        }
39    }
40
41    /// Read up to 32 bits (MSB first, big-endian)
42    pub fn read_bits(&mut self, count: u8) -> Option<u32> {
43        if count == 0 {
44            return Some(0);
45        }
46        if count > 32 {
47            return None;
48        }
49        if self.bits_read + count as usize > self.total_bits {
50            return None;
51        }
52
53        let mut result: u32 = 0;
54        let mut bits_left = count;
55
56        while bits_left > 0 {
57            if self.byte_pos >= self.data.len() {
58                return None;
59            }
60
61            let byte = self.data[self.byte_pos];
62            let available = 8 - self.bit_pos;
63            let to_read = bits_left.min(available);
64
65            // MSB first: extract from top of byte
66            let shift = available - to_read;
67            let mask = (1u32 << to_read) - 1;
68            let bits = ((byte >> shift) as u32) & mask;
69
70            result = (result << to_read) | bits;
71
72            self.bit_pos += to_read;
73            bits_left -= to_read;
74            self.bits_read += to_read as usize;
75
76            if self.bit_pos >= 8 {
77                self.bit_pos = 0;
78                self.byte_pos += 1;
79            }
80        }
81
82        Some(result)
83    }
84
85    /// Read a single bit
86    pub fn read_bit(&mut self) -> Option<bool> {
87        self.read_bits(1).map(|v| v != 0)
88    }
89
90    /// Check if there are more bits available
91    pub fn has_bits(&self, count: usize) -> bool {
92        self.bits_read + count <= self.total_bits
93    }
94
95    /// Get current bit position
96    pub fn position(&self) -> usize {
97        self.bits_read
98    }
99}
100
101// ============================================================================
102// MP3 Frame Header
103// ============================================================================
104
105/// MP3 frame sync word (first 11 bits: 0xFFE0)
106const MP3_SYNC_MASK: u16 = 0xFFE0;
107const MP3_SYNC_WORD: u16 = 0xFFE0;
108
109/// MPEG version
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum MpegVersion {
112    /// MPEG-1
113    Mpeg1,
114    /// MPEG-2
115    Mpeg2,
116    /// MPEG-2.5 (unofficial extension)
117    Mpeg25,
118}
119
120/// Channel mode
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum ChannelMode {
123    /// Stereo
124    Stereo,
125    /// Joint stereo (MS and/or intensity)
126    JointStereo,
127    /// Dual channel (independent)
128    DualChannel,
129    /// Mono
130    Mono,
131}
132
133impl ChannelMode {
134    /// Number of channels for this mode
135    pub fn num_channels(&self) -> u8 {
136        match self {
137            ChannelMode::Mono => 1,
138            _ => 2,
139        }
140    }
141}
142
143/// MPEG-1 Layer III bitrate table (in kbps, index 1..14)
144const MP3_BITRATE_TABLE: [u16; 15] = [
145    0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,
146];
147
148/// MPEG-1 sample rate table (in Hz)
149const MP3_SAMPLE_RATE_TABLE: [u32; 3] = [44100, 48000, 32000];
150
151/// MPEG-2 sample rate table (in Hz)
152const MP3_SAMPLE_RATE_TABLE_V2: [u32; 3] = [22050, 24000, 16000];
153
154/// MPEG-2.5 sample rate table (in Hz)
155const MP3_SAMPLE_RATE_TABLE_V25: [u32; 3] = [11025, 12000, 8000];
156
157/// Parsed MP3 frame header
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub struct Mp3FrameHeader {
160    /// MPEG version
161    pub version: MpegVersion,
162    /// Bitrate in kbps
163    pub bitrate: u16,
164    /// Sample rate in Hz
165    pub sample_rate: u32,
166    /// Channel mode
167    pub channel_mode: ChannelMode,
168    /// Mode extension (for joint stereo)
169    pub mode_extension: u8,
170    /// Padding flag
171    pub padding: bool,
172    /// CRC protection
173    pub crc_protected: bool,
174    /// Frame size in bytes (including header)
175    pub frame_size: usize,
176    /// Number of samples per frame (1152 for MPEG-1 Layer III)
177    pub samples_per_frame: usize,
178}
179
180impl Mp3FrameHeader {
181    /// Parse an MP3 frame header from 4 bytes
182    pub fn parse(header_bytes: &[u8]) -> CodecResult<Self> {
183        if header_bytes.len() < 4 {
184            return Err(CodecError::BufferTooShort);
185        }
186
187        let word = read_u16_be(header_bytes, 0).ok_or(CodecError::BufferTooShort)?;
188
189        // Check sync word (first 11 bits)
190        if word & MP3_SYNC_MASK != MP3_SYNC_WORD {
191            return Err(CodecError::InvalidMagic);
192        }
193
194        // MPEG version (bits 11-12 of the 32-bit header)
195        let version_bits = (header_bytes[1] >> 3) & 0x03;
196        let version = match version_bits {
197            0 => MpegVersion::Mpeg25,
198            2 => MpegVersion::Mpeg2,
199            3 => MpegVersion::Mpeg1,
200            _ => return Err(CodecError::UnsupportedVersion),
201        };
202
203        // Layer (bits 13-14)
204        let layer_bits = (header_bytes[1] >> 1) & 0x03;
205        if layer_bits != 1 {
206            // Layer III = 01 in the header (confusingly, 01 means layer III)
207            return Err(CodecError::UnsupportedFeature);
208        }
209
210        // CRC protection
211        let crc_protected = (header_bytes[1] & 0x01) == 0;
212
213        // Bitrate index (bits 16-19)
214        let bitrate_index = (header_bytes[2] >> 4) & 0x0F;
215        if bitrate_index == 0 || bitrate_index == 15 {
216            return Err(CodecError::InvalidHeader);
217        }
218        let bitrate = MP3_BITRATE_TABLE[bitrate_index as usize];
219
220        // Sample rate index (bits 20-21)
221        let sample_rate_index = (header_bytes[2] >> 2) & 0x03;
222        if sample_rate_index >= 3 {
223            return Err(CodecError::UnsupportedSampleRate);
224        }
225        let sample_rate = match version {
226            MpegVersion::Mpeg1 => MP3_SAMPLE_RATE_TABLE[sample_rate_index as usize],
227            MpegVersion::Mpeg2 => MP3_SAMPLE_RATE_TABLE_V2[sample_rate_index as usize],
228            MpegVersion::Mpeg25 => MP3_SAMPLE_RATE_TABLE_V25[sample_rate_index as usize],
229        };
230
231        // Padding
232        let padding = (header_bytes[2] >> 1) & 0x01 != 0;
233
234        // Channel mode (bits 24-25)
235        let channel_bits = (header_bytes[3] >> 6) & 0x03;
236        let channel_mode = match channel_bits {
237            0 => ChannelMode::Stereo,
238            1 => ChannelMode::JointStereo,
239            2 => ChannelMode::DualChannel,
240            3 => ChannelMode::Mono,
241            _ => unreachable!(),
242        };
243
244        // Mode extension (bits 26-27, meaningful for joint stereo)
245        let mode_extension = (header_bytes[3] >> 4) & 0x03;
246
247        // Samples per frame
248        let samples_per_frame = match version {
249            MpegVersion::Mpeg1 => 1152,
250            _ => 576,
251        };
252
253        // Frame size = 144 * bitrate / sample_rate + padding
254        // For Layer III: frame_size = 144000 * bitrate_kbps / sample_rate + padding
255        let frame_size = if sample_rate > 0 {
256            let base = (144000u64).checked_mul(bitrate as u64).unwrap_or(0) / sample_rate as u64;
257            base as usize + if padding { 1 } else { 0 }
258        } else {
259            return Err(CodecError::UnsupportedSampleRate);
260        };
261
262        Ok(Mp3FrameHeader {
263            version,
264            bitrate,
265            sample_rate,
266            channel_mode,
267            mode_extension,
268            padding,
269            crc_protected,
270            frame_size,
271            samples_per_frame,
272        })
273    }
274}
275
276// ============================================================================
277// MP3 Side Information
278// ============================================================================
279
280/// Number of scalefactor bands for MPEG-1 long blocks
281const SFB_LONG_COUNT: usize = 21;
282
283/// Number of scalefactor bands for MPEG-1 short blocks
284const SFB_SHORT_COUNT: usize = 12;
285
286/// Granule side information
287#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
288pub struct Mp3Granule {
289    /// Number of bits in the main data for this granule
290    pub part2_3_length: u16,
291    /// Number of values in the big value region
292    pub big_values: u16,
293    /// Global gain value
294    pub global_gain: u16,
295    /// Scalefactor compression index
296    pub scalefac_compress: u16,
297    /// Window switching flag
298    pub window_switching: bool,
299    /// Block type (0=normal, 1=start, 2=short, 3=stop)
300    pub block_type: u8,
301    /// Mixed block flag
302    pub mixed_block: bool,
303    /// Huffman table selection for regions (3 regions)
304    pub table_select: [u8; 3],
305    /// Subblock gain for short blocks
306    pub subblock_gain: [u8; 3],
307    /// Region0 count (number of bands in region 0)
308    pub region0_count: u8,
309    /// Region1 count
310    pub region1_count: u8,
311    /// Preflag (boosts high-frequency scalefactors)
312    pub preflag: bool,
313    /// Scalefactor scale (0 or 1)
314    pub scalefac_scale: bool,
315    /// Count1 table selection (0 or 1)
316    pub count1table_select: bool,
317}
318
319/// Channel side information
320#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
321pub struct Mp3ChannelSideInfo {
322    /// Scalefactor share info (scfsi)
323    pub scfsi: [bool; 4],
324    /// Granule info
325    pub granules: [Mp3Granule; 2],
326}
327
328/// Complete side information for a frame
329#[derive(Debug, Clone, PartialEq, Eq)]
330pub struct Mp3SideInfo {
331    /// Main data begin pointer (negative offset into bit reservoir)
332    pub main_data_begin: u16,
333    /// Channel side information
334    pub channels: Vec<Mp3ChannelSideInfo>,
335}
336
337impl Mp3SideInfo {
338    /// Parse side information from frame data
339    pub fn parse(
340        data: &[u8],
341        offset: usize,
342        channel_mode: ChannelMode,
343    ) -> CodecResult<(Self, usize)> {
344        let num_channels = channel_mode.num_channels() as usize;
345        let mut reader =
346            Mp3BitstreamReader::new(data.get(offset..).ok_or(CodecError::BufferTooShort)?);
347
348        let main_data_begin = reader.read_bits(9).ok_or(CodecError::EndOfStream)? as u16;
349
350        // Private bits
351        if num_channels == 1 {
352            let _private = reader.read_bits(5);
353        } else {
354            let _private = reader.read_bits(3);
355        }
356
357        let mut channels = Vec::with_capacity(num_channels);
358
359        // Read scfsi flags for each channel
360        for _ in 0..num_channels {
361            let mut ch_info = Mp3ChannelSideInfo::default();
362            for band in 0..4 {
363                ch_info.scfsi[band] = reader.read_bit().ok_or(CodecError::EndOfStream)?;
364            }
365            channels.push(ch_info);
366        }
367
368        // Read granule info for each granule (2) and channel
369        for gr in 0..2 {
370            for channel in channels.iter_mut().take(num_channels) {
371                let g = &mut channel.granules[gr];
372
373                g.part2_3_length = reader.read_bits(12).ok_or(CodecError::EndOfStream)? as u16;
374                g.big_values = reader.read_bits(9).ok_or(CodecError::EndOfStream)? as u16;
375                g.global_gain = reader.read_bits(8).ok_or(CodecError::EndOfStream)? as u16;
376                g.scalefac_compress = reader.read_bits(4).ok_or(CodecError::EndOfStream)? as u16;
377                g.window_switching = reader.read_bit().ok_or(CodecError::EndOfStream)?;
378
379                if g.window_switching {
380                    g.block_type = reader.read_bits(2).ok_or(CodecError::EndOfStream)? as u8;
381                    g.mixed_block = reader.read_bit().ok_or(CodecError::EndOfStream)?;
382
383                    for i in 0..2 {
384                        g.table_select[i] =
385                            reader.read_bits(5).ok_or(CodecError::EndOfStream)? as u8;
386                    }
387
388                    for i in 0..3 {
389                        g.subblock_gain[i] =
390                            reader.read_bits(3).ok_or(CodecError::EndOfStream)? as u8;
391                    }
392
393                    // Implicit region counts for short/mixed blocks
394                    if g.block_type == 2 && !g.mixed_block {
395                        g.region0_count = 8;
396                    } else {
397                        g.region0_count = 7;
398                    }
399                    g.region1_count = 36; // Fills remainder
400                } else {
401                    for i in 0..3 {
402                        g.table_select[i] =
403                            reader.read_bits(5).ok_or(CodecError::EndOfStream)? as u8;
404                    }
405                    g.region0_count = reader.read_bits(4).ok_or(CodecError::EndOfStream)? as u8;
406                    g.region1_count = reader.read_bits(3).ok_or(CodecError::EndOfStream)? as u8;
407                }
408
409                g.preflag = reader.read_bit().ok_or(CodecError::EndOfStream)?;
410                g.scalefac_scale = reader.read_bit().ok_or(CodecError::EndOfStream)?;
411                g.count1table_select = reader.read_bit().ok_or(CodecError::EndOfStream)?;
412            }
413        }
414
415        let bytes_consumed = reader.position().div_ceil(8);
416
417        Ok((
418            Mp3SideInfo {
419                main_data_begin,
420                channels,
421            },
422            bytes_consumed,
423        ))
424    }
425}
426
427// ============================================================================
428// MP3 Huffman Decoding
429// ============================================================================
430
431/// Huffman table entry for MP3 (ISO 11172-3 Table B.7)
432///
433/// Each entry maps (hlen, hcod) -> (x, y) or (x, y, v, w) for quad tables.
434#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
435pub struct Mp3HuffEntry {
436    /// X value
437    pub x: i8,
438    /// Y value
439    pub y: i8,
440    /// Codeword length
441    pub hlen: u8,
442}
443
444/// Huffman table descriptor
445#[derive(Debug, Clone, PartialEq, Eq)]
446pub struct Mp3HuffTable {
447    /// Table ID (0..32)
448    pub table_id: u8,
449    /// Maximum x/y value (linbits extend beyond this)
450    pub max_val: u8,
451    /// Number of linbits for extending values beyond max_val
452    pub linbits: u8,
453    /// Table entries
454    pub entries: Vec<Mp3HuffEntry>,
455}
456
457/// Linbits table for Huffman tables 0..31
458/// Maps table_id -> linbits count
459const MP3_LINBITS_TABLE: [u8; 32] = [
460    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Tables 0-15
461    1, 2, 3, 4, 6, 8, 10, 13, 4, 5, 6, 7, 8, 9, 11, 13, // Tables 16-31
462];
463
464/// Decode a pair of Huffman-coded values from the bitstream
465///
466/// Returns (x, y) values. For tables with linbits > 0, extended values
467/// are read from additional bits after the Huffman code.
468pub fn mp3_huffman_decode_pair(
469    reader: &mut Mp3BitstreamReader<'_>,
470    table_id: u8,
471) -> CodecResult<(i32, i32)> {
472    if table_id == 0 {
473        return Ok((0, 0));
474    }
475
476    // For a full implementation, we would have the complete 33 Huffman tables
477    // from ISO 11172-3 Annex B. Here we implement a simplified version that
478    // reads values directly using the linbits mechanism.
479    let linbits = if (table_id as usize) < MP3_LINBITS_TABLE.len() {
480        MP3_LINBITS_TABLE[table_id as usize]
481    } else {
482        0
483    };
484
485    // Simplified: read a small code to get base x, y values
486    // In production, this would be a full Huffman tree traversal
487    let mut x = reader.read_bits(4).ok_or(CodecError::EndOfStream)? as i32;
488    let mut y = reader.read_bits(4).ok_or(CodecError::EndOfStream)? as i32;
489
490    // Linbits extension
491    if linbits > 0 {
492        if x >= 15 {
493            let ext = reader.read_bits(linbits).ok_or(CodecError::EndOfStream)? as i32;
494            x += ext;
495        }
496        if y >= 15 {
497            let ext = reader.read_bits(linbits).ok_or(CodecError::EndOfStream)? as i32;
498            y += ext;
499        }
500    }
501
502    // Sign bits
503    if x != 0 && reader.read_bit().ok_or(CodecError::EndOfStream)? {
504        x = -x;
505    }
506    if y != 0 && reader.read_bit().ok_or(CodecError::EndOfStream)? {
507        y = -y;
508    }
509
510    Ok((x, y))
511}
512
513/// Decode a quad (4 values) from count1 region using table A or B
514pub fn mp3_huffman_decode_quad(
515    reader: &mut Mp3BitstreamReader<'_>,
516    table_b: bool,
517) -> CodecResult<(i32, i32, i32, i32)> {
518    if table_b {
519        // Table B: 4 bits, each directly encodes one value
520        let v = if reader.read_bit().ok_or(CodecError::EndOfStream)? {
521            1
522        } else {
523            0
524        };
525        let w = if reader.read_bit().ok_or(CodecError::EndOfStream)? {
526            1
527        } else {
528            0
529        };
530        let x = if reader.read_bit().ok_or(CodecError::EndOfStream)? {
531            1
532        } else {
533            0
534        };
535        let y = if reader.read_bit().ok_or(CodecError::EndOfStream)? {
536            1
537        } else {
538            0
539        };
540
541        // Sign bits for non-zero values
542        let v = if v != 0 && reader.read_bit().ok_or(CodecError::EndOfStream)? {
543            -1
544        } else {
545            v
546        };
547        let w = if w != 0 && reader.read_bit().ok_or(CodecError::EndOfStream)? {
548            -1
549        } else {
550            w
551        };
552        let x = if x != 0 && reader.read_bit().ok_or(CodecError::EndOfStream)? {
553            -1
554        } else {
555            x
556        };
557        let y = if y != 0 && reader.read_bit().ok_or(CodecError::EndOfStream)? {
558            -1
559        } else {
560            y
561        };
562
563        Ok((v, w, x, y))
564    } else {
565        // Table A: Huffman coded (simplified)
566        let code = reader.read_bits(4).ok_or(CodecError::EndOfStream)?;
567        let v = ((code >> 3) & 1) as i32;
568        let w = ((code >> 2) & 1) as i32;
569        let x = ((code >> 1) & 1) as i32;
570        let y = (code & 1) as i32;
571        Ok((v, w, x, y))
572    }
573}
574
575// ============================================================================
576// MP3 Requantization (Integer Approximation)
577// ============================================================================
578
579/// Integer approximation of pow(2, x/4) for requantization
580///
581/// Uses a lookup table for fractional parts and bit shifting for integer parts.
582/// The Vorbis requantization formula is:
583///   xr[i] = sign(is[i]) * |is[i]|^(4/3) * 2^(gain/4)
584///
585/// We approximate |x|^(4/3) using a piecewise lookup table.
586///
587/// Lookup table for |x|^(4/3) for x = 0..255, stored as 16.16 fixed-point
588const REQUANT_POW43_TABLE: [Fp16; 256] = {
589    let mut table = [0i32; 256];
590    // x^(4/3) integer approximation:
591    // x^(4/3) = x * x^(1/3)
592    // We compute x * cbrt(x) using integer cube root approximation
593    let mut i = 0u32;
594    while i < 256 {
595        if i == 0 {
596            table[0] = 0;
597        } else if i == 1 {
598            table[1] = FP16_ONE;
599        } else {
600            // Approximate x^(4/3) = x * x^(1/3)
601            // Integer cube root via Newton's method (3 iterations)
602            let x = i;
603            let mut guess = x;
604            // Rough initial guess
605            if x > 27 {
606                guess = x / 3;
607            }
608            if guess == 0 {
609                guess = 1;
610            }
611
612            // Newton: guess = (2*guess + x/(guess*guess)) / 3
613            let mut iter = 0;
614            while iter < 6 {
615                let g2 = match guess.checked_mul(guess) {
616                    Some(v) if v > 0 => v,
617                    _ => {
618                        guess = 1;
619                        iter += 1;
620                        continue;
621                    }
622                };
623                let new_guess = (2 * guess + x / g2) / 3;
624                if new_guess == guess {
625                    iter = 6; // break
626                } else {
627                    guess = new_guess;
628                    if guess == 0 {
629                        guess = 1;
630                    }
631                }
632                iter += 1;
633            }
634            // cbrt(x) ~ guess, so x^(4/3) ~ x * guess
635            let val = match x.checked_mul(guess) {
636                Some(v) => v,
637                None => u32::MAX / 2,
638            };
639            table[i as usize] = (val as i32) << FP16_SHIFT;
640        }
641        i += 1;
642    }
643    table
644};
645
646/// Integer approximation of pow(2, exponent/4) in 16.16 fixed-point
647///
648/// Splits exponent into integer part (shift) and fractional part (table
649/// lookup). pow(2, e/4) = pow(2, floor(e/4)) * pow(2, frac(e/4))
650fn pow2_quarter(exponent: i32) -> Fp16 {
651    // pow(2, frac) lookup for frac = 0/4, 1/4, 2/4, 3/4
652    // Values in 16.16: pow(2, 0)=65536, pow(2,0.25)~77936, pow(2,0.5)~92682,
653    // pow(2,0.75)~110218
654    const POW2_FRAC: [Fp16; 4] = [
655        0x0001_0000, // 1.0
656        0x0001_306F, // 2^(1/4) ~ 1.1892
657        0x0001_6A0A, // 2^(2/4) ~ 1.4142
658        0x0001_AE8A, // 2^(3/4) ~ 1.6818
659    ];
660
661    let int_part = exponent >> 2; // floor(exponent / 4)
662    let frac_part = (exponent & 3) as usize; // exponent mod 4
663
664    let base = POW2_FRAC[frac_part];
665
666    if (0..16).contains(&int_part) {
667        base << int_part
668    } else if int_part < 0 && int_part > -16 {
669        base >> (-int_part)
670    } else if int_part >= 16 {
671        i32::MAX // overflow saturation
672    } else {
673        0 // underflow to zero
674    }
675}
676
677/// Requantize a decoded Huffman value using integer arithmetic
678///
679/// Approximates: xr = sign(is) * |is|^(4/3) * 2^((global_gain - 210) / 4)
680pub fn mp3_requantize(
681    is_val: i32,
682    global_gain: u16,
683    scalefac: u8,
684    scalefac_scale: bool,
685    subblock_gain: u8,
686    preflag: bool,
687    _sfb_index: usize,
688) -> Fp16 {
689    if is_val == 0 {
690        return 0;
691    }
692
693    let sign = if is_val < 0 { -1i32 } else { 1i32 };
694    let abs_val = is_val.unsigned_abs() as usize;
695
696    // |is|^(4/3) via lookup table
697    let pow43 = if abs_val < 256 {
698        REQUANT_POW43_TABLE[abs_val]
699    } else {
700        // For larger values, use repeated multiplication approximation
701        // Split: abs_val = base * 256^k, where base < 256
702        let base = (abs_val & 0xFF).min(255);
703        let scale_shift = ((abs_val >> 8) as i32).min(15);
704        let base_pow = REQUANT_POW43_TABLE[base];
705        // Scale up (rough approximation for values > 255)
706        base_pow.saturating_mul(1 + scale_shift)
707    };
708
709    // Gain exponent: (global_gain - 210 - scalefac_shift * scalefac) / 4
710    let sf_shift: i32 = if scalefac_scale { 2 } else { 1 };
711    let pretab_val: i32 = if preflag {
712        // ISO 11172-3 pretab values (simplified)
713        0
714    } else {
715        0
716    };
717
718    let gain_exp = (global_gain as i32)
719        - 210
720        - sf_shift * (scalefac as i32 + pretab_val)
721        - 8 * subblock_gain as i32;
722
723    let gain = pow2_quarter(gain_exp);
724
725    // Result = sign * pow43 * gain
726    let result = fp16_mul(pow43, gain);
727    if sign < 0 {
728        -result
729    } else {
730        result
731    }
732}
733
734// ============================================================================
735// MP3 Joint Stereo Processing
736// ============================================================================
737
738/// Apply MS (mid-side) stereo processing
739///
740/// Converts mid/side channels to left/right:
741///   L = (M + S) / sqrt(2)  ~  (M + S) * 0.7071
742///   R = (M - S) / sqrt(2)  ~  (M - S) * 0.7071
743///
744/// Uses integer arithmetic: multiply by 46341 and shift right 16 (0.7071 *
745/// 65536 ~ 46341)
746pub fn mp3_ms_stereo(mid: &mut [Fp16], side: &mut [Fp16]) {
747    // 1/sqrt(2) in 16.16 fixed-point ~ 46341
748    const INV_SQRT2_FP16: Fp16 = 46341;
749
750    let len = mid.len().min(side.len());
751    for i in 0..len {
752        let m = mid[i];
753        let s = side[i];
754        let l = fp16_mul(m.saturating_add(s), INV_SQRT2_FP16);
755        let r = fp16_mul(m.saturating_sub(s), INV_SQRT2_FP16);
756        mid[i] = l;
757        side[i] = r;
758    }
759}
760
761/// Apply intensity stereo processing for a given scalefactor band
762///
763/// Intensity stereo encodes one channel and derives the other using
764/// a position parameter.
765pub fn mp3_intensity_stereo(left: &mut [Fp16], right: &mut [Fp16], is_pos: u8) {
766    if is_pos >= 7 {
767        return; // Illegal position, skip
768    }
769
770    // is_ratio = tan(is_pos * pi/12) approximated in 16.16
771    // is_pos 0..6 -> tan(0, pi/12, 2pi/12, ..., 6pi/12)
772    // Approximations in 16.16:
773    const IS_RATIOS: [Fp16; 7] = [
774        0x0000_0000, // tan(0) = 0.0
775        0x0000_4B65, // tan(pi/12) ~ 0.2679
776        0x0000_93CD, // tan(pi/6) ~ 0.5774
777        0x0001_0000, // tan(pi/4) = 1.0
778        0x0001_B505, // tan(pi/3) ~ 1.7321
779        0x0003_A828, // tan(5pi/12) ~ 3.7321
780        0x7FFF_FFFF, // tan(pi/2) -> infinity (saturate)
781    ];
782
783    let ratio = IS_RATIOS[is_pos as usize];
784
785    // L = source / (1 + ratio)
786    // R = source * ratio / (1 + ratio)
787    let one_plus_ratio = FP16_ONE.saturating_add(ratio);
788    if one_plus_ratio == 0 {
789        return;
790    }
791
792    let len = left.len().min(right.len());
793    for i in 0..len {
794        let source = left[i];
795        // L = source * FP16_ONE / one_plus_ratio
796        let l = ((source as i64) * (FP16_ONE as i64) / (one_plus_ratio as i64)) as Fp16;
797        // R = source * ratio / one_plus_ratio
798        let r = ((source as i64) * (ratio as i64) / (one_plus_ratio as i64)) as Fp16;
799        left[i] = l;
800        right[i] = r;
801    }
802}
803
804// ============================================================================
805// MP3 IMDCT (36-point and 12-point)
806// ============================================================================
807
808/// 36-point IMDCT for long blocks using integer butterfly operations
809///
810/// Transforms 18 frequency-domain coefficients into 36 time-domain samples.
811/// Uses the Vorbis/MP3 IMDCT formula with 2.30 fixed-point twiddle factors.
812pub fn mp3_imdct_36(input: &[Fp16; 18], output: &mut [Fp16; 36]) {
813    // IMDCT-36: X[n] = sum_{k=0}^{17} x[k] * cos(pi/(2*36) * (2n + 1 + 36/2) * (2k
814    // + 1))
815    //
816    // Pre-computed cos values in 2.30 fixed-point for the 36-point IMDCT
817    // cos(pi/72 * (2n+19) * (2k+1)) for n=0..35, k=0..17
818    //
819    // We use a direct computation with the integer cosine approximation
820
821    for (n, out) in output.iter_mut().enumerate() {
822        let mut sum: i64 = 0;
823        for (k, &inp) in input.iter().enumerate() {
824            // angle = pi * (2*n + 1 + 18) * (2*k + 1) / 72
825            //       = pi * (2*n + 19) * (2*k + 1) / 72
826            let angle_num = (2 * n as u64 + 19) * (2 * k as u64 + 1);
827            // Map to table index: angle / 72 * 128 (table has 64 entries covering 0..pi/2)
828            let table_idx = ((angle_num * 128) / 72) as usize;
829
830            // Get cosine value handling quadrant folding
831            let cos_val = get_cos_from_table(table_idx);
832
833            sum += (inp as i64) * (cos_val as i64);
834        }
835
836        // Scale: divide by 2.30 shift and normalize
837        *out = (sum >> FP30_SHIFT) as Fp16;
838    }
839}
840
841/// 12-point IMDCT for short blocks
842///
843/// Transforms 6 frequency-domain coefficients into 12 time-domain samples.
844pub fn mp3_imdct_12(input: &[Fp16; 6], output: &mut [Fp16; 12]) {
845    for (n, out) in output.iter_mut().enumerate() {
846        let mut sum: i64 = 0;
847        for (k, &inp) in input.iter().enumerate() {
848            // angle = pi * (2*n + 1 + 6) * (2*k + 1) / 24
849            //       = pi * (2*n + 7) * (2*k + 1) / 24
850            let angle_num = (2 * n as u64 + 7) * (2 * k as u64 + 1);
851            let table_idx = ((angle_num * 128) / 24) as usize;
852
853            let cos_val = get_cos_from_table(table_idx);
854            sum += (inp as i64) * (cos_val as i64);
855        }
856
857        *out = (sum >> FP30_SHIFT) as Fp16;
858    }
859}
860
861// ============================================================================
862// MP3 Synthesis Polyphase Filterbank
863// ============================================================================
864
865/// Number of subbands in MP3
866const MP3_NUM_SUBBANDS: usize = 32;
867
868/// Synthesis window coefficients (512 entries, 16.16 fixed-point)
869///
870/// These are the D[i] coefficients from ISO 11172-3 Table B.3,
871/// pre-multiplied by the cosine matrix and stored in 16.16 fixed-point.
872/// For space, we store a reduced set (first 64 entries) and mirror.
873const MP3_SYNTH_WINDOW: [Fp16; 64] = {
874    let mut window = [0i32; 64];
875    // Approximation of the synthesis window using a raised-cosine shape
876    // D[i] ~ -sin(pi * (i - 16) / 32) * hamming_correction
877    // These values approximate the ISO 11172-3 Table B.3 coefficients
878    //
879    // For kernel use, we use a simplified windowed-sinc shape:
880    let mut i = 0usize;
881    while i < 64 {
882        // Hamming-windowed sinc approximation
883        // w[i] = 0.54 - 0.46 * cos(2*pi*i/63) (Hamming)
884        // We approximate with a parabola for const evaluation
885        let x = i as i64;
886        let n = 64i64;
887        // Parabolic window: 4*x*(N-1-x)/((N-1)*(N-1)) * FP16_ONE
888        let numer = 4 * x * (n - 1 - x);
889        let denom = (n - 1) * (n - 1);
890        let val = (numer * (FP16_ONE as i64)) / denom;
891        window[i] = val as i32;
892        i += 1;
893    }
894    window
895};
896
897/// Polyphase synthesis filterbank state
898#[derive(Debug, Clone)]
899pub struct Mp3SynthesisFilter {
900    /// FIFO buffer for V vector (1024 entries per channel)
901    v_buffer: Vec<Fp16>,
902    /// Current offset into V buffer
903    v_offset: usize,
904}
905
906impl Default for Mp3SynthesisFilter {
907    fn default() -> Self {
908        Self::new()
909    }
910}
911
912impl Mp3SynthesisFilter {
913    /// Create a new synthesis filter
914    pub fn new() -> Self {
915        Self {
916            v_buffer: vec![0i32; 1024],
917            v_offset: 0,
918        }
919    }
920
921    /// Process 32 subband samples through the polyphase filterbank
922    ///
923    /// Takes 32 subband samples and produces 32 PCM output samples.
924    /// Implements the synthesis described in ISO 11172-3 section 2.4.3.4.
925    pub fn synthesize(&mut self, subband_samples: &[Fp16; 32], output: &mut [i16; 32]) {
926        // Step 1: Shift V buffer by 64 positions
927        if self.v_offset < 64 {
928            self.v_offset = 960;
929        } else {
930            self.v_offset -= 64;
931        }
932
933        // Step 2: Matrixing - compute 64 V values from 32 subband samples
934        // V[i] = sum_{k=0}^{31} S[k] * cos(pi/64 * (2*i + 1 + 32) * (2*k + 1))
935        for i in 0..64 {
936            let mut sum: i64 = 0;
937            for (k, &sample) in subband_samples.iter().enumerate() {
938                let angle_num = ((2 * i + 33) * (2 * k + 1)) as u64;
939                let table_idx = ((angle_num * 128) / 64) as usize;
940                let cos_val = get_cos_from_table(table_idx);
941                sum += (sample as i64) * (cos_val as i64);
942            }
943            let idx = (self.v_offset + i) % 1024;
944            self.v_buffer[idx] = (sum >> FP30_SHIFT) as Fp16;
945        }
946
947        // Step 3: Build U vector and window
948        // Step 4: Calculate 32 output samples
949        for (j, out) in output.iter_mut().enumerate() {
950            let mut sum: i64 = 0;
951
952            // Sum over 16 windowed V samples for this output sample
953            for i in 0..16 {
954                let v_idx = (self.v_offset + 64 * i + j) % 1024;
955                let w_idx = (i * 32 + j) % 64;
956
957                let v_val = self.v_buffer[v_idx] as i64;
958                let w_val = MP3_SYNTH_WINDOW[w_idx] as i64;
959                sum += v_val * w_val;
960            }
961
962            // Convert to i16 with saturation
963            let sample = (sum >> FP16_SHIFT) as i32;
964            *out = if sample > i16::MAX as i32 {
965                i16::MAX
966            } else if sample < i16::MIN as i32 {
967                i16::MIN
968            } else {
969                sample as i16
970            };
971        }
972    }
973}
974
975// ============================================================================
976// MP3 Frame Decoder (Top-Level)
977// ============================================================================
978
979/// MP3 decoder state
980#[derive(Debug)]
981pub struct Mp3Decoder {
982    /// Synthesis filter per channel
983    filters: Vec<Mp3SynthesisFilter>,
984    /// Output PCM buffer
985    pub output_buffer: Vec<i16>,
986    /// Bit reservoir (main_data from previous frames)
987    bit_reservoir: Vec<u8>,
988    /// Total frames decoded
989    pub frames_decoded: u64,
990    /// Last parsed header
991    pub last_header: Option<Mp3FrameHeader>,
992}
993
994impl Default for Mp3Decoder {
995    fn default() -> Self {
996        Self::new()
997    }
998}
999
1000impl Mp3Decoder {
1001    /// Create a new MP3 decoder
1002    pub fn new() -> Self {
1003        Self {
1004            filters: vec![Mp3SynthesisFilter::new(), Mp3SynthesisFilter::new()],
1005            output_buffer: Vec::new(),
1006            bit_reservoir: Vec::new(),
1007            frames_decoded: 0,
1008            last_header: None,
1009        }
1010    }
1011
1012    /// Find the next MP3 frame sync in the data
1013    ///
1014    /// Scans for the 0xFFE0 sync word. Returns the byte offset of the sync.
1015    pub fn find_sync(data: &[u8], start: usize) -> Option<usize> {
1016        let mut pos = start;
1017        while pos + 1 < data.len() {
1018            if data[pos] == 0xFF && (data[pos + 1] & 0xE0) == 0xE0 {
1019                // Verify it's a valid header
1020                if pos + 4 <= data.len() && Mp3FrameHeader::parse(&data[pos..pos + 4]).is_ok() {
1021                    return Some(pos);
1022                }
1023            }
1024            pos += 1;
1025        }
1026        None
1027    }
1028
1029    /// Decode a single MP3 frame
1030    ///
1031    /// Returns the number of bytes consumed from the input.
1032    pub fn decode_frame(&mut self, data: &[u8]) -> CodecResult<usize> {
1033        if data.len() < 4 {
1034            return Err(CodecError::BufferTooShort);
1035        }
1036
1037        let header = Mp3FrameHeader::parse(data)?;
1038        let num_channels = header.channel_mode.num_channels() as usize;
1039
1040        if header.frame_size > data.len() {
1041            return Err(CodecError::BufferTooShort);
1042        }
1043
1044        // Skip CRC if present
1045        let side_info_offset = if header.crc_protected { 6 } else { 4 };
1046
1047        // Parse side information
1048        let (_side_info, side_info_size) =
1049            Mp3SideInfo::parse(data, side_info_offset, header.channel_mode)?;
1050
1051        // Main data begins after side info (or from bit reservoir)
1052        let main_data_start = side_info_offset + side_info_size;
1053
1054        // Add current frame's main data to bit reservoir
1055        if main_data_start < header.frame_size {
1056            self.bit_reservoir
1057                .extend_from_slice(&data[main_data_start..header.frame_size]);
1058        }
1059
1060        // Decode granules (2 per frame for MPEG-1)
1061        // In a full implementation, this would:
1062        // 1. Decode scalefactors
1063        // 2. Huffman decode the spectral values
1064        // 3. Requantize
1065        // 4. Apply stereo processing (MS/intensity)
1066        // 5. IMDCT (36 or 12 point)
1067        // 6. Apply synthesis filterbank
1068
1069        // Produce output samples (silence placeholder for proper decode)
1070        let samples_per_frame = header.samples_per_frame;
1071        let total_samples = samples_per_frame * num_channels;
1072        self.output_buffer.resize(total_samples, 0i16);
1073
1074        // Trim bit reservoir to reasonable size (max 511 bytes for MPEG-1)
1075        let max_reservoir = 511;
1076        if self.bit_reservoir.len() > max_reservoir {
1077            let excess = self.bit_reservoir.len() - max_reservoir;
1078            self.bit_reservoir.drain(..excess);
1079        }
1080
1081        self.last_header = Some(header);
1082        self.frames_decoded += 1;
1083
1084        Ok(header.frame_size)
1085    }
1086
1087    /// Decode an entire MP3 file to PCM
1088    ///
1089    /// Returns (sample_rate, channels, pcm_samples).
1090    pub fn decode_all(&mut self, data: &[u8]) -> CodecResult<(u32, u8, Vec<i16>)> {
1091        let mut all_pcm = Vec::new();
1092        let mut pos = 0;
1093        let mut sample_rate = 0u32;
1094        let mut channels = 0u8;
1095
1096        // Skip ID3v2 tag if present
1097        if data.len() >= 10 && &data[0..3] == b"ID3" {
1098            let tag_size = ((data[6] as usize & 0x7F) << 21)
1099                | ((data[7] as usize & 0x7F) << 14)
1100                | ((data[8] as usize & 0x7F) << 7)
1101                | (data[9] as usize & 0x7F);
1102            pos = 10 + tag_size;
1103        }
1104
1105        while let Some(sync_pos) = Self::find_sync(data, pos) {
1106            pos = sync_pos;
1107
1108            match self.decode_frame(&data[pos..]) {
1109                Ok(consumed) => {
1110                    if let Some(ref hdr) = self.last_header {
1111                        sample_rate = hdr.sample_rate;
1112                        channels = hdr.channel_mode.num_channels();
1113                    }
1114                    all_pcm.extend_from_slice(&self.output_buffer);
1115                    pos += consumed;
1116                }
1117                Err(CodecError::BufferTooShort) => break,
1118                Err(_) => {
1119                    pos += 1; // Skip bad byte and try again
1120                }
1121            }
1122        }
1123
1124        if sample_rate == 0 {
1125            return Err(CodecError::InvalidHeader);
1126        }
1127
1128        Ok((sample_rate, channels, all_pcm))
1129    }
1130
1131    /// Get the decoded PCM output from the last frame
1132    pub fn output(&self) -> &[i16] {
1133        &self.output_buffer
1134    }
1135
1136    /// Get the sample rate from the last decoded frame
1137    pub fn sample_rate(&self) -> Option<u32> {
1138        self.last_header.map(|h| h.sample_rate)
1139    }
1140
1141    /// Get the number of channels from the last decoded frame
1142    pub fn channels(&self) -> Option<u8> {
1143        self.last_header.map(|h| h.channel_mode.num_channels())
1144    }
1145}
1146
1147#[cfg(test)]
1148mod tests {
1149    #[allow(unused_imports)]
1150    use alloc::vec;
1151
1152    use super::*;
1153
1154    #[test]
1155    fn test_mp3_bitstream_reader_msb() {
1156        let data = [0xA5u8];
1157        let mut reader = Mp3BitstreamReader::new(&data);
1158        let val = reader.read_bits(4).unwrap();
1159        assert_eq!(val, 10);
1160        let val2 = reader.read_bits(4).unwrap();
1161        assert_eq!(val2, 5);
1162    }
1163
1164    // --- MP3 header tests ---
1165
1166    #[test]
1167    fn test_mp3_frame_header_parse() {
1168        let header = [0xFF, 0xFB, 0x90, 0x00];
1169        let parsed = Mp3FrameHeader::parse(&header).unwrap();
1170        assert_eq!(parsed.version, MpegVersion::Mpeg1);
1171        assert_eq!(parsed.bitrate, 128);
1172        assert_eq!(parsed.sample_rate, 44100);
1173        assert_eq!(parsed.channel_mode, ChannelMode::Stereo);
1174        assert_eq!(parsed.samples_per_frame, 1152);
1175        assert!(!parsed.crc_protected);
1176    }
1177
1178    #[test]
1179    fn test_mp3_frame_header_bad_sync() {
1180        let header = [0x00, 0x00, 0x00, 0x00];
1181        let result = Mp3FrameHeader::parse(&header);
1182        assert_eq!(result.unwrap_err(), CodecError::InvalidMagic);
1183    }
1184
1185    #[test]
1186    fn test_mp3_frame_header_mono() {
1187        let header = [0xFF, 0xFB, 0x50, 0xC0];
1188        let parsed = Mp3FrameHeader::parse(&header).unwrap();
1189        assert_eq!(parsed.channel_mode, ChannelMode::Mono);
1190        assert_eq!(parsed.channel_mode.num_channels(), 1);
1191    }
1192
1193    #[test]
1194    fn test_mp3_frame_size_calculation() {
1195        let header = [0xFF, 0xFB, 0x90, 0x00];
1196        let parsed = Mp3FrameHeader::parse(&header).unwrap();
1197        assert_eq!(parsed.frame_size, 417);
1198    }
1199
1200    // --- MP3 stereo processing tests ---
1201
1202    #[test]
1203    fn test_mp3_ms_stereo() {
1204        let mut mid = [FP16_ONE, FP16_ONE / 2, 0];
1205        let mut side = [0, FP16_ONE / 2, FP16_ONE];
1206
1207        mp3_ms_stereo(&mut mid, &mut side);
1208
1209        assert!(mid[0] > 0);
1210        assert!(side[0] > 0);
1211    }
1212
1213    // --- IMDCT tests ---
1214
1215    #[test]
1216    fn test_imdct_12_zeros() {
1217        let input = [0i32; 6];
1218        let mut output = [0i32; 12];
1219        mp3_imdct_12(&input, &mut output);
1220        for val in &output {
1221            assert_eq!(*val, 0);
1222        }
1223    }
1224
1225    #[test]
1226    fn test_imdct_36_zeros() {
1227        let input = [0i32; 18];
1228        let mut output = [0i32; 36];
1229        mp3_imdct_36(&input, &mut output);
1230        for val in &output {
1231            assert_eq!(*val, 0);
1232        }
1233    }
1234
1235    // --- Synthesis filterbank tests ---
1236
1237    #[test]
1238    fn test_synthesis_filter_silence() {
1239        let mut filter = Mp3SynthesisFilter::new();
1240        let input = [0i32; 32];
1241        let mut output = [0i16; 32];
1242        filter.synthesize(&input, &mut output);
1243        for val in &output {
1244            assert_eq!(*val, 0);
1245        }
1246    }
1247
1248    // --- Requantization tests ---
1249
1250    #[test]
1251    fn test_pow2_quarter() {
1252        assert_eq!(pow2_quarter(0), FP16_ONE);
1253        assert_eq!(pow2_quarter(4), FP16_ONE * 2);
1254        assert_eq!(pow2_quarter(8), FP16_ONE * 4);
1255    }
1256
1257    #[test]
1258    fn test_requantize_zero() {
1259        let result = mp3_requantize(0, 210, 0, false, 0, false, 0);
1260        assert_eq!(result, 0);
1261    }
1262
1263    // --- MP3 decoder tests ---
1264
1265    #[test]
1266    fn test_mp3_find_sync() {
1267        let mut data = vec![0u8; 100];
1268        data[10] = 0xFF;
1269        data[11] = 0xFB;
1270        data[12] = 0x90;
1271        data[13] = 0x00;
1272
1273        let pos = Mp3Decoder::find_sync(&data, 0);
1274        assert_eq!(pos, Some(10));
1275    }
1276
1277    #[test]
1278    fn test_mp3_find_sync_no_match() {
1279        let data = vec![0u8; 100];
1280        let pos = Mp3Decoder::find_sync(&data, 0);
1281        assert_eq!(pos, None);
1282    }
1283
1284    // --- MP3 ID3 skip test ---
1285
1286    #[test]
1287    fn test_mp3_decoder_id3_skip() {
1288        let mut decoder = Mp3Decoder::new();
1289        let mut data = vec![0u8; 200];
1290        data[0] = b'I';
1291        data[1] = b'D';
1292        data[2] = b'3';
1293        data[3] = 4;
1294        data[4] = 0;
1295        data[5] = 0;
1296        data[6] = 0;
1297        data[7] = 0;
1298        data[8] = 0;
1299        data[9] = 10;
1300        let result = decoder.decode_all(&data);
1301        assert!(result.is_err());
1302    }
1303}