#![forbid(unsafe_code)]
pub mod bitwriter;
pub mod crc;
pub mod decoder;
pub mod encoder;
pub mod frame;
pub mod rice;
pub mod subframe;
use crate::AudioError;
pub use bitwriter::BitWriter;
pub use crc::{crc16, crc8, Crc16, Crc8};
pub use decoder::FlacDecoder;
pub use encoder::{CompressionLevel, FlacEncoder};
pub use frame::{BlockingStrategy, ChannelAssignment, FlacFrame, FrameHeader};
pub use rice::{RiceDecoder, RicePartition};
pub use subframe::{Subframe, SubframeHeader, SubframeType, WarmupSamples};
#[derive(Debug, Clone, Default)]
#[allow(dead_code)]
pub struct StreamInfo {
pub min_block_size: u16,
pub max_block_size: u16,
pub min_frame_size: u32,
pub max_frame_size: u32,
pub sample_rate: u32,
pub channels: u8,
pub bits_per_sample: u8,
pub total_samples: u64,
pub md5_signature: [u8; 16],
}
impl StreamInfo {
pub fn parse(data: &[u8]) -> Result<Self, AudioError> {
if data.len() < 34 {
return Err(AudioError::InvalidData("STREAMINFO too short".into()));
}
let min_block_size = u16::from_be_bytes([data[0], data[1]]);
let max_block_size = u16::from_be_bytes([data[2], data[3]]);
let min_frame_size = u32::from_be_bytes([0, data[4], data[5], data[6]]);
let max_frame_size = u32::from_be_bytes([0, data[7], data[8], data[9]]);
let sample_rate =
(u32::from(data[10]) << 12) | (u32::from(data[11]) << 4) | (u32::from(data[12]) >> 4);
let channels = ((data[12] >> 1) & 0x07) + 1;
let bits_per_sample = (((data[12] & 0x01) << 4) | (data[13] >> 4)) + 1;
let total_samples = (u64::from(data[13] & 0x0F) << 32)
| (u64::from(data[14]) << 24)
| (u64::from(data[15]) << 16)
| (u64::from(data[16]) << 8)
| u64::from(data[17]);
let mut md5_signature = [0u8; 16];
md5_signature.copy_from_slice(&data[18..34]);
Ok(Self {
min_block_size,
max_block_size,
min_frame_size,
max_frame_size,
sample_rate,
channels,
bits_per_sample,
total_samples,
md5_signature,
})
}
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn duration_secs(&self) -> Option<f64> {
if self.sample_rate == 0 || self.total_samples == 0 {
None
} else {
Some(self.total_samples as f64 / f64::from(self.sample_rate))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stream_info_parse() {
let mut data = vec![0u8; 34];
data[0] = 0x10;
data[1] = 0x00;
data[2] = 0x10;
data[3] = 0x00;
data[10] = 0x0A; data[11] = 0xC4; data[12] = (4 << 4) | (1 << 1); data[13] = 0xF << 4;
let info = StreamInfo::parse(&data).expect("should succeed");
assert_eq!(info.min_block_size, 4096);
assert_eq!(info.max_block_size, 4096);
assert_eq!(info.sample_rate, 44100);
assert_eq!(info.channels, 2);
assert_eq!(info.bits_per_sample, 16);
}
#[test]
fn test_stream_info_duration() {
let mut info = StreamInfo::default();
info.sample_rate = 44100;
info.total_samples = 44100 * 60;
let duration = info.duration_secs().expect("should succeed");
assert!((duration - 60.0).abs() < 0.001);
}
#[test]
fn test_stream_info_duration_unknown() {
let info = StreamInfo::default();
assert!(info.duration_secs().is_none());
}
}