Skip to main content

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}