| Lift freeq's AV media plane out of sleek 90f8b89 nandi 19d ago | 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 | |
| 9 | use std::{fmt::Display, mem}; |
| 10 | #[cfg(all( |
| 11 | target_arch = "wasm32", |
| 12 | any(target_os = "emscripten", feature = "wasm-bindgen") |
| 13 | ))] |
| 14 | use wasm_bindgen::prelude::*; |
| 15 | |
| 16 | pub 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). |
| 25 | pub 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). |
| 34 | pub 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] |
| 63 | pub 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 | |
| 119 | impl 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 | |
| 216 | impl 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 | /// ``` |
| 254 | pub trait SizedSample: Sample { |
| 255 | /// The corresponding [`SampleFormat`] for this sample type. |
| 256 | const FORMAT: SampleFormat; |
| 257 | } |
| 258 | |
| 259 | impl SizedSample for i8 { |
| 260 | const FORMAT: SampleFormat = SampleFormat::I8; |
| 261 | } |
| 262 | |
| 263 | impl SizedSample for i16 { |
| 264 | const FORMAT: SampleFormat = SampleFormat::I16; |
| 265 | } |
| 266 | |
| 267 | impl SizedSample for I24 { |
| 268 | const FORMAT: SampleFormat = SampleFormat::I24; |
| 269 | } |
| 270 | |
| 271 | impl SizedSample for i32 { |
| 272 | const FORMAT: SampleFormat = SampleFormat::I32; |
| 273 | } |
| 274 | |
| 275 | // impl SizedSample for I48 { |
| 276 | // const FORMAT: SampleFormat = SampleFormat::I48; |
| 277 | // } |
| 278 | |
| 279 | impl SizedSample for i64 { |
| 280 | const FORMAT: SampleFormat = SampleFormat::I64; |
| 281 | } |
| 282 | |
| 283 | impl SizedSample for u8 { |
| 284 | const FORMAT: SampleFormat = SampleFormat::U8; |
| 285 | } |
| 286 | |
| 287 | impl SizedSample for u16 { |
| 288 | const FORMAT: SampleFormat = SampleFormat::U16; |
| 289 | } |
| 290 | |
| 291 | impl SizedSample for U24 { |
| 292 | const FORMAT: SampleFormat = SampleFormat::U24; |
| 293 | } |
| 294 | |
| 295 | impl SizedSample for u32 { |
| 296 | const FORMAT: SampleFormat = SampleFormat::U32; |
| 297 | } |
| 298 | |
| 299 | // impl SizedSample for U48 { |
| 300 | // const FORMAT: SampleFormat = SampleFormat::U48; |
| 301 | // } |
| 302 | |
| 303 | impl SizedSample for u64 { |
| 304 | const FORMAT: SampleFormat = SampleFormat::U64; |
| 305 | } |
| 306 | |
| 307 | impl SizedSample for f32 { |
| 308 | const FORMAT: SampleFormat = SampleFormat::F32; |
| 309 | } |
| 310 | |
| 311 | impl SizedSample for f64 { |
| 312 | const FORMAT: SampleFormat = SampleFormat::F64; |
| 313 | } |