#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AudioCodec {
Pcm,
Mp3,
Aac,
Opus,
Vorbis,
Flac,
Ac3,
Eac3,
Dts,
TrueHd,
Alac,
Wma,
}
#[derive(Debug, Clone)]
pub struct CodecInfo {
pub codec: AudioCodec,
pub name: &'static str,
pub lossy: bool,
pub max_bitrate_kbps: u32,
pub max_channels: u8,
pub max_sample_rate: u32,
}
pub fn codec_info(codec: AudioCodec) -> CodecInfo {
match codec {
AudioCodec::Pcm => CodecInfo {
codec,
name: "PCM",
lossy: false,
max_bitrate_kbps: 4608,
max_channels: 32,
max_sample_rate: 192_000,
},
AudioCodec::Mp3 => CodecInfo {
codec,
name: "MP3",
lossy: true,
max_bitrate_kbps: 320,
max_channels: 2,
max_sample_rate: 48_000,
},
AudioCodec::Aac => CodecInfo {
codec,
name: "AAC",
lossy: true,
max_bitrate_kbps: 512,
max_channels: 48,
max_sample_rate: 96_000,
},
AudioCodec::Opus => CodecInfo {
codec,
name: "Opus",
lossy: true,
max_bitrate_kbps: 510,
max_channels: 255,
max_sample_rate: 48_000,
},
AudioCodec::Vorbis => CodecInfo {
codec,
name: "Vorbis",
lossy: true,
max_bitrate_kbps: 500,
max_channels: 255,
max_sample_rate: 192_000,
},
AudioCodec::Flac => CodecInfo {
codec,
name: "FLAC",
lossy: false,
max_bitrate_kbps: 4608,
max_channels: 8,
max_sample_rate: 655_350,
},
AudioCodec::Ac3 => CodecInfo {
codec,
name: "AC-3",
lossy: true,
max_bitrate_kbps: 640,
max_channels: 6,
max_sample_rate: 48_000,
},
AudioCodec::Eac3 => CodecInfo {
codec,
name: "E-AC-3",
lossy: true,
max_bitrate_kbps: 6144,
max_channels: 16,
max_sample_rate: 48_000,
},
AudioCodec::Dts => CodecInfo {
codec,
name: "DTS",
lossy: true,
max_bitrate_kbps: 1509,
max_channels: 8,
max_sample_rate: 96_000,
},
AudioCodec::TrueHd => CodecInfo {
codec,
name: "TrueHD",
lossy: false,
max_bitrate_kbps: 18_000,
max_channels: 14,
max_sample_rate: 192_000,
},
AudioCodec::Alac => CodecInfo {
codec,
name: "ALAC",
lossy: false,
max_bitrate_kbps: 4608,
max_channels: 8,
max_sample_rate: 384_000,
},
AudioCodec::Wma => CodecInfo {
codec,
name: "WMA",
lossy: true,
max_bitrate_kbps: 768,
max_channels: 8,
max_sample_rate: 96_000,
},
}
}
pub fn is_lossless(codec: AudioCodec) -> bool {
!codec_info(codec).lossy
}
pub fn supports_channels(codec: AudioCodec, channels: u8) -> bool {
codec_info(codec).max_channels >= channels
}
pub struct CodecRegistry {
codecs: Vec<CodecInfo>,
}
impl CodecRegistry {
pub fn new() -> Self {
let all = [
AudioCodec::Pcm,
AudioCodec::Mp3,
AudioCodec::Aac,
AudioCodec::Opus,
AudioCodec::Vorbis,
AudioCodec::Flac,
AudioCodec::Ac3,
AudioCodec::Eac3,
AudioCodec::Dts,
AudioCodec::TrueHd,
AudioCodec::Alac,
AudioCodec::Wma,
];
Self {
codecs: all.iter().map(|c| codec_info(*c)).collect(),
}
}
pub fn by_name(&self, name: &str) -> Option<&CodecInfo> {
let lower = name.to_lowercase();
self.codecs.iter().find(|c| c.name.to_lowercase() == lower)
}
pub fn lossless_codecs(&self) -> Vec<&CodecInfo> {
self.codecs.iter().filter(|c| !c.lossy).collect()
}
pub fn codecs_for_bitrate(&self, kbps: u32) -> Vec<&CodecInfo> {
self.codecs
.iter()
.filter(|c| c.max_bitrate_kbps >= kbps)
.collect()
}
pub fn all(&self) -> &[CodecInfo] {
&self.codecs
}
}
impl Default for CodecRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pcm_is_lossless() {
assert!(is_lossless(AudioCodec::Pcm));
}
#[test]
fn test_mp3_is_lossy() {
assert!(!is_lossless(AudioCodec::Mp3));
}
#[test]
fn test_flac_is_lossless() {
assert!(is_lossless(AudioCodec::Flac));
}
#[test]
fn test_truehd_is_lossless() {
assert!(is_lossless(AudioCodec::TrueHd));
}
#[test]
fn test_alac_is_lossless() {
assert!(is_lossless(AudioCodec::Alac));
}
#[test]
fn test_supports_channels_opus() {
assert!(supports_channels(AudioCodec::Opus, 8));
assert!(supports_channels(AudioCodec::Opus, 255));
}
#[test]
fn test_mp3_max_two_channels() {
assert!(supports_channels(AudioCodec::Mp3, 2));
assert!(!supports_channels(AudioCodec::Mp3, 3));
}
#[test]
fn test_registry_by_name() {
let reg = CodecRegistry::new();
let info = reg.by_name("opus").unwrap();
assert_eq!(info.name, "Opus");
}
#[test]
fn test_registry_by_name_case_insensitive() {
let reg = CodecRegistry::new();
assert!(reg.by_name("FLAC").is_some());
assert!(reg.by_name("flac").is_some());
}
#[test]
fn test_registry_by_name_missing() {
let reg = CodecRegistry::new();
assert!(reg.by_name("nonexistent").is_none());
}
#[test]
fn test_lossless_codecs_count() {
let reg = CodecRegistry::new();
let lossless = reg.lossless_codecs();
assert_eq!(lossless.len(), 4);
}
#[test]
fn test_codecs_for_bitrate_high() {
let reg = CodecRegistry::new();
let result = reg.codecs_for_bitrate(5000);
assert!(!result.is_empty());
for c in &result {
assert!(c.max_bitrate_kbps >= 5000);
}
}
#[test]
fn test_codec_info_name_pcm() {
let info = codec_info(AudioCodec::Pcm);
assert_eq!(info.name, "PCM");
}
#[test]
fn test_codec_info_aac_bitrate() {
let info = codec_info(AudioCodec::Aac);
assert_eq!(info.max_bitrate_kbps, 512);
}
#[test]
fn test_registry_all_count() {
let reg = CodecRegistry::new();
assert_eq!(reg.all().len(), 12);
}
}