From bcf962c447db7cf979d5555cf5d6f57b5af812c3 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 16 Apr 2020 14:50:36 +0200 Subject: [PATCH 1/4] Add a `CallbackInfo` argument to the stream data callback I began on an implementation of the timestamp API described in #363 but quickly realised that it might be best to land the API for providing extra information to the user's callback first. This PR adds two new types: `InputCallbackInfo` and `OutputCallbackInfo`. These types are delivered to the user's data callback as a new, second argument. While these types are currently empty, the intention is for these types to provide information relevant to the current request for or delivery of data. This includes: - Timestamp information #363. - Flags related to the state of the stream (e.g buffer underflow/overflow). In order to maintain flexibility to avoid breaking things, I figure we can keep the fields of these types private and provide methods for retrieving this info. @Ralith, @ishitatsuyuki does this seem OK to you? --- examples/beep.rs | 4 +++- examples/feedback.rs | 4 ++-- examples/record_wav.rs | 6 +++--- src/host/alsa/mod.rs | 28 +++++++++++++++------------- src/host/asio/mod.rs | 8 ++++---- src/host/asio/stream.rs | 23 ++++++++++------------- src/host/coreaudio/mod.rs | 20 +++++++++++--------- src/host/emscripten/mod.rs | 14 ++++++++------ src/host/null/mod.rs | 9 +++++---- src/host/wasapi/device.rs | 9 +++++---- src/host/wasapi/stream.rs | 21 ++++++++++++--------- src/lib.rs | 16 ++++++++++++---- src/platform/mod.rs | 4 ++-- src/traits.rs | 21 ++++++++++++--------- 14 files changed, 104 insertions(+), 83 deletions(-) diff --git a/examples/beep.rs b/examples/beep.rs index 60ec8aa..dec1fdf 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -37,7 +37,9 @@ where let stream = device.build_output_stream( config, - move |data: &mut [T]| write_data(data, channels, &mut next_value), + move |data: &mut [T], _: &cpal::OutputCallbackInfo| { + write_data(data, channels, &mut next_value) + }, err_fn, )?; stream.play()?; diff --git a/examples/feedback.rs b/examples/feedback.rs index 2601136..e44f2d0 100644 --- a/examples/feedback.rs +++ b/examples/feedback.rs @@ -46,7 +46,7 @@ fn main() -> Result<(), anyhow::Error> { producer.push(0.0).unwrap(); } - let input_data_fn = move |data: &[f32]| { + let input_data_fn = move |data: &[f32], _: &cpal::InputCallbackInfo| { let mut output_fell_behind = false; for &sample in data { if producer.push(sample).is_err() { @@ -58,7 +58,7 @@ fn main() -> Result<(), anyhow::Error> { } }; - let output_data_fn = move |data: &mut [f32]| { + let output_data_fn = move |data: &mut [f32], _: &cpal::OutputCallbackInfo| { let mut input_fell_behind = None; for sample in data { *sample = match consumer.pop() { diff --git a/examples/record_wav.rs b/examples/record_wav.rs index fcee828..ac6d237 100644 --- a/examples/record_wav.rs +++ b/examples/record_wav.rs @@ -43,17 +43,17 @@ fn main() -> Result<(), anyhow::Error> { let stream = match config.sample_format() { cpal::SampleFormat::F32 => device.build_input_stream( &config.into(), - move |data| write_input_data::(data, &writer_2), + move |data, _: &_| write_input_data::(data, &writer_2), err_fn, )?, cpal::SampleFormat::I16 => device.build_input_stream( &config.into(), - move |data| write_input_data::(data, &writer_2), + 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), + move |data, _: &_| write_input_data::(data, &writer_2), err_fn, )?, }; diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index c094bf1..b66079c 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -4,9 +4,9 @@ extern crate libc; use self::alsa::poll::Descriptors; use crate::{ BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError, - DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate, - StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange, - SupportedStreamConfigsError, + DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo, PauseStreamError, + PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError, SupportedStreamConfig, + SupportedStreamConfigRange, SupportedStreamConfigsError, }; use std::cmp; use std::sync::Arc; @@ -90,7 +90,7 @@ impl DeviceTrait for Device { error_callback: E, ) -> Result where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let stream_inner = @@ -107,7 +107,7 @@ impl DeviceTrait for Device { error_callback: E, ) -> Result where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let stream_inner = @@ -464,7 +464,7 @@ struct StreamWorkerContext { fn input_stream_worker( rx: TriggerReceiver, stream: &StreamInner, - data_callback: &mut (dyn FnMut(&Data) + Send + 'static), + data_callback: &mut (dyn FnMut(&Data, &InputCallbackInfo) + Send + 'static), error_callback: &mut (dyn FnMut(StreamError) + Send + 'static), ) { let mut ctxt = StreamWorkerContext::default(); @@ -499,7 +499,7 @@ fn input_stream_worker( fn output_stream_worker( rx: TriggerReceiver, stream: &StreamInner, - data_callback: &mut (dyn FnMut(&mut Data) + Send + 'static), + data_callback: &mut (dyn FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static), error_callback: &mut (dyn FnMut(StreamError) + Send + 'static), ) { let mut ctxt = StreamWorkerContext::default(); @@ -636,14 +636,15 @@ fn poll_descriptors_and_prepare_buffer( fn process_input( stream: &StreamInner, buffer: &mut [u8], - data_callback: &mut (dyn FnMut(&Data) + Send + 'static), + data_callback: &mut (dyn FnMut(&Data, &InputCallbackInfo) + Send + 'static), ) -> Result<(), BackendSpecificError> { stream.channel.io().readi(buffer)?; 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); + let info = crate::InputCallbackInfo {}; + data_callback(&data, &info); Ok(()) } @@ -655,7 +656,7 @@ fn process_output( stream: &StreamInner, buffer: &mut [u8], available_frames: usize, - data_callback: &mut (dyn FnMut(&mut Data) + Send + 'static), + data_callback: &mut (dyn FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static), error_callback: &mut dyn FnMut(StreamError), ) { { @@ -664,7 +665,8 @@ fn process_output( 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); + let info = crate::OutputCallbackInfo {}; + data_callback(&mut data, &info); } loop { match stream.channel.io().writei(buffer) { @@ -700,7 +702,7 @@ impl Stream { mut error_callback: E, ) -> Stream where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let (tx, rx) = trigger(); @@ -722,7 +724,7 @@ impl Stream { mut error_callback: E, ) -> Stream where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let (tx, rx) = trigger(); diff --git a/src/host/asio/mod.rs b/src/host/asio/mod.rs index a885db6..ae99c3e 100644 --- a/src/host/asio/mod.rs +++ b/src/host/asio/mod.rs @@ -3,8 +3,8 @@ extern crate parking_lot; use crate::{ BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, - PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError, - SupportedStreamConfig, SupportedStreamConfigsError, + InputCallbackInfo, OutputCallbackInfo, PauseStreamError, PlayStreamError, SampleFormat, + StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigsError, }; use traits::{DeviceTrait, HostTrait, StreamTrait}; @@ -90,7 +90,7 @@ impl DeviceTrait for Device { error_callback: E, ) -> Result where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { Device::build_input_stream_raw(self, config, sample_format, data_callback, error_callback) @@ -104,7 +104,7 @@ impl DeviceTrait for Device { error_callback: E, ) -> Result where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { Device::build_output_stream_raw(self, config, sample_format, data_callback, error_callback) diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index 33f7974..0c1345b 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -4,19 +4,14 @@ extern crate num_traits; use self::num_traits::PrimInt; use super::parking_lot::Mutex; use super::Device; +use crate::{ + BackendSpecificError, BuildStreamError, Data, InputCallbackInfo, OutputCallbackInfo, + PauseStreamError, PlayStreamError, Sample, SampleFormat, StreamConfig, StreamError, + SupportedStreamConfig, +}; use std; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use BackendSpecificError; -use BuildStreamError; -use Data; -use PauseStreamError; -use PlayStreamError; -use Sample; -use SampleFormat; -use StreamConfig; -use StreamError; -use SupportedStreamConfig; /// Sample types whose constant silent value is known. trait Silence { @@ -66,7 +61,7 @@ impl Device { _error_callback: E, ) -> Result where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let stream_type = self.driver.input_data_type().map_err(build_stream_err)?; @@ -133,7 +128,8 @@ impl Device { let data = interleaved.as_mut_ptr() as *mut (); let len = interleaved.len(); let data = Data::from_parts(data, len, B::FORMAT); - callback(&data); + let info = InputCallbackInfo {}; + callback(&data, &info); } match (&stream_type, sample_format) { @@ -233,7 +229,7 @@ impl Device { _error_callback: E, ) -> Result where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let stream_type = self.driver.output_data_type().map_err(build_stream_err)?; @@ -315,6 +311,7 @@ impl Device { let data = interleaved.as_mut_ptr() as *mut (); let len = interleaved.len(); let mut data = Data::from_parts(data, len, A::FORMAT); + let info = OutputCallbackInfo {}; callback(&mut data); // 2. Silence ASIO channels if necessary. diff --git a/src/host/coreaudio/mod.rs b/src/host/coreaudio/mod.rs index e3a699f..10173af 100644 --- a/src/host/coreaudio/mod.rs +++ b/src/host/coreaudio/mod.rs @@ -21,9 +21,9 @@ use self::coreaudio::sys::{ use crate::traits::{DeviceTrait, HostTrait, StreamTrait}; use crate::{ BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError, - DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate, - StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange, - SupportedStreamConfigsError, + DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo, PauseStreamError, + PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError, SupportedStreamConfig, + SupportedStreamConfigRange, SupportedStreamConfigsError, }; use std::cell::RefCell; use std::ffi::CStr; @@ -111,7 +111,7 @@ impl DeviceTrait for Device { error_callback: E, ) -> Result where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { Device::build_input_stream_raw(self, config, sample_format, data_callback, error_callback) @@ -125,7 +125,7 @@ impl DeviceTrait for Device { error_callback: E, ) -> Result where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { Device::build_output_stream_raw(self, config, sample_format, data_callback, error_callback) @@ -486,7 +486,7 @@ impl Device { _error_callback: E, ) -> Result where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { // The scope and element for working with a device's input stream. @@ -653,7 +653,8 @@ impl Device { 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); + let info = InputCallbackInfo {}; + data_callback(&data, &info); Ok(()) })?; @@ -674,7 +675,7 @@ impl Device { _error_callback: E, ) -> Result where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let mut audio_unit = audio_unit_from_device(self, false)?; @@ -704,7 +705,8 @@ impl Device { 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); + let info = OutputCallbackInfo {}; + data_callback(&mut data, &info); Ok(()) })?; diff --git a/src/host/emscripten/mod.rs b/src/host/emscripten/mod.rs index 999746c..fba0a04 100644 --- a/src/host/emscripten/mod.rs +++ b/src/host/emscripten/mod.rs @@ -9,8 +9,9 @@ use stdweb::Reference; use crate::{ BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, - PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError, - SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, + InputCallbackInfo, OutputCallbackInfo, PauseStreamError, PlayStreamError, SampleFormat, + StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange, + SupportedStreamConfigsError, }; use traits::{DeviceTrait, HostTrait, StreamTrait}; @@ -160,7 +161,7 @@ impl DeviceTrait for Device { _error_callback: E, ) -> Result where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { unimplemented!() @@ -174,7 +175,7 @@ impl DeviceTrait for Device { error_callback: E, ) -> Result where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { assert_eq!( @@ -224,7 +225,7 @@ impl StreamTrait for Stream { // and to the `callback` parameter that was passed to `run`. fn audio_callback_fn(user_data_ptr: *mut c_void) where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { unsafe { @@ -241,7 +242,8 @@ where 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); + let info = OutputCallbackInfo {}; + data_cb(&mut data, &info); } // TODO: directly use a TypedArray once this is supported by stdweb diff --git a/src/host/null/mod.rs b/src/host/null/mod.rs index 1424ca8..dc83b39 100644 --- a/src/host/null/mod.rs +++ b/src/host/null/mod.rs @@ -1,7 +1,8 @@ use crate::{ BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, - PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError, - SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, + InputCallbackInfo, OutputCallbackInfo, PauseStreamError, PlayStreamError, SampleFormat, + StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange, + SupportedStreamConfigsError, }; use traits::{DeviceTrait, HostTrait, StreamTrait}; @@ -74,7 +75,7 @@ impl DeviceTrait for Device { _error_callback: E, ) -> Result where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { unimplemented!() @@ -89,7 +90,7 @@ impl DeviceTrait for Device { _error_callback: E, ) -> Result where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { unimplemented!() diff --git a/src/host/wasapi/device.rs b/src/host/wasapi/device.rs index f1e1e1b..1106bb7 100644 --- a/src/host/wasapi/device.rs +++ b/src/host/wasapi/device.rs @@ -1,7 +1,8 @@ use crate::{ BackendSpecificError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, - SampleFormat, SampleRate, StreamConfig, SupportedStreamConfig, SupportedStreamConfigRange, - SupportedStreamConfigsError, COMMON_SAMPLE_RATES, + InputCallbackInfo, OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig, + SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, + COMMON_SAMPLE_RATES, }; use std; use std::ffi::OsString; @@ -104,7 +105,7 @@ impl DeviceTrait for Device { error_callback: E, ) -> Result where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let stream_inner = self.build_input_stream_raw_inner(config, sample_format)?; @@ -123,7 +124,7 @@ impl DeviceTrait for Device { error_callback: E, ) -> Result where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let stream_inner = self.build_output_stream_raw_inner(config, sample_format)?; diff --git a/src/host/wasapi/stream.rs b/src/host/wasapi/stream.rs index 8d996d3..5719cbc 100644 --- a/src/host/wasapi/stream.rs +++ b/src/host/wasapi/stream.rs @@ -8,7 +8,8 @@ use super::winapi::um::winbase; use super::winapi::um::winnt; use crate::traits::StreamTrait; use crate::{ - BackendSpecificError, Data, PauseStreamError, PlayStreamError, SampleFormat, StreamError, + BackendSpecificError, Data, InputCallbackInfo, OutputCallbackInfo, PauseStreamError, + PlayStreamError, SampleFormat, StreamError, }; use std::mem; use std::ptr; @@ -83,7 +84,7 @@ impl Stream { mut error_callback: E, ) -> Stream where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let pending_scheduled_event = @@ -112,7 +113,7 @@ impl Stream { mut error_callback: E, ) -> Stream where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { let pending_scheduled_event = @@ -277,7 +278,7 @@ fn stream_error_from_hresult(hresult: winnt::HRESULT) -> Result<(), StreamError> fn run_input( mut run_ctxt: RunContext, - data_callback: &mut dyn FnMut(&Data), + data_callback: &mut dyn FnMut(&Data, &InputCallbackInfo), error_callback: &mut dyn FnMut(StreamError), ) { loop { @@ -304,7 +305,7 @@ fn run_input( fn run_output( mut run_ctxt: RunContext, - data_callback: &mut dyn FnMut(&mut Data), + data_callback: &mut dyn FnMut(&mut Data, &OutputCallbackInfo), error_callback: &mut dyn FnMut(StreamError), ) { loop { @@ -370,7 +371,7 @@ fn process_commands_and_await_signal( fn process_input( stream: &StreamInner, capture_client: *mut audioclient::IAudioCaptureClient, - data_callback: &mut dyn FnMut(&Data), + data_callback: &mut dyn FnMut(&Data, &InputCallbackInfo), error_callback: &mut dyn FnMut(StreamError), ) -> ControlFlow { let mut frames_available = 0; @@ -409,7 +410,8 @@ fn process_input( let len = frames_available as usize * stream.bytes_per_frame as usize / stream.sample_format.sample_size(); let data = Data::from_parts(data, len, stream.sample_format); - data_callback(&data); + let info = InputCallbackInfo {}; + data_callback(&data, &info); // Release the buffer. let hresult = (*capture_client).ReleaseBuffer(frames_available); @@ -425,7 +427,7 @@ fn process_input( fn process_output( stream: &StreamInner, render_client: *mut audioclient::IAudioRenderClient, - data_callback: &mut dyn FnMut(&mut Data), + data_callback: &mut dyn FnMut(&mut Data, &OutputCallbackInfo), error_callback: &mut dyn FnMut(StreamError), ) -> ControlFlow { // The number of frames available for writing. @@ -453,7 +455,8 @@ fn process_output( 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 info = OutputCallbackInfo {}; + data_callback(&mut data, &info); let hresult = (*render_client).ReleaseBuffer(frames_available as u32, 0); if let Err(err) = stream_error_from_hresult(hresult) { diff --git a/src/lib.rs b/src/lib.rs index da10a9f..31a6b45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ //! # let config = device.default_output_config().unwrap().into(); //! let stream = device.build_output_stream( //! &config, -//! move |data: &mut [f32]| { +//! move |data: &mut [f32], _: &cpal::OutputCallbackInfo| { //! // react to stream events and read or write stream data here. //! }, //! move |err| { @@ -102,7 +102,7 @@ //! SampleFormat::U16 => device.build_output_stream(&config, write_silence::, err_fn), //! }.unwrap(); //! -//! fn write_silence(data: &mut [T]) { +//! fn write_silence(data: &mut [T], _: &cpal::OutputCallbackInfo) { //! for sample in data.iter_mut() { //! *sample = Sample::from(&0.0); //! } @@ -119,7 +119,7 @@ //! # let supported_config = device.default_output_config().unwrap(); //! # let sample_format = supported_config.sample_format(); //! # let config = supported_config.into(); -//! # let data_fn = move |_data: &mut cpal::Data| {}; +//! # let data_fn = move |_data: &mut cpal::Data, _: &cpal::OutputCallbackInfo| {}; //! # let err_fn = move |_err| {}; //! # let stream = device.build_output_stream_raw(&config, sample_format, data_fn, err_fn).unwrap(); //! stream.play().unwrap(); @@ -135,7 +135,7 @@ //! # let supported_config = device.default_output_config().unwrap(); //! # let sample_format = supported_config.sample_format(); //! # let config = supported_config.into(); -//! # let data_fn = move |_data: &mut cpal::Data| {}; +//! # let data_fn = move |_data: &mut cpal::Data, _: &cpal::OutputCallbackInfo| {}; //! # let err_fn = move |_err| {}; //! # let stream = device.build_output_stream_raw(&config, sample_format, data_fn, err_fn).unwrap(); //! stream.pause().unwrap(); @@ -220,6 +220,14 @@ pub struct Data { sample_format: SampleFormat, } +/// Information relevant to a single call to the user's output stream data callback. +#[derive(Debug, Clone, PartialEq)] +pub struct OutputCallbackInfo {} + +/// Information relevant to a single call to the user's input stream data callback. +#[derive(Debug, Clone, PartialEq)] +pub struct InputCallbackInfo {} + impl SupportedStreamConfig { pub fn channels(&self) -> ChannelCount { self.channels diff --git a/src/platform/mod.rs b/src/platform/mod.rs index cd5f529..3c6db74 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -263,7 +263,7 @@ macro_rules! impl_platform_host { error_callback: E, ) -> Result where - D: FnMut(&crate::Data) + Send + 'static, + D: FnMut(&crate::Data, &crate::InputCallbackInfo) + Send + 'static, E: FnMut(crate::StreamError) + Send + 'static, { match self.0 { @@ -289,7 +289,7 @@ macro_rules! impl_platform_host { error_callback: E, ) -> Result where - D: FnMut(&mut crate::Data) + Send + 'static, + D: FnMut(&mut crate::Data, &crate::OutputCallbackInfo) + Send + 'static, E: FnMut(crate::StreamError) + Send + 'static, { match self.0 { diff --git a/src/traits.rs b/src/traits.rs index ef94aa7..51228ee 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,9 +1,10 @@ //! The suite of traits allowing CPAL to abstract over hosts, devices, event loops and stream IDs. use { - BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, InputDevices, - OutputDevices, PauseStreamError, PlayStreamError, Sample, SampleFormat, StreamConfig, - StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, + BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, + InputCallbackInfo, InputDevices, OutputCallbackInfo, OutputDevices, PauseStreamError, + PlayStreamError, Sample, SampleFormat, StreamConfig, StreamError, SupportedStreamConfig, + SupportedStreamConfigRange, SupportedStreamConfigsError, }; /// A **Host** provides access to the available audio devices on the system. @@ -122,16 +123,17 @@ pub trait DeviceTrait { ) -> Result where T: Sample, - D: FnMut(&[T]) + Send + 'static, + D: FnMut(&[T], &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { self.build_input_stream_raw( config, T::FORMAT, - move |data| { + move |data, info| { data_callback( data.as_slice() .expect("host supplied incorrect sample type"), + info, ) }, error_callback, @@ -147,16 +149,17 @@ pub trait DeviceTrait { ) -> Result where T: Sample, - D: FnMut(&mut [T]) + Send + 'static, + D: FnMut(&mut [T], &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static, { self.build_output_stream_raw( config, T::FORMAT, - move |data| { + move |data, info| { data_callback( data.as_slice_mut() .expect("host supplied incorrect sample type"), + info, ) }, error_callback, @@ -172,7 +175,7 @@ pub trait DeviceTrait { error_callback: E, ) -> Result where - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static; /// Create a dynamically typed output stream. @@ -184,7 +187,7 @@ pub trait DeviceTrait { error_callback: E, ) -> Result where - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, E: FnMut(StreamError) + Send + 'static; } From 3f69770471317b414c52df5ed67c9ec1f2c79e50 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 16 Apr 2020 15:20:45 +0200 Subject: [PATCH 2/4] Update checkout github action. Clean up workflow formatting. --- .github/workflows/cpal.yml | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/.github/workflows/cpal.yml b/.github/workflows/cpal.yml index 9a3d14a..539cc27 100644 --- a/.github/workflows/cpal.yml +++ b/.github/workflows/cpal.yml @@ -4,11 +4,11 @@ on: [push, pull_request] jobs: clippy-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 + - name: Update apt + run: sudo apt update - name: Install alsa run: sudo apt-get install libasound2-dev - name: Install stable @@ -27,7 +27,7 @@ jobs: rustfmt-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install stable uses: actions-rs/toolchain@v1 with: @@ -42,16 +42,14 @@ jobs: args: --all -- --check cargo-publish: - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - env: CRATESIO_TOKEN: ${{ secrets.CRATESIO_TOKEN }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 + - name: Update apt + run: sudo apt update - name: Install alsa run: sudo apt-get install libasound2-dev - name: Run cargo publish for cpal @@ -68,11 +66,11 @@ jobs: [ $empty -eq 1 ] || grep -q "is already uploaded" < $CPAL_TMP ubuntu-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 + - name: Update apt + run: sudo apt update - name: Install alsa run: sudo apt-get install libasound2-dev - name: Install stable @@ -93,15 +91,12 @@ jobs: args: --all --all-features --verbose asmjs-wasm32-test: - strategy: matrix: target: [asmjs-unknown-emscripten, wasm32-unknown-emscripten] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install emscripten run: sudo apt-get install emscripten - name: Install stable @@ -114,15 +109,12 @@ jobs: run: cargo build --example beep --target ${{ matrix.target }} windows-test: - strategy: matrix: version: [x86_64, i686] - runs-on: windows-latest - steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install ASIO SDK env: LINK: https://www.steinberg.net/asiosdk @@ -150,9 +142,8 @@ jobs: macos-test: runs-on: macOS-latest - steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: Install llvm and clang run: brew install llvm - name: Install stable From aae44cf3d60b20f246eef8c4ec1dd57230bcdecd Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 16 Apr 2020 15:23:17 +0200 Subject: [PATCH 3/4] Fix process_callback function signatures in asio backend --- src/host/asio/stream.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index 0c1345b..766e9d2 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -111,7 +111,7 @@ impl Device { ) where A: AsioSample, B: Sample, - D: FnMut(&Data) + Send + 'static, + D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, F: Fn(A) -> A, { // 1. Write the ASIO channels to the CPAL buffer. @@ -303,7 +303,7 @@ impl Device { ) where A: Sample, B: AsioSample, - D: FnMut(&mut Data) + Send + 'static, + D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, F: Fn(B) -> B, { // 1. Render interleaved buffer from callback. @@ -312,7 +312,7 @@ impl Device { let len = interleaved.len(); let mut data = Data::from_parts(data, len, A::FORMAT); let info = OutputCallbackInfo {}; - callback(&mut data); + callback(&mut data, &info); // 2. Silence ASIO channels if necessary. let n_channels = interleaved.len() / asio_stream.buffer_size as usize; From 95f5f2e54ad9a05c394f72b019a6bd020b77a7ff Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Thu, 16 Apr 2020 15:39:05 +0200 Subject: [PATCH 4/4] Update CHANGELOG for addition of CallbackInfo types --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5099dd1..c05cffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ - Added `build_input/output_stream_raw` methods allowing for dynamically handling sample format type. - Added support for DragonFly platform. +- Add `InputCallbackInfo` and `OutputCallbackInfo` types and update expected + user data callback function signature to provide these. # Version 0.11.0 (2019-12-11) @@ -48,7 +50,7 @@ - Better buffer handling - Fix logic error in frame/sample size -- Added error handling for unknown ALSA device errors +- Added error handling for unknown ALSA device errors - Fix resuming a paused stream on Windows (wasapi). - Implement `default_output_format` for emscripten backend.