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 if reader.read(15)? != 0x7FFC {
69 return Err(FLACError::InvalidSyncCode);
70 }
71
72 fi.is_var_size = reader.read_bit()?;
74 fi.blocking_strategy = fi.is_var_size as u8;
75
76 let bs_code = reader.read(4)? as u8;
78 let sr_code = reader.read(4)? as u8;
79
80 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; } 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 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 if reader.read_bit()? {
101 return Err(FLACError::InvalidPadding);
102 }
103
104 fi.frame_or_sample_num = read_utf8(&mut reader)?;
106
107 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 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 reader.skip(8)?; 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 fn is_flac_sync(slice: &[u8]) -> bool {
201 slice.len() >= 2 && slice[0] == 0xFF && (slice[1] & 0xFC) == 0xF8
202 }
203
204 while start_index < data.len() {
206 if is_flac_sync(&data[start_index..]) {
207 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 frames.push(data[start_index..end_index].to_vec());
218
219 start_index = end_index;
221 } else {
222 start_index += 1;
224 }
225 }
226
227 frames
228}
229
230pub fn extract_flac_frame(data: &[u8]) -> &[u8] {
231 for i in 0..data.len() - 1 {
234 if data[i] == 0xFF && (data[i + 1] & 0xFC) == 0xF8 {
235 return &data[i..];
236 }
237 }
238 &[] }
240
241pub fn create_streaminfo(frame_info: &FLACFrameInfo) -> Vec<u8> {
242 let mut streaminfo = Vec::with_capacity(34);
243
244 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 streaminfo.extend_from_slice(&[0, 0, 0]); streaminfo.extend_from_slice(&[0, 0, 0]); 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 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 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}