access_unit/
flac.rs

1use std::fmt;
2
3#[derive(Debug, Default)]
4pub struct FLACFrameInfo {
5    pub is_var_size: bool,
6    pub blocking_strategy: u8,
7    pub block_size: u16,
8    pub sample_rate: u32,
9    pub ch_mode: u8,
10    pub channels: u8,
11    pub bps: u8,
12    pub frame_or_sample_num: u64,
13}
14
15#[derive(Debug)]
16pub enum FLACError {
17    InvalidSyncCode,
18    InvalidChannelMode(u8),
19    InvalidSampleSizeCode(u8),
20    InvalidPadding,
21    UTF8DecodingError,
22    ReservedBlocksizeCode,
23    IllegalSampleRateCode(u8),
24    UnexpectedEndOfInput,
25}
26
27impl fmt::Display for FLACError {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            FLACError::InvalidSyncCode => write!(f, "Invalid sync code"),
31            FLACError::InvalidChannelMode(mode) => write!(f, "Invalid channel mode: {}", mode),
32            FLACError::InvalidSampleSizeCode(code) => {
33                write!(f, "Invalid sample size code: {}", code)
34            }
35            FLACError::InvalidPadding => write!(f, "Invalid padding"),
36            FLACError::UTF8DecodingError => write!(f, "UTF-8 decoding error"),
37            FLACError::ReservedBlocksizeCode => write!(f, "Reserved blocksize code"),
38            FLACError::IllegalSampleRateCode(code) => {
39                write!(f, "Illegal sample rate code: {}", code)
40            }
41            FLACError::UnexpectedEndOfInput => write!(f, "Unexpected end of input"),
42        }
43    }
44}
45
46impl std::error::Error for FLACError {}
47
48const SAMPLE_SIZE_TABLE: [u8; 8] = [0, 8, 12, 0, 16, 20, 24, 32];
49const FLAC_BLOCKSIZE_TABLE: [u16; 16] = [
50    0, 192, 576, 1152, 2304, 4608, 0, 0, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
51];
52const FLAC_SAMPLE_RATE_TABLE: [u32; 12] = [
53    0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000,
54];
55
56pub fn is_flac(input: &[u8]) -> bool {
57    if input.len() < 2 {
58        return false;
59    }
60    input[0] == 0xFF && input[1] == 0xF8
61}
62
63pub fn decode_frame_header(input: &[u8]) -> Result<FLACFrameInfo, FLACError> {
64    let mut reader = BitReader::new(input);
65    let mut fi = FLACFrameInfo::default();
66
67    // Frame sync code
68    if reader.read(15)? != 0x7FFC {
69        return Err(FLACError::InvalidSyncCode);
70    }
71
72    // Variable block size stream code
73    fi.is_var_size = reader.read_bit()?;
74    fi.blocking_strategy = fi.is_var_size as u8;
75
76    // Block size and sample rate codes
77    let bs_code = reader.read(4)? as u8;
78    let sr_code = reader.read(4)? as u8;
79
80    // Channels and decorrelation
81    fi.ch_mode = reader.read(4)? as u8;
82    if fi.ch_mode < 8 {
83        fi.channels = fi.ch_mode + 1;
84        fi.ch_mode = 0; // FLAC_CHMODE_INDEPENDENT
85    } else if fi.ch_mode < 11 {
86        fi.channels = 2;
87        fi.ch_mode -= 7;
88    } else {
89        return Err(FLACError::InvalidChannelMode(fi.ch_mode));
90    }
91
92    // Bits per sample
93    let bps_code = reader.read(3)? as u8;
94    if bps_code == 3 {
95        return Err(FLACError::InvalidSampleSizeCode(bps_code));
96    }
97    fi.bps = SAMPLE_SIZE_TABLE[bps_code as usize];
98
99    // Reserved bit
100    if reader.read_bit()? {
101        return Err(FLACError::InvalidPadding);
102    }
103
104    // Sample or frame count
105    fi.frame_or_sample_num = read_utf8(&mut reader)?;
106
107    // Blocksize
108    fi.block_size = match bs_code {
109        0 => return Err(FLACError::ReservedBlocksizeCode),
110        6 => reader.read(8)? as u16 + 1,
111        7 => reader.read(16)? as u16 + 1,
112        8..=15 => {
113            let bs = FLAC_BLOCKSIZE_TABLE[bs_code as usize];
114            if bs == 0 {
115                return Err(FLACError::ReservedBlocksizeCode);
116            }
117            bs
118        }
119        _ => FLAC_BLOCKSIZE_TABLE[bs_code as usize],
120    };
121
122    // Sample rate
123    fi.sample_rate = match sr_code {
124        0..=11 => FLAC_SAMPLE_RATE_TABLE[sr_code as usize],
125        12 => (reader.read(8)? as u32) * 1000,
126        13 => reader.read(16)? as u32,
127        14 => (reader.read(16)? as u32) * 10,
128        _ => return Err(FLACError::IllegalSampleRateCode(sr_code)),
129    };
130
131    // Header CRC-8 check
132    reader.skip(8)?; // Skip CRC for now
133
134    Ok(fi)
135}
136
137fn read_utf8(reader: &mut BitReader) -> Result<u64, FLACError> {
138    let mut value = 0u64;
139
140    loop {
141        let byte = reader.read(8)? as u8;
142        value = (value << 7) | ((byte & 0x7F) as u64);
143        if (byte & 0x80) == 0 {
144            break;
145        }
146    }
147
148    Ok(value)
149}
150
151struct BitReader<'a> {
152    data: &'a [u8],
153    bit_position: usize,
154}
155
156impl<'a> BitReader<'a> {
157    fn new(data: &'a [u8]) -> Self {
158        Self {
159            data,
160            bit_position: 0,
161        }
162    }
163
164    fn read(&mut self, num_bits: usize) -> Result<u32, FLACError> {
165        let mut result = 0u32;
166        for _ in 0..num_bits {
167            result = (result << 1) | self.read_bit()? as u32;
168        }
169        Ok(result)
170    }
171
172    fn read_bit(&mut self) -> Result<bool, FLACError> {
173        let byte_index = self.bit_position / 8;
174        let bit_index = 7 - (self.bit_position % 8);
175
176        if byte_index >= self.data.len() {
177            return Err(FLACError::UnexpectedEndOfInput);
178        }
179
180        let bit = (self.data[byte_index] >> bit_index) & 1;
181        self.bit_position += 1;
182
183        Ok(bit == 1)
184    }
185
186    fn skip(&mut self, num_bits: usize) -> Result<(), FLACError> {
187        self.bit_position += num_bits;
188        if self.bit_position / 8 >= self.data.len() {
189            return Err(FLACError::UnexpectedEndOfInput);
190        }
191        Ok(())
192    }
193}
194
195pub fn split_flac_frames(data: &[u8]) -> Vec<Vec<u8>> {
196    let mut frames = Vec::new();
197    let mut start_index = 0;
198
199    // Function to check if a slice starts with a valid FLAC sync code
200    fn is_flac_sync(slice: &[u8]) -> bool {
201        slice.len() >= 2 && slice[0] == 0xFF && (slice[1] & 0xFC) == 0xF8
202    }
203
204    // Iterate through the data to find FLAC frame boundaries
205    while start_index < data.len() {
206        if is_flac_sync(&data[start_index..]) {
207            // Find the start of the next frame
208            let mut end_index = start_index + 1;
209            while end_index < data.len() {
210                if is_flac_sync(&data[end_index..]) {
211                    break;
212                }
213                end_index += 1;
214            }
215
216            // Add the frame (including its header) to our list
217            frames.push(data[start_index..end_index].to_vec());
218
219            // Move to the start of the next frame
220            start_index = end_index;
221        } else {
222            // If we don't find a sync code, move to the next byte
223            start_index += 1;
224        }
225    }
226
227    frames
228}
229
230pub fn extract_flac_frame(data: &[u8]) -> &[u8] {
231    // Find the start of the FLAC frame
232    // FLAC frames typically start with 0xFF (11111111) followed by 0xF8 to 0xFB
233    for i in 0..data.len() - 1 {
234        if data[i] == 0xFF && (data[i + 1] & 0xFC) == 0xF8 {
235            return &data[i..];
236        }
237    }
238    &[] // Return empty slice if no frame is found
239}
240
241pub fn create_streaminfo(frame_info: &FLACFrameInfo) -> Vec<u8> {
242    let mut streaminfo = Vec::with_capacity(34);
243
244    // Min and max block size
245    streaminfo.extend_from_slice(&frame_info.block_size.to_be_bytes());
246    streaminfo.extend_from_slice(&frame_info.block_size.to_be_bytes());
247
248    // Min and max frame size (using placeholders)
249    streaminfo.extend_from_slice(&[0, 0, 0]); // min_frame_size (24 bits)
250    streaminfo.extend_from_slice(&[0, 0, 0]); // max_frame_size (24 bits)
251
252    // Sample rate, channels, bits per sample, and total samples
253    let combined = (frame_info.sample_rate & 0xFFFFF) << 12
254        | ((u32::from(frame_info.channels) - 1) & 0x7) << 9
255        | ((u32::from(frame_info.bps) - 1) & 0x1F) << 4
256        | ((frame_info.frame_or_sample_num >> 32) & 0xF) as u32;
257    streaminfo.extend_from_slice(&combined.to_be_bytes());
258
259    streaminfo.extend_from_slice(&(frame_info.frame_or_sample_num as u32).to_be_bytes());
260
261    // MD5 signature (using default value of all zeros)
262    streaminfo.extend_from_slice(&[0u8; 16]);
263
264    assert_eq!(streaminfo.len(), 34);
265    streaminfo
266}
267
268mod tests {
269    use super::*;
270    use std::fs::File;
271    use std::io::Read;
272
273    fn read_test_file() -> Vec<u8> {
274        let mut file = File::open("testdata/s24le.wav.flac").expect("Failed to open test file");
275        let mut buffer = Vec::new();
276        file.read_to_end(&mut buffer)
277            .expect("Failed to read test file");
278        buffer
279    }
280
281    #[test]
282    fn test_decode_frame_header() {
283        let data = read_test_file();
284        let frame_info = decode_frame_header(&data).unwrap();
285
286        assert_eq!(frame_info.is_var_size, false);
287        assert_eq!(frame_info.blocking_strategy, 0);
288        assert_eq!(frame_info.block_size, 4096);
289        assert_eq!(frame_info.sample_rate, 44100);
290        assert_eq!(frame_info.ch_mode, 3);
291        assert_eq!(frame_info.channels, 2);
292        assert_eq!(frame_info.bps, 16);
293        assert_eq!(frame_info.frame_or_sample_num, 0);
294    }
295
296    #[test]
297    fn test_split_flac_frames() {
298        let data = read_test_file();
299        let frames = split_flac_frames(&data);
300
301        assert!(!frames.is_empty(), "Should have at least one frame");
302        assert_eq!(frames.len(), 120);
303        // Check that each frame starts with a valid FLAC sync code
304        for frame in &frames {
305            assert!(frame.len() >= 2, "Frame should be at least 2 bytes long");
306            assert_eq!(frame[0], 0xFF, "Frame should start with 0xFF");
307            assert_eq!(
308                frame[1] & 0xFC,
309                0xF8,
310                "Second byte should match FLAC sync pattern"
311            );
312        }
313    }
314
315    #[test]
316    fn test_extract_flac_frame() {
317        let data = read_test_file();
318        let frame = extract_flac_frame(&data);
319
320        assert!(!frame.is_empty(), "Should extract a non-empty frame");
321        assert_eq!(frame[0], 0xFF, "Frame should start with 0xFF");
322        assert_eq!(
323            frame[1] & 0xFC,
324            0xF8,
325            "Second byte should match FLAC sync pattern"
326        );
327    }
328}