Skip to main content

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