use serde_with::SerializeDisplay;
use std::{fmt, str::FromStr, time::Duration};
use crate::{error::Error, util::ToF32};
#[derive(Copy, Clone, Default, Eq, PartialEq, SerializeDisplay, Debug, Hash)]
pub enum Codec {
ADTS,
FLAC,
#[default]
MP3,
MP4,
WAV,
}
impl Codec {
const AAC_SAMPLES_PER_FRAME: usize = 1_024;
const FLAC_MAX_SAMPLES_PER_FRAME: usize = 4_608;
const FLAC_MAX_SAMPLES_PER_FRAME_HI_RES: usize = 16_384;
const MP3_SAMPLES_PER_FRAME: usize = 1_152;
const WAV_SAMPLES_PER_FRAME: usize = 1;
#[must_use]
pub fn max_frame_length(&self, sample_rate: u32, channels: u16) -> usize {
match self {
Codec::ADTS | Codec::MP4 => Self::AAC_SAMPLES_PER_FRAME,
Codec::FLAC => {
if sample_rate > 48_000 {
Self::FLAC_MAX_SAMPLES_PER_FRAME_HI_RES
} else {
Self::FLAC_MAX_SAMPLES_PER_FRAME
}
}
Codec::MP3 => Self::MP3_SAMPLES_PER_FRAME,
Codec::WAV => Self::WAV_SAMPLES_PER_FRAME * channels as usize,
}
}
#[must_use]
pub fn max_frame_duration(&self, sample_rate: u32, channels: u16) -> Duration {
let samples = self.max_frame_length(sample_rate, channels).to_f32_lossy();
let span = (samples / sample_rate.to_f32_lossy()).clamp(0.0, Duration::MAX.as_secs_f32());
if span.is_nan() {
Duration::default()
} else {
Duration::from_secs_f32(span)
}
}
#[must_use]
#[inline]
pub fn extension(&self) -> &'static str {
match self {
Codec::ADTS => "aac",
Codec::FLAC => "flac",
Codec::MP3 => "mp3",
Codec::MP4 => "m4a",
Codec::WAV => "wav",
}
}
#[must_use]
#[inline]
pub fn mime_type(&self) -> &'static str {
match self {
Codec::ADTS => "audio/aac",
Codec::FLAC => "audio/flac",
Codec::MP3 => "audio/mpeg",
Codec::MP4 => "audio/mp4",
Codec::WAV => "audio/wav",
}
}
}
impl fmt::Display for Codec {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Codec::ADTS | Codec::MP4 => write!(f, "aac"),
Codec::FLAC => write!(f, "flac"),
Codec::MP3 => write!(f, "mp3"),
Codec::WAV => write!(f, "wav"),
}
}
}
impl FromStr for Codec {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"aac" | "adts" => Ok(Codec::ADTS),
"flac" => Ok(Codec::FLAC),
"mp3" => Ok(Codec::MP3),
"m4a" | "m4b" | "mp4" => Ok(Codec::MP4),
"wav" => Ok(Codec::WAV),
_ => Err(Error::invalid_argument(format!(
"unable to parse codec from {s}",
))),
}
}
}