From 738a799e90a9a40bfc8b71578eaeb6d20ab212e1 Mon Sep 17 00:00:00 2001 From: Julien Tregoat Date: Tue, 26 May 2020 21:17:56 -0400 Subject: [PATCH 1/9] add i32 to SampleFormat, update examples --- examples/beep.rs | 1 + examples/record_wav.rs | 10 ++++- src/lib.rs | 3 +- src/samples_formats.rs | 98 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/examples/beep.rs b/examples/beep.rs index 696f3f0a1..2469033b3 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -40,6 +40,7 @@ fn main() { match config.sample_format() { cpal::SampleFormat::F32 => run::(&device, &config.into()).unwrap(), cpal::SampleFormat::I16 => run::(&device, &config.into()).unwrap(), + cpal::SampleFormat::I32 => run::(&device, &config.into()).unwrap(), cpal::SampleFormat::U16 => run::(&device, &config.into()).unwrap(), } } diff --git a/examples/record_wav.rs b/examples/record_wav.rs index ac6d23779..dce46d92d 100644 --- a/examples/record_wav.rs +++ b/examples/record_wav.rs @@ -51,6 +51,11 @@ fn main() -> Result<(), anyhow::Error> { move |data, _: &_| write_input_data::(data, &writer_2), err_fn, )?, + cpal::SampleFormat::I32 => device.build_input_stream( + &config.into(), + move |data, _: &_| write_input_data::(data, &writer_2), + err_fn, + )?, cpal::SampleFormat::U16 => device.build_input_stream( &config.into(), move |data, _: &_| write_input_data::(data, &writer_2), @@ -70,8 +75,9 @@ fn main() -> Result<(), anyhow::Error> { fn sample_format(format: cpal::SampleFormat) -> hound::SampleFormat { match format { - cpal::SampleFormat::U16 => hound::SampleFormat::Int, - cpal::SampleFormat::I16 => hound::SampleFormat::Int, + cpal::SampleFormat::U16 | cpal::SampleFormat::I16 | cpal::SampleFormat::I32 => { + hound::SampleFormat::Int + } cpal::SampleFormat::F32 => hound::SampleFormat::Float, } } diff --git a/src/lib.rs b/src/lib.rs index b82a7c95a..a9da72595 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,6 +99,7 @@ //! let stream = match sample_format { //! SampleFormat::F32 => device.build_output_stream(&config, write_silence::, err_fn), //! SampleFormat::I16 => device.build_output_stream(&config, write_silence::, err_fn), +//! SampleFormat::I32 => device.build_output_stream(&config, write_silence::, err_fn), //! SampleFormat::U16 => device.build_output_stream(&config, write_silence::, err_fn), //! }.unwrap(); //! @@ -610,7 +611,7 @@ impl SupportedStreamConfigRange { /// - Max sample rate pub fn cmp_default_heuristics(&self, other: &Self) -> std::cmp::Ordering { use std::cmp::Ordering::Equal; - use SampleFormat::{F32, I16, U16}; + use SampleFormat::{F32, I16, I32, U16}; let cmp_stereo = (self.channels == 2).cmp(&(other.channels == 2)); if cmp_stereo != Equal { diff --git a/src/samples_formats.rs b/src/samples_formats.rs index faa02319e..affbd1777 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -5,6 +5,7 @@ use std::mem; pub enum SampleFormat { /// The value 0 corresponds to 0. I16, + I32, /// The value 0 corresponds to 32768. U16, /// The boundaries are (-1.0, 1.0). @@ -19,6 +20,7 @@ impl SampleFormat { SampleFormat::I16 => mem::size_of::(), SampleFormat::U16 => mem::size_of::(), SampleFormat::F32 => mem::size_of::(), + SampleFormat::I32 => mem::size_of::(), } } } @@ -32,10 +34,12 @@ pub unsafe trait Sample: Copy + Clone { fn to_f32(&self) -> f32; /// Converts this sample into a standard i16 sample. fn to_i16(&self) -> i16; + /// Converts this sample into a standard i32 sample. + fn to_i32(&self) -> i32; /// Converts this sample into a standard u16 sample. fn to_u16(&self) -> u16; - /// Converts any sample type to this one by calling `to_i16`, `to_u16` or `to_f32`. + /// Converts any sample type to this one by calling `to_i16`, `to_i32`, `to_u16`, or `to_f32`. fn from(&S) -> Self where S: Sample; @@ -58,6 +62,10 @@ unsafe impl Sample for u16 { } } + fn to_i32(&self) -> i32 { + (*self).into() + } + #[inline] fn to_u16(&self) -> u16 { *self @@ -89,6 +97,11 @@ unsafe impl Sample for i16 { *self } + #[inline] + fn to_i32(&self) -> i32 { + *self as i32 + } + #[inline] fn to_u16(&self) -> u16 { if *self < 0 { @@ -124,6 +137,15 @@ unsafe impl Sample for f32 { } } + #[inline] + fn to_i32(&self) -> i32 { + if *self >= 0.0 { + (*self * ::std::i32::MAX as f32) as i32 + } else { + (-*self * ::std::i32::MIN as f32) as i32 + } + } + #[inline] fn to_u16(&self) -> u16 { (((*self + 1.0) * 0.5) * ::std::u16::MAX as f32).round() as u16 @@ -138,10 +160,83 @@ unsafe impl Sample for f32 { } } +unsafe impl Sample for i32 { + const FORMAT: SampleFormat = SampleFormat::I32; + + #[inline] + fn to_f32(&self) -> f32 { + if *self < 0 { + *self as f32 / -(::std::i32::MIN as f32) + } else { + *self as f32 / ::std::i32::MAX as f32 + } + } + + #[inline] + fn to_i16(&self) -> i16 { + let ratio = self.to_f32(); + let scale = match ratio.is_sign_negative() { + true => -(std::i16::MIN as f32), + false => std::i16::MAX as f32, + }; + + (ratio * scale) as i16 + } + + #[inline] + fn to_u16(&self) -> u16 { + let ratio = (self.to_f32() + 1f32) / 2f32; + let scale = std::u16::MAX as f32; + + (ratio * scale) as u16 + } + + #[inline] + fn to_i32(&self) -> i32 { + *self + } + + #[inline] + fn from(sample: &S) -> Self + where + S: Sample, + { + sample.to_i32() + } +} + #[cfg(test)] mod test { use super::Sample; + #[test] + fn i32_to_i16() { + assert_eq!(std::i32::MAX.to_i16(), std::i16::MAX); + assert_eq!((std::i32::MIN / 2).to_i16(), std::i16::MIN / 2); + assert_eq!(std::i32::MIN.to_i16(), std::i16::MIN); + assert_eq!(0i32.to_i16(), 0); + } + + #[test] + fn i32_to_u16() { + assert_eq!(std::i32::MAX.to_u16(), std::u16::MAX); + assert_eq!( + (std::i32::MIN / 2).to_u16(), + (std::u16::MAX as f32 / 4f32) as u16 + ); + assert_eq!(std::i32::MIN.to_u16(), std::u16::MIN); + } + + #[test] + fn i32_to_f32() { + assert_eq!(std::i32::MAX.to_f32(), 1.0f32); + assert_eq!((std::i32::MAX / 8).to_f32(), 0.125f32); + assert_eq!((std::i32::MAX / -16).to_f32(), -0.0625f32); + assert_eq!((std::i32::MAX / -4).to_f32(), -0.25f32); + assert_eq!(std::i32::MIN.to_f32(), -1.0f32); + assert_eq!(0.to_f32(), 0f32); + } + #[test] fn i16_to_i16() { assert_eq!(0i16.to_i16(), 0); @@ -162,6 +257,7 @@ mod test { fn i16_to_f32() { assert_eq!(0i16.to_f32(), 0.0); assert_eq!((-16384i16).to_f32(), -0.5); + assert_eq!((-16384i16 / 2).to_f32(), -0.25); assert_eq!(32767i16.to_f32(), 1.0); assert_eq!((-32768i16).to_f32(), -1.0); } From ac44d96ecbf5d94ca081099fe4229ced63ca1ba4 Mon Sep 17 00:00:00 2001 From: Julien Tregoat Date: Wed, 27 May 2020 00:37:01 -0400 Subject: [PATCH 2/9] add fix for f32 i32 conversion, add tests --- src/samples_formats.rs | 45 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/samples_formats.rs b/src/samples_formats.rs index affbd1777..75aa1a868 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -139,10 +139,14 @@ unsafe impl Sample for f32 { #[inline] fn to_i32(&self) -> i32 { - if *self >= 0.0 { - (*self * ::std::i32::MAX as f32) as i32 + let res = match self.is_sign_negative() { + false => (self * std::i32::MAX as f32), + true => (self * -(std::i32::MIN as f32)), + }; + if *self == 1.0f32 && res as i32 == std::i32::MIN { + std::i32::MAX } else { - (-*self * ::std::i32::MIN as f32) as i32 + res as i32 } } @@ -217,6 +221,14 @@ mod test { assert_eq!(0i32.to_i16(), 0); } + #[test] + fn i32_to_i32() { + assert_eq!(std::i32::MAX.to_i32(), std::i32::MAX); + assert_eq!((std::i32::MIN / 2).to_i32(), std::i32::MIN / 2); + assert_eq!(std::i32::MIN.to_i32(), std::i32::MIN); + assert_eq!(0i32.to_i32(), 0); + } + #[test] fn i32_to_u16() { assert_eq!(std::i32::MAX.to_u16(), std::u16::MAX); @@ -245,6 +257,15 @@ mod test { assert_eq!((-32768i16).to_i16(), -32768); } + #[test] + fn i16_to_i32() { + // TODO + // assert_eq!(0i16.to_i16(), 0); + // assert_eq!((-467i16).to_i16(), -467); + // assert_eq!(32767i16.to_i16(), 32767); + // assert_eq!((-32768i16).to_i16(), -32768); + } + #[test] fn i16_to_u16() { assert_eq!(0i16.to_u16(), 32768); @@ -270,6 +291,14 @@ mod test { assert_eq!(0u16.to_i16(), -32768); } + fn u16_to_i32() { + // TODO + // assert_eq!(32768u16.to_i16(), 0); + // assert_eq!(16384u16.to_i16(), -16384); + // assert_eq!(65535u16.to_i16(), 32767); + // assert_eq!(0u16.to_i16(), -32768); + } + #[test] fn u16_to_u16() { assert_eq!(0u16.to_u16(), 0); @@ -293,6 +322,16 @@ mod test { assert_eq!((-1.0f32).to_i16(), ::std::i16::MIN); } + #[test] + fn f32_to_i32() { + assert_eq!(1.0f32.to_i32(), std::i32::MAX); + assert_eq!(0.5f32.to_i32(), 1073741824); + assert_eq!(0.25f32.to_i32(), 536870912); + assert_eq!(0.to_i32(), 0); + assert_eq!((-0.5f32).to_i32(), std::i32::MIN / 2); + assert_eq!((-1.0f32).to_i32(), std::i32::MIN); + } + #[test] fn f32_to_u16() { assert_eq!((-1.0f32).to_u16(), 0); From ec7d9ede57e510c391cd4e997d667cfbc55f6ea0 Mon Sep 17 00:00:00 2001 From: Julien Tregoat Date: Wed, 27 May 2020 01:19:19 -0400 Subject: [PATCH 3/9] finish tests --- src/samples_formats.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/samples_formats.rs b/src/samples_formats.rs index 75aa1a868..a8c21fe8e 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -63,7 +63,7 @@ unsafe impl Sample for u16 { } fn to_i32(&self) -> i32 { - (*self).into() + self.to_i16() as i32 } #[inline] @@ -259,11 +259,10 @@ mod test { #[test] fn i16_to_i32() { - // TODO - // assert_eq!(0i16.to_i16(), 0); - // assert_eq!((-467i16).to_i16(), -467); - // assert_eq!(32767i16.to_i16(), 32767); - // assert_eq!((-32768i16).to_i16(), -32768); + assert_eq!(0i16.to_i32(), 0); + assert_eq!((-467i16).to_i32(), -467); + assert_eq!(std::i16::MAX.to_i32(), std::i16::MAX as i32); + assert_eq!(std::i16::MIN.to_i32(), std::i16::MIN as i32); } #[test] @@ -291,12 +290,12 @@ mod test { assert_eq!(0u16.to_i16(), -32768); } + #[test] fn u16_to_i32() { - // TODO - // assert_eq!(32768u16.to_i16(), 0); - // assert_eq!(16384u16.to_i16(), -16384); - // assert_eq!(65535u16.to_i16(), 32767); - // assert_eq!(0u16.to_i16(), -32768); + assert_eq!(32768u16.to_i32(), 0); + assert_eq!(16384u16.to_i32(), -16384); + assert_eq!(65535u16.to_i32(), std::i16::MAX as i32); + assert_eq!(0u16.to_i32(), std::i16::MIN as i32); } #[test] From f7c01e5e559da6ba7e1b31c2cc7296db3bcf7a07 Mon Sep 17 00:00:00 2001 From: Julien Tregoat Date: Wed, 27 May 2020 01:35:02 -0400 Subject: [PATCH 4/9] add missing inline attr --- src/samples_formats.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/samples_formats.rs b/src/samples_formats.rs index a8c21fe8e..47d6e0722 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -62,6 +62,7 @@ unsafe impl Sample for u16 { } } + #[inline] fn to_i32(&self) -> i32 { self.to_i16() as i32 } From 412dfb300c4e112ef31612b458305d15c391cacf Mon Sep 17 00:00:00 2001 From: Julien Tregoat Date: Tue, 13 Oct 2020 23:18:08 -0400 Subject: [PATCH 5/9] implement 24 bit sample support refactor 32 bit implementation scale down all vals when casting up or down --- src/lib.rs | 2 +- src/samples_formats.rs | 186 ++++++++++++++++++++++++++++++++--------- 2 files changed, 149 insertions(+), 39 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a9da72595..ff9e21c4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -158,7 +158,7 @@ pub use platform::{ available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream, SupportedInputConfigs, SupportedOutputConfigs, ALL_HOSTS, }; -pub use samples_formats::{Sample, SampleFormat}; +pub use samples_formats::{Sample, SampleFormat, Padded24}; use std::convert::TryInto; use std::ops::{Div, Mul}; use std::time::Duration; diff --git a/src/samples_formats.rs b/src/samples_formats.rs index 47d6e0722..376c19064 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -5,6 +5,7 @@ use std::mem; pub enum SampleFormat { /// The value 0 corresponds to 0. I16, + I24, I32, /// The value 0 corresponds to 32768. U16, @@ -20,6 +21,7 @@ impl SampleFormat { SampleFormat::I16 => mem::size_of::(), SampleFormat::U16 => mem::size_of::(), SampleFormat::F32 => mem::size_of::(), + SampleFormat::I24 => mem::size_of::(), SampleFormat::I32 => mem::size_of::(), } } @@ -32,15 +34,17 @@ pub unsafe trait Sample: Copy + Clone { /// Turns the sample into its equivalent as a floating-point. fn to_f32(&self) -> f32; + /// Converts this sample into a standard u16 sample. + fn to_u16(&self) -> u16; /// Converts this sample into a standard i16 sample. fn to_i16(&self) -> i16; + /// Converts this sample into a 24 bit integer stored in an i32. + fn to_i24(&self) -> Padded24; /// Converts this sample into a standard i32 sample. fn to_i32(&self) -> i32; - /// Converts this sample into a standard u16 sample. - fn to_u16(&self) -> u16; /// Converts any sample type to this one by calling `to_i16`, `to_i32`, `to_u16`, or `to_f32`. - fn from(&S) -> Self + fn from(sample: &S) -> Self where S: Sample; } @@ -50,7 +54,12 @@ unsafe impl Sample for u16 { #[inline] fn to_f32(&self) -> f32 { - self.to_i16().to_f32() + *self as f32 / ::std::u16::MAX as f32 + } + + #[inline] + fn to_u16(&self) -> u16 { + *self } #[inline] @@ -62,14 +71,13 @@ unsafe impl Sample for u16 { } } - #[inline] - fn to_i32(&self) -> i32 { - self.to_i16() as i32 + fn to_i24(&self) -> Padded24 { + self.to_f32().to_i24() } #[inline] - fn to_u16(&self) -> u16 { - *self + fn to_i32(&self) -> i32 { + self.to_f32().to_i32() } #[inline] @@ -93,23 +101,28 @@ unsafe impl Sample for i16 { } } + #[inline] + fn to_u16(&self) -> u16 { + if *self < 0 { + (*self - ::std::i16::MIN) as u16 + } else { + (*self as u16) + 32768 + } + } + #[inline] fn to_i16(&self) -> i16 { *self } #[inline] - fn to_i32(&self) -> i32 { - *self as i32 + fn to_i24(&self) -> Padded24 { + self.to_f32().to_i24() } #[inline] - fn to_u16(&self) -> u16 { - if *self < 0 { - (*self - ::std::i16::MIN) as u16 - } else { - (*self as u16) + 32768 - } + fn to_i32(&self) -> i32 { + self.to_f32().to_i32() } #[inline] @@ -129,6 +142,11 @@ unsafe impl Sample for f32 { *self } + #[inline] + fn to_u16(&self) -> u16 { + (((*self + 1.0) * 0.5) * ::std::u16::MAX as f32).round() as u16 + } + #[inline] fn to_i16(&self) -> i16 { if *self >= 0.0 { @@ -138,22 +156,118 @@ unsafe impl Sample for f32 { } } + #[inline] + fn to_i24(&self) -> Padded24 { + let result: f32; + if self.is_sign_positive() { + result = self * Padded24::MAX as f32; + } else { + result = self.abs() * Padded24::MIN as f32; + } + Padded24(result.round() as i32) + } + #[inline] fn to_i32(&self) -> i32 { - let res = match self.is_sign_negative() { - false => (self * std::i32::MAX as f32), - true => (self * -(std::i32::MIN as f32)), - }; - if *self == 1.0f32 && res as i32 == std::i32::MIN { - std::i32::MAX + (*self as f64 * std::i32::MAX as f64).round() as i32 + } + + #[inline] + fn from(sample: &S) -> Self + where + S: Sample, + { + sample.to_f32() + } +} + +#[derive(Copy, Clone)] +pub struct Padded24(i32); + +impl Padded24 { + const MAX: i32 = 8_388_607; + const MIN: i32 = -8_388_608; + + // assumes i24 has been correctly parsed already + pub fn new(val: i32) -> Self { + Padded24(val) + } + + pub fn from_be_bytes(b: [u8; 3]) -> Self { + let is_pos = b[0] & 0b1000_0000 == 0; + let extra_byte; + if is_pos { + extra_byte = u8::MIN; } else { - res as i32 + extra_byte = u8::MAX; } + + Padded24(i32::from_be_bytes([extra_byte, b[0], b[1], b[2]])) + } + + pub fn to_be_bytes(&self) -> [u8; 3] { + let [_, mut byte1, byte2, byte3] = self.0.to_be_bytes(); + + if self.0.is_negative() { + byte1 |= 0b1000_0000; + } + + [byte1, byte2, byte3] + } + + pub fn from_le_bytes(b: [u8; 3]) -> Self { + let is_pos = b[2] & 0b1000_0000 == 0; + let extra_byte; + if is_pos { + extra_byte = u8::MIN; + } else { + extra_byte = u8::MAX; + } + + Padded24(i32::from_le_bytes([b[0], b[1], b[2], extra_byte])) + } + + pub fn to_le_bytes(&self) -> [u8; 3] { + let [byte1, byte2, mut byte3, _] = self.0.to_be_bytes(); + + if self.0.is_negative() { + byte3 |= 0b1000_0000; + } + + [byte1, byte2, byte3] + } +} + +unsafe impl Sample for Padded24 { + const FORMAT: SampleFormat = SampleFormat::I24; + + #[inline] + fn to_f32(&self) -> f32 { + if self.0 < 0 { + (self.0 as f64 * (-1.0 / (Self::MIN as f64))) as f32 + } else { + (self.0 as f64 * (1.0 / Self::MAX as f64)) as f32 + } + } + + #[inline] + fn to_i16(&self) -> i16 { + self.to_f32().to_i16() } #[inline] fn to_u16(&self) -> u16 { - (((*self + 1.0) * 0.5) * ::std::u16::MAX as f32).round() as u16 + self.to_f32().to_u16() + } + + #[inline] + fn to_i24(&self) -> Padded24 { + *self + } + + #[inline] + fn to_i32(&self) -> i32 { + self.to_f32().to_i32() } #[inline] @@ -161,7 +275,7 @@ unsafe impl Sample for f32 { where S: Sample, { - sample.to_f32() + sample.to_i24() } } @@ -171,29 +285,25 @@ unsafe impl Sample for i32 { #[inline] fn to_f32(&self) -> f32 { if *self < 0 { - *self as f32 / -(::std::i32::MIN as f32) + (*self as f64 * (1.0 / -(::std::i32::MIN as f64))) as f32 } else { - *self as f32 / ::std::i32::MAX as f32 + (*self as f64 * (1.0 / ::std::i32::MAX as f64)) as f32 } } #[inline] fn to_i16(&self) -> i16 { - let ratio = self.to_f32(); - let scale = match ratio.is_sign_negative() { - true => -(std::i16::MIN as f32), - false => std::i16::MAX as f32, - }; - - (ratio * scale) as i16 + self.to_f32().to_i16() } #[inline] fn to_u16(&self) -> u16 { - let ratio = (self.to_f32() + 1f32) / 2f32; - let scale = std::u16::MAX as f32; + self.to_f32().to_u16() + } - (ratio * scale) as u16 + #[inline] + fn to_i24(&self) -> Padded24 { + self.to_f32().to_i24() } #[inline] From 0bff7709f9888f3d51287d576b69b42ed2910d5a Mon Sep 17 00:00:00 2001 From: Julien Tregoat Date: Wed, 14 Oct 2020 00:25:58 -0400 Subject: [PATCH 6/9] fix tests leave unimplemented placeholder for examples --- examples/beep.rs | 1 + examples/record_wav.rs | 2 + src/lib.rs | 49 ++++++++++++++++++++--- src/samples_formats.rs | 88 +++++++++++++++++++++++++++++++++++------- 4 files changed, 120 insertions(+), 20 deletions(-) diff --git a/examples/beep.rs b/examples/beep.rs index 2469033b3..f024d3373 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -42,6 +42,7 @@ fn main() { cpal::SampleFormat::I16 => run::(&device, &config.into()).unwrap(), cpal::SampleFormat::I32 => run::(&device, &config.into()).unwrap(), cpal::SampleFormat::U16 => run::(&device, &config.into()).unwrap(), + cpal::SampleFormat::I24 => unimplemented!(), } } diff --git a/examples/record_wav.rs b/examples/record_wav.rs index dce46d92d..e372e3d49 100644 --- a/examples/record_wav.rs +++ b/examples/record_wav.rs @@ -51,6 +51,7 @@ fn main() -> Result<(), anyhow::Error> { move |data, _: &_| write_input_data::(data, &writer_2), err_fn, )?, + cpal::SampleFormat::I24 => unimplemented!(), cpal::SampleFormat::I32 => device.build_input_stream( &config.into(), move |data, _: &_| write_input_data::(data, &writer_2), @@ -79,6 +80,7 @@ fn sample_format(format: cpal::SampleFormat) -> hound::SampleFormat { hound::SampleFormat::Int } cpal::SampleFormat::F32 => hound::SampleFormat::Float, + cpal::SampleFormat::I24 => unimplemented!() } } diff --git a/src/lib.rs b/src/lib.rs index ff9e21c4c..899b9d6df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,6 +101,7 @@ //! SampleFormat::I16 => device.build_output_stream(&config, write_silence::, err_fn), //! SampleFormat::I32 => device.build_output_stream(&config, write_silence::, err_fn), //! SampleFormat::U16 => device.build_output_stream(&config, write_silence::, err_fn), +//! SampleFormat::I24 => unimplemented!(), //! }.unwrap(); //! //! fn write_silence(data: &mut [T], _: &cpal::OutputCallbackInfo) { @@ -604,6 +605,8 @@ impl SupportedStreamConfigRange { /// - f32 /// - i16 /// - u16 + /// - i32 + /// - i24 /// /// **Sample rate**: /// @@ -611,7 +614,7 @@ impl SupportedStreamConfigRange { /// - Max sample rate pub fn cmp_default_heuristics(&self, other: &Self) -> std::cmp::Ordering { use std::cmp::Ordering::Equal; - use SampleFormat::{F32, I16, I32, U16}; + use SampleFormat::{F32, I16, I32, U16, I24}; let cmp_stereo = (self.channels == 2).cmp(&(other.channels == 2)); if cmp_stereo != Equal { @@ -643,6 +646,16 @@ impl SupportedStreamConfigRange { return cmp_u16; } + let cmp_i32 = (self.sample_format == I32).cmp(&(other.sample_format == I32)); + if cmp_i32 != Equal { + return cmp_i32; + } + + let cmp_i24 = (self.sample_format == I24).cmp(&(other.sample_format == I24)); + if cmp_i24 != Equal { + return cmp_i24; + } + const HZ_44100: SampleRate = SampleRate(44_100); let r44100_in_self = self.min_sample_rate <= HZ_44100 && HZ_44100 <= self.max_sample_rate; let r44100_in_other = @@ -694,6 +707,20 @@ fn test_cmp_default_heuristics() { max_sample_rate: SampleRate(22050), sample_format: SampleFormat::F32, }, + SupportedStreamConfigRange { + buffer_size: SupportedBufferSize::Range { min: 256, max: 512 }, + channels: 2, + min_sample_rate: SampleRate(1), + max_sample_rate: SampleRate(96000), + sample_format: SampleFormat::I24, + }, + SupportedStreamConfigRange { + buffer_size: SupportedBufferSize::Range { min: 256, max: 512 }, + channels: 2, + min_sample_rate: SampleRate(1), + max_sample_rate: SampleRate(96000), + sample_format: SampleFormat::I32, + }, ]; formats.sort_by(|a, b| a.cmp_default_heuristics(b)); @@ -704,25 +731,35 @@ fn test_cmp_default_heuristics() { assert_eq!(formats[0].max_sample_rate(), SampleRate(96000)); assert_eq!(formats[0].channels(), 1); - assert_eq!(formats[1].sample_format(), SampleFormat::U16); + assert_eq!(formats[1].sample_format(), SampleFormat::I24); assert_eq!(formats[1].min_sample_rate(), SampleRate(1)); assert_eq!(formats[1].max_sample_rate(), SampleRate(96000)); assert_eq!(formats[1].channels(), 2); - assert_eq!(formats[2].sample_format(), SampleFormat::I16); + assert_eq!(formats[2].sample_format(), SampleFormat::I32); assert_eq!(formats[2].min_sample_rate(), SampleRate(1)); assert_eq!(formats[2].max_sample_rate(), SampleRate(96000)); assert_eq!(formats[2].channels(), 2); - assert_eq!(formats[3].sample_format(), SampleFormat::F32); + assert_eq!(formats[3].sample_format(), SampleFormat::U16); assert_eq!(formats[3].min_sample_rate(), SampleRate(1)); - assert_eq!(formats[3].max_sample_rate(), SampleRate(22050)); + assert_eq!(formats[3].max_sample_rate(), SampleRate(96000)); assert_eq!(formats[3].channels(), 2); - assert_eq!(formats[4].sample_format(), SampleFormat::F32); + assert_eq!(formats[4].sample_format(), SampleFormat::I16); assert_eq!(formats[4].min_sample_rate(), SampleRate(1)); assert_eq!(formats[4].max_sample_rate(), SampleRate(96000)); assert_eq!(formats[4].channels(), 2); + + assert_eq!(formats[5].sample_format(), SampleFormat::F32); + assert_eq!(formats[5].min_sample_rate(), SampleRate(1)); + assert_eq!(formats[5].max_sample_rate(), SampleRate(22050)); + assert_eq!(formats[5].channels(), 2); + + assert_eq!(formats[6].sample_format(), SampleFormat::F32); + assert_eq!(formats[6].min_sample_rate(), SampleRate(1)); + assert_eq!(formats[6].max_sample_rate(), SampleRate(96000)); + assert_eq!(formats[6].channels(), 2); } impl From for StreamConfig { diff --git a/src/samples_formats.rs b/src/samples_formats.rs index 376c19064..5283780f7 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -54,7 +54,7 @@ unsafe impl Sample for u16 { #[inline] fn to_f32(&self) -> f32 { - *self as f32 / ::std::u16::MAX as f32 + self.to_i16().to_f32() } #[inline] @@ -142,11 +142,13 @@ unsafe impl Sample for f32 { *self } + /// This function inherently returns a lossy value due to scaling. #[inline] fn to_u16(&self) -> u16 { (((*self + 1.0) * 0.5) * ::std::u16::MAX as f32).round() as u16 } + /// This function inherently returns a lossy value due to scaling. #[inline] fn to_i16(&self) -> i16 { if *self >= 0.0 { @@ -156,6 +158,7 @@ unsafe impl Sample for f32 { } } + /// This function inherently returns a lossy value due to scaling. #[inline] fn to_i24(&self) -> Padded24 { let result: f32; @@ -169,7 +172,11 @@ unsafe impl Sample for f32 { #[inline] fn to_i32(&self) -> i32 { - (*self as f64 * std::i32::MAX as f64).round() as i32 + if self.is_sign_positive() { + (*self as f64 * std::i32::MAX as f64).round() as i32 + } else { + (*self as f64 * -(std::i32::MIN as f64)).round() as i32 + } } #[inline] @@ -250,11 +257,13 @@ unsafe impl Sample for Padded24 { } } + /// This function inherently returns a lossy value due to scaling. #[inline] fn to_i16(&self) -> i16 { self.to_f32().to_i16() } + /// This function inherently returns a lossy value due to scaling. #[inline] fn to_u16(&self) -> u16 { self.to_f32().to_u16() @@ -282,6 +291,7 @@ unsafe impl Sample for Padded24 { unsafe impl Sample for i32 { const FORMAT: SampleFormat = SampleFormat::I32; + /// This function inherently returns a lossy value due to scaling. #[inline] fn to_f32(&self) -> f32 { if *self < 0 { @@ -291,16 +301,19 @@ unsafe impl Sample for i32 { } } + /// This function inherently returns a lossy value due to scaling. #[inline] fn to_i16(&self) -> i16 { self.to_f32().to_i16() } + /// This function inherently returns a lossy value due to scaling. #[inline] fn to_u16(&self) -> u16 { self.to_f32().to_u16() } + /// This function inherently returns a lossy value due to scaling. #[inline] fn to_i24(&self) -> Padded24 { self.to_f32().to_i24() @@ -320,9 +333,61 @@ unsafe impl Sample for i32 { } } +// TODO add _to_i24 tests #[cfg(test)] mod test { - use super::Sample; + use super::{Padded24, Sample}; + + #[test] + fn i24_to_i16() { + assert_eq!(Padded24::new(Padded24::MAX).to_i16(), std::i16::MAX); + assert_eq!(Padded24::new(Padded24::MIN / 2).to_i16(), std::i16::MIN / 2); + assert_eq!(Padded24::new(Padded24::MIN).to_i16(), std::i16::MIN); + assert_eq!(Padded24::new(0).to_i16(), 0); + } + + #[test] + fn i24_to_i24() { + // let max = Padded24::new(Padded24::MAX); + // let min = Padded24::new(Padded24::MIN); + + // assert_eq!(max.to_i16(), std::i16::MAX); + // assert_eq!((std::i32::MIN / 2).to_i16(), std::i16::MIN / 2); + // assert_eq!(std::i32::MIN.to_i16(), std::i16::MIN); + // assert_eq!(0i32.to_i16(), 0); + } + + #[test] + fn i24_to_i32() { + assert_eq!(Padded24::new(Padded24::MAX).to_i32(), std::i32::MAX); + assert_eq!(Padded24::new(Padded24::MIN / 2).to_i32(), std::i32::MIN / 2); + assert_eq!(Padded24::new(Padded24::MIN).to_i32(), std::i32::MIN); + assert_eq!(Padded24::new(0).to_i32(), 0); + } + + #[test] + fn i24_to_u16() { + assert_eq!(Padded24::new(Padded24::MAX).to_u16(), std::u16::MAX); + // half of the int max will be 3/4 of the uint max + assert_eq!( + Padded24::new(Padded24::MAX / 2).to_u16(), + (std::u16::MAX as f32 / 4.0 * 3.0).round() as u16 + ); + assert_eq!(Padded24::new(Padded24::MIN).to_u16(), std::u16::MIN); + } + + #[test] + fn i24_to_f32() { + let max = Padded24::new(Padded24::MAX); + let min = Padded24::new(Padded24::MIN); + + assert_eq!(max.to_f32(), 1.0f32); + assert_eq!(max.to_f32() / 8.0, 0.125f32); + assert_eq!(max.to_f32() / -16.0, -0.0625f32); + assert_eq!(max.to_f32() / -4.0, -0.25f32); + assert_eq!(min.to_f32(), -1.0f32); + assert_eq!(Padded24::new(0).to_f32(), 0f32); + } #[test] fn i32_to_i16() { @@ -343,10 +408,7 @@ mod test { #[test] fn i32_to_u16() { assert_eq!(std::i32::MAX.to_u16(), std::u16::MAX); - assert_eq!( - (std::i32::MIN / 2).to_u16(), - (std::u16::MAX as f32 / 4f32) as u16 - ); + assert_eq!(0i32.to_u16(), (std::u16::MAX as f32 / 2.0).round() as u16); assert_eq!(std::i32::MIN.to_u16(), std::u16::MIN); } @@ -371,9 +433,8 @@ mod test { #[test] fn i16_to_i32() { assert_eq!(0i16.to_i32(), 0); - assert_eq!((-467i16).to_i32(), -467); - assert_eq!(std::i16::MAX.to_i32(), std::i16::MAX as i32); - assert_eq!(std::i16::MIN.to_i32(), std::i16::MIN as i32); + assert_eq!(std::i16::MAX.to_i32(), std::i32::MAX); + assert_eq!(std::i16::MIN.to_i32(), std::i32::MIN); } #[test] @@ -403,10 +464,9 @@ mod test { #[test] fn u16_to_i32() { - assert_eq!(32768u16.to_i32(), 0); - assert_eq!(16384u16.to_i32(), -16384); - assert_eq!(65535u16.to_i32(), std::i16::MAX as i32); - assert_eq!(0u16.to_i32(), std::i16::MIN as i32); + assert_eq!(((std::u16::MAX as f32 / 2.0).round() as u16).to_i32(), 0); + assert_eq!(std::u16::MAX.to_i32(), std::i32::MAX); + assert_eq!(std::u16::MIN.to_i32(), std::i32::MIN); } #[test] From 2c65d908f172e3a755c0bbf170b9c6cb0ae8b027 Mon Sep 17 00:00:00 2001 From: Julien Tregoat Date: Mon, 19 Oct 2020 19:20:51 -0400 Subject: [PATCH 7/9] add i24 tests rename 24bit impl --- src/lib.rs | 2 +- src/samples_formats.rs | 149 ++++++++++++++++++++++++++++++----------- 2 files changed, 110 insertions(+), 41 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 899b9d6df..f048e9ee7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -159,7 +159,7 @@ pub use platform::{ available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream, SupportedInputConfigs, SupportedOutputConfigs, ALL_HOSTS, }; -pub use samples_formats::{Sample, SampleFormat, Padded24}; +pub use samples_formats::{Sample, SampleFormat, Unpacked24}; use std::convert::TryInto; use std::ops::{Div, Mul}; use std::time::Duration; diff --git a/src/samples_formats.rs b/src/samples_formats.rs index 5283780f7..d43dd473f 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -1,4 +1,4 @@ -use std::mem; +use std::{mem, ops::Deref}; /// Format that each sample has. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -39,7 +39,7 @@ pub unsafe trait Sample: Copy + Clone { /// Converts this sample into a standard i16 sample. fn to_i16(&self) -> i16; /// Converts this sample into a 24 bit integer stored in an i32. - fn to_i24(&self) -> Padded24; + fn to_i24(&self) -> Unpacked24; /// Converts this sample into a standard i32 sample. fn to_i32(&self) -> i32; @@ -71,7 +71,7 @@ unsafe impl Sample for u16 { } } - fn to_i24(&self) -> Padded24 { + fn to_i24(&self) -> Unpacked24 { self.to_f32().to_i24() } @@ -116,7 +116,7 @@ unsafe impl Sample for i16 { } #[inline] - fn to_i24(&self) -> Padded24 { + fn to_i24(&self) -> Unpacked24 { self.to_f32().to_i24() } @@ -160,14 +160,14 @@ unsafe impl Sample for f32 { /// This function inherently returns a lossy value due to scaling. #[inline] - fn to_i24(&self) -> Padded24 { + fn to_i24(&self) -> Unpacked24 { let result: f32; if self.is_sign_positive() { - result = self * Padded24::MAX as f32; + result = self * Unpacked24::MAX as f32; } else { - result = self.abs() * Padded24::MIN as f32; + result = self.abs() * Unpacked24::MIN as f32; } - Padded24(result.round() as i32) + Unpacked24(result.round() as i32) } #[inline] @@ -188,16 +188,16 @@ unsafe impl Sample for f32 { } } -#[derive(Copy, Clone)] -pub struct Padded24(i32); +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Unpacked24(i32); -impl Padded24 { +impl Unpacked24 { const MAX: i32 = 8_388_607; const MIN: i32 = -8_388_608; // assumes i24 has been correctly parsed already pub fn new(val: i32) -> Self { - Padded24(val) + Unpacked24(val) } pub fn from_be_bytes(b: [u8; 3]) -> Self { @@ -209,7 +209,7 @@ impl Padded24 { extra_byte = u8::MAX; } - Padded24(i32::from_be_bytes([extra_byte, b[0], b[1], b[2]])) + Unpacked24(i32::from_be_bytes([extra_byte, b[0], b[1], b[2]])) } pub fn to_be_bytes(&self) -> [u8; 3] { @@ -231,7 +231,7 @@ impl Padded24 { extra_byte = u8::MAX; } - Padded24(i32::from_le_bytes([b[0], b[1], b[2], extra_byte])) + Unpacked24(i32::from_le_bytes([b[0], b[1], b[2], extra_byte])) } pub fn to_le_bytes(&self) -> [u8; 3] { @@ -245,7 +245,37 @@ impl Padded24 { } } -unsafe impl Sample for Padded24 { +impl PartialEq for Unpacked24 { + fn eq(&self, other: &i8) -> bool { + *other as i32 == self.0 + } +} + +impl PartialEq for Unpacked24 { + fn eq(&self, other: &i16) -> bool { + *other as i32 == self.0 + } +} + +impl PartialEq for Unpacked24 { + fn eq(&self, other: &i32) -> bool { + *other == self.0 + } +} + +impl PartialEq for Unpacked24 { + fn eq(&self, other: &u8) -> bool { + *other as i32 == self.0 + } +} + +impl PartialEq for Unpacked24 { + fn eq(&self, other: &u16) -> bool { + *other as i32 == self.0 + } +} + +unsafe impl Sample for Unpacked24 { const FORMAT: SampleFormat = SampleFormat::I24; #[inline] @@ -270,7 +300,7 @@ unsafe impl Sample for Padded24 { } #[inline] - fn to_i24(&self) -> Padded24 { + fn to_i24(&self) -> Unpacked24 { *self } @@ -315,7 +345,7 @@ unsafe impl Sample for i32 { /// This function inherently returns a lossy value due to scaling. #[inline] - fn to_i24(&self) -> Padded24 { + fn to_i24(&self) -> Unpacked24 { self.to_f32().to_i24() } @@ -333,60 +363,65 @@ unsafe impl Sample for i32 { } } -// TODO add _to_i24 tests #[cfg(test)] mod test { - use super::{Padded24, Sample}; + use super::{Sample, Unpacked24}; #[test] fn i24_to_i16() { - assert_eq!(Padded24::new(Padded24::MAX).to_i16(), std::i16::MAX); - assert_eq!(Padded24::new(Padded24::MIN / 2).to_i16(), std::i16::MIN / 2); - assert_eq!(Padded24::new(Padded24::MIN).to_i16(), std::i16::MIN); - assert_eq!(Padded24::new(0).to_i16(), 0); + assert_eq!(Unpacked24::new(Unpacked24::MAX).to_i16(), std::i16::MAX); + assert_eq!( + Unpacked24::new(Unpacked24::MIN / 2).to_i16(), + std::i16::MIN / 2 + ); + assert_eq!(Unpacked24::new(Unpacked24::MIN).to_i16(), std::i16::MIN); + assert_eq!(Unpacked24::new(0).to_i16(), 0); } #[test] fn i24_to_i24() { - // let max = Padded24::new(Padded24::MAX); - // let min = Padded24::new(Padded24::MIN); - - // assert_eq!(max.to_i16(), std::i16::MAX); - // assert_eq!((std::i32::MIN / 2).to_i16(), std::i16::MIN / 2); - // assert_eq!(std::i32::MIN.to_i16(), std::i16::MIN); - // assert_eq!(0i32.to_i16(), 0); + assert_eq!(Unpacked24::new(Unpacked24::MAX).to_i24(), Unpacked24::MAX); + assert_eq!( + Unpacked24::new(Unpacked24::MIN / 2).to_i24(), + Unpacked24::MIN / 2 + ); + assert_eq!(Unpacked24::new(Unpacked24::MIN).to_i24(), Unpacked24::MIN); + assert_eq!(Unpacked24::new(0).to_i24(), 0i32); } #[test] fn i24_to_i32() { - assert_eq!(Padded24::new(Padded24::MAX).to_i32(), std::i32::MAX); - assert_eq!(Padded24::new(Padded24::MIN / 2).to_i32(), std::i32::MIN / 2); - assert_eq!(Padded24::new(Padded24::MIN).to_i32(), std::i32::MIN); - assert_eq!(Padded24::new(0).to_i32(), 0); + assert_eq!(Unpacked24::new(Unpacked24::MAX).to_i32(), std::i32::MAX); + assert_eq!( + Unpacked24::new(Unpacked24::MIN / 2).to_i32(), + std::i32::MIN / 2 + ); + assert_eq!(Unpacked24::new(Unpacked24::MIN).to_i32(), std::i32::MIN); + assert_eq!(Unpacked24::new(0).to_i32(), 0); } #[test] fn i24_to_u16() { - assert_eq!(Padded24::new(Padded24::MAX).to_u16(), std::u16::MAX); + assert_eq!(Unpacked24::new(Unpacked24::MAX).to_u16(), std::u16::MAX); // half of the int max will be 3/4 of the uint max assert_eq!( - Padded24::new(Padded24::MAX / 2).to_u16(), + Unpacked24::new(Unpacked24::MAX / 2).to_u16(), (std::u16::MAX as f32 / 4.0 * 3.0).round() as u16 ); - assert_eq!(Padded24::new(Padded24::MIN).to_u16(), std::u16::MIN); + assert_eq!(Unpacked24::new(Unpacked24::MIN).to_u16(), std::u16::MIN); } #[test] fn i24_to_f32() { - let max = Padded24::new(Padded24::MAX); - let min = Padded24::new(Padded24::MIN); + let max = Unpacked24::new(Unpacked24::MAX); + let min = Unpacked24::new(Unpacked24::MIN); assert_eq!(max.to_f32(), 1.0f32); assert_eq!(max.to_f32() / 8.0, 0.125f32); assert_eq!(max.to_f32() / -16.0, -0.0625f32); assert_eq!(max.to_f32() / -4.0, -0.25f32); assert_eq!(min.to_f32(), -1.0f32); - assert_eq!(Padded24::new(0).to_f32(), 0f32); + assert_eq!(Unpacked24::new(0).to_f32(), 0f32); } #[test] @@ -397,6 +432,14 @@ mod test { assert_eq!(0i32.to_i16(), 0); } + #[test] + fn i32_to_i24() { + assert_eq!(std::i32::MAX.to_i24(), Unpacked24::MAX); + assert_eq!((std::i32::MIN / 2).to_i24(), Unpacked24::MIN / 2); + assert_eq!(std::i32::MIN.to_i24(), Unpacked24::MIN); + assert_eq!(0i32.to_i24(), Unpacked24::new(0)); + } + #[test] fn i32_to_i32() { assert_eq!(std::i32::MAX.to_i32(), std::i32::MAX); @@ -429,6 +472,12 @@ mod test { assert_eq!(32767i16.to_i16(), 32767); assert_eq!((-32768i16).to_i16(), -32768); } + #[test] + fn i16_to_i24() { + assert_eq!(0i16.to_i24(), 0i32); + assert_eq!(i16::MIN.to_i24(), Unpacked24::MIN); + assert_eq!(i16::MAX.to_i24(), Unpacked24::MAX); + } #[test] fn i16_to_i32() { @@ -462,6 +511,14 @@ mod test { assert_eq!(0u16.to_i16(), -32768); } + #[test] + fn u16_to_i24() { + assert_eq!(u16::MAX.to_i24(), Unpacked24::MAX); + assert_eq!(u16::MIN.to_i24(), Unpacked24::MIN); + assert_eq!(32768u16.to_i24(), 0i32); + assert_eq!(16384u16.to_i24(), Unpacked24::MIN / 2); + } + #[test] fn u16_to_i32() { assert_eq!(((std::u16::MAX as f32 / 2.0).round() as u16).to_i32(), 0); @@ -492,6 +549,18 @@ mod test { assert_eq!((-1.0f32).to_i16(), ::std::i16::MIN); } + #[test] + fn f32_to_i24() { + assert_eq!(1.0f32.to_i24(), Unpacked24::MAX); + assert_eq!( + (0.5f32).to_i24(), + (Unpacked24::MAX as f32 / 2.0).round() as i32 + ); + assert_eq!(0.0f32.to_i24(), 0i32); + assert_eq!((-0.5f32).to_i24(), Unpacked24::MIN / 2); + assert_eq!((-1.0f32).to_i24(), Unpacked24::MIN); + } + #[test] fn f32_to_i32() { assert_eq!(1.0f32.to_i32(), std::i32::MAX); From 1c30c5f6d059d632a8b6754788c2ef3c8de98bd7 Mon Sep 17 00:00:00 2001 From: Julien Tregoat Date: Tue, 8 Dec 2020 23:21:24 -0500 Subject: [PATCH 8/9] add linux 24 + 32 bit support --- src/host/alsa/mod.rs | 12 ++++++++---- src/samples_formats.rs | 25 +++++++++++++++---------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index d306519dd..4663a4640 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -321,18 +321,18 @@ impl Device { let hw_params = alsa::pcm::HwParams::any(&handle)?; // TODO: check endianess - const FORMATS: [(SampleFormat, alsa::pcm::Format); 3] = [ + const FORMATS: [(SampleFormat, alsa::pcm::Format); 5] = [ //SND_PCM_FORMAT_S8, //SND_PCM_FORMAT_U8, (SampleFormat::I16, alsa::pcm::Format::S16LE), //SND_PCM_FORMAT_S16_BE, (SampleFormat::U16, alsa::pcm::Format::U16LE), //SND_PCM_FORMAT_U16_BE, - //SND_PCM_FORMAT_S24_LE, + (SampleFormat::I24, alsa::pcm::Format::S24LE), //SND_PCM_FORMAT_S24_BE, //SND_PCM_FORMAT_U24_LE, //SND_PCM_FORMAT_U24_BE, - //SND_PCM_FORMAT_S32_LE, + (SampleFormat::I32, alsa::pcm::Format::S32LE), //SND_PCM_FORMAT_S32_BE, //SND_PCM_FORMAT_U32_LE, //SND_PCM_FORMAT_U32_BE, @@ -348,7 +348,7 @@ impl Device { //SND_PCM_FORMAT_MPEG, //SND_PCM_FORMAT_GSM, //SND_PCM_FORMAT_SPECIAL, - //SND_PCM_FORMAT_S24_3LE, + //(SampleFormat::I24, alsa::pcm::Format::S243LE), //SND_PCM_FORMAT_S24_3BE, //SND_PCM_FORMAT_U24_3LE, //SND_PCM_FORMAT_U24_3BE, @@ -975,12 +975,16 @@ fn set_hw_params_from_format<'a>( match sample_format { SampleFormat::I16 => alsa::pcm::Format::S16BE, SampleFormat::U16 => alsa::pcm::Format::U16BE, + SampleFormat::I24 => alsa::pcm::Format::S24BE, + SampleFormat::I32 => alsa::pcm::Format::S32BE, SampleFormat::F32 => alsa::pcm::Format::FloatBE, } } else { match sample_format { SampleFormat::I16 => alsa::pcm::Format::S16LE, SampleFormat::U16 => alsa::pcm::Format::U16LE, + SampleFormat::I24 => alsa::pcm::Format::S24LE, + SampleFormat::I32 => alsa::pcm::Format::S32LE, SampleFormat::F32 => alsa::pcm::Format::FloatLE, } }; diff --git a/src/samples_formats.rs b/src/samples_formats.rs index d43dd473f..952d60a5e 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -1,4 +1,4 @@ -use std::{mem, ops::Deref}; +use std::mem; /// Format that each sample has. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -195,21 +195,22 @@ impl Unpacked24 { const MAX: i32 = 8_388_607; const MIN: i32 = -8_388_608; - // assumes i24 has been correctly parsed already + /// Assumes bytes has been parsed to a rust i32 previously. Values outside + /// the 24 bit range will be truncated when converting to bytes. pub fn new(val: i32) -> Self { Unpacked24(val) } pub fn from_be_bytes(b: [u8; 3]) -> Self { let is_pos = b[0] & 0b1000_0000 == 0; - let extra_byte; + let sign_byte; if is_pos { - extra_byte = u8::MIN; + sign_byte = u8::MIN; } else { - extra_byte = u8::MAX; + sign_byte = u8::MAX; } - Unpacked24(i32::from_be_bytes([extra_byte, b[0], b[1], b[2]])) + Unpacked24(i32::from_be_bytes([sign_byte, b[0], b[1], b[2]])) } pub fn to_be_bytes(&self) -> [u8; 3] { @@ -224,14 +225,14 @@ impl Unpacked24 { pub fn from_le_bytes(b: [u8; 3]) -> Self { let is_pos = b[2] & 0b1000_0000 == 0; - let extra_byte; + let sign_byte; if is_pos { - extra_byte = u8::MIN; + sign_byte = u8::MIN; } else { - extra_byte = u8::MAX; + sign_byte = u8::MAX; } - Unpacked24(i32::from_le_bytes([b[0], b[1], b[2], extra_byte])) + Unpacked24(i32::from_le_bytes([b[0], b[1], b[2], sign_byte])) } pub fn to_le_bytes(&self) -> [u8; 3] { @@ -243,6 +244,10 @@ impl Unpacked24 { [byte1, byte2, byte3] } + + pub fn into_inner(&self) -> i32 { + self.0 + } } impl PartialEq for Unpacked24 { From d865d936871ad882aab58afaac39c517676dd52d Mon Sep 17 00:00:00 2001 From: Julien Tregoat Date: Sun, 10 Jan 2021 17:27:59 -0500 Subject: [PATCH 9/9] asio 32bit --- src/host/asio/device.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/host/asio/device.rs b/src/host/asio/device.rs index 1a1a1aace..a11e03b70 100644 --- a/src/host/asio/device.rs +++ b/src/host/asio/device.rs @@ -213,13 +213,8 @@ pub(crate) fn convert_data_type(ty: &sys::AsioSampleType) -> Option SampleFormat::I16, sys::AsioSampleType::ASIOSTFloat32MSB => SampleFormat::F32, sys::AsioSampleType::ASIOSTFloat32LSB => SampleFormat::F32, - // NOTE: While ASIO does not support these formats directly, the stream callback created by - // CPAL supports converting back and forth between the following. This is because many ASIO - // drivers only support `Int32` formats, while CPAL does not support this format at all. We - // allow for this implicit conversion temporarily until CPAL gets support for an `I32` - // format. - sys::AsioSampleType::ASIOSTInt32MSB => SampleFormat::I16, - sys::AsioSampleType::ASIOSTInt32LSB => SampleFormat::I16, + sys::AsioSampleType::ASIOSTInt32MSB => SampleFormat::I32, + sys::AsioSampleType::ASIOSTInt32LSB => SampleFormat::I32, _ => return None, }; Some(fmt)