diff --git a/examples/beep.rs b/examples/beep.rs index 696f3f0a1..f024d3373 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -40,7 +40,9 @@ 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(), + cpal::SampleFormat::I24 => unimplemented!(), } } diff --git a/examples/record_wav.rs b/examples/record_wav.rs index ac6d23779..e372e3d49 100644 --- a/examples/record_wav.rs +++ b/examples/record_wav.rs @@ -51,6 +51,12 @@ 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), + err_fn, + )?, cpal::SampleFormat::U16 => device.build_input_stream( &config.into(), move |data, _: &_| write_input_data::(data, &writer_2), @@ -70,9 +76,11 @@ 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, + cpal::SampleFormat::I24 => unimplemented!() } } 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/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) diff --git a/src/lib.rs b/src/lib.rs index b82a7c95a..f048e9ee7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,7 +99,9 @@ //! 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), +//! SampleFormat::I24 => unimplemented!(), //! }.unwrap(); //! //! fn write_silence(data: &mut [T], _: &cpal::OutputCallbackInfo) { @@ -157,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}; +pub use samples_formats::{Sample, SampleFormat, Unpacked24}; use std::convert::TryInto; use std::ops::{Div, Mul}; use std::time::Duration; @@ -603,6 +605,8 @@ impl SupportedStreamConfigRange { /// - f32 /// - i16 /// - u16 + /// - i32 + /// - i24 /// /// **Sample rate**: /// @@ -610,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, U16}; + use SampleFormat::{F32, I16, I32, U16, I24}; let cmp_stereo = (self.channels == 2).cmp(&(other.channels == 2)); if cmp_stereo != Equal { @@ -642,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 = @@ -693,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)); @@ -703,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 faa02319e..952d60a5e 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -5,6 +5,8 @@ use std::mem; pub enum SampleFormat { /// The value 0 corresponds to 0. I16, + I24, + I32, /// The value 0 corresponds to 32768. U16, /// The boundaries are (-1.0, 1.0). @@ -19,6 +21,8 @@ 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::(), } } } @@ -30,13 +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 i16 sample. - fn to_i16(&self) -> i16; /// 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) -> Unpacked24; + /// Converts this sample into a standard i32 sample. + fn to_i32(&self) -> i32; - /// Converts any sample type to this one by calling `to_i16`, `to_u16` or `to_f32`. - fn from(&S) -> Self + /// Converts any sample type to this one by calling `to_i16`, `to_i32`, `to_u16`, or `to_f32`. + fn from(sample: &S) -> Self where S: Sample; } @@ -49,6 +57,11 @@ unsafe impl Sample for u16 { self.to_i16().to_f32() } + #[inline] + fn to_u16(&self) -> u16 { + *self + } + #[inline] fn to_i16(&self) -> i16 { if *self >= 32768 { @@ -58,9 +71,13 @@ unsafe impl Sample for u16 { } } + fn to_i24(&self) -> Unpacked24 { + self.to_f32().to_i24() + } + #[inline] - fn to_u16(&self) -> u16 { - *self + fn to_i32(&self) -> i32 { + self.to_f32().to_i32() } #[inline] @@ -84,11 +101,6 @@ unsafe impl Sample for i16 { } } - #[inline] - fn to_i16(&self) -> i16 { - *self - } - #[inline] fn to_u16(&self) -> u16 { if *self < 0 { @@ -98,6 +110,21 @@ unsafe impl Sample for i16 { } } + #[inline] + fn to_i16(&self) -> i16 { + *self + } + + #[inline] + fn to_i24(&self) -> Unpacked24 { + self.to_f32().to_i24() + } + + #[inline] + fn to_i32(&self) -> i32 { + self.to_f32().to_i32() + } + #[inline] fn from(sample: &S) -> Self where @@ -115,6 +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 { @@ -124,9 +158,25 @@ unsafe impl Sample for f32 { } } + /// 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 + fn to_i24(&self) -> Unpacked24 { + let result: f32; + if self.is_sign_positive() { + result = self * Unpacked24::MAX as f32; + } else { + result = self.abs() * Unpacked24::MIN as f32; + } + Unpacked24(result.round() as i32) + } + + #[inline] + fn to_i32(&self) -> 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] @@ -138,9 +188,287 @@ unsafe impl Sample for f32 { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Unpacked24(i32); + +impl Unpacked24 { + const MAX: i32 = 8_388_607; + const MIN: i32 = -8_388_608; + + /// 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 sign_byte; + if is_pos { + sign_byte = u8::MIN; + } else { + sign_byte = u8::MAX; + } + + Unpacked24(i32::from_be_bytes([sign_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 sign_byte; + if is_pos { + sign_byte = u8::MIN; + } else { + sign_byte = u8::MAX; + } + + Unpacked24(i32::from_le_bytes([b[0], b[1], b[2], sign_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] + } + + pub fn into_inner(&self) -> i32 { + self.0 + } +} + +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] + 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 + } + } + + /// 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() + } + + #[inline] + fn to_i24(&self) -> Unpacked24 { + *self + } + + #[inline] + fn to_i32(&self) -> i32 { + self.to_f32().to_i32() + } + + #[inline] + fn from(sample: &S) -> Self + where + S: Sample, + { + sample.to_i24() + } +} + +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 { + (*self as f64 * (1.0 / -(::std::i32::MIN as f64))) as f32 + } else { + (*self as f64 * (1.0 / ::std::i32::MAX as f64)) as f32 + } + } + + /// 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) -> Unpacked24 { + self.to_f32().to_i24() + } + + #[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; + use super::{Sample, Unpacked24}; + + #[test] + fn i24_to_i16() { + 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() { + 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!(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!(Unpacked24::new(Unpacked24::MAX).to_u16(), std::u16::MAX); + // half of the int max will be 3/4 of the uint max + assert_eq!( + Unpacked24::new(Unpacked24::MAX / 2).to_u16(), + (std::u16::MAX as f32 / 4.0 * 3.0).round() as u16 + ); + assert_eq!(Unpacked24::new(Unpacked24::MIN).to_u16(), std::u16::MIN); + } + + #[test] + fn i24_to_f32() { + 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!(Unpacked24::new(0).to_f32(), 0f32); + } + + #[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_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); + 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); + 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); + } + + #[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() { @@ -149,6 +477,19 @@ 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() { + assert_eq!(0i16.to_i32(), 0); + assert_eq!(std::i16::MAX.to_i32(), std::i32::MAX); + assert_eq!(std::i16::MIN.to_i32(), std::i32::MIN); + } #[test] fn i16_to_u16() { @@ -162,6 +503,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); } @@ -174,6 +516,21 @@ 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); + assert_eq!(std::u16::MAX.to_i32(), std::i32::MAX); + assert_eq!(std::u16::MIN.to_i32(), std::i32::MIN); + } + #[test] fn u16_to_u16() { assert_eq!(0u16.to_u16(), 0); @@ -197,6 +554,28 @@ 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); + 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);