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]