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