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 if input.starts_with(b"fLaC") {
63 return true;
64 }
65
66 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 if reader.read(15)? != 0x7FFC {
76 return Err(FLACError::InvalidSyncCode);
77 }
78
79 fi.is_var_size = reader.read_bit()?;
81 fi.blocking_strategy = fi.is_var_size as u8;
82
83 let bs_code = reader.read(4)? as u8;
85 let sr_code = reader.read(4)? as u8;
86
87 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; } 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 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 if reader.read_bit()? {
108 return Err(FLACError::InvalidPadding);
109 }
110
111 fi.frame_or_sample_num = read_utf8(&mut reader)?;
113
114 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 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 reader.skip(8)?; 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 fn is_flac_sync(slice: &[u8]) -> bool {
208 slice.len() >= 2 && slice[0] == 0xFF && (slice[1] & 0xFC) == 0xF8
209 }
210
211 while start_index < data.len() {
213 if is_flac_sync(&data[start_index..]) {
214 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 frames.push(data[start_index..end_index].to_vec());
225
226 start_index = end_index;
228 } else {
229 start_index += 1;
231 }
232 }
233
234 frames
235}
236
237pub fn extract_flac_frame(data: &[u8]) -> &[u8] {
238 for i in 0..data.len() - 1 {
241 if data[i] == 0xFF && (data[i + 1] & 0xFC) == 0xF8 {
242 return &data[i..];
243 }
244 }
245 &[] }
247
248pub fn create_streaminfo(frame_info: &FLACFrameInfo) -> Vec<u8> {
249 let mut streaminfo = Vec::with_capacity(34);
250
251 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 streaminfo.extend_from_slice(&[0, 0, 0]); streaminfo.extend_from_slice(&[0, 0, 0]); 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 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 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}