diff --git a/examples/beep.rs b/examples/beep.rs index d143fff..35718d2 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -19,27 +19,15 @@ fn main() -> Result<(), anyhow::Error> { (sample_clock * 440.0 * 2.0 * 3.141592 / sample_rate).sin() }; - let err_fn = |err| { - eprintln!("an error occurred on stream: {}", err); + let err_fn = |err| eprintln!("an error occurred on stream: {}", err); + + let data_fn = move |data: &mut cpal::Data| match data.sample_format() { + cpal::SampleFormat::F32 => write_data::(data, channels, &mut next_value), + cpal::SampleFormat::I16 => write_data::(data, channels, &mut next_value), + cpal::SampleFormat::U16 => write_data::(data, channels, &mut next_value), }; - let stream = match format.data_type { - cpal::SampleFormat::F32 => device.build_output_stream( - &format, - move |mut data| write_data::(&mut *data, channels, &mut next_value), - err_fn, - ), - cpal::SampleFormat::I16 => device.build_output_stream( - &format, - move |mut data| write_data::(&mut *data, channels, &mut next_value), - err_fn, - ), - cpal::SampleFormat::U16 => device.build_output_stream( - &format, - move |mut data| write_data::(&mut *data, channels, &mut next_value), - err_fn, - ), - }?; + let stream = device.build_output_stream(&format, data_fn, err_fn)?; stream.play()?; @@ -48,10 +36,11 @@ fn main() -> Result<(), anyhow::Error> { Ok(()) } -fn write_data(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> f32) +fn write_data(output: &mut cpal::Data, channels: usize, next_sample: &mut dyn FnMut() -> f32) where T: cpal::Sample, { + let output = output.as_slice_mut::().expect("unexpected sample type"); for frame in output.chunks_mut(channels) { let value: T = cpal::Sample::from::(&next_sample()); for sample in frame.iter_mut() { diff --git a/examples/feedback.rs b/examples/feedback.rs index 6697718..6e8c48e 100644 --- a/examples/feedback.rs +++ b/examples/feedback.rs @@ -47,46 +47,40 @@ fn main() -> Result<(), anyhow::Error> { producer.push(0.0).unwrap(); } + let input_data_fn = move |data: &cpal::Data| { + let mut output_fell_behind = false; + let data = data.as_slice::().expect("unexpected sample type"); + for &sample in data { + if producer.push(sample).is_err() { + output_fell_behind = true; + } + } + if output_fell_behind { + eprintln!("output stream fell behind: try increasing latency"); + } + }; + + let output_data_fn = move |data: &mut cpal::Data| { + let mut input_fell_behind = None; + let data = data.as_slice_mut::().expect("unexpected sample type"); + for sample in data { + *sample = match consumer.pop() { + Ok(s) => s, + Err(err) => { + input_fell_behind = Some(err); + 0.0 + }, + }; + } + if let Some(err) = input_fell_behind { + eprintln!("input stream fell behind: {:?}: try increasing latency", err); + } + }; + // Build streams. println!("Attempting to build both streams with `{:?}`.", format); - let input_stream = input_device.build_input_stream( - &format, - move |data| { - let mut output_fell_behind = false; - for &sample in data.iter() { - if producer.push(sample).is_err() { - output_fell_behind = true; - } - } - if output_fell_behind { - eprintln!("output stream fell behind: try increasing latency"); - } - }, - |err| { - eprintln!("an error occurred on stream: {}", err); - }, - )?; - let output_stream = output_device.build_output_stream( - &format, - move |mut data| { - let mut input_fell_behind = None; - for sample in data.iter_mut() { - *sample = match consumer.pop() { - Ok(s) => s, - Err(err) => { - input_fell_behind = Some(err); - 0.0 - }, - }; - } - if let Some(err) = input_fell_behind { - eprintln!("input stream fell behind: {:?}: try increasing latency", err); - } - }, - move |err| { - eprintln!("an error occurred on output stream: {}", err); - }, - )?; + let input_stream = input_device.build_input_stream(&format, input_data_fn, err_fn)?; + let output_stream = output_device.build_output_stream(&format, output_data_fn, err_fn)?; println!("Successfully built streams."); // Play the streams. @@ -105,3 +99,7 @@ fn main() -> Result<(), anyhow::Error> { println!("Done!"); Ok(()) } + +fn err_fn(err: cpal::StreamError) { + eprintln!("an error occurred on stream: {}", err); +} diff --git a/examples/record_wav.rs b/examples/record_wav.rs index fb48286..5d00b8e 100644 --- a/examples/record_wav.rs +++ b/examples/record_wav.rs @@ -40,23 +40,15 @@ fn main() -> Result<(), anyhow::Error> { eprintln!("an error occurred on stream: {}", err); }; - let stream = match format.data_type { - cpal::SampleFormat::F32 => device.build_input_stream( - &format, - move |data| write_input_data::(&*data, &writer_2), - err_fn, - ), - cpal::SampleFormat::I16 => device.build_input_stream( - &format, - move |data| write_input_data::(&*data, &writer_2), - err_fn, - ), - cpal::SampleFormat::U16 => device.build_input_stream( - &format, - move |data| write_input_data::(&*data, &writer_2), - err_fn, - ), - }?; + let data_fn = move |data: &cpal::Data| { + match data.sample_format() { + cpal::SampleFormat::F32 => write_input_data::(data, &writer_2), + cpal::SampleFormat::I16 => write_input_data::(data, &writer_2), + cpal::SampleFormat::U16 => write_input_data::(data, &writer_2), + } + }; + + let stream = device.build_input_stream(&format, data_fn, err_fn)?; stream.play()?; @@ -87,11 +79,12 @@ fn wav_spec_from_format(format: &cpal::Format) -> hound::WavSpec { type WavWriterHandle = Arc>>>>; -fn write_input_data(input: &[T], writer: &WavWriterHandle) +fn write_input_data(input: &cpal::Data, writer: &WavWriterHandle) where T: cpal::Sample, U: cpal::Sample + hound::Sample, { + let input = input.as_slice::().expect("unexpected sample format"); if let Ok(mut guard) = writer.try_lock() { if let Some(writer) = guard.as_mut() { for &sample in input.iter() { diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index f1724aa..512ed0c 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -5,15 +5,13 @@ use crate::{ BackendSpecificError, BuildStreamError, ChannelCount, + Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - InputData, - OutputData, PauseStreamError, PlayStreamError, - Sample, SampleFormat, SampleRate, StreamError, @@ -90,37 +88,31 @@ impl DeviceTrait for Device { Device::default_output_format(self) } - fn build_input_stream( + fn build_input_stream( &self, format: &Format, data_callback: D, error_callback: E, ) -> Result where - T: Sample, - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - // TODO: Consider removing `data_type` field from `Format` and removing this. - assert_eq!(format.data_type, T::FORMAT, "sample format mismatch"); let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_CAPTURE)?; let stream = Stream::new_input(Arc::new(stream_inner), data_callback, error_callback); Ok(stream) } - fn build_output_stream( + fn build_output_stream( &self, format: &Format, data_callback: D, error_callback: E, ) -> Result where - T: Sample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - // TODO: Consider removing `data_type` field from `Format` and removing this. - assert_eq!(format.data_type, T::FORMAT, "sample format mismatch"); let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_PLAYBACK)?; let stream = Stream::new_output(Arc::new(stream_inner), data_callback, error_callback); Ok(stream) @@ -564,14 +556,12 @@ struct StreamWorkerContext { buffer: Vec, } -fn input_stream_worker( +fn input_stream_worker( rx: TriggerReceiver, stream: &StreamInner, - data_callback: &mut (dyn FnMut(InputData) + Send + 'static), + data_callback: &mut (dyn FnMut(&Data) + Send + 'static), error_callback: &mut (dyn FnMut(StreamError) + Send + 'static), -) where - T: Sample, -{ +) { let mut ctxt = StreamWorkerContext::default(); loop { match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) { @@ -583,7 +573,7 @@ fn input_stream_worker( StreamType::Input, "expected input stream, but polling descriptors indicated output", ); - process_input::( + process_input( stream, &mut ctxt.buffer, available_frames, @@ -595,14 +585,12 @@ fn input_stream_worker( } } -fn output_stream_worker( +fn output_stream_worker( rx: TriggerReceiver, stream: &StreamInner, - data_callback: &mut (dyn FnMut(OutputData) + Send + 'static), + data_callback: &mut (dyn FnMut(&mut Data) + Send + 'static), error_callback: &mut (dyn FnMut(StreamError) + Send + 'static), -) where - T: Sample, -{ +) { let mut ctxt = StreamWorkerContext::default(); loop { match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) { @@ -614,7 +602,7 @@ fn output_stream_worker( StreamType::Output, "expected output stream, but polling descriptors indicated input", ); - process_output::( + process_output( stream, &mut ctxt.buffer, available_frames, @@ -729,15 +717,13 @@ fn poll_descriptors_and_prepare_buffer( } // Read input data from ALSA and deliver it to the user. -fn process_input( +fn process_input( stream: &StreamInner, buffer: &mut [u8], available_frames: usize, - data_callback: &mut (dyn FnMut(InputData) + Send + 'static), + data_callback: &mut (dyn FnMut(&Data) + Send + 'static), error_callback: &mut dyn FnMut(StreamError), -) where - T: Sample, -{ +) { let result = unsafe { alsa::snd_pcm_readi( stream.channel, @@ -750,28 +736,34 @@ fn process_input( error_callback(BackendSpecificError { description }.into()); return; } - let buffer = unsafe { cast_input_buffer::(buffer) }; - let input_data = InputData { buffer }; - data_callback(input_data); + let sample_format = stream.sample_format; + let data = buffer.as_mut_ptr() as *mut (); + let len = buffer.len() / sample_format.sample_size(); + let data = unsafe { + Data::from_parts(data, len, sample_format) + }; + data_callback(&data); } // Request data from the user's function and write it via ALSA. // // Returns `true` -fn process_output( +fn process_output( stream: &StreamInner, buffer: &mut [u8], available_frames: usize, - data_callback: &mut (dyn FnMut(OutputData) + Send + 'static), + data_callback: &mut (dyn FnMut(&mut Data) + Send + 'static), error_callback: &mut dyn FnMut(StreamError), -) where - T: Sample, -{ +) { { // We're now sure that we're ready to write data. - let buffer = unsafe { cast_output_buffer::(buffer) }; - let output_data = OutputData { buffer }; - data_callback(output_data); + let sample_format = stream.sample_format; + let data = buffer.as_mut_ptr() as *mut (); + let len = buffer.len() / sample_format.sample_size(); + let mut data = unsafe { + Data::from_parts(data, len, sample_format) + }; + data_callback(&mut data); } loop { let result = unsafe { @@ -805,21 +797,20 @@ fn process_output( } impl Stream { - fn new_input( + fn new_input( inner: Arc, mut data_callback: D, mut error_callback: E, ) -> Stream where - T: Sample, - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let (tx, rx) = trigger(); // Clone the handle for passing into worker thread. let stream = inner.clone(); let thread = thread::spawn(move || { - input_stream_worker::(rx, &*stream, &mut data_callback, &mut error_callback); + input_stream_worker(rx, &*stream, &mut data_callback, &mut error_callback); }); Stream { thread: Some(thread), @@ -828,21 +819,20 @@ impl Stream { } } - fn new_output( + fn new_output( inner: Arc, mut data_callback: D, mut error_callback: E, ) -> Stream where - T: Sample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let (tx, rx) = trigger(); // Clone the handle for passing into worker thread. let stream = inner.clone(); let thread = thread::spawn(move || { - output_stream_worker::(rx, &*stream, &mut data_callback, &mut error_callback); + output_stream_worker(rx, &*stream, &mut data_callback, &mut error_callback); }); Stream { thread: Some(thread), @@ -1080,17 +1070,3 @@ fn check_errors(err: libc::c_int) -> Result<(), String> { Ok(()) } - -/// Cast a byte slice into a (immutable) slice of desired type. -/// Safety: it's up to the caller to ensure that the input slice has valid bit representations. -unsafe fn cast_input_buffer(v: &[u8]) -> &[T] { - debug_assert!(v.len() % std::mem::size_of::() == 0); - std::slice::from_raw_parts(v.as_ptr() as *const T, v.len() / std::mem::size_of::()) -} - -/// Cast a byte slice into a mutable slice of desired type. -/// Safety: it's up to the caller to ensure that the input slice has valid bit representations. -unsafe fn cast_output_buffer(v: &mut [u8]) -> &mut [T] { - debug_assert!(v.len() % std::mem::size_of::() == 0); - std::slice::from_raw_parts_mut(v.as_mut_ptr() as *mut T, v.len() / std::mem::size_of::()) -} diff --git a/src/host/asio/mod.rs b/src/host/asio/mod.rs index b667b6d..3295470 100644 --- a/src/host/asio/mod.rs +++ b/src/host/asio/mod.rs @@ -3,15 +3,13 @@ extern crate parking_lot; use crate::{ BuildStreamError, + Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - InputData, - OutputData, PauseStreamError, PlayStreamError, - Sample, StreamError, SupportedFormatsError, }; @@ -91,29 +89,27 @@ impl DeviceTrait for Device { Device::default_output_format(self) } - fn build_input_stream( + fn build_input_stream( &self, format: &Format, data_callback: D, error_callback: E, ) -> Result where - T: Sample, - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static { Device::build_input_stream(self, format, data_callback, error_callback) } - fn build_output_stream( + fn build_output_stream( &self, format: &Format, data_callback: D, error_callback: E, ) -> Result where - T: Sample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static { Device::build_output_stream(self, format, data_callback, error_callback) diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index 0ad3710..696491c 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -9,9 +9,8 @@ use std::sync::Arc; use super::parking_lot::Mutex; use BackendSpecificError; use BuildStreamError; +use Data; use Format; -use InputData; -use OutputData; use PauseStreamError; use PlayStreamError; use Sample; @@ -58,18 +57,16 @@ impl Stream { } impl Device { - pub fn build_input_stream( + pub fn build_input_stream( &self, format: &Format, mut data_callback: D, _error_callback: E, ) -> Result where - T: Sample, - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - assert_eq!(format.data_type, T::FORMAT, "sample type does not match `format.data_type`"); let stream_type = self.driver.input_data_type().map_err(build_stream_err)?; // Ensure that the desired sample type is supported. @@ -118,7 +115,7 @@ impl Device { where A: AsioSample, B: Sample, - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, F: Fn(A) -> A, { // 1. Write the ASIO channels to the CPAL buffer. @@ -132,13 +129,15 @@ impl Device { } // 2. Deliver the interleaved buffer to the callback. - let data = InputData { buffer: interleaved }; - callback(data); + let data = interleaved.as_mut_ptr() as *mut (); + let len = interleaved.len(); + let data = Data::from_parts(data, len, B::FORMAT); + callback(&data); } match (&stream_type, data_type) { (&sys::AsioSampleType::ASIOSTInt16LSB, SampleFormat::I16) => { - process_input_callback::( + process_input_callback::( &mut data_callback, &mut interleaved, asio_stream, @@ -147,7 +146,7 @@ impl Device { ); } (&sys::AsioSampleType::ASIOSTInt16MSB, SampleFormat::I16) => { - process_input_callback::( + process_input_callback::( &mut data_callback, &mut interleaved, asio_stream, @@ -160,7 +159,7 @@ impl Device { // trait for the `to_le` and `to_be` methods, but this does not support floats. (&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32) | (&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => { - process_input_callback::( + process_input_callback::( &mut data_callback, &mut interleaved, asio_stream, @@ -173,7 +172,7 @@ impl Device { // `process_output_callback` function above by removing the unnecessary sample // conversion function. (&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I16) => { - process_input_callback::( + process_input_callback::( &mut data_callback, &mut interleaved, asio_stream, @@ -182,7 +181,7 @@ impl Device { ); } (&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I16) => { - process_input_callback::( + process_input_callback::( &mut data_callback, &mut interleaved, asio_stream, @@ -194,7 +193,7 @@ impl Device { // trait for the `to_le` and `to_be` methods, but this does not support floats. (&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F32) | (&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F32) => { - process_input_callback::( + process_input_callback::( &mut data_callback, &mut interleaved, asio_stream, @@ -224,18 +223,16 @@ impl Device { }) } - pub fn build_output_stream( + pub fn build_output_stream( &self, format: &Format, mut data_callback: D, _error_callback: E, ) -> Result where - T: Sample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - assert_eq!(format.data_type, T::FORMAT, "sample type does not match `format.data_type`"); let stream_type = self.driver.output_data_type().map_err(build_stream_err)?; // Ensure that the desired sample type is supported. @@ -308,12 +305,15 @@ impl Device { where A: Sample, B: AsioSample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, F: Fn(B) -> B, { // 1. Render interleaved buffer from callback. let interleaved: &mut [A] = cast_slice_mut(interleaved); - callback(OutputData { buffer: interleaved }); + let data = interleaved.as_mut_ptr() as *mut (); + let len = interleaved.len(); + let mut data = Data::from_parts(data, len, A::FORMAT); + callback(&mut data); // 2. Silence ASIO channels if necessary. let n_channels = interleaved.len() / asio_stream.buffer_size as usize; @@ -337,7 +337,7 @@ impl Device { match (data_type, &stream_type) { (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16LSB) => { - process_output_callback::( + process_output_callback::( &mut data_callback, &mut interleaved, silence, @@ -347,7 +347,7 @@ impl Device { ); } (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16MSB) => { - process_output_callback::( + process_output_callback::( &mut data_callback, &mut interleaved, silence, @@ -361,7 +361,7 @@ impl Device { // trait for the `to_le` and `to_be` methods, but this does not support floats. (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB) | (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => { - process_output_callback::( + process_output_callback::( &mut data_callback, &mut interleaved, silence, @@ -375,7 +375,7 @@ impl Device { // `process_output_callback` function above by removing the unnecessary sample // conversion function. (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32LSB) => { - process_output_callback::( + process_output_callback::( &mut data_callback, &mut interleaved, silence, @@ -385,7 +385,7 @@ impl Device { ); } (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32MSB) => { - process_output_callback::( + process_output_callback::( &mut data_callback, &mut interleaved, silence, @@ -398,7 +398,7 @@ impl Device { // trait for the `to_le` and `to_be` methods, but this does not support floats. (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64LSB) | (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64MSB) => { - process_output_callback::( + process_output_callback::( &mut data_callback, &mut interleaved, silence, diff --git a/src/host/coreaudio/mod.rs b/src/host/coreaudio/mod.rs index aeef5af..e56ab61 100644 --- a/src/host/coreaudio/mod.rs +++ b/src/host/coreaudio/mod.rs @@ -5,15 +5,13 @@ use crate::{ ChannelCount, BackendSpecificError, BuildStreamError, + Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - InputData, - OutputData, PauseStreamError, PlayStreamError, - Sample, SampleFormat, SampleRate, StreamError, @@ -131,33 +129,29 @@ impl DeviceTrait for Device { Device::default_output_format(self) } - fn build_input_stream( + fn build_input_stream( &self, format: &Format, data_callback: D, error_callback: E, ) -> Result where - T: Sample, - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - assert_eq!(T::FORMAT, format.data_type); Device::build_input_stream(self, format, data_callback, error_callback) } - fn build_output_stream( + fn build_output_stream( &self, format: &Format, data_callback: D, error_callback: E, ) -> Result where - T: Sample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - assert_eq!(T::FORMAT, format.data_type); Device::build_output_stream(self, format, data_callback, error_callback) } } @@ -500,15 +494,14 @@ fn audio_unit_from_device(device: &Device, input: bool) -> Result( + fn build_input_stream( &self, format: &Format, mut data_callback: D, _error_callback: E, ) -> Result where - T: Sample, - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { // The scope and element for working with a device's input stream. @@ -657,7 +650,8 @@ impl Device { // Register the callback that is being called by coreaudio whenever it needs data to be // fed to the audio buffer. - let bytes_per_channel = std::mem::size_of::(); + let sample_format = format.data_type; + let bytes_per_channel = sample_format.sample_size(); type Args = render_callback::Args; audio_unit.set_input_callback(move |args: Args| unsafe { let ptr = (*args.data.data).mBuffers.as_ptr() as *const AudioBuffer; @@ -671,10 +665,10 @@ impl Device { mData: data } = buffers[0]; - let data_len = (data_byte_size as usize / bytes_per_channel) as usize; - let data_slice = slice::from_raw_parts(data as *const T, data_len); - let input_data = InputData { buffer: data_slice }; - data_callback(input_data); + let data = data as *mut (); + let len = (data_byte_size as usize / bytes_per_channel) as usize; + let data = Data::from_parts(data, len, sample_format); + data_callback(&data); Ok(()) })?; @@ -687,15 +681,14 @@ impl Device { })) } - fn build_output_stream( + fn build_output_stream( &self, format: &Format, mut data_callback: D, _error_callback: E, ) -> Result where - T: Sample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let mut audio_unit = audio_unit_from_device(self, false)?; @@ -710,7 +703,8 @@ impl Device { // Register the callback that is being called by coreaudio whenever it needs data to be // fed to the audio buffer. - let bytes_per_channel = std::mem::size_of::(); + let sample_format = format.data_type; + let bytes_per_channel = sample_format.sample_size(); type Args = render_callback::Args; audio_unit.set_render_callback(move |args: Args| unsafe { // If `run()` is currently running, then a callback will be available from this list. @@ -722,10 +716,10 @@ impl Device { mData: data } = (*args.data.data).mBuffers[0]; - let data_len = (data_byte_size as usize / bytes_per_channel) as usize; - let data_slice = slice::from_raw_parts_mut(data as *mut T, data_len); - let output_data = OutputData { buffer: data_slice }; - data_callback(output_data); + let data = data as *mut (); + let len = (data_byte_size as usize / bytes_per_channel) as usize; + let mut data = Data::from_parts(data, len, sample_format); + data_callback(&mut data); Ok(()) })?; diff --git a/src/host/emscripten/mod.rs b/src/host/emscripten/mod.rs index 176671a..0eb951f 100644 --- a/src/host/emscripten/mod.rs +++ b/src/host/emscripten/mod.rs @@ -9,15 +9,13 @@ use stdweb::web::set_timeout; use crate::{ BuildStreamError, + Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - InputData, - OutputData, PauseStreamError, PlayStreamError, - Sample, SampleFormat, StreamError, SupportedFormat, @@ -160,32 +158,34 @@ impl DeviceTrait for Device { Device::default_output_format(self) } - fn build_input_stream( + fn build_input_stream( &self, _format: &Format, _data_callback: D, _error_callback: E, ) -> Result where - T: Sample, - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { unimplemented!() } - fn build_output_stream( + fn build_output_stream( &self, - _format: &Format, + format: &Format, data_callback: D, error_callback: E, ) -> Result where - T: Sample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { - assert_eq!(T::FORMAT, SampleFormat::F32, "emscripten backend only supports `f32` data"); + assert_eq!( + format.data_type, + SampleFormat::F32, + "emscripten backend currently only supports `f32` data", + ); // Create the stream. let audio_ctxt_ref = js!(return new AudioContext()).into_reference().unwrap(); @@ -201,7 +201,7 @@ impl DeviceTrait for Device { // // See also: The call to `set_timeout` at the end of the `audio_callback_fn` which creates // the loop. - set_timeout(|| audio_callback_fn::(user_data_ptr as *mut c_void), 10); + set_timeout(|| audio_callback_fn::(user_data_ptr as *mut c_void), 10); Ok(stream) } @@ -223,10 +223,9 @@ impl StreamTrait for Stream { // The first argument of the callback function (a `void*`) is a casted pointer to `self` // and to the `callback` parameter that was passed to `run`. -fn audio_callback_fn(user_data_ptr: *mut c_void) +fn audio_callback_fn(user_data_ptr: *mut c_void) where - T: Sample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { unsafe { @@ -236,12 +235,14 @@ where let audio_ctxt = &stream.audio_ctxt_ref; // TODO: We should be re-using a buffer. - let mut temporary_buffer: Vec<_> = (0..44100 * 2 / 3).map(|_| T::from(&0.0)).collect(); + let mut temporary_buffer = vec![0.0; 44100 * 2 / 3]; { - let buffer = &mut temporary_buffer; - let data = OutputData { buffer }; - data_cb(data); + let len = temporary_buffer.len(); + let data = temporary_buffer.as_mut_ptr() as *mut (); + let sample_format = SampleFormat::F32; + let mut data = Data::from_parts(data, len, sample_format); + data_cb(&mut data); } // TODO: directly use a TypedArray once this is supported by stdweb @@ -281,7 +282,7 @@ where // TODO: handle latency better ; right now we just use setInterval with the amount of sound // data that is in each buffer ; this is obviously bad, and also the schedule is too tight // and there may be underflows - set_timeout(|| audio_callback_fn::(user_data_ptr), 330); + set_timeout(|| audio_callback_fn::(user_data_ptr), 330); } } diff --git a/src/host/null/mod.rs b/src/host/null/mod.rs index 09a19c8..def00c9 100644 --- a/src/host/null/mod.rs +++ b/src/host/null/mod.rs @@ -1,11 +1,10 @@ use crate::{ BuildStreamError, + Data, DefaultFormatError, DevicesError, DeviceNameError, Format, - InputData, - OutputData, PauseStreamError, PlayStreamError, StreamError, @@ -71,28 +70,28 @@ impl DeviceTrait for Device { unimplemented!() } - fn build_input_stream( + fn build_input_stream( &self, _format: &Format, _data_callback: D, _error_callback: E, ) -> Result where - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { unimplemented!() } /// Create an output stream. - fn build_output_stream( + fn build_output_stream( &self, _format: &Format, _data_callback: D, _error_callback: E, ) -> Result where - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { unimplemented!() diff --git a/src/host/wasapi/device.rs b/src/host/wasapi/device.rs index db5c154..de8aa3e 100644 --- a/src/host/wasapi/device.rs +++ b/src/host/wasapi/device.rs @@ -1,12 +1,10 @@ use crate::{ BackendSpecificError, + Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - InputData, - OutputData, - Sample, SampleFormat, SampleRate, SupportedFormat, @@ -106,30 +104,28 @@ impl DeviceTrait for Device { Device::default_output_format(self) } - fn build_input_stream( + fn build_input_stream( &self, format: &Format, data_callback: D, error_callback: E, ) -> Result where - T: Sample, - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let stream_inner = self.build_input_stream_inner(format)?; Ok(Stream::new_input(stream_inner, data_callback, error_callback)) } - fn build_output_stream( + fn build_output_stream( &self, format: &Format, data_callback: D, error_callback: E, ) -> Result where - T: Sample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let stream_inner = self.build_output_stream_inner(format)?; diff --git a/src/host/wasapi/stream.rs b/src/host/wasapi/stream.rs index 1c49a13..cfeb71f 100644 --- a/src/host/wasapi/stream.rs +++ b/src/host/wasapi/stream.rs @@ -1,17 +1,14 @@ use crate::{ BackendSpecificError, - InputData, - OutputData, + Data, PauseStreamError, PlayStreamError, - Sample, SampleFormat, StreamError, }; use crate::traits::StreamTrait; use std::mem; use std::ptr; -use std::slice; use std::sync::mpsc::{channel, Receiver, Sender}; use std::thread::{self, JoinHandle}; use super::check_result; @@ -85,14 +82,13 @@ pub struct StreamInner { } impl Stream { - pub(crate) fn new_input( + pub(crate) fn new_input( stream_inner: StreamInner, mut data_callback: D, mut error_callback: E, ) -> Stream where - T: Sample, - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let pending_scheduled_event = @@ -115,14 +111,13 @@ impl Stream { } } - pub(crate) fn new_output( + pub(crate) fn new_output( stream_inner: StreamInner, mut data_callback: D, mut error_callback: E, ) -> Stream where - T: Sample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let pending_scheduled_event = @@ -285,13 +280,11 @@ fn stream_error_from_hresult(hresult: winnt::HRESULT) -> Result<(), StreamError> Ok(()) } -fn run_input( +fn run_input( mut run_ctxt: RunContext, - data_callback: &mut dyn FnMut(InputData), + data_callback: &mut dyn FnMut(&Data), error_callback: &mut dyn FnMut(StreamError), -) where - T: Sample, -{ +) { loop { match process_commands_and_await_signal(&mut run_ctxt, error_callback) { Some(ControlFlow::Break) => break, @@ -309,13 +302,11 @@ fn run_input( } } -fn run_output( +fn run_output( mut run_ctxt: RunContext, - data_callback: &mut dyn FnMut(OutputData), + data_callback: &mut dyn FnMut(&mut Data), error_callback: &mut dyn FnMut(StreamError), -) where - T: Sample, -{ +) { loop { match process_commands_and_await_signal(&mut run_ctxt, error_callback) { Some(ControlFlow::Break) => break, @@ -371,15 +362,12 @@ fn process_commands_and_await_signal( } // The loop for processing pending input data. -fn process_input( +fn process_input( stream: &StreamInner, capture_client: *mut audioclient::IAudioCaptureClient, - data_callback: &mut dyn FnMut(InputData), + data_callback: &mut dyn FnMut(&Data), error_callback: &mut dyn FnMut(StreamError), -) -> ControlFlow -where - T: Sample, -{ +) -> ControlFlow { let mut frames_available = 0; unsafe { // Get the available data in the shared buffer. @@ -412,15 +400,13 @@ where debug_assert!(!buffer.is_null()); - let buffer_len = frames_available as usize + let data = buffer as *mut (); + let len = frames_available as usize * stream.bytes_per_frame as usize - / mem::size_of::(); + / stream.sample_format.sample_size(); + let data = Data::from_parts(data, len, stream.sample_format); + data_callback(&data); - // Simplify the capture callback sample format branches. - let buffer_data = buffer as *mut _ as *const T; - let slice = slice::from_raw_parts(buffer_data, buffer_len); - let input_data = InputData { buffer: slice }; - data_callback(input_data); // Release the buffer. let hresult = (*capture_client).ReleaseBuffer(frames_available); if let Err(err) = stream_error_from_hresult(hresult) { @@ -432,15 +418,12 @@ where } // The loop for writing output data. -fn process_output( +fn process_output( stream: &StreamInner, render_client: *mut audioclient::IAudioRenderClient, - data_callback: &mut dyn FnMut(OutputData), + data_callback: &mut dyn FnMut(&mut Data), error_callback: &mut dyn FnMut(StreamError), -) -> ControlFlow -where - T: Sample, -{ +) -> ControlFlow { // The number of frames available for writing. let frames_available = match get_available_frames(&stream) { Ok(0) => return ControlFlow::Continue, // TODO: Can this happen? @@ -462,13 +445,14 @@ where } debug_assert!(!buffer.is_null()); - let buffer_len = - frames_available as usize * stream.bytes_per_frame as usize / mem::size_of::(); - let buffer_data = buffer as *mut T; - let slice = slice::from_raw_parts_mut(buffer_data, buffer_len); - let output_data = OutputData { buffer: slice }; - data_callback(output_data); + let data = buffer as *mut (); + let len = frames_available as usize + * stream.bytes_per_frame as usize + / stream.sample_format.sample_size(); + let mut data = Data::from_parts(data, len, stream.sample_format); + data_callback(&mut data); + let hresult = (*render_client).ReleaseBuffer(frames_available as u32, 0); if let Err(err) = stream_error_from_hresult(hresult) { error_callback(err); diff --git a/src/lib.rs b/src/lib.rs index a80f3a7..b77d454 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,14 +55,14 @@ //! Now that we have everything for the stream, we are ready to create it from our selected device: //! //! ```no_run -//! use cpal::OutputData; +//! use cpal::Data; //! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); //! # let format = device.default_output_format().unwrap(); //! let stream = device.build_output_stream( //! &format, -//! move |data: OutputData| { +//! move |data: &mut Data| { //! // react to stream events and read or write stream data here. //! }, //! move |err| { @@ -72,9 +72,8 @@ //! ``` //! //! While the stream is running, the selected audio device will periodically call the data callback -//! that was passed to the function. The callback is passed an instance of either `InputData` or -//! `OutputData` depending on whether the stream is an input stream or output stream -//! respectively. Type `T` represents the desired sample format type. Supported format types +//! that was passed to the function. The callback is passed an instance of either `&Data` or +//! `&mut Data` depending on whether the stream is an input stream or output stream respectively. //! //! > **Note**: Creating and running a stream will *not* block the thread. On modern platforms, the //! > given callback is called by a dedicated, high-priority thread responsible for delivering @@ -85,22 +84,24 @@ //! > please share your issue and use-case with the CPAL team on the github issue tracker for //! > consideration.* //! -//! In this example, we simply fill the given output buffer with zeroes. +//! In this example, we simply fill the given output buffer with silence. //! //! ```no_run -//! use cpal::{OutputData, Sample, SampleFormat}; +//! use cpal::{Data, Sample, SampleFormat}; //! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); //! # let format = device.default_output_format().unwrap(); -//! let err_fn = move |err| eprintln!("an error occurred on the output audio stream: {}", err); -//! let stream = match format.data_type { -//! SampleFormat::F32 => device.build_output_stream(&format, write_silence::, err_fn), -//! SampleFormat::I16 => device.build_output_stream(&format, write_silence::, err_fn), -//! SampleFormat::U16 => device.build_output_stream(&format, write_silence::, err_fn), +//! let err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err); +//! let data_fn = move |data: &mut Data| match data.sample_format() { +//! SampleFormat::F32 => write_silence::(data), +//! SampleFormat::I16 => write_silence::(data), +//! SampleFormat::U16 => write_silence::(data), //! }; +//! let stream = device.build_output_stream(&format, data_fn, err_fn).unwrap(); //! -//! fn write_silence(mut data: OutputData) { +//! fn write_silence(data: &mut Data) { +//! let data = data.as_slice_mut::().unwrap(); //! for sample in data.iter_mut() { //! *sample = Sample::from(&0.0); //! } @@ -115,7 +116,7 @@ //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); //! # let format = device.default_output_format().unwrap(); -//! # let data_fn = move |_data: cpal::OutputData| {}; +//! # let data_fn = move |_data: &mut cpal::Data| {}; //! # let err_fn = move |_err| {}; //! # let stream = device.build_output_stream(&format, data_fn, err_fn).unwrap(); //! stream.play().unwrap(); @@ -129,7 +130,7 @@ //! # let host = cpal::default_host(); //! # let device = host.default_output_device().unwrap(); //! # let format = device.default_output_format().unwrap(); -//! # let data_fn = move |_data: cpal::OutputData| {}; +//! # let data_fn = move |_data: &mut cpal::Data| {}; //! # let err_fn = move |_err| {}; //! # let stream = device.build_output_stream(&format, data_fn, err_fn).unwrap(); //! stream.pause().unwrap(); @@ -152,7 +153,6 @@ pub use platform::{ HostId, Stream, SupportedInputFormats, SupportedOutputFormats, }; pub use samples_formats::{Sample, SampleFormat}; -use std::ops::{Deref, DerefMut}; mod error; mod host; @@ -193,34 +193,106 @@ pub struct SupportedFormat { pub data_type: SampleFormat, } -/// Represents a buffer containing audio data that may be read. +/// Represents a buffer of audio data, delivered via a user's stream data callback function. /// -/// This struct implements the `Deref` trait targeting `[T]`. Therefore this buffer can be read the -/// same way as reading from a `Vec` or any other kind of Rust array. -// TODO: explain audio stuff in general -// TODO: Consider making this an `enum` with `Interleaved` and `NonInterleaved` variants. +/// Input stream callbacks receive `&Data`, while output stream callbacks expect `&mut Data`. #[derive(Debug)] -pub struct InputData<'a, T: 'a> -where - T: Sample, -{ - buffer: &'a [T], +pub struct Data { + data: *mut (), + len: usize, + sample_format: SampleFormat, } -/// Represents a buffer that must be filled with audio data. The buffer in unfilled state may -/// contain garbage values. -/// -/// This struct implements the `Deref` and `DerefMut` traits to `[T]`. Therefore writing to this -/// buffer is done in the same way as writing to a `Vec` or any other kind of Rust array. -// TODO: explain audio stuff in general -// TODO: Consider making this an `enum` with `Interleaved` and `NonInterleaved` variants. -#[must_use] -#[derive(Debug)] -pub struct OutputData<'a, T: 'a> -where - T: Sample, -{ - buffer: &'a mut [T], +impl Data { + // Internal constructor for host implementations to use. + // + // The following requirements must be met in order for the safety of `Data`'s public API. + // + // - The `data` pointer must point to the first sample in the slice containing all samples. + // - The `len` must describe the length of the buffer as a number of samples in the expected + // format specified via the `sample_format` argument. + // - The `sample_format` must correctly represent the underlying sample data delivered/expected + // by the stream. + pub(crate) unsafe fn from_parts( + data: *mut (), + len: usize, + sample_format: SampleFormat, + ) -> Self { + Data { data, len, sample_format } + } + + /// The sample format of the internal audio data. + pub fn sample_format(&self) -> SampleFormat { + self.sample_format + } + + /// The full length of the buffer in samples. + /// + /// The returned length is the same length as the slice of type `T` that would be returned via + /// `as_slice` given a sample type that matches the inner sample format. + pub fn len(&self) -> usize { + self.len + } + + /// The raw slice of memory representing the underlying audio data as a slice of bytes. + /// + /// It is up to the user to interpret the slice of memory based on `Data::sample_format`. + pub fn bytes(&self) -> &[u8] { + let len = self.len * self.sample_format.sample_size(); + // The safety of this block relies on correct construction of the `Data` instance. See + // the unsafe `from_parts` constructor for these requirements. + unsafe { + std::slice::from_raw_parts(self.data as *const u8, len) + } + } + + /// The raw slice of memory representing the underlying audio data as a slice of bytes. + /// + /// It is up to the user to interpret the slice of memory based on `Data::sample_format`. + pub fn bytes_mut(&mut self) -> &mut [u8] { + let len = self.len * self.sample_format.sample_size(); + // The safety of this block relies on correct construction of the `Data` instance. See + // the unsafe `from_parts` constructor for these requirements. + unsafe { + std::slice::from_raw_parts_mut(self.data as *mut u8, len) + } + } + + /// Access the data as a slice of sample type `T`. + /// + /// Returns `None` if the sample type does not match the expected sample format. + pub fn as_slice(&self) -> Option<&[T]> + where + T: Sample, + { + if T::FORMAT == self.sample_format { + // The safety of this block relies on correct construction of the `Data` instance. See + // the unsafe `from_parts` constructor for these requirements. + unsafe { + Some(std::slice::from_raw_parts(self.data as *const T, self.len)) + } + } else { + None + } + } + + /// Access the data as a slice of sample type `T`. + /// + /// Returns `None` if the sample type does not match the expected sample format. + pub fn as_slice_mut(&mut self) -> Option<&mut [T]> + where + T: Sample, + { + if T::FORMAT == self.sample_format { + // The safety of this block relies on correct construction of the `Data` instance. See + // the unsafe `from_parts` constructor for these requirements. + unsafe { + Some(std::slice::from_raw_parts_mut(self.data as *mut T, self.len)) + } + } else { + None + } + } } impl SupportedFormat { @@ -306,40 +378,6 @@ impl SupportedFormat { } } -impl<'a, T> Deref for InputData<'a, T> -where - T: Sample, -{ - type Target = [T]; - - #[inline] - fn deref(&self) -> &[T] { - self.buffer - } -} - -impl<'a, T> Deref for OutputData<'a, T> -where - T: Sample, -{ - type Target = [T]; - - #[inline] - fn deref(&self) -> &[T] { - self.buffer - } -} - -impl<'a, T> DerefMut for OutputData<'a, T> -where - T: Sample, -{ - #[inline] - fn deref_mut(&mut self) -> &mut [T] { - self.buffer - } -} - impl From for SupportedFormat { #[inline] fn from(format: Format) -> SupportedFormat { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index dfa2632..8b936a9 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -255,15 +255,14 @@ macro_rules! impl_platform_host { } } - fn build_input_stream( + fn build_input_stream( &self, format: &crate::Format, data_callback: D, error_callback: E, ) -> Result where - T: crate::Sample, - D: FnMut(crate::InputData) + Send + 'static, + D: FnMut(&crate::Data) + Send + 'static, E: FnMut(crate::StreamError) + Send + 'static, { match self.0 { @@ -275,15 +274,14 @@ macro_rules! impl_platform_host { } } - fn build_output_stream( + fn build_output_stream( &self, format: &crate::Format, data_callback: D, error_callback: E, ) -> Result where - T: crate::Sample, - D: FnMut(crate::OutputData) + Send + 'static, + D: FnMut(&mut crate::Data) + Send + 'static, E: FnMut(crate::StreamError) + Send + 'static, { match self.0 { diff --git a/src/traits.rs b/src/traits.rs index c00e294..4aeb8e6 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -2,17 +2,15 @@ use { BuildStreamError, + Data, DefaultFormatError, DeviceNameError, DevicesError, Format, - InputData, InputDevices, - OutputData, OutputDevices, PauseStreamError, PlayStreamError, - Sample, StreamError, SupportedFormat, SupportedFormatsError, @@ -120,27 +118,25 @@ pub trait DeviceTrait { fn default_output_format(&self) -> Result; /// Create an input stream. - fn build_input_stream( + fn build_input_stream( &self, format: &Format, data_callback: D, error_callback: E, ) -> Result where - T: Sample, - D: FnMut(InputData) + Send + 'static, + D: FnMut(&Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static; /// Create an output stream. - fn build_output_stream( + fn build_output_stream( &self, format: &Format, data_callback: D, error_callback: E, ) -> Result where - T: Sample, - D: FnMut(OutputData) + Send + 'static, + D: FnMut(&mut Data) + Send + 'static, E: FnMut(StreamError) + Send + 'static; }