1use std::borrow::Cow;
2
3use crate::AudioError;
4
5#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum AudioFormat {
17 U8,
18 S16,
19 S32,
20 #[default]
22 F32,
23 U8Planar,
24 S16Planar,
25 S32Planar,
26 F32Planar,
27}
28
29impl AudioFormat {
30 pub fn bytes_per_sample(self) -> usize {
32 match self {
33 Self::U8 | Self::U8Planar => 1,
34 Self::S16 | Self::S16Planar => 2,
35 Self::S32 | Self::S32Planar | Self::F32 | Self::F32Planar => 4,
36 }
37 }
38
39 pub fn is_planar(self) -> bool {
41 matches!(
42 self,
43 Self::U8Planar | Self::S16Planar | Self::S32Planar | Self::F32Planar
44 )
45 }
46
47 pub fn is_float(self) -> bool {
49 matches!(self, Self::F32 | Self::F32Planar)
50 }
51
52 pub fn as_interleaved_f32<'a>(self, data: &'a [u8], channels: u32) -> Result<Cow<'a, [f32]>, AudioError> {
58 let channels = channels as usize;
59 if channels == 0 {
60 return Err(AudioError::Unsupported("channel count must be > 0".into()));
61 }
62
63 let bps = self.bytes_per_sample();
64 if data.len() % (bps * channels) != 0 {
65 return Err(AudioError::Misaligned {
66 got: data.len(),
67 expected: data.len().next_multiple_of(bps * channels),
68 });
69 }
70
71 if self == Self::F32 {
73 let (head, body, tail) = unsafe { data.align_to::<f32>() };
74 if head.is_empty() && tail.is_empty() {
75 return Ok(Cow::Borrowed(body));
76 }
77 }
78
79 Ok(Cow::Owned(self.to_interleaved_f32_owned(data, channels)))
80 }
81
82 fn to_interleaved_f32_owned(self, data: &[u8], channels: usize) -> Vec<f32> {
83 let total_samples = data.len() / self.bytes_per_sample();
84 let frames = total_samples / channels;
85 let mut out = vec![0.0f32; total_samples];
86
87 match self {
88 Self::F32 => {
89 for (i, chunk) in data.chunks_exact(4).enumerate() {
90 out[i] = f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
91 }
92 }
93 Self::F32Planar => {
94 for ch in 0..channels {
95 let plane = &data[ch * frames * 4..(ch + 1) * frames * 4];
96 for (frame, chunk) in plane.chunks_exact(4).enumerate() {
97 out[frame * channels + ch] = f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
98 }
99 }
100 }
101 Self::S16 => {
102 for (i, chunk) in data.chunks_exact(2).enumerate() {
103 let v = i16::from_le_bytes([chunk[0], chunk[1]]);
104 out[i] = (v as f32) / 32768.0;
105 }
106 }
107 Self::S16Planar => {
108 for ch in 0..channels {
109 let plane = &data[ch * frames * 2..(ch + 1) * frames * 2];
110 for (frame, chunk) in plane.chunks_exact(2).enumerate() {
111 let v = i16::from_le_bytes([chunk[0], chunk[1]]);
112 out[frame * channels + ch] = (v as f32) / 32768.0;
113 }
114 }
115 }
116 Self::S32 => {
117 for (i, chunk) in data.chunks_exact(4).enumerate() {
118 let v = i32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
119 out[i] = (v as f32) / (i32::MAX as f32 + 1.0);
120 }
121 }
122 Self::S32Planar => {
123 for ch in 0..channels {
124 let plane = &data[ch * frames * 4..(ch + 1) * frames * 4];
125 for (frame, chunk) in plane.chunks_exact(4).enumerate() {
126 let v = i32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
127 out[frame * channels + ch] = (v as f32) / (i32::MAX as f32 + 1.0);
128 }
129 }
130 }
131 Self::U8 => {
132 for (i, &b) in data.iter().enumerate() {
133 out[i] = (b as f32 - 128.0) / 128.0;
134 }
135 }
136 Self::U8Planar => {
137 for ch in 0..channels {
138 let plane = &data[ch * frames..(ch + 1) * frames];
139 for (frame, &b) in plane.iter().enumerate() {
140 out[frame * channels + ch] = (b as f32 - 128.0) / 128.0;
141 }
142 }
143 }
144 }
145
146 out
147 }
148
149 pub fn from_interleaved_f32(self, samples: &[f32], channels: u32) -> Result<Vec<u8>, AudioError> {
153 let channels = channels as usize;
154 if channels == 0 {
155 return Err(AudioError::Unsupported("channel count must be > 0".into()));
156 }
157 if samples.len() % channels != 0 {
158 return Err(AudioError::Misaligned {
159 got: samples.len(),
160 expected: samples.len().next_multiple_of(channels),
161 });
162 }
163
164 let frames = samples.len() / channels;
165 let mut out = vec![0u8; samples.len() * self.bytes_per_sample()];
166
167 match self {
168 Self::F32 => {
169 for (i, &s) in samples.iter().enumerate() {
170 out[i * 4..i * 4 + 4].copy_from_slice(&s.to_le_bytes());
171 }
172 }
173 Self::F32Planar => {
174 for ch in 0..channels {
175 let plane = &mut out[ch * frames * 4..(ch + 1) * frames * 4];
176 for (frame, chunk) in plane.chunks_exact_mut(4).enumerate() {
177 chunk.copy_from_slice(&samples[frame * channels + ch].to_le_bytes());
178 }
179 }
180 }
181 Self::S16 => {
182 for (i, &s) in samples.iter().enumerate() {
183 let v = (s.clamp(-1.0, 1.0) * 32767.0).round() as i16;
184 out[i * 2..i * 2 + 2].copy_from_slice(&v.to_le_bytes());
185 }
186 }
187 Self::S16Planar => {
188 for ch in 0..channels {
189 let plane = &mut out[ch * frames * 2..(ch + 1) * frames * 2];
190 for (frame, chunk) in plane.chunks_exact_mut(2).enumerate() {
191 let v = (samples[frame * channels + ch].clamp(-1.0, 1.0) * 32767.0).round() as i16;
192 chunk.copy_from_slice(&v.to_le_bytes());
193 }
194 }
195 }
196 Self::S32 => {
197 for (i, &s) in samples.iter().enumerate() {
198 let v = (s.clamp(-1.0, 1.0) as f64 * i32::MAX as f64).round() as i32;
199 out[i * 4..i * 4 + 4].copy_from_slice(&v.to_le_bytes());
200 }
201 }
202 Self::S32Planar => {
203 for ch in 0..channels {
204 let plane = &mut out[ch * frames * 4..(ch + 1) * frames * 4];
205 for (frame, chunk) in plane.chunks_exact_mut(4).enumerate() {
206 let v =
207 (samples[frame * channels + ch].clamp(-1.0, 1.0) as f64 * i32::MAX as f64).round() as i32;
208 chunk.copy_from_slice(&v.to_le_bytes());
209 }
210 }
211 }
212 Self::U8 => {
213 for (i, &s) in samples.iter().enumerate() {
214 out[i] = ((s.clamp(-1.0, 1.0) * 127.0).round() + 128.0) as u8;
215 }
216 }
217 Self::U8Planar => {
218 for ch in 0..channels {
219 let plane = &mut out[ch * frames..(ch + 1) * frames];
220 for (frame, byte) in plane.iter_mut().enumerate() {
221 *byte = ((samples[frame * channels + ch].clamp(-1.0, 1.0) * 127.0).round() + 128.0) as u8;
222 }
223 }
224 }
225 }
226
227 Ok(out)
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn f32_interleaved_is_borrowed() {
237 let samples: Vec<f32> = vec![0.1, 0.2, 0.3, 0.4];
238 let bytes: Vec<u8> = samples.iter().flat_map(|s| s.to_le_bytes()).collect();
239 let cow = AudioFormat::F32.as_interleaved_f32(&bytes, 2).unwrap();
240 assert!(matches!(cow, Cow::Borrowed(_)));
241 assert_eq!(cow.as_ref(), samples.as_slice());
242 }
243
244 #[test]
245 fn s16_interleaved_is_owned_but_close() {
246 let samples = vec![-1.0_f32, -0.5, 0.0, 0.5, 0.9999];
247 let bytes = AudioFormat::S16.from_interleaved_f32(&samples, 1).unwrap();
248 let cow = AudioFormat::S16.as_interleaved_f32(&bytes, 1).unwrap();
249 assert!(matches!(cow, Cow::Owned(_)));
250 for (a, b) in samples.iter().zip(cow.iter()) {
251 assert!((a - b).abs() < 1.0 / 32767.0, "{a} vs {b}");
252 }
253 }
254
255 #[test]
256 fn planar_to_interleaved_orders_correctly() {
257 let planar: Vec<f32> = vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6];
258 let bytes: Vec<u8> = planar.iter().flat_map(|s| s.to_le_bytes()).collect();
259 let cow = AudioFormat::F32Planar.as_interleaved_f32(&bytes, 2).unwrap();
260 assert_eq!(cow.as_ref(), &[0.1, 0.4, 0.2, 0.5, 0.3, 0.6]);
261 }
262
263 #[test]
264 fn s16_clamps_out_of_range() {
265 let samples = vec![2.0_f32, -3.0];
266 let bytes = AudioFormat::S16.from_interleaved_f32(&samples, 1).unwrap();
267 let cow = AudioFormat::S16.as_interleaved_f32(&bytes, 1).unwrap();
268 assert!((cow[0] - 0.99997).abs() < 1e-4);
269 assert!((cow[1] + 1.0).abs() < 1e-4);
270 }
271
272 #[test]
273 fn rejects_misaligned_buffer() {
274 let result = AudioFormat::S16.as_interleaved_f32(&[0u8; 5], 2);
275 assert!(matches!(result, Err(AudioError::Misaligned { .. })));
276 }
277}