#![forbid(unsafe_code)]
pub mod bitwriter;
pub mod crc;
pub mod encoder;
pub mod frame;
pub mod rice;
pub mod subframe;
use crate::{AudioDecoder, AudioDecoderConfig, AudioError, AudioFrame, AudioResult, ChannelLayout};
use oximedia_core::{CodecId, SampleFormat};
pub use bitwriter::BitWriter;
pub use crc::{crc16, crc8, Crc16, Crc8};
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))
}
}
}
pub struct FlacDecoder {
#[allow(dead_code)]
config: AudioDecoderConfig,
sample_rate: u32,
channels: u8,
flushing: bool,
#[allow(dead_code)]
stream_info: Option<StreamInfo>,
#[allow(dead_code)]
current_frame: Option<FlacFrame>,
}
impl FlacDecoder {
pub fn new(config: &AudioDecoderConfig) -> AudioResult<Self> {
if config.codec != CodecId::Flac {
return Err(AudioError::InvalidParameter("Expected FLAC codec".into()));
}
Ok(Self {
config: config.clone(),
sample_rate: config.sample_rate,
channels: config.channels,
flushing: false,
stream_info: None,
current_frame: None,
})
}
pub fn parse_stream_info(data: &[u8]) -> AudioResult<StreamInfo> {
StreamInfo::parse(data)
}
pub fn parse_frame_header(&self, data: &[u8]) -> AudioResult<(FrameHeader, usize)> {
let bps = self.stream_info.as_ref().map_or(16, |s| s.bits_per_sample);
FrameHeader::parse(data, bps)
}
}
impl AudioDecoder for FlacDecoder {
fn codec(&self) -> CodecId {
CodecId::Flac
}
fn send_packet(&mut self, _data: &[u8], _pts: i64) -> AudioResult<()> {
Ok(())
}
fn receive_frame(&mut self) -> AudioResult<Option<AudioFrame>> {
Ok(None)
}
fn flush(&mut self) -> AudioResult<()> {
self.flushing = true;
Ok(())
}
fn reset(&mut self) {
self.flushing = false;
self.current_frame = None;
}
fn output_format(&self) -> Option<SampleFormat> {
Some(SampleFormat::S16)
}
fn sample_rate(&self) -> Option<u32> {
Some(self.sample_rate)
}
fn channel_layout(&self) -> Option<ChannelLayout> {
Some(ChannelLayout::from_count(usize::from(self.channels)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_flac_decoder() {
let config = AudioDecoderConfig {
codec: CodecId::Flac,
..Default::default()
};
let decoder = FlacDecoder::new(&config).expect("should succeed");
assert_eq!(decoder.codec(), CodecId::Flac);
}
#[test]
fn test_flac_decoder_wrong_codec() {
let config = AudioDecoderConfig {
codec: CodecId::Opus,
..Default::default()
};
assert!(FlacDecoder::new(&config).is_err());
}
#[test]
fn test_flac_decoder_reset() {
let config = AudioDecoderConfig {
codec: CodecId::Flac,
..Default::default()
};
let mut decoder = FlacDecoder::new(&config).expect("should succeed");
decoder.reset();
assert!(!decoder.flushing);
}
#[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());
}
}