#![forbid(unsafe_code)]
use crate::AudioError;
pub const SYNC_CODE: u16 = 0x3FFE;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BlockingStrategy {
#[default]
Fixed,
Variable,
}
impl BlockingStrategy {
#[must_use]
pub fn from_bit(bit: bool) -> Self {
if bit {
BlockingStrategy::Variable
} else {
BlockingStrategy::Fixed
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChannelAssignment {
Independent(u8),
LeftSide,
RightSide,
MidSide,
}
impl Default for ChannelAssignment {
fn default() -> Self {
ChannelAssignment::Independent(1)
}
}
impl ChannelAssignment {
#[must_use]
pub fn from_value(value: u8) -> Option<Self> {
match value {
0..=7 => Some(ChannelAssignment::Independent(value + 1)),
8 => Some(ChannelAssignment::LeftSide),
9 => Some(ChannelAssignment::RightSide),
10 => Some(ChannelAssignment::MidSide),
_ => None,
}
}
#[must_use]
pub fn channels(self) -> u8 {
match self {
ChannelAssignment::Independent(n) => n,
ChannelAssignment::LeftSide
| ChannelAssignment::RightSide
| ChannelAssignment::MidSide => 2,
}
}
#[must_use]
pub fn is_stereo_decorrelated(self) -> bool {
matches!(
self,
ChannelAssignment::LeftSide | ChannelAssignment::RightSide | ChannelAssignment::MidSide
)
}
#[must_use]
pub fn side_channel(self) -> Option<usize> {
match self {
ChannelAssignment::LeftSide | ChannelAssignment::MidSide => Some(1),
ChannelAssignment::RightSide => Some(0),
ChannelAssignment::Independent(_) => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SampleSize {
#[default]
FromStreamInfo,
Bits8,
Bits12,
Bits16,
Bits20,
Bits24,
Bits32,
}
impl SampleSize {
#[must_use]
pub fn from_value(value: u8) -> Option<Self> {
match value {
0 => Some(SampleSize::FromStreamInfo),
1 => Some(SampleSize::Bits8),
2 => Some(SampleSize::Bits12),
4 => Some(SampleSize::Bits16),
5 => Some(SampleSize::Bits20),
6 => Some(SampleSize::Bits24),
7 => Some(SampleSize::Bits32),
_ => None,
}
}
#[must_use]
pub fn bits(self) -> Option<u8> {
match self {
SampleSize::FromStreamInfo => None,
SampleSize::Bits8 => Some(8),
SampleSize::Bits12 => Some(12),
SampleSize::Bits16 => Some(16),
SampleSize::Bits20 => Some(20),
SampleSize::Bits24 => Some(24),
SampleSize::Bits32 => Some(32),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[allow(dead_code)]
pub enum BlockSize {
#[default]
Reserved,
Samples192,
Samples576Mult(u8),
Samples256Mult(u8),
GetFromEnd8Bit,
GetFromEnd16Bit,
}
impl BlockSize {
#[must_use]
pub fn from_value(value: u8) -> Self {
match value {
1 => BlockSize::Samples192,
2..=5 => BlockSize::Samples576Mult(value - 2),
6 => BlockSize::GetFromEnd8Bit,
7 => BlockSize::GetFromEnd16Bit,
8..=15 => BlockSize::Samples256Mult(value - 8),
_ => BlockSize::Reserved,
}
}
#[must_use]
pub fn fixed_size(self) -> Option<u32> {
match self {
BlockSize::Samples192 => Some(192),
BlockSize::Samples576Mult(n) => Some(576 * (1 << n)),
BlockSize::Samples256Mult(n) => Some(256 * (1 << n)),
BlockSize::Reserved | BlockSize::GetFromEnd8Bit | BlockSize::GetFromEnd16Bit => None,
}
}
#[must_use]
pub fn needs_extra_bytes(self) -> bool {
matches!(self, BlockSize::GetFromEnd8Bit | BlockSize::GetFromEnd16Bit)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[allow(dead_code)]
pub enum SampleRateSpec {
#[default]
FromStreamInfo,
Rate88200,
Rate176400,
Rate192000,
Rate8000,
Rate16000,
Rate22050,
Rate24000,
Rate32000,
Rate44100,
Rate48000,
Rate96000,
GetFromEnd8BitKHz,
GetFromEnd16BitHz,
GetFromEnd16BitTensHz,
Invalid,
}
impl SampleRateSpec {
#[must_use]
pub fn from_value(value: u8) -> Self {
match value {
0 => SampleRateSpec::FromStreamInfo,
1 => SampleRateSpec::Rate88200,
2 => SampleRateSpec::Rate176400,
3 => SampleRateSpec::Rate192000,
4 => SampleRateSpec::Rate8000,
5 => SampleRateSpec::Rate16000,
6 => SampleRateSpec::Rate22050,
7 => SampleRateSpec::Rate24000,
8 => SampleRateSpec::Rate32000,
9 => SampleRateSpec::Rate44100,
10 => SampleRateSpec::Rate48000,
11 => SampleRateSpec::Rate96000,
12 => SampleRateSpec::GetFromEnd8BitKHz,
13 => SampleRateSpec::GetFromEnd16BitHz,
14 => SampleRateSpec::GetFromEnd16BitTensHz,
_ => SampleRateSpec::Invalid,
}
}
#[must_use]
pub fn fixed_rate(self) -> Option<u32> {
match self {
SampleRateSpec::Rate88200 => Some(88200),
SampleRateSpec::Rate176400 => Some(176_400),
SampleRateSpec::Rate192000 => Some(192_000),
SampleRateSpec::Rate8000 => Some(8000),
SampleRateSpec::Rate16000 => Some(16000),
SampleRateSpec::Rate22050 => Some(22050),
SampleRateSpec::Rate24000 => Some(24000),
SampleRateSpec::Rate32000 => Some(32000),
SampleRateSpec::Rate44100 => Some(44100),
SampleRateSpec::Rate48000 => Some(48000),
SampleRateSpec::Rate96000 => Some(96000),
_ => None,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct FrameHeader {
pub blocking_strategy: BlockingStrategy,
pub block_size: u32,
pub sample_rate: u32,
pub channel_assignment: ChannelAssignment,
pub sample_size: SampleSize,
pub bits_per_sample: u8,
pub frame_number: Option<u32>,
pub sample_number: Option<u64>,
pub crc8: u8,
}
impl FrameHeader {
pub const MAX_BLOCK_SIZE: u32 = 65535;
pub const MIN_BLOCK_SIZE: u32 = 16;
#[allow(clippy::too_many_lines)]
pub fn parse(data: &[u8], streaminfo_bps: u8) -> Result<(Self, usize), AudioError> {
if data.len() < 4 {
return Err(AudioError::InvalidData("Frame header too short".into()));
}
let sync = u16::from_be_bytes([data[0], data[1]]) >> 2;
if sync != SYNC_CODE {
return Err(AudioError::InvalidData("Invalid sync code".into()));
}
if (data[1] & 0x02) != 0 {
return Err(AudioError::InvalidData("Reserved bit set".into()));
}
let blocking_strategy = BlockingStrategy::from_bit((data[1] & 0x01) != 0);
let block_size_spec = BlockSize::from_value((data[2] >> 4) & 0x0F);
let sample_rate_spec = SampleRateSpec::from_value(data[2] & 0x0F);
let channel_assignment = ChannelAssignment::from_value((data[3] >> 4) & 0x0F)
.ok_or_else(|| AudioError::InvalidData("Invalid channel assignment".into()))?;
let sample_size = SampleSize::from_value((data[3] >> 1) & 0x07)
.ok_or_else(|| AudioError::InvalidData("Invalid sample size".into()))?;
if (data[3] & 0x01) != 0 {
return Err(AudioError::InvalidData("Reserved bit set".into()));
}
let mut offset = 4;
let (frame_number, sample_number) = if blocking_strategy == BlockingStrategy::Fixed {
let (num, bytes) = Self::parse_utf8_u32(&data[offset..])?;
offset += bytes;
(Some(num), None)
} else {
let (num, bytes) = Self::parse_utf8_u64(&data[offset..])?;
offset += bytes;
(None, Some(num))
};
let block_size = if let Some(size) = block_size_spec.fixed_size() {
size
} else {
match block_size_spec {
BlockSize::GetFromEnd8Bit => {
if offset >= data.len() {
return Err(AudioError::InvalidData("Missing block size byte".into()));
}
let size = u32::from(data[offset]) + 1;
offset += 1;
size
}
BlockSize::GetFromEnd16Bit => {
if offset + 1 >= data.len() {
return Err(AudioError::InvalidData("Missing block size bytes".into()));
}
let size = u32::from(u16::from_be_bytes([data[offset], data[offset + 1]])) + 1;
offset += 2;
size
}
_ => {
return Err(AudioError::InvalidData("Invalid block size".into()));
}
}
};
let sample_rate = if let Some(rate) = sample_rate_spec.fixed_rate() {
rate
} else {
match sample_rate_spec {
SampleRateSpec::GetFromEnd8BitKHz => {
if offset >= data.len() {
return Err(AudioError::InvalidData("Missing sample rate byte".into()));
}
let rate = u32::from(data[offset]) * 1000;
offset += 1;
rate
}
SampleRateSpec::GetFromEnd16BitHz => {
if offset + 1 >= data.len() {
return Err(AudioError::InvalidData("Missing sample rate bytes".into()));
}
let rate = u32::from(u16::from_be_bytes([data[offset], data[offset + 1]]));
offset += 2;
rate
}
SampleRateSpec::GetFromEnd16BitTensHz => {
if offset + 1 >= data.len() {
return Err(AudioError::InvalidData("Missing sample rate bytes".into()));
}
let rate = u32::from(u16::from_be_bytes([data[offset], data[offset + 1]])) * 10;
offset += 2;
rate
}
SampleRateSpec::Invalid => {
return Err(AudioError::InvalidData("Invalid sample rate".into()));
}
_ => 0,
}
};
if offset >= data.len() {
return Err(AudioError::InvalidData("Missing CRC-8".into()));
}
let crc8 = data[offset];
offset += 1;
let bits_per_sample = sample_size.bits().unwrap_or(streaminfo_bps);
Ok((
Self {
blocking_strategy,
block_size,
sample_rate,
channel_assignment,
sample_size,
bits_per_sample,
frame_number,
sample_number,
crc8,
},
offset,
))
}
fn parse_utf8_u32(data: &[u8]) -> Result<(u32, usize), AudioError> {
if data.is_empty() {
return Err(AudioError::InvalidData("Empty UTF-8 data".into()));
}
let first = data[0];
let (value, bytes) = if first & 0x80 == 0 {
(u32::from(first), 1)
} else if first & 0xE0 == 0xC0 {
if data.len() < 2 {
return Err(AudioError::InvalidData("Truncated UTF-8".into()));
}
let v = ((u32::from(first) & 0x1F) << 6) | (u32::from(data[1]) & 0x3F);
(v, 2)
} else if first & 0xF0 == 0xE0 {
if data.len() < 3 {
return Err(AudioError::InvalidData("Truncated UTF-8".into()));
}
let v = ((u32::from(first) & 0x0F) << 12)
| ((u32::from(data[1]) & 0x3F) << 6)
| (u32::from(data[2]) & 0x3F);
(v, 3)
} else if first & 0xF8 == 0xF0 {
if data.len() < 4 {
return Err(AudioError::InvalidData("Truncated UTF-8".into()));
}
let v = ((u32::from(first) & 0x07) << 18)
| ((u32::from(data[1]) & 0x3F) << 12)
| ((u32::from(data[2]) & 0x3F) << 6)
| (u32::from(data[3]) & 0x3F);
(v, 4)
} else {
return Err(AudioError::InvalidData("Invalid UTF-8 lead byte".into()));
};
Ok((value, bytes))
}
fn parse_utf8_u64(data: &[u8]) -> Result<(u64, usize), AudioError> {
if data.is_empty() {
return Err(AudioError::InvalidData("Empty UTF-8 data".into()));
}
let first = data[0];
let leading_ones = first.leading_ones() as usize;
let bytes = if leading_ones == 0 {
1
} else {
leading_ones.min(7)
};
if data.len() < bytes {
return Err(AudioError::InvalidData("Truncated UTF-8".into()));
}
let mut value = u64::from(first & (0xFF >> (leading_ones + 1)));
for byte in data.iter().take(bytes).skip(1) {
value = (value << 6) | u64::from(byte & 0x3F);
}
Ok((value, bytes))
}
#[must_use]
pub fn channels(&self) -> u8 {
self.channel_assignment.channels()
}
}
#[derive(Debug, Clone, Default)]
#[allow(dead_code)]
pub struct FlacFrame {
pub header: FrameHeader,
pub samples: Vec<Vec<i32>>,
pub crc16: u16,
}
impl FlacFrame {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_header(header: FrameHeader) -> Self {
let channels = header.channels() as usize;
Self {
header,
samples: vec![Vec::new(); channels],
crc16: 0,
}
}
#[must_use]
pub fn sample_count(&self) -> usize {
self.header.block_size as usize
}
#[must_use]
pub fn channel_count(&self) -> usize {
self.header.channels() as usize
}
pub fn apply_decorrelation(&mut self) {
if self.samples.len() != 2 {
return;
}
let block_size = self.sample_count();
if self.samples[0].len() != block_size || self.samples[1].len() != block_size {
return;
}
match self.header.channel_assignment {
ChannelAssignment::LeftSide => {
for i in 0..block_size {
let left = self.samples[0][i];
let side = self.samples[1][i];
self.samples[1][i] = left - side;
}
}
ChannelAssignment::RightSide => {
for i in 0..block_size {
let side = self.samples[0][i];
let right = self.samples[1][i];
self.samples[0][i] = side + right;
}
}
ChannelAssignment::MidSide => {
for i in 0..block_size {
let mid = self.samples[0][i];
let side = self.samples[1][i];
let left = mid + (side >> 1) + (side & 1);
let right = mid - (side >> 1);
self.samples[0][i] = left;
self.samples[1][i] = right;
}
}
ChannelAssignment::Independent(_) => {
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_blocking_strategy() {
assert_eq!(BlockingStrategy::from_bit(false), BlockingStrategy::Fixed);
assert_eq!(BlockingStrategy::from_bit(true), BlockingStrategy::Variable);
}
#[test]
fn test_channel_assignment() {
assert_eq!(
ChannelAssignment::from_value(0),
Some(ChannelAssignment::Independent(1))
);
assert_eq!(
ChannelAssignment::from_value(1),
Some(ChannelAssignment::Independent(2))
);
assert_eq!(
ChannelAssignment::from_value(7),
Some(ChannelAssignment::Independent(8))
);
assert_eq!(
ChannelAssignment::from_value(8),
Some(ChannelAssignment::LeftSide)
);
assert_eq!(
ChannelAssignment::from_value(9),
Some(ChannelAssignment::RightSide)
);
assert_eq!(
ChannelAssignment::from_value(10),
Some(ChannelAssignment::MidSide)
);
assert_eq!(ChannelAssignment::from_value(11), None);
}
#[test]
fn test_channel_assignment_channels() {
assert_eq!(ChannelAssignment::Independent(1).channels(), 1);
assert_eq!(ChannelAssignment::Independent(6).channels(), 6);
assert_eq!(ChannelAssignment::LeftSide.channels(), 2);
assert_eq!(ChannelAssignment::MidSide.channels(), 2);
}
#[test]
fn test_sample_size() {
assert_eq!(SampleSize::from_value(0), Some(SampleSize::FromStreamInfo));
assert_eq!(SampleSize::from_value(1), Some(SampleSize::Bits8));
assert_eq!(SampleSize::from_value(4), Some(SampleSize::Bits16));
assert_eq!(SampleSize::from_value(6), Some(SampleSize::Bits24));
assert_eq!(SampleSize::from_value(3), None); }
#[test]
fn test_sample_size_bits() {
assert_eq!(SampleSize::Bits8.bits(), Some(8));
assert_eq!(SampleSize::Bits16.bits(), Some(16));
assert_eq!(SampleSize::Bits24.bits(), Some(24));
assert_eq!(SampleSize::FromStreamInfo.bits(), None);
}
#[test]
fn test_block_size() {
assert_eq!(BlockSize::from_value(1).fixed_size(), Some(192));
assert_eq!(BlockSize::from_value(2).fixed_size(), Some(576));
assert_eq!(BlockSize::from_value(8).fixed_size(), Some(256));
assert!(BlockSize::from_value(6).needs_extra_bytes());
assert!(BlockSize::from_value(7).needs_extra_bytes());
}
#[test]
fn test_sample_rate_spec() {
assert_eq!(SampleRateSpec::from_value(9).fixed_rate(), Some(44100));
assert_eq!(SampleRateSpec::from_value(10).fixed_rate(), Some(48000));
assert_eq!(SampleRateSpec::from_value(11).fixed_rate(), Some(96000));
assert_eq!(SampleRateSpec::from_value(0).fixed_rate(), None);
}
#[test]
fn test_flac_frame() {
let header = FrameHeader {
block_size: 4096,
channel_assignment: ChannelAssignment::Independent(2),
bits_per_sample: 16,
..Default::default()
};
let frame = FlacFrame::with_header(header);
assert_eq!(frame.sample_count(), 4096);
assert_eq!(frame.channel_count(), 2);
}
#[test]
fn test_stereo_decorrelation_side_channel() {
assert_eq!(ChannelAssignment::LeftSide.side_channel(), Some(1));
assert_eq!(ChannelAssignment::RightSide.side_channel(), Some(0));
assert_eq!(ChannelAssignment::MidSide.side_channel(), Some(1));
assert_eq!(ChannelAssignment::Independent(2).side_channel(), None);
}
}