use enum_primitive_derive::Primitive;
use num_traits::Euclid;
use super::*;
use crate::protocol::ProtocolError;
pub const MAX_RATE: u32 = 48000 * 16;
pub const MAX_CHANNELS: u8 = 32;
#[derive(Debug, Copy, Clone, Primitive, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum SampleFormat {
#[default]
Invalid = u8::MAX,
U8 = 0,
Alaw = 1,
Ulaw = 2,
S16Le = 3,
S16Be = 4,
Float32Le = 5,
Float32Be = 6,
S32Le = 7,
S32Be = 8,
S24Le = 9,
S24Be = 10,
S24In32Le = 11,
S24In32Be = 12,
}
impl SampleFormat {
pub fn bytes_per_sample(&self) -> usize {
match self {
SampleFormat::Invalid => 0,
SampleFormat::U8 => 1,
SampleFormat::Alaw => 1,
SampleFormat::Ulaw => 1,
SampleFormat::S16Le => 2,
SampleFormat::S16Be => 2,
SampleFormat::Float32Le => 4,
SampleFormat::Float32Be => 4,
SampleFormat::S32Le => 4,
SampleFormat::S32Be => 4,
SampleFormat::S24Le => 3,
SampleFormat::S24Be => 3,
SampleFormat::S24In32Le => 4,
SampleFormat::S24In32Be => 4,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SampleSpec {
pub format: SampleFormat,
pub channels: u8,
pub sample_rate: u32,
}
impl SampleSpec {
pub fn bytes_to_duration(&self, len: usize) -> time::Duration {
let frames = len / self.format.bytes_per_sample() / self.channels as usize;
let (secs, rem) = (frames as u32).div_rem_euclid(&self.sample_rate);
const NANOS_PER_SECOND: u64 = 1_000_000_000;
let nanos = (rem as u64 * NANOS_PER_SECOND) / self.sample_rate as u64;
time::Duration::new(secs as u64, nanos as u32)
}
pub fn protocol_downgrade(self, protocol_version: u16) -> SampleSpec {
use self::SampleFormat::*;
let mut fixed = self;
if protocol_version < 15 {
match fixed.format {
S24Le | S24In32Le => fixed.format = Float32Le,
S24Be | S24In32Be => fixed.format = Float32Be,
_ => {} }
}
fixed
}
}
impl Default for SampleSpec {
fn default() -> Self {
Self {
format: SampleFormat::default(),
channels: 1,
sample_rate: 44100,
}
}
}
impl TagStructRead for SampleSpec {
fn read(ts: &mut TagStructReader<'_>, _protocol_version: u16) -> Result<Self, ProtocolError> {
ts.expect_tag(Tag::SampleSpec)?;
let format = ts.inner.read_u8()?;
let format = SampleFormat::from_u8(format)
.ok_or_else(|| ProtocolError::Invalid(format!("invalid sample format {format}")))?;
let channels = ts.inner.read_u8()?;
let sample_rate = ts.inner.read_u32::<NetworkEndian>()?;
Ok(Self {
format,
channels,
sample_rate,
})
}
}
impl TagStructWrite for SampleSpec {
fn write(
&self,
w: &mut TagStructWriter<'_>,
_protocol_version: u16,
) -> Result<(), ProtocolError> {
w.inner.write_u8(Tag::SampleSpec as u8)?;
w.inner.write_u8(self.format as u8)?;
w.inner.write_u8(self.channels)?;
w.inner.write_u32::<NetworkEndian>(self.sample_rate)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::protocol::test_util::test_serde;
use super::*;
#[test]
fn bytes_to_duration() {
let spec = SampleSpec {
format: SampleFormat::S16Le,
channels: 2,
sample_rate: 48000,
};
assert_eq!(spec.bytes_to_duration(48000 * 4).as_millis(), 1000);
assert_eq!(spec.bytes_to_duration(1920).as_millis(), 10);
assert_eq!(spec.bytes_to_duration(usize::MAX).as_millis(), 89478485);
assert_eq!(spec.bytes_to_duration((48000 * 400) - 1).as_millis(), 99999);
}
#[test]
fn sample_spec_serde() -> anyhow::Result<()> {
let spec = SampleSpec {
format: SampleFormat::S16Le,
channels: 2,
sample_rate: 44100,
};
test_serde(&spec)
}
}