cpal/sample_format.rs
1//! Audio sample format types and conversions.
2//!
3//! # Byte Order
4//!
5//! All multi-byte sample formats use the native endianness of the target platform.
6//! CPAL handles any necessary conversions when interfacing with hardware that uses
7//! a different byte order.
8
9use std::{
10 fmt::{self, Display},
11 mem,
12};
13
14/// 24-bit signed integer sample type.
15///
16/// Represents 24-bit audio with range `-(1 << 23)..=((1 << 23) - 1)`.
17///
18/// **Note:** While representing 24-bit audio, this format uses 4 bytes (i32) of storage
19/// with the most significant byte unused. Use [`SampleFormat::bits_per_sample`] to get
20/// the actual bit depth (24) vs [`SampleFormat::sample_size`] for storage size (4 bytes).
21pub use dasp_sample::I24;
22/// 24-bit unsigned integer sample type.
23///
24/// Represents 24-bit audio with range `0..=((1 << 24) - 1)`, with origin at `1 << 23 == 8388608`.
25///
26/// **Note:** While representing 24-bit audio, this format uses 4 bytes (u32) of storage
27/// with the most significant byte unused. Use [`SampleFormat::bits_per_sample`] to get
28/// the actual bit depth (24) vs [`SampleFormat::sample_size`] for storage size (4 bytes).
29pub use dasp_sample::U24;
30pub use dasp_sample::{FromSample, Sample};
31#[cfg(all(
32 target_arch = "wasm32",
33 target_os = "unknown",
34 feature = "wasm-bindgen"
35))]
36use wasm_bindgen::prelude::*;
37
38// I48 and U48 are not currently supported by cpal but available in dasp_sample:
39// pub use dasp_sample::{I48, U48};
40
41/// Format that each sample has. Usually, this corresponds to the sampling
42/// depth of the audio source. For example, 16 bit quantized samples can be
43/// encoded in `i16` or `u16`. Note that the quantized sampling depth is not
44/// directly visible for formats where [`is_float`] is true.
45///
46/// Also note that the backend must support the encoding of the quantized
47/// samples in the given format, as there is no generic transformation from one
48/// format into the other done inside the frontend-library code. You can query
49/// the supported formats by using [`supported_input_configs`].
50///
51/// A good rule of thumb is to use [`SampleFormat::I16`] as this covers typical
52/// music (WAV, MP3) as well as typical audio input devices on most platforms,
53///
54/// [`is_float`]: SampleFormat::is_float
55/// [`supported_input_configs`]: crate::traits::DeviceTrait::supported_input_configs
56#[cfg_attr(
57 all(
58 target_arch = "wasm32",
59 target_os = "unknown",
60 feature = "wasm-bindgen"
61 ),
62 wasm_bindgen
63)]
64#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
65#[non_exhaustive]
66pub enum SampleFormat {
67 /// `i8` with a valid range of `i8::MIN..=i8::MAX` with `0` being the origin.
68 I8,
69
70 /// `i16` with a valid range of `i16::MIN..=i16::MAX` with `0` being the origin.
71 I16,
72
73 /// `I24` with a valid range of `-(1 << 23)..=((1 << 23) - 1)` with `0` being the origin.
74 ///
75 /// This format uses 4 bytes of storage but only 24 bits are significant.
76 I24,
77
78 /// `i32` with a valid range of `i32::MIN..=i32::MAX` with `0` being the origin.
79 I32,
80
81 // /// `I48` with a valid range of '-(1 << 47)..(1 << 47)' with `0` being the origin
82 // I48,
83 /// `i64` with a valid range of `i64::MIN..=i64::MAX` with `0` being the origin.
84 I64,
85
86 /// `u8` with a valid range of `u8::MIN..=u8::MAX` with `1 << 7 == 128` being the origin.
87 U8,
88
89 /// `u16` with a valid range of `u16::MIN..=u16::MAX` with `1 << 15 == 32768` being the origin.
90 U16,
91
92 /// `U24` with a valid range of `0..=((1 << 24) - 1)` with `1 << 23 == 8388608` being the origin.
93 ///
94 /// This format uses 4 bytes of storage but only 24 bits are significant.
95 U24,
96
97 /// `u32` with a valid range of `u32::MIN..=u32::MAX` with `1 << 31` being the origin.
98 U32,
99
100 /// `U48` with a valid range of '0..(1 << 48)' with `1 << 47` being the origin
101 // U48,
102
103 /// `u64` with a valid range of `u64::MIN..=u64::MAX` with `1 << 63` being the origin.
104 U64,
105
106 /// `f32` with a valid range of `-1.0..=1.0` with `0.0` being the origin.
107 F32,
108
109 /// `f64` with a valid range of `-1.0..=1.0` with `0.0` being the origin.
110 F64,
111
112 /// DSD 1-bit stream in u8 container (8 bits = 8 DSD samples) with 0x69 being the silence byte pattern.
113 DsdU8,
114
115 /// DSD 1-bit stream in u16 container (16 bits = 16 DSD samples) with 0x69 being the silence byte pattern.
116 DsdU16,
117
118 /// DSD 1-bit stream in u32 container (32 bits = 32 DSD samples) with 0x69 being the silence byte pattern.
119 DsdU32,
120}
121
122impl SampleFormat {
123 /// Returns the size in bytes of a sample of this format. This corresponds to
124 /// the internal size of the rust primitives that are used to represent this
125 /// sample format (e.g., i24 has size of i32).
126 #[inline]
127 #[must_use]
128 pub fn sample_size(&self) -> usize {
129 match *self {
130 SampleFormat::I8 => mem::size_of::<i8>(),
131 SampleFormat::U8 => mem::size_of::<u8>(),
132 SampleFormat::I16 => mem::size_of::<i16>(),
133 SampleFormat::U16 => mem::size_of::<u16>(),
134 SampleFormat::I24 => mem::size_of::<i32>(),
135 SampleFormat::U24 => mem::size_of::<i32>(),
136 SampleFormat::I32 => mem::size_of::<i32>(),
137 SampleFormat::U32 => mem::size_of::<u32>(),
138 // SampleFormat::I48 => mem::size_of::<i64>(),
139 // SampleFormat::U48 => mem::size_of::<i64>(),
140 SampleFormat::I64 => mem::size_of::<i64>(),
141 SampleFormat::U64 => mem::size_of::<u64>(),
142 SampleFormat::F32 => mem::size_of::<f32>(),
143 SampleFormat::F64 => mem::size_of::<f64>(),
144 SampleFormat::DsdU8 => mem::size_of::<u8>(),
145 SampleFormat::DsdU16 => mem::size_of::<u16>(),
146 SampleFormat::DsdU32 => mem::size_of::<u32>(),
147 }
148 }
149
150 /// Returns the number of bits of a sample of this format. Note that this is
151 /// not necessarily the same as the size of the primitive used to represent
152 /// this sample format (e.g., I24 has size of i32 but 24 bits per sample).
153 #[inline]
154 #[must_use]
155 pub fn bits_per_sample(&self) -> u32 {
156 match *self {
157 SampleFormat::I8 => i8::BITS,
158 SampleFormat::U8 => u8::BITS,
159 SampleFormat::I16 => i16::BITS,
160 SampleFormat::U16 => u16::BITS,
161 SampleFormat::I24 => 24,
162 SampleFormat::U24 => 24,
163 SampleFormat::I32 => i32::BITS,
164 SampleFormat::U32 => u32::BITS,
165 // SampleFormat::I48 => 48,
166 // SampleFormat::U48 => 48,
167 SampleFormat::I64 => i64::BITS,
168 SampleFormat::U64 => u64::BITS,
169 SampleFormat::F32 => 32,
170 SampleFormat::F64 => 64,
171 SampleFormat::DsdU8 | SampleFormat::DsdU16 | SampleFormat::DsdU32 => 1,
172 }
173 }
174
175 #[inline]
176 #[must_use]
177 pub fn is_int(&self) -> bool {
178 matches!(
179 *self,
180 SampleFormat::I8
181 | SampleFormat::I16
182 | SampleFormat::I24
183 | SampleFormat::I32
184 // | SampleFormat::I48
185 | SampleFormat::I64
186 )
187 }
188
189 #[inline]
190 #[must_use]
191 pub fn is_uint(&self) -> bool {
192 matches!(
193 *self,
194 SampleFormat::U8
195 | SampleFormat::U16
196 | SampleFormat::U24
197 | SampleFormat::U32
198 // | SampleFormat::U48
199 | SampleFormat::U64
200 )
201 }
202
203 #[inline]
204 #[must_use]
205 pub fn is_float(&self) -> bool {
206 matches!(*self, SampleFormat::F32 | SampleFormat::F64)
207 }
208
209 #[inline]
210 #[must_use]
211 pub fn is_dsd(&self) -> bool {
212 matches!(
213 *self,
214 SampleFormat::DsdU8 | SampleFormat::DsdU16 | SampleFormat::DsdU32
215 )
216 }
217}
218
219impl Display for SampleFormat {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 match *self {
222 SampleFormat::I8 => "i8",
223 SampleFormat::I16 => "i16",
224 SampleFormat::I24 => "i24",
225 SampleFormat::I32 => "i32",
226 // SampleFormat::I48 => "i48",
227 SampleFormat::I64 => "i64",
228 SampleFormat::U8 => "u8",
229 SampleFormat::U16 => "u16",
230 SampleFormat::U24 => "u24",
231 SampleFormat::U32 => "u32",
232 // SampleFormat::U48 => "u48",
233 SampleFormat::U64 => "u64",
234 SampleFormat::F32 => "f32",
235 SampleFormat::F64 => "f64",
236 SampleFormat::DsdU8 => "dsdu8",
237 SampleFormat::DsdU16 => "dsdu16",
238 SampleFormat::DsdU32 => "dsdu32",
239 }
240 .fmt(f)
241 }
242}
243
244/// A [`Sample`] type with a known corresponding [`SampleFormat`].
245///
246/// This trait is automatically implemented for all primitive sample types and provides
247/// a way to determine the [`SampleFormat`] at compile time.
248///
249/// # Example
250///
251/// ```
252/// use cpal::SizedSample;
253///
254/// assert_eq!(i16::FORMAT, cpal::SampleFormat::I16);
255/// assert_eq!(f32::FORMAT, cpal::SampleFormat::F32);
256/// ```
257pub trait SizedSample: Sample {
258 /// The corresponding [`SampleFormat`] for this sample type.
259 const FORMAT: SampleFormat;
260}
261
262impl SizedSample for i8 {
263 const FORMAT: SampleFormat = SampleFormat::I8;
264}
265
266impl SizedSample for i16 {
267 const FORMAT: SampleFormat = SampleFormat::I16;
268}
269
270impl SizedSample for I24 {
271 const FORMAT: SampleFormat = SampleFormat::I24;
272}
273
274impl SizedSample for i32 {
275 const FORMAT: SampleFormat = SampleFormat::I32;
276}
277
278// impl SizedSample for I48 {
279// const FORMAT: SampleFormat = SampleFormat::I48;
280// }
281
282impl SizedSample for i64 {
283 const FORMAT: SampleFormat = SampleFormat::I64;
284}
285
286impl SizedSample for u8 {
287 const FORMAT: SampleFormat = SampleFormat::U8;
288}
289
290impl SizedSample for u16 {
291 const FORMAT: SampleFormat = SampleFormat::U16;
292}
293
294impl SizedSample for U24 {
295 const FORMAT: SampleFormat = SampleFormat::U24;
296}
297
298impl SizedSample for u32 {
299 const FORMAT: SampleFormat = SampleFormat::U32;
300}
301
302// impl SizedSample for U48 {
303// const FORMAT: SampleFormat = SampleFormat::U48;
304// }
305
306impl SizedSample for u64 {
307 const FORMAT: SampleFormat = SampleFormat::U64;
308}
309
310impl SizedSample for f32 {
311 const FORMAT: SampleFormat = SampleFormat::F32;
312}
313
314impl SizedSample for f64 {
315 const FORMAT: SampleFormat = SampleFormat::F64;
316}