snapcast_proto/
sample_format.rs1use std::fmt;
8use std::str::FromStr;
9
10use thiserror::Error;
11
12#[derive(Debug, Error)]
14pub enum SampleFormatError {
15 #[error("sample format must be <rate>:<bits>:<channels>, got {0:?}")]
17 InvalidFormat(String),
18 #[error("invalid number in sample format: {0}")]
20 InvalidNumber(#[from] std::num::ParseIntError),
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub struct SampleFormat {
26 rate: u32,
27 bits: u16,
28 channels: u16,
29}
30
31impl SampleFormat {
32 pub const fn new(rate: u32, bits: u16, channels: u16) -> Self {
34 Self {
35 rate,
36 bits,
37 channels,
38 }
39 }
40
41 pub fn rate(&self) -> u32 {
43 self.rate
44 }
45
46 pub fn bits(&self) -> u16 {
48 self.bits
49 }
50
51 pub fn channels(&self) -> u16 {
53 self.channels
54 }
55
56 pub fn sample_size(&self) -> u16 {
60 if self.bits == 24 { 4 } else { self.bits / 8 }
61 }
62
63 pub fn frame_size(&self) -> u16 {
65 self.channels * self.sample_size()
66 }
67
68 pub fn ms_rate(&self) -> f64 {
70 f64::from(self.rate) / 1000.0
71 }
72
73 pub fn frames_to_ms(&self, frames: usize) -> f64 {
75 frames as f64 * 1000.0 / f64::from(self.rate)
76 }
77
78 pub fn is_initialized(&self) -> bool {
80 self.rate != 0 || self.bits != 0 || self.channels != 0
81 }
82}
83
84impl FromStr for SampleFormat {
85 type Err = SampleFormatError;
86
87 fn from_str(s: &str) -> Result<Self, Self::Err> {
89 let parts: Vec<&str> = s.split(':').collect();
90 if parts.len() != 3 {
91 return Err(SampleFormatError::InvalidFormat(s.to_string()));
92 }
93 let parse = |p: &str| -> Result<u32, SampleFormatError> {
94 if p == "*" { Ok(0) } else { Ok(p.parse()?) }
95 };
96 let rate = parse(parts[0])?;
97 let bits = parse(parts[1])? as u16;
98 let channels = parse(parts[2])? as u16;
99 Ok(Self::new(rate, bits, channels))
100 }
101}
102
103impl Default for SampleFormat {
104 fn default() -> Self {
105 Self::new(0, 0, 0)
106 }
107}
108
109impl fmt::Display for SampleFormat {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 write!(f, "{}:{}:{}", self.rate, self.bits, self.channels)
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn parse_standard_format() {
121 let sf: SampleFormat = "48000:16:2".parse().unwrap();
122 assert_eq!(sf.rate(), 48000);
123 assert_eq!(sf.bits(), 16);
124 assert_eq!(sf.channels(), 2);
125 }
126
127 #[test]
128 fn parse_wildcard_channels() {
129 let sf: SampleFormat = "44100:16:*".parse().unwrap();
130 assert_eq!(sf.rate(), 44100);
131 assert_eq!(sf.bits(), 16);
132 assert_eq!(sf.channels(), 0);
133 }
134
135 #[test]
136 fn parse_all_wildcards() {
137 let sf: SampleFormat = "*:*:*".parse().unwrap();
138 assert_eq!(sf, SampleFormat::default());
139 assert!(!sf.is_initialized());
140 }
141
142 #[test]
143 fn parse_invalid_format() {
144 assert!("48000:16".parse::<SampleFormat>().is_err());
145 assert!("48000:16:2:1".parse::<SampleFormat>().is_err());
146 assert!("".parse::<SampleFormat>().is_err());
147 }
148
149 #[test]
150 fn sample_size_16bit() {
151 let sf = SampleFormat::new(48000, 16, 2);
152 assert_eq!(sf.sample_size(), 2);
153 }
154
155 #[test]
156 fn sample_size_24bit_padded_to_4() {
157 let sf = SampleFormat::new(48000, 24, 2);
159 assert_eq!(sf.sample_size(), 4);
160 }
161
162 #[test]
163 fn sample_size_32bit() {
164 let sf = SampleFormat::new(48000, 32, 2);
165 assert_eq!(sf.sample_size(), 4);
166 }
167
168 #[test]
169 fn frame_size_stereo_16bit() {
170 let sf = SampleFormat::new(48000, 16, 2);
172 assert_eq!(sf.frame_size(), 4);
173 }
174
175 #[test]
176 fn frame_size_stereo_24bit() {
177 let sf = SampleFormat::new(48000, 24, 2);
179 assert_eq!(sf.frame_size(), 8);
180 }
181
182 #[test]
183 fn ms_rate() {
184 let sf = SampleFormat::new(48000, 16, 2);
185 assert!((sf.ms_rate() - 48.0).abs() < f64::EPSILON);
186 }
187
188 #[test]
189 fn display_format() {
190 let sf = SampleFormat::new(48000, 16, 2);
191 assert_eq!(sf.to_string(), "48000:16:2");
192 }
193
194 #[test]
195 fn round_trip_string() {
196 let original = SampleFormat::new(44100, 24, 6);
197 let s = original.to_string();
198 let parsed: SampleFormat = s.parse().unwrap();
199 assert_eq!(original, parsed);
200 }
201}