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}