Skip to main content

oaat_core/
format.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
4#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
5pub enum AudioFormat {
6    PcmS16le,
7    PcmS24le,
8    PcmS24le4,
9    PcmS32le,
10    PcmF32le,
11    DsdU8,
12    DsdU16le,
13    DsdU32le,
14    Flac,
15    Opus,
16}
17
18impl AudioFormat {
19    pub fn wire_id(self) -> u8 {
20        match self {
21            Self::PcmS16le => 0x01,
22            Self::PcmS24le => 0x02,
23            Self::PcmS24le4 => 0x03,
24            Self::PcmS32le => 0x04,
25            Self::PcmF32le => 0x05,
26            Self::DsdU8 => 0x10,
27            Self::DsdU16le => 0x11,
28            Self::DsdU32le => 0x12,
29            Self::Flac => 0x20,
30            Self::Opus => 0x21,
31        }
32    }
33
34    pub fn from_wire_id(id: u8) -> Option<Self> {
35        match id {
36            0x01 => Some(Self::PcmS16le),
37            0x02 => Some(Self::PcmS24le),
38            0x03 => Some(Self::PcmS24le4),
39            0x04 => Some(Self::PcmS32le),
40            0x05 => Some(Self::PcmF32le),
41            0x10 => Some(Self::DsdU8),
42            0x11 => Some(Self::DsdU16le),
43            0x12 => Some(Self::DsdU32le),
44            0x20 => Some(Self::Flac),
45            0x21 => Some(Self::Opus),
46            _ => None,
47        }
48    }
49
50    pub fn is_pcm(self) -> bool {
51        matches!(
52            self,
53            Self::PcmS16le | Self::PcmS24le | Self::PcmS24le4 | Self::PcmS32le | Self::PcmF32le
54        )
55    }
56
57    pub fn is_dsd(self) -> bool {
58        matches!(self, Self::DsdU8 | Self::DsdU16le | Self::DsdU32le)
59    }
60
61    pub fn is_compressed(self) -> bool {
62        matches!(self, Self::Flac | Self::Opus)
63    }
64
65    pub fn bytes_per_sample(self) -> Option<usize> {
66        match self {
67            Self::PcmS16le => Some(2),
68            Self::PcmS24le => Some(3),
69            Self::PcmS24le4 | Self::PcmS32le | Self::PcmF32le => Some(4),
70            _ => None,
71        }
72    }
73}
74
75impl std::fmt::Display for AudioFormat {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        match self {
78            Self::PcmS16le => write!(f, "PCM_S16LE"),
79            Self::PcmS24le => write!(f, "PCM_S24LE"),
80            Self::PcmS24le4 => write!(f, "PCM_S24LE4"),
81            Self::PcmS32le => write!(f, "PCM_S32LE"),
82            Self::PcmF32le => write!(f, "PCM_F32LE"),
83            Self::DsdU8 => write!(f, "DSD_U8"),
84            Self::DsdU16le => write!(f, "DSD_U16LE"),
85            Self::DsdU32le => write!(f, "DSD_U32LE"),
86            Self::Flac => write!(f, "FLAC"),
87            Self::Opus => write!(f, "OPUS"),
88        }
89    }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
93#[serde(rename_all = "snake_case")]
94pub enum ChannelLayout {
95    Mono,
96    Stereo,
97    #[serde(rename = "2.1")]
98    TwoPointOne,
99    Quad,
100    #[serde(rename = "5.1")]
101    FivePointOne,
102    #[serde(rename = "7.1")]
103    SevenPointOne,
104}
105
106impl ChannelLayout {
107    pub fn channel_count(self) -> u8 {
108        match self {
109            Self::Mono => 1,
110            Self::Stereo => 2,
111            Self::TwoPointOne => 3,
112            Self::Quad => 4,
113            Self::FivePointOne => 6,
114            Self::SevenPointOne => 8,
115        }
116    }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
120pub enum DsdRate {
121    Dsd64 = 64,
122    Dsd128 = 128,
123    Dsd256 = 256,
124    Dsd512 = 512,
125}
126
127impl DsdRate {
128    pub fn bitstream_rate_hz(self) -> u64 {
129        match self {
130            Self::Dsd64 => 2_822_400,
131            Self::Dsd128 => 5_644_800,
132            Self::Dsd256 => 11_289_600,
133            Self::Dsd512 => 22_579_200,
134        }
135    }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139pub enum SampleRateFamily {
140    F44100,
141    F48000,
142}
143
144impl SampleRateFamily {
145    pub fn of(rate: u32) -> Option<Self> {
146        const F441: [u32; 5] = [44100, 88200, 176400, 352800, 705600];
147        const F480: [u32; 5] = [48000, 96000, 192000, 384000, 768000];
148        if F441.contains(&rate) {
149            Some(Self::F44100)
150        } else if F480.contains(&rate) {
151            Some(Self::F48000)
152        } else {
153            None
154        }
155    }
156
157    pub fn rates(self) -> &'static [u32] {
158        match self {
159            Self::F44100 => &[44100, 88200, 176400, 352800, 705600],
160            Self::F48000 => &[48000, 96000, 192000, 384000, 768000],
161        }
162    }
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168
169    #[test]
170    fn wire_id_roundtrip() {
171        for fmt in [
172            AudioFormat::PcmS16le,
173            AudioFormat::PcmS24le,
174            AudioFormat::PcmS24le4,
175            AudioFormat::PcmS32le,
176            AudioFormat::PcmF32le,
177            AudioFormat::DsdU8,
178            AudioFormat::DsdU16le,
179            AudioFormat::DsdU32le,
180            AudioFormat::Flac,
181            AudioFormat::Opus,
182        ] {
183            assert_eq!(AudioFormat::from_wire_id(fmt.wire_id()), Some(fmt));
184        }
185    }
186
187    #[test]
188    fn sample_rate_family() {
189        assert_eq!(SampleRateFamily::of(44100), Some(SampleRateFamily::F44100));
190        assert_eq!(SampleRateFamily::of(192000), Some(SampleRateFamily::F48000));
191        assert_eq!(SampleRateFamily::of(50000), None);
192    }
193}