From 5315cbd1b3c923584386165ffc3d41558d19b246 Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:21:00 +0100 Subject: [PATCH 1/6] added new sample format i24 --- src/host/asio/device.rs | 8 ++++-- src/samples_formats.rs | 58 +++++++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/host/asio/device.rs b/src/host/asio/device.rs index f503158e8..dd854136e 100644 --- a/src/host/asio/device.rs +++ b/src/host/asio/device.rs @@ -209,10 +209,14 @@ pub(crate) fn convert_data_type(ty: &sys::AsioSampleType) -> Option SampleFormat::I16, sys::AsioSampleType::ASIOSTInt16LSB => SampleFormat::I16, - sys::AsioSampleType::ASIOSTFloat32MSB => SampleFormat::F32, - sys::AsioSampleType::ASIOSTFloat32LSB => SampleFormat::F32, + sys::AsioSampleType::ASIOSTInt24MSB => SampleFormat::I24, + sys::AsioSampleType::ASIOSTInt24LSB => SampleFormat::I24, sys::AsioSampleType::ASIOSTInt32MSB => SampleFormat::I32, sys::AsioSampleType::ASIOSTInt32LSB => SampleFormat::I32, + sys::AsioSampleType::ASIOSTFloat32MSB => SampleFormat::F32, + sys::AsioSampleType::ASIOSTFloat32LSB => SampleFormat::F32, + sys::AsioSampleType::ASIOSTFloat64MSB => SampleFormat::F64, + sys::AsioSampleType::ASIOSTFloat64LSB => SampleFormat::F64, _ => return None, }; Some(fmt) diff --git a/src/samples_formats.rs b/src/samples_formats.rs index bf2197f91..d5610243a 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -29,12 +29,13 @@ pub enum SampleFormat { /// `i16` with a valid range of `i16::MIN..=i16::MAX` with `0` being the origin. I16, - // /// `I24` with a valid range of '-(1 << 23)..(1 << 23)' with `0` being the origin - // I24, + /// `I24` with a valid range of '-(1 << 23)..(1 << 23)' with `0` being the origin + I24, + /// `i32` with a valid range of `i32::MIN..=i32::MAX` with `0` being the origin. I32, - // /// `I24` with a valid range of '-(1 << 47)..(1 << 47)' with `0` being the origin + // /// `I48` with a valid range of '-(1 << 47)..(1 << 47)' with `0` being the origin // I48, /// `i64` with a valid range of `i64::MIN..=i64::MAX` with `0` being the origin. I64, @@ -45,13 +46,15 @@ pub enum SampleFormat { /// `u16` with a valid range of `u16::MIN..=u16::MAX` with `1 << 15 == 32768` being the origin. U16, - // /// `U24` with a valid range of '0..16777216' with `1 << 23 == 8388608` being the origin + /// `U24` with a valid range of '0..16777216' with `1 << 23 == 8388608` being the origin // U24, + /// `u32` with a valid range of `u32::MIN..=u32::MAX` with `1 << 31` being the origin. U32, - // /// `U48` with a valid range of '0..(1 << 48)' with `1 << 47` being the origin + /// `U48` with a valid range of '0..(1 << 48)' with `1 << 47` being the origin // U48, + /// `u64` with a valid range of `u64::MIN..=u64::MAX` with `1 << 63` being the origin. U64, @@ -63,16 +66,21 @@ pub enum SampleFormat { } impl SampleFormat { - /// Returns the size in bytes of a sample of this format. + /// Returns the size in bytes of a sample of this format. This corresponds to + /// the internal size of the rust primitives that are used to represent this + /// sample format (e.g., i24 has size of i32). #[inline] #[must_use] pub fn sample_size(&self) -> usize { match *self { SampleFormat::I8 | SampleFormat::U8 => mem::size_of::(), SampleFormat::I16 | SampleFormat::U16 => mem::size_of::(), - // SampleFormat::I24 | SampleFormat::U24 => 3, + SampleFormat::I24 => mem::size_of::(), // Use internal size of i32 + // SampleFormat::U24 => 3, SampleFormat::I32 | SampleFormat::U32 => mem::size_of::(), - // SampleFormat::I48 | SampleFormat::U48 => 6, + + // SampleFormat::I48 => 6, + // SampleFormat::U48 => 6, SampleFormat::I64 | SampleFormat::U64 => mem::size_of::(), SampleFormat::F32 => mem::size_of::(), SampleFormat::F64 => mem::size_of::(), @@ -82,20 +90,28 @@ impl SampleFormat { #[inline] #[must_use] pub fn is_int(&self) -> bool { - //matches!(*self, SampleFormat::I8 | SampleFormat::I16 | SampleFormat::I24 | SampleFormat::I32 | SampleFormat::I48 | SampleFormat::I64) matches!( *self, - SampleFormat::I8 | SampleFormat::I16 | SampleFormat::I32 | SampleFormat::I64 + SampleFormat::I8 + | SampleFormat::I16 + | SampleFormat::I24 + | SampleFormat::I32 + // | SampleFormat::I48 + | SampleFormat::I64 ) } #[inline] #[must_use] pub fn is_uint(&self) -> bool { - //matches!(*self, SampleFormat::U8 | SampleFormat::U16 | SampleFormat::U24 | SampleFormat::U32 | SampleFormat::U48 | SampleFormat::U64) matches!( *self, - SampleFormat::U8 | SampleFormat::U16 | SampleFormat::U32 | SampleFormat::U64 + SampleFormat::U8 + | SampleFormat::U16 + // | SampleFormat::U24 + | SampleFormat::U32 + // | SampleFormat::U48 + | SampleFormat::U64 ) } @@ -111,7 +127,7 @@ impl Display for SampleFormat { match *self { SampleFormat::I8 => "i8", SampleFormat::I16 => "i16", - // SampleFormat::I24 => "i24", + SampleFormat::I24 => "i24", SampleFormat::I32 => "i32", // SampleFormat::I48 => "i48", SampleFormat::I64 => "i64", @@ -140,13 +156,17 @@ impl SizedSample for i16 { const FORMAT: SampleFormat = SampleFormat::I16; } -// impl SizedSample for I24 { const FORMAT: SampleFormat = SampleFormat::I24; } +impl SizedSample for I24 { + const FORMAT: SampleFormat = SampleFormat::I24; +} impl SizedSample for i32 { const FORMAT: SampleFormat = SampleFormat::I32; } -// impl SizedSample for I48 { const FORMAT: SampleFormat = SampleFormat::I48; } +// impl SizedSample for I48 { +// const FORMAT: SampleFormat = SampleFormat::I48; +// } impl SizedSample for i64 { const FORMAT: SampleFormat = SampleFormat::I64; @@ -160,13 +180,17 @@ impl SizedSample for u16 { const FORMAT: SampleFormat = SampleFormat::U16; } -// impl SizedSample for U24 { const FORMAT: SampleFormat = SampleFormat::U24; } +// impl SizedSample for U24 { +// const FORMAT: SampleFormat = SampleFormat::U24; +// } impl SizedSample for u32 { const FORMAT: SampleFormat = SampleFormat::U32; } -// impl SizedSample for U48 { const FORMAT: SampleFormat = SampleFormat::U48; } +// impl SizedSample for U48 { +// const FORMAT: SampleFormat = SampleFormat::U48; +// } impl SizedSample for u64 { const FORMAT: SampleFormat = SampleFormat::U64; From 5361c2a84243f3e5d1abed133d2b69319b17ea1c Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:22:36 +0100 Subject: [PATCH 2/6] added output callback for i24 --- src/host/asio/stream.rs | 180 ++++++++++++++++++++++++++++++++++------ 1 file changed, 153 insertions(+), 27 deletions(-) diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index c9070005a..b37516281 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -1,6 +1,8 @@ extern crate asio_sys as sys; extern crate num_traits; +use crate::I24; + use self::num_traits::PrimInt; use super::Device; use crate::{ @@ -315,36 +317,25 @@ impl Device { D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, F: Fn(A, A) -> A, { - // 1. Render interleaved buffer from callback. let interleaved: &mut [A] = cast_slice_mut(interleaved); - let data = interleaved.as_mut_ptr() as *mut (); - let len = interleaved.len(); - let mut data = Data::from_parts(data, len, format); - let callback = system_time_to_stream_instant(asio_info.system_time); - let n_frames = asio_stream.buffer_size as usize; - let delay = frames_to_duration(n_frames, sample_rate); - let playback = callback - .add(delay) - .expect("`playback` occurs beyond representation supported by `StreamInstant`"); - let timestamp = crate::OutputStreamTimestamp { callback, playback }; - let info = OutputCallbackInfo { timestamp }; - data_callback(&mut data, &info); - - // 2. Silence ASIO channels if necessary. - let n_channels = interleaved.len() / n_frames; + apply_output_callback_to_data::( + data_callback, + interleaved, + asio_stream, + asio_info, + sample_rate, + format, + ); + let n_channels = interleaved.len() / asio_stream.buffer_size as usize; let buffer_index = asio_info.buffer_index as usize; - if silence_asio_buffer { - for ch_ix in 0..n_channels { - let asio_channel = - asio_channel_slice_mut::(asio_stream, buffer_index, ch_ix); - asio_channel.align_to_mut::().1.fill(0); - } - } - // 3. Write interleaved samples to ASIO channels, one channel at a time. + // Write interleaved samples to ASIO channels, one channel at a time. for ch_ix in 0..n_channels { let asio_channel = - asio_channel_slice_mut::(asio_stream, buffer_index, ch_ix); + asio_channel_slice_mut::(asio_stream, buffer_index, ch_ix, None); + if silence_asio_buffer { + asio_channel.align_to_mut::().1.fill(0); + } for (frame, s_asio) in interleaved.chunks(n_channels).zip(asio_channel) { *s_asio = mix_samples(*s_asio, frame[ch_ix]); } @@ -477,6 +468,30 @@ impl Device { ); } + (SampleFormat::I24, &sys::AsioSampleType::ASIOSTInt24LSB) => { + process_output_callback_i24::<_>( + &mut data_callback, + &mut interleaved, + silence, + true, + asio_stream, + callback_info, + config.sample_rate, + ); + } + + (SampleFormat::I24, &sys::AsioSampleType::ASIOSTInt24MSB) => { + process_output_callback_i24::<_>( + &mut data_callback, + &mut interleaved, + silence, + false, + asio_stream, + callback_info, + config.sample_rate, + ); + } + unsupported_format_pair => unreachable!( "`build_output_stream_raw` should have returned with unsupported \ format {:?}", @@ -655,7 +670,7 @@ fn check_config( } // unsigned formats are not supported by asio match sample_format { - SampleFormat::I16 | SampleFormat::I32 | SampleFormat::F32 => (), + SampleFormat::I16 | SampleFormat::I24 | SampleFormat::I32 | SampleFormat::F32 => (), _ => return Err(BuildStreamError::StreamConfigNotSupported), } if *channels > num_asio_channels { @@ -698,10 +713,12 @@ unsafe fn asio_channel_slice_mut( asio_stream: &mut sys::AsioStream, buffer_index: usize, channel_index: usize, + requested_channel_length: Option, ) -> &mut [T] { + let channel_length = requested_channel_length.unwrap_or(asio_stream.buffer_size as usize); let buff_ptr: *mut T = asio_stream.buffer_infos[channel_index].buffers[buffer_index as usize] as *mut _; - std::slice::from_raw_parts_mut(buff_ptr, asio_stream.buffer_size as usize) + std::slice::from_raw_parts_mut(buff_ptr, channel_length) } fn build_stream_err(e: sys::AsioError) -> BuildStreamError { @@ -716,3 +733,112 @@ fn build_stream_err(e: sys::AsioError) -> BuildStreamError { } } } + +/// Convert i24 bytes to i32 +fn i24_bytes_to_i32(i24_bytes: &[u8; 3], little_endian: bool) -> i32 { + let sample = if little_endian { + i32::from_le_bytes([i24_bytes[0], i24_bytes[1], i24_bytes[2], 0u8]) + } else { + i32::from_le_bytes([i24_bytes[2], i24_bytes[1], i24_bytes[0], 0u8]) + }; + if sample & 0x800000 != 0 { + sample | -0x1000000 + } else { + sample + } +} + +/// Only for i24 formats +unsafe fn process_output_callback_i24( + data_callback: &mut D, + interleaved: &mut [u8], + silence_asio_buffer: bool, + little_endian: bool, + asio_stream: &mut sys::AsioStream, + asio_info: &sys::CallbackInfo, + sample_rate: crate::SampleRate, +) where + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, +{ + let format = SampleFormat::I24; + let interleaved: &mut [I24] = cast_slice_mut(interleaved); + apply_output_callback_to_data::( + data_callback, + interleaved, + asio_stream, + asio_info, + sample_rate, + format, + ); + + // Size of samples in the ASIO buffer (has to be 3 in this case) + let asio_sample_size_bytes = 3; + let n_channels = interleaved.len() / asio_stream.buffer_size as usize; + let buffer_index = asio_info.buffer_index as usize; + + // Write interleaved samples to ASIO channels, one channel at a time. + for ch_ix in 0..n_channels { + // Take channel as u8 array ([u8; 3] packets to represent i24) + let asio_channel = asio_channel_slice_mut( + asio_stream, + buffer_index, + ch_ix, + Some(asio_stream.buffer_size as usize * asio_sample_size_bytes), + ); + + if silence_asio_buffer { + asio_channel.align_to_mut::().1.fill(0); + } + + // Fill in every channel from the interleaved vector + let frame: Vec<&I24> = interleaved.iter().skip(ch_ix).step_by(n_channels).collect(); + + for (index, channel_sample) in asio_channel.chunks_mut(asio_sample_size_bytes).enumerate() { + // Add samples from buffer if no silence was applied, otherwise just overwrite + let result = if silence_asio_buffer { + frame[index].inner() + } else { + let sample = i24_bytes_to_i32( + &[channel_sample[0], channel_sample[1], channel_sample[2]], + little_endian, + ); + (frame[index].inner() + sample).clamp(-8388608, 8388607) + }; + let bytes = result.to_le_bytes(); + if little_endian { + channel_sample[0] = bytes[0]; + channel_sample[1] = bytes[1]; + channel_sample[2] = bytes[2]; + } else { + channel_sample[2] = bytes[0]; + channel_sample[1] = bytes[1]; + channel_sample[0] = bytes[2]; + } + } + } +} +/// Apply the output callback to the interleaved buffer. +unsafe fn apply_output_callback_to_data( + data_callback: &mut D, + interleaved: &mut [A], + asio_stream: &mut sys::AsioStream, + asio_info: &sys::CallbackInfo, + sample_rate: crate::SampleRate, + sample_format: SampleFormat, +) where + A: Copy, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, +{ + let data = interleaved.as_mut_ptr() as *mut (); + let len = interleaved.len(); + let mut data = Data::from_parts(data, len, sample_format); + let callback = system_time_to_stream_instant(asio_info.system_time); + let n_frames = asio_stream.buffer_size as usize; + let delay = frames_to_duration(n_frames, sample_rate); + let playback = callback + .add(delay) + .expect("`playback` occurs beyond representation supported by `StreamInstant`"); + let timestamp = crate::OutputStreamTimestamp { callback, playback }; + let info = OutputCallbackInfo { timestamp }; + data_callback(&mut data, &info); +} From 9757d9dee96a4f523bff52f265e42526213ea52b Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:23:43 +0100 Subject: [PATCH 3/6] added input callback i24 --- src/host/asio/stream.rs | 127 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 13 deletions(-) diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index b37516281..e0a827ad1 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -105,24 +105,22 @@ impl Device { let n_channels = interleaved.len() / n_frames; let buffer_index = asio_info.buffer_index as usize; for ch_ix in 0..n_channels { - let asio_channel = asio_channel_slice::(asio_stream, buffer_index, ch_ix); + let asio_channel = + asio_channel_slice::(asio_stream, buffer_index, ch_ix, None); for (frame, s_asio) in interleaved.chunks_mut(n_channels).zip(asio_channel) { frame[ch_ix] = from_endianness(*s_asio); } } // 2. Deliver the interleaved buffer to the callback. - let data = interleaved.as_mut_ptr() as *mut (); - let len = interleaved.len(); - let data = Data::from_parts(data, len, format); - let callback = system_time_to_stream_instant(asio_info.system_time); - let delay = frames_to_duration(n_frames, sample_rate); - let capture = callback - .sub(delay) - .expect("`capture` occurs before origin of alsa `StreamInstant`"); - let timestamp = crate::InputStreamTimestamp { callback, capture }; - let info = InputCallbackInfo { timestamp }; - data_callback(&data, &info); + apply_input_callback_to_data::( + data_callback, + interleaved, + asio_stream, + asio_info, + sample_rate, + format, + ); } match (&stream_type, sample_format) { @@ -218,6 +216,27 @@ impl Device { ); } + (&sys::AsioSampleType::ASIOSTInt24LSB, SampleFormat::I24) => { + process_input_callback_i24::( + &mut data_callback, + &mut interleaved, + asio_stream, + callback_info, + config.sample_rate, + true, + ); + } + (&sys::AsioSampleType::ASIOSTInt24MSB, SampleFormat::I24) => { + process_input_callback_i24::( + &mut data_callback, + &mut interleaved, + asio_stream, + callback_info, + config.sample_rate, + false, + ); + } + unsupported_format_pair => unreachable!( "`build_input_stream_raw` should have returned with unsupported \ format {:?}", @@ -702,10 +721,12 @@ unsafe fn asio_channel_slice( asio_stream: &sys::AsioStream, buffer_index: usize, channel_index: usize, + requested_channel_length: Option, ) -> &[T] { + let channel_length = requested_channel_length.unwrap_or(asio_stream.buffer_size as usize); let buff_ptr: *const T = asio_stream.buffer_infos[channel_index].buffers[buffer_index as usize] as *const _; - std::slice::from_raw_parts(buff_ptr, asio_stream.buffer_size as usize) + std::slice::from_raw_parts(buff_ptr, channel_length) } /// Shorthand for retrieving the asio buffer slice associated with a channel. @@ -817,6 +838,61 @@ unsafe fn process_output_callback_i24( } } } + +/// 1. Write from the ASIO buffer to the interleaved CPAL buffer. +/// 2. Deliver the CPAL buffer to the user callback. +unsafe fn process_input_callback_i24( + data_callback: &mut D, + interleaved: &mut [u8], + asio_stream: &sys::AsioStream, + asio_info: &sys::CallbackInfo, + sample_rate: crate::SampleRate, + little_endian: bool, +) where + A: Copy, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, +{ + let format = SampleFormat::I24; + + // 1. Write the ASIO channels to the CPAL buffer. + let interleaved: &mut [I24] = cast_slice_mut(interleaved); + let n_frames = asio_stream.buffer_size as usize; + let n_channels = interleaved.len() / n_frames; + let buffer_index = asio_info.buffer_index as usize; + let asio_sample_size_bytes = 3; + + for ch_ix in 0..n_channels { + let asio_channel = asio_channel_slice::( + asio_stream, + buffer_index, + ch_ix, + Some(n_frames * asio_sample_size_bytes), + ); + let mut frame: Vec<&mut I24> = interleaved + .iter_mut() + .skip(ch_ix) + .step_by(n_channels) + .collect(); + for (index, channel_sample) in asio_channel.chunks(asio_sample_size_bytes).enumerate() { + let sample = i24_bytes_to_i32( + &[channel_sample[0], channel_sample[1], channel_sample[2]], + little_endian, + ); + *frame[index] = I24::new(sample).unwrap(); + } + } + + // 2. Deliver the interleaved buffer to the callback. + apply_input_callback_to_data::( + data_callback, + interleaved, + asio_stream, + asio_info, + sample_rate, + format, + ); +} + /// Apply the output callback to the interleaved buffer. unsafe fn apply_output_callback_to_data( data_callback: &mut D, @@ -842,3 +918,28 @@ unsafe fn apply_output_callback_to_data( let info = OutputCallbackInfo { timestamp }; data_callback(&mut data, &info); } + +/// Apply the input callback to the interleaved buffer. +unsafe fn apply_input_callback_to_data( + data_callback: &mut D, + interleaved: &mut [A], + asio_stream: &sys::AsioStream, + asio_info: &sys::CallbackInfo, + sample_rate: crate::SampleRate, + format: SampleFormat, +) where + A: Copy, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, +{ + let data = interleaved.as_mut_ptr() as *mut (); + let len = interleaved.len(); + let data = Data::from_parts(data, len, format); + let callback = system_time_to_stream_instant(asio_info.system_time); + let delay = frames_to_duration(asio_stream.buffer_size as usize, sample_rate); + let capture = callback + .sub(delay) + .expect("`capture` occurs before origin of alsa `StreamInstant`"); + let timestamp = crate::InputStreamTimestamp { callback, capture }; + let info = InputCallbackInfo { timestamp }; + data_callback(&data, &info); +} From c79edf37ecef381cd30bc0745afa5910f89d2224 Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:29:54 +0100 Subject: [PATCH 4/6] minor cleanup --- src/host/asio/stream.rs | 28 +++++++++++++++++----------- src/samples_formats.rs | 8 ++++---- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index e0a827ad1..1ed6496cc 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -717,6 +717,9 @@ fn from_be(t: T) -> T { } /// Shorthand for retrieving the asio buffer slice associated with a channel. +/// +/// The channel length is automatically inferred from the buffer size or some +/// value can be passed to enforce a certain length (for odd sized sample formats) unsafe fn asio_channel_slice( asio_stream: &sys::AsioStream, buffer_index: usize, @@ -730,6 +733,9 @@ unsafe fn asio_channel_slice( } /// Shorthand for retrieving the asio buffer slice associated with a channel. +/// +/// The channel length is automatically inferred from the buffer size or some +/// value can be passed to enforce a certain length (for odd sized sample formats) unsafe fn asio_channel_slice_mut( asio_stream: &mut sys::AsioStream, buffer_index: usize, @@ -769,7 +775,6 @@ fn i24_bytes_to_i32(i24_bytes: &[u8; 3], little_endian: bool) -> i32 { } } -/// Only for i24 formats unsafe fn process_output_callback_i24( data_callback: &mut D, interleaved: &mut [u8], @@ -839,8 +844,6 @@ unsafe fn process_output_callback_i24( } } -/// 1. Write from the ASIO buffer to the interleaved CPAL buffer. -/// 2. Deliver the CPAL buffer to the user callback. unsafe fn process_input_callback_i24( data_callback: &mut D, interleaved: &mut [u8], @@ -905,12 +908,13 @@ unsafe fn apply_output_callback_to_data( A: Copy, D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, { - let data = interleaved.as_mut_ptr() as *mut (); - let len = interleaved.len(); - let mut data = Data::from_parts(data, len, sample_format); + let mut data = Data::from_parts( + interleaved.as_mut_ptr() as *mut (), + interleaved.len(), + sample_format, + ); let callback = system_time_to_stream_instant(asio_info.system_time); - let n_frames = asio_stream.buffer_size as usize; - let delay = frames_to_duration(n_frames, sample_rate); + let delay = frames_to_duration(asio_stream.buffer_size as usize, sample_rate); let playback = callback .add(delay) .expect("`playback` occurs beyond representation supported by `StreamInstant`"); @@ -931,9 +935,11 @@ unsafe fn apply_input_callback_to_data( A: Copy, D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, { - let data = interleaved.as_mut_ptr() as *mut (); - let len = interleaved.len(); - let data = Data::from_parts(data, len, format); + let data = Data::from_parts( + interleaved.as_mut_ptr() as *mut (), + interleaved.len(), + format, + ); let callback = system_time_to_stream_instant(asio_info.system_time); let delay = frames_to_duration(asio_stream.buffer_size as usize, sample_rate); let capture = callback diff --git a/src/samples_formats.rs b/src/samples_formats.rs index d5610243a..27e4fa46f 100644 --- a/src/samples_formats.rs +++ b/src/samples_formats.rs @@ -75,12 +75,12 @@ impl SampleFormat { match *self { SampleFormat::I8 | SampleFormat::U8 => mem::size_of::(), SampleFormat::I16 | SampleFormat::U16 => mem::size_of::(), - SampleFormat::I24 => mem::size_of::(), // Use internal size of i32 - // SampleFormat::U24 => 3, + SampleFormat::I24 => mem::size_of::(), + // SampleFormat::U24 => mem::size_of::(), SampleFormat::I32 | SampleFormat::U32 => mem::size_of::(), - // SampleFormat::I48 => 6, - // SampleFormat::U48 => 6, + // SampleFormat::I48 => mem::size_of::(), + // SampleFormat::U48 => mem::size_of::(), SampleFormat::I64 | SampleFormat::U64 => mem::size_of::(), SampleFormat::F32 => mem::size_of::(), SampleFormat::F64 => mem::size_of::(), From 95022fa874aeeb8abac22506124cec79d0ffdc85 Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:33:17 +0100 Subject: [PATCH 5/6] added i24 to examples --- examples/android.rs | 4 ++-- examples/beep.rs | 4 ++-- examples/synth_tones.rs | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/android.rs b/examples/android.rs index b58731dbf..beb46acb1 100644 --- a/examples/android.rs +++ b/examples/android.rs @@ -5,7 +5,7 @@ extern crate cpal; use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, - SizedSample, + SizedSample, I24, }; use cpal::{FromSample, Sample}; @@ -22,7 +22,7 @@ fn main() { match config.sample_format() { cpal::SampleFormat::I8 => run::(&device, &config.into()).unwrap(), cpal::SampleFormat::I16 => run::(&device, &config.into()).unwrap(), - // cpal::SampleFormat::I24 => run::(&device, &config.into()).unwrap(), + cpal::SampleFormat::I24 => run::(&device, &config.into()).unwrap(), cpal::SampleFormat::I32 => run::(&device, &config.into()).unwrap(), // cpal::SampleFormat::I48 => run::(&device, &config.into()).unwrap(), cpal::SampleFormat::I64 => run::(&device, &config.into()).unwrap(), diff --git a/examples/beep.rs b/examples/beep.rs index 7d3b23d88..fb1ff45a3 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -1,7 +1,7 @@ use clap::Parser; use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, - FromSample, Sample, SizedSample, + FromSample, Sample, SizedSample, I24, }; #[derive(Parser, Debug)] @@ -78,7 +78,7 @@ fn main() -> anyhow::Result<()> { match config.sample_format() { cpal::SampleFormat::I8 => run::(&device, &config.into()), cpal::SampleFormat::I16 => run::(&device, &config.into()), - // cpal::SampleFormat::I24 => run::(&device, &config.into()), + cpal::SampleFormat::I24 => run::(&device, &config.into()), cpal::SampleFormat::I32 => run::(&device, &config.into()), // cpal::SampleFormat::I48 => run::(&device, &config.into()), cpal::SampleFormat::I64 => run::(&device, &config.into()), diff --git a/examples/synth_tones.rs b/examples/synth_tones.rs index c83f816d9..fedfdb808 100644 --- a/examples/synth_tones.rs +++ b/examples/synth_tones.rs @@ -8,7 +8,7 @@ extern crate cpal; use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, - SizedSample, + SizedSample, I24, }; use cpal::{FromSample, Sample}; @@ -98,6 +98,7 @@ where match config.sample_format() { cpal::SampleFormat::I8 => make_stream::(&device, &config.into()), cpal::SampleFormat::I16 => make_stream::(&device, &config.into()), + cpal::SampleFormat::I24 => make_stream::(&device, &config.into()), cpal::SampleFormat::I32 => make_stream::(&device, &config.into()), cpal::SampleFormat::I64 => make_stream::(&device, &config.into()), cpal::SampleFormat::U8 => make_stream::(&device, &config.into()), From 271d1588f5f70f99a5bec6b39f730fdaa51b7929 Mon Sep 17 00:00:00 2001 From: Nicolas Franco Gomez <80042895+nico-franco-gomez@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:56:47 +0100 Subject: [PATCH 6/6] avoided allocations in callback --- src/host/asio/stream.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index 1ed6496cc..445310c68 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -817,18 +817,19 @@ unsafe fn process_output_callback_i24( } // Fill in every channel from the interleaved vector - let frame: Vec<&I24> = interleaved.iter().skip(ch_ix).step_by(n_channels).collect(); - - for (index, channel_sample) in asio_channel.chunks_mut(asio_sample_size_bytes).enumerate() { + for (channel_sample, sample_in_buffer) in asio_channel + .chunks_mut(asio_sample_size_bytes) + .zip(interleaved.iter().skip(ch_ix).step_by(n_channels)) + { // Add samples from buffer if no silence was applied, otherwise just overwrite let result = if silence_asio_buffer { - frame[index].inner() + sample_in_buffer.inner() } else { let sample = i24_bytes_to_i32( &[channel_sample[0], channel_sample[1], channel_sample[2]], little_endian, ); - (frame[index].inner() + sample).clamp(-8388608, 8388607) + (sample_in_buffer.inner() + sample).clamp(-8388608, 8388607) }; let bytes = result.to_le_bytes(); if little_endian { @@ -871,17 +872,15 @@ unsafe fn process_input_callback_i24( ch_ix, Some(n_frames * asio_sample_size_bytes), ); - let mut frame: Vec<&mut I24> = interleaved - .iter_mut() - .skip(ch_ix) - .step_by(n_channels) - .collect(); - for (index, channel_sample) in asio_channel.chunks(asio_sample_size_bytes).enumerate() { + for (channel_sample, sample_in_buffer) in asio_channel + .chunks(asio_sample_size_bytes) + .zip(interleaved.iter_mut().skip(ch_ix).step_by(n_channels)) + { let sample = i24_bytes_to_i32( &[channel_sample[0], channel_sample[1], channel_sample[2]], little_endian, ); - *frame[index] = I24::new(sample).unwrap(); + *sample_in_buffer = I24::new(sample).unwrap(); } }