From 0139a3f4b8f01c6ca1f49911fcce491276276389 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Mon, 13 Jul 2026 07:32:39 +0200 Subject: [PATCH 01/10] feat: report buffer over/underrun via CallbackInfo::xrun() in the data callback --- Cargo.toml | 4 ++ examples/android/src/lib.rs | 12 ++--- examples/beep.rs | 4 +- examples/custom.rs | 29 +++++------- examples/feedback.rs | 6 +-- examples/ios-feedback/src/feedback.rs | 22 ++++++--- examples/synth_tones.rs | 4 +- src/duplex.rs | 36 +++++++------- src/host/aaudio/mod.rs | 41 +++++++--------- src/host/alsa/mod.rs | 45 +++++++++++------- src/host/asio/mod.rs | 8 ++-- src/host/asio/stream.rs | 39 +++++++++------- src/host/audioworklet/mod.rs | 25 +++++----- src/host/coreaudio/ios/mod.rs | 43 +++++++++++------ src/host/coreaudio/macos/device.rs | 39 +++++++++++----- src/host/coreaudio/macos/mod.rs | 8 ++-- src/host/custom/mod.rs | 13 +++--- src/host/jack/device.rs | 11 ++--- src/host/jack/stream.rs | 32 +++++++++---- src/host/mod.rs | 12 ++--- src/host/null/mod.rs | 9 ++-- src/host/pipewire/device.rs | 12 ++--- src/host/pipewire/stream.rs | 59 ++++++++++++++--------- src/host/pulseaudio/mod.rs | 11 ++--- src/host/pulseaudio/stream.rs | 33 +++++++++---- src/host/wasapi/device.rs | 10 ++-- src/host/wasapi/stream.rs | 58 ++++++++++++----------- src/host/webaudio/mod.rs | 27 ++++++----- src/lib.rs | 15 +++--- src/platform/mod.rs | 4 +- src/timestamp.rs | 67 +++++++++++---------------- src/traits.rs | 18 +++---- 32 files changed, 415 insertions(+), 341 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 138630579..0c7e2fe1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -224,6 +224,10 @@ name = "record_wav" [[example]] name = "synth_tones" +[[example]] +name = "custom" +required-features = ["custom"] + [package.metadata.docs.rs] # The docs.rs build environment does not have all the dependencies for all features features = [ diff --git a/examples/android/src/lib.rs b/examples/android/src/lib.rs index 59b941454..0654789c5 100644 --- a/examples/android/src/lib.rs +++ b/examples/android/src/lib.rs @@ -4,9 +4,9 @@ extern crate anyhow; extern crate cpal; use cpal::{ + CallbackInfo, Device, Error, ErrorKind, FromSample, I24, Sample, SampleFormat, SizedSample, + StreamConfig, traits::{DeviceTrait, HostTrait, StreamTrait}, - Device, Error, ErrorKind, FromSample, OutputCallbackInfo, Sample, SampleFormat, SizedSample, - StreamConfig, I24, }; #[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "full"))] @@ -53,15 +53,15 @@ where }; let err_fn = |err: Error| match err.kind() { - ErrorKind::DeviceChanged | ErrorKind::Xrun | ErrorKind::RealtimeDenied => eprintln!("{err}"), + ErrorKind::DeviceChanged | ErrorKind::Xrun | ErrorKind::RealtimeDenied => { + eprintln!("{err}") + } _ => eprintln!("Stream error: {err}"), }; let stream = device.build_output_stream( config, - move |data: &mut [T], _: &OutputCallbackInfo| { - write_data(data, channels, &mut next_value) - }, + move |data: &mut [T], _: &CallbackInfo| write_data(data, channels, &mut next_value), err_fn, None, )?; diff --git a/examples/beep.rs b/examples/beep.rs index 840869699..8a97675f2 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -13,7 +13,7 @@ use clap::Parser; use cpal::{ - Device, Error, ErrorKind, FromSample, HostId, I24, OutputCallbackInfo, Sample, SampleFormat, + CallbackInfo, Device, Error, ErrorKind, FromSample, HostId, I24, Sample, SampleFormat, SizedSample, StreamConfig, traits::{DeviceTrait, HostTrait, StreamTrait}, }; @@ -143,7 +143,7 @@ where let stream = device.build_output_stream( config, - move |data: &mut [T], _: &OutputCallbackInfo| write_data(data, channels, &mut next_value), + move |data: &mut [T], _: &CallbackInfo| write_data(data, channels, &mut next_value), err_fn, None, )?; diff --git a/examples/custom.rs b/examples/custom.rs index f6df54984..2f0e73d14 100644 --- a/examples/custom.rs +++ b/examples/custom.rs @@ -8,14 +8,13 @@ use std::{ }; use cpal::{ - ChannelCount, Data, Device, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, - ErrorKind, FrameCount, FromSample, InputCallbackInfo, OutputCallbackInfo, - OutputStreamTimestamp, Sample, SampleFormat, Stream, StreamConfig, StreamInstant, - SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + CallbackInfo, ChannelCount, Data, Device, DeviceDescription, DeviceDescriptionBuilder, + DeviceId, Error, ErrorKind, FrameCount, FromSample, Sample, SampleFormat, Stream, StreamConfig, + StreamInstant, StreamTimestamp, SupportedBufferSize, SupportedStreamConfig, + SupportedStreamConfigRange, traits::{DeviceTrait, HostTrait, StreamTrait}, }; -#[allow(dead_code)] #[derive(Clone)] // Clone, Send+Sync are required struct MyHost; @@ -117,7 +116,7 @@ impl DeviceTrait for MyDevice { _: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { Err(Error::new(ErrorKind::UnsupportedConfig)) @@ -136,7 +135,7 @@ impl DeviceTrait for MyDevice { _: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { let controls = Arc::new(StreamControls { @@ -167,11 +166,11 @@ impl DeviceTrait for MyDevice { let secs = duration.as_nanos() / 1_000_000_000; let subsec_nanos = duration.as_nanos() - secs * 1_000_000_000; let stream_instant = StreamInstant::new(secs as _, subsec_nanos as _); - let timestamp = OutputStreamTimestamp { + let timestamp = StreamTimestamp { callback: stream_instant, - playback: stream_instant, + device: stream_instant, }; - data_callback(&mut data, &OutputCallbackInfo::new(timestamp)); + data_callback(&mut data, &CallbackInfo::new(timestamp, false)); let avg = buffer.iter().sum::() / buffer.len() as f32; println!("avg: {avg}"); @@ -228,7 +227,6 @@ impl Drop for MyStream { } } -#[cfg(feature = "custom")] fn main() { let custom_host = cpal::platform::CustomHost::from_host(MyHost); // alternatively, use cpal::platform::CustomDevice and skip enumerating devices @@ -242,11 +240,6 @@ fn main() { std::thread::sleep(std::time::Duration::from_millis(4000)); } -#[cfg(not(feature = "custom"))] -fn main() { - panic!("please run with -F custom to try this example") -} - // rest of this example is mostly based off of synth_tones.rs pub enum Waveform { @@ -329,7 +322,7 @@ pub fn make_stream(device: &Device, config: StreamConfig) -> Result { + ErrorKind::DeviceChanged | ErrorKind::RealtimeDenied => { eprintln!("{err}") } _ => eprintln!("Stream error: {err}"), @@ -340,7 +333,7 @@ pub fn make_stream(device: &Device, config: StreamConfig) -> Result anyhow::Result<()> { producer.try_push(f32::EQUILIBRIUM).unwrap(); } - let input_data_fn = move |data: &[f32], _: &InputCallbackInfo| { + let input_data_fn = move |data: &[f32], _: &CallbackInfo| { if producer.push_slice(data) < data.len() { eprintln!("output stream fell behind: try increasing latency"); } }; - let output_data_fn = move |data: &mut [f32], _: &OutputCallbackInfo| { + let output_data_fn = move |data: &mut [f32], _: &CallbackInfo| { let read = consumer.pop_slice(data); if read < data.len() { data[read..].fill(f32::EQUILIBRIUM); diff --git a/examples/ios-feedback/src/feedback.rs b/examples/ios-feedback/src/feedback.rs index 4d1105261..4b33724ee 100644 --- a/examples/ios-feedback/src/feedback.rs +++ b/examples/ios-feedback/src/feedback.rs @@ -11,12 +11,12 @@ extern crate cpal; extern crate ringbuf; use cpal::{ + CallbackInfo, Error, ErrorKind, Sample, StreamConfig, traits::{DeviceTrait, HostTrait, StreamTrait}, - Error, ErrorKind, InputCallbackInfo, OutputCallbackInfo, Sample, StreamConfig, }; use ringbuf::{ - traits::{Consumer, Producer, Split}, HeapRb, + traits::{Consumer, Producer, Split}, }; const LATENCY_MS: f32 = 1000.0; @@ -31,8 +31,14 @@ pub fn run_example() -> Result<(), anyhow::Error> { let output_device = host .default_output_device() .expect("failed to get default output device"); - println!("Using default input device: \"{}\"", input_device.description()?.name()); - println!("Using default output device: \"{}\"", output_device.description()?.name()); + println!( + "Using default input device: \"{}\"", + input_device.description()?.name() + ); + println!( + "Using default output device: \"{}\"", + output_device.description()?.name() + ); // We'll try and use the same configuration between streams to keep it simple. let config: StreamConfig = input_device.default_input_config()?.into(); @@ -52,13 +58,13 @@ pub fn run_example() -> Result<(), anyhow::Error> { producer.try_push(f32::EQUILIBRIUM).unwrap(); } - let input_data_fn = move |data: &[f32], _: &InputCallbackInfo| { + let input_data_fn = move |data: &[f32], _: &CallbackInfo| { if producer.push_slice(data) < data.len() { eprintln!("output stream fell behind: try increasing latency"); } }; - let output_data_fn = move |data: &mut [f32], _: &OutputCallbackInfo| { + let output_data_fn = move |data: &mut [f32], _: &CallbackInfo| { let read = consumer.pop_slice(data); if read < data.len() { data[read..].fill(f32::EQUILIBRIUM); @@ -88,7 +94,9 @@ pub fn run_example() -> Result<(), anyhow::Error> { fn err_fn(err: Error) { match err.kind() { - ErrorKind::DeviceChanged | ErrorKind::Xrun | ErrorKind::RealtimeDenied => eprintln!("{err}"), + ErrorKind::DeviceChanged | ErrorKind::Xrun | ErrorKind::RealtimeDenied => { + eprintln!("{err}") + } _ => eprintln!("Stream error: {err}"), } } diff --git a/examples/synth_tones.rs b/examples/synth_tones.rs index b369a36af..4ce4722bb 100644 --- a/examples/synth_tones.rs +++ b/examples/synth_tones.rs @@ -7,7 +7,7 @@ extern crate clap; extern crate cpal; use cpal::{ - Device, Error, ErrorKind, FromSample, Host, I24, OutputCallbackInfo, Sample, SampleFormat, + CallbackInfo, Device, Error, ErrorKind, FromSample, Host, I24, Sample, SampleFormat, SizedSample, Stream, StreamConfig, SupportedStreamConfig, U24, traits::{DeviceTrait, HostTrait, StreamTrait}, }; @@ -151,7 +151,7 @@ where let stream = device.build_output_stream( config, - move |output: &mut [T], _: &OutputCallbackInfo| { + move |output: &mut [T], _: &CallbackInfo| { // for 0-1s play sine, 1-2s play square, 2-3s play saw, 3-4s play triangle_wave let time_since_start = std::time::Instant::now() .duration_since(time_at_start) diff --git a/src/duplex.rs b/src/duplex.rs index fb8b13084..3800172ff 100644 --- a/src/duplex.rs +++ b/src/duplex.rs @@ -1,35 +1,31 @@ -use crate::{BufferSize, ChannelCount, InputStreamTimestamp, OutputStreamTimestamp, SampleRate}; +use crate::{BufferSize, CallbackInfo, ChannelCount, SampleRate}; /// Information relevant to a single call to the user's duplex stream data callback. /// -/// Combines the input and output timestamps for the callback. Because a duplex stream's input and -/// output share a single clock, both timestamps are drawn from the same time source. +/// Because a duplex stream's input and output share a single clock, `input.timestamp()` and +/// `output.timestamp()` are drawn from the same time source. The two directions have independent +/// buffers, so `input.xrun()` and `output.xrun()` can each report a glitch independently for the +/// same invocation. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct DuplexCallbackInfo { - input_timestamp: InputStreamTimestamp, - output_timestamp: OutputStreamTimestamp, + input: CallbackInfo, + output: CallbackInfo, } impl DuplexCallbackInfo { - /// Construct a `DuplexCallbackInfo` from its input and output timestamps. - pub fn new( - input_timestamp: InputStreamTimestamp, - output_timestamp: OutputStreamTimestamp, - ) -> Self { - Self { - input_timestamp, - output_timestamp, - } + /// Construct a `DuplexCallbackInfo` from its input and output callback info. + pub fn new(input: CallbackInfo, output: CallbackInfo) -> Self { + Self { input, output } } - /// The timestamp for the captured input data passed to the callback. - pub fn input_timestamp(&self) -> InputStreamTimestamp { - self.input_timestamp + /// The timestamp and xrun status for the captured input data passed to the callback. + pub fn input(&self) -> CallbackInfo { + self.input } - /// The timestamp for the output data written by the callback. - pub fn output_timestamp(&self) -> OutputStreamTimestamp { - self.output_timestamp + /// The timestamp and xrun status for the output data written by the callback. + pub fn output(&self) -> CallbackInfo { + self.output } } diff --git a/src/host/aaudio/mod.rs b/src/host/aaudio/mod.rs index 582cb1bc4..c86ea144c 100644 --- a/src/host/aaudio/mod.rs +++ b/src/host/aaudio/mod.rs @@ -14,11 +14,10 @@ use std::{ }; use crate::{ - BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, - DeviceId, DeviceType, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, - InterfaceType, OutputCallbackInfo, OutputStreamTimestamp, ResultExt, SampleFormat, SampleRate, - StreamConfig, StreamInstant, SupportedBufferSize, SupportedStreamConfig, - SupportedStreamConfigRange, + BufferSize, CallbackInfo, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, + DeviceDirection, DeviceId, DeviceType, Error, ErrorKind, FrameCount, InterfaceType, ResultExt, + SampleFormat, SampleRate, StreamConfig, StreamInstant, StreamTimestamp, SupportedBufferSize, + SupportedStreamConfig, SupportedStreamConfigRange, host::{ErrorCallbackArc, emit_error}, traits::{DeviceTrait, HostTrait, StreamTrait}, }; @@ -308,7 +307,7 @@ fn build_input_stream( sample_format: SampleFormat, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { let builder = configure_for_device(builder, device, config); @@ -318,7 +317,6 @@ where let error_callback: ErrorCallbackArc = Arc::new(Mutex::new(error_callback)); let error_callback_for_stream = error_callback.clone(); let error_callback_for_data = error_callback.clone(); - let error_callback_for_xrun = error_callback.clone(); let last_xrun_count = Arc::new(AtomicI32::new(0)); let draining = Arc::new(AtomicBool::new(false)); @@ -348,9 +346,7 @@ where } let xrun_count = stream.x_run_count(); - if last_xrun_count.swap(xrun_count, Ordering::Relaxed) < xrun_count { - let _ = try_emit_error(&error_callback_for_xrun, ErrorKind::Xrun.into()); - } + let xrun = last_xrun_count.swap(xrun_count, Ordering::Relaxed) < xrun_count; let Some(n_samples) = u64::try_from(num_frames) .ok() @@ -369,11 +365,12 @@ where return ndk::audio::AudioCallbackResult::Stop; }; if !draining_for_data.load(Ordering::Relaxed) { - let cb_info = InputCallbackInfo { - timestamp: InputStreamTimestamp { + let cb_info = CallbackInfo { + timestamp: StreamTimestamp { callback: now_stream_instant(), - capture: input_stream_instant(stream, sample_rate), + device: input_stream_instant(stream, sample_rate), }, + xrun, }; (data_callback)( &unsafe { Data::from_parts(data as *mut _, n_samples, sample_format) }, @@ -407,7 +404,7 @@ fn build_output_stream( sample_format: SampleFormat, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { let builder = configure_for_device(builder, device, config); @@ -421,7 +418,6 @@ where let error_callback: ErrorCallbackArc = Arc::new(Mutex::new(error_callback)); let error_callback_for_stream = error_callback.clone(); let error_callback_for_data = error_callback.clone(); - let error_callback_for_xrun = error_callback.clone(); let last_xrun_count = Arc::new(AtomicI32::new(0)); let draining = Arc::new(AtomicBool::new(false)); @@ -451,9 +447,7 @@ where } let xrun_count = stream.x_run_count(); - if last_xrun_count.swap(xrun_count, Ordering::Relaxed) < xrun_count { - let _ = try_emit_error(&error_callback_for_xrun, ErrorKind::Xrun.into()); - } + let xrun = last_xrun_count.swap(xrun_count, Ordering::Relaxed) < xrun_count; let Some(n_samples) = u64::try_from(num_frames) .ok() @@ -480,11 +474,12 @@ where } if !draining_for_data.load(Ordering::Relaxed) { - let cb_info = OutputCallbackInfo { - timestamp: OutputStreamTimestamp { + let cb_info = CallbackInfo { + timestamp: StreamTimestamp { callback: now_stream_instant(), - playback: output_stream_instant(stream, sample_rate), + device: output_stream_instant(stream, sample_rate), }, + xrun, }; (data_callback)( &mut unsafe { Data::from_parts(data as *mut _, n_samples, sample_format) }, @@ -673,7 +668,7 @@ impl DeviceTrait for Device { _timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { crate::validate_stream_config(&config)?; @@ -720,7 +715,7 @@ impl DeviceTrait for Device { _timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { crate::validate_stream_config(&config)?; diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index 9ae0fef2e..db935e11f 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -22,10 +22,9 @@ use std::{ use self::alsa::poll::Descriptors; pub use self::enumerate::Devices; use crate::{ - BufferSize, COMMON_SAMPLE_RATES, ChannelCount, Data, DeviceDescription, + BufferSize, COMMON_SAMPLE_RATES, CallbackInfo, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, DeviceId, Error, ErrorKind, FrameCount, - InputCallbackInfo, InputStreamTimestamp, OutputCallbackInfo, OutputStreamTimestamp, - SampleFormat, SampleRate, StreamConfig, StreamInstant, SupportedBufferSize, + SampleFormat, SampleRate, StreamConfig, StreamInstant, StreamTimestamp, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, host::{ Notify, @@ -266,7 +265,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { // Keep `capture` monotonic: avail_delay() varies between cycles, and a capture overrun @@ -292,7 +291,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { // Keep `playback` monotonic: avail_delay() varies between cycles, and a playback @@ -976,7 +975,7 @@ impl StreamWorkerContext { fn input_stream_worker( rx: Arc, stream: &StreamInner, - data_callback: &mut (dyn FnMut(&Data, &InputCallbackInfo) + Send + 'static), + data_callback: &mut (dyn FnMut(&Data, &CallbackInfo) + Send + 'static), error_callback: &mut (dyn FnMut(Error) + Send + 'static), timeout: Option, ) { @@ -1037,7 +1036,7 @@ fn input_stream_worker( fn output_stream_worker( rx: Arc, stream: &StreamInner, - data_callback: &mut (dyn FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static), + data_callback: &mut (dyn FnMut(&mut Data, &CallbackInfo) + Send + 'static), error_callback: &mut (dyn FnMut(Error) + Send + 'static), timeout: Option, ) { @@ -1240,7 +1239,7 @@ fn process_input( buffer: &mut [u8], status: alsa::pcm::Status, delay_frames: usize, - data_callback: &mut (dyn FnMut(&Data, &InputCallbackInfo) + Send + 'static), + data_callback: &mut (dyn FnMut(&Data, &CallbackInfo) + Send + 'static), ) -> Result<(), Error> { let mut frames_read = 0; while frames_read < stream.period_size { @@ -1277,11 +1276,17 @@ fn process_input( let capture = callback_instant .checked_sub(delay_duration) .unwrap_or(StreamInstant::ZERO); - let timestamp = InputStreamTimestamp { + let timestamp = StreamTimestamp { callback: callback_instant, - capture, + device: capture, }; - data_callback(&data, &InputCallbackInfo { timestamp }); + data_callback( + &data, + &CallbackInfo { + timestamp, + xrun: false, + }, + ); } Ok(()) @@ -1293,7 +1298,7 @@ fn process_output( buffer: &mut [u8], status: alsa::pcm::Status, delay_frames: usize, - data_callback: &mut (dyn FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static), + data_callback: &mut (dyn FnMut(&mut Data, &CallbackInfo) + Send + 'static), ) -> Result<(), Error> { // Pre-fill buffer with equilibrium; user callback overwrites what it wants. stream.equilibrium.fill(buffer); @@ -1305,11 +1310,17 @@ fn process_output( let callback_instant = stream.callback_instant(&status); let delay_duration = frames_to_duration(delay_frames as FrameCount, stream.sample_rate); let playback = callback_instant + delay_duration; - let timestamp = OutputStreamTimestamp { + let timestamp = StreamTimestamp { callback: callback_instant, - playback, + device: playback, }; - data_callback(&mut data, &OutputCallbackInfo { timestamp }); + data_callback( + &mut data, + &CallbackInfo { + timestamp, + xrun: false, + }, + ); } let mut frames_written = 0; @@ -1383,7 +1394,7 @@ impl Stream { timeout: Option, ) -> Stream where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { let (tx, rx) = trigger(); @@ -1426,7 +1437,7 @@ impl Stream { timeout: Option, ) -> Stream where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { let (tx, rx) = trigger(); diff --git a/src/host/asio/mod.rs b/src/host/asio/mod.rs index ea5a98a15..3d91a1818 100644 --- a/src/host/asio/mod.rs +++ b/src/host/asio/mod.rs @@ -15,8 +15,8 @@ pub use self::{ stream::Stream, }; use crate::{ - Data, DeviceDescription, DeviceId, Error, FrameCount, InputCallbackInfo, OutputCallbackInfo, - SampleFormat, StreamConfig, StreamInstant, SupportedStreamConfig, + CallbackInfo, Data, DeviceDescription, DeviceId, Error, FrameCount, SampleFormat, StreamConfig, + StreamInstant, SupportedStreamConfig, host::com, traits::{DeviceTrait, HostTrait, StreamTrait}, }; @@ -109,7 +109,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { Device::build_input_stream_raw( @@ -131,7 +131,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { Device::build_output_stream_raw( diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index 2674dac1f..13c54d2cc 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -13,9 +13,8 @@ use std::{ use self::num_traits::{FromPrimitive, PrimInt}; use super::Device; use crate::{ - BufferSize, Data, Error, ErrorKind, FrameCount, I24, InputCallbackInfo, InputStreamTimestamp, - OutputCallbackInfo, OutputStreamTimestamp, SampleFormat, SampleRate, StreamConfig, - StreamInstant, + BufferSize, CallbackInfo, Data, Error, ErrorKind, FrameCount, I24, SampleFormat, SampleRate, + StreamConfig, StreamInstant, StreamTimestamp, host::{ com, error_emit::{emit_error, try_emit_error}, @@ -127,7 +126,7 @@ impl Device { _timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { crate::validate_stream_config(&config)?; @@ -263,7 +262,7 @@ impl Device { callback_instant: StreamInstant, ) where A: Copy, - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, F: Fn(A) -> A, { // 1. Write the ASIO channels to the CPAL buffer. @@ -466,7 +465,7 @@ impl Device { _timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { crate::validate_stream_config(&config)?; @@ -615,7 +614,7 @@ impl Device { callback_instant: StreamInstant, ) where A: Copy, - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, F: Fn(A, A) -> A, { let interleaved: &mut [A] = unsafe { cast_slice_mut(interleaved) }; @@ -1289,7 +1288,7 @@ unsafe fn process_output_callback_i24( hardware_latency_frames: usize, callback_instant: StreamInstant, ) where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, { let format = SampleFormat::I24; let interleaved: &mut [I24] = unsafe { cast_slice_mut(interleaved) }; @@ -1365,7 +1364,7 @@ unsafe fn process_input_callback_i24( hardware_latency_frames: usize, callback_instant: StreamInstant, ) where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, { let format = SampleFormat::I24; @@ -1421,7 +1420,7 @@ unsafe fn apply_output_callback_to_data( hardware_latency_frames: usize, ) where A: Copy, - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, { let mut data = unsafe { Data::from_parts( @@ -1432,11 +1431,14 @@ unsafe fn apply_output_callback_to_data( }; let delay = frames_to_duration(hardware_latency_frames as FrameCount, sample_rate); let playback = callback_instant + delay; - let timestamp = OutputStreamTimestamp { + let timestamp = StreamTimestamp { callback: callback_instant, - playback, + device: playback, + }; + let info = CallbackInfo { + timestamp, + xrun: false, }; - let info = OutputCallbackInfo { timestamp }; data_callback(&mut data, &info); } @@ -1451,7 +1453,7 @@ unsafe fn apply_input_callback_to_data( hardware_latency_frames: usize, ) where A: Copy, - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, { let data = unsafe { Data::from_parts( @@ -1464,10 +1466,13 @@ unsafe fn apply_input_callback_to_data( let capture = callback_instant .checked_sub(delay) .unwrap_or(StreamInstant::ZERO); - let timestamp = InputStreamTimestamp { + let timestamp = StreamTimestamp { callback: callback_instant, - capture, + device: capture, + }; + let info = CallbackInfo { + timestamp, + xrun: false, }; - let info = InputCallbackInfo { timestamp }; data_callback(&data, &info); } diff --git a/src/host/audioworklet/mod.rs b/src/host/audioworklet/mod.rs index bcc239d47..1a603c7d3 100644 --- a/src/host/audioworklet/mod.rs +++ b/src/host/audioworklet/mod.rs @@ -18,10 +18,10 @@ use js_sys::wasm_bindgen; use wasm_bindgen::prelude::*; use crate::{ - BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, - DeviceId, Error, ErrorKind, FrameCount, InputCallbackInfo, OutputCallbackInfo, - OutputStreamTimestamp, SampleFormat, SampleRate, StreamConfig, StreamInstant, - SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + BufferSize, CallbackInfo, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, + DeviceDirection, DeviceId, Error, ErrorKind, FrameCount, SampleFormat, SampleRate, + StreamConfig, StreamInstant, StreamTimestamp, SupportedBufferSize, SupportedStreamConfig, + SupportedStreamConfigRange, host::frames_to_duration, traits::{DeviceTrait, HostTrait, StreamTrait}, }; @@ -238,7 +238,7 @@ impl DeviceTrait for Device { _timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { Err(Error::with_message( @@ -280,12 +280,12 @@ impl DeviceTrait for Device { _timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { crate::validate_stream_config(&config)?; - // Keep `playback` monotonic: the polled outputLatency can drop when the - // page calls `setSinkId()` to switch output devices, pulling `playback` backward. + // Keep `device` monotonic: the polled outputLatency can drop when the + // page calls `setSinkId()` to switch output devices, pulling `device` backward. let mut data_callback = crate::host::monotonic_output_callback(data_callback); if config.channels > MAX_CHANNELS { return Err(Error::with_message( @@ -416,9 +416,12 @@ impl DeviceTrait for Device { frames_to_duration(frame_size as FrameCount, sample_rate); let latency = Duration::from_nanos(latency_nanos_cb.load(Ordering::Relaxed)); - let playback = callback + (buffer_duration + latency); - let timestamp = OutputStreamTimestamp { callback, playback }; - let info = OutputCallbackInfo { timestamp }; + let device = callback + (buffer_duration + latency); + let timestamp = StreamTimestamp { callback, device }; + let info = CallbackInfo { + timestamp, + xrun: false, + }; (data_callback)(&mut data, &info); }, )) diff --git a/src/host/coreaudio/ios/mod.rs b/src/host/coreaudio/ios/mod.rs index 7957baacf..99dc8f574 100644 --- a/src/host/coreaudio/ios/mod.rs +++ b/src/host/coreaudio/ios/mod.rs @@ -24,10 +24,10 @@ use self::enumerate::{ }; use super::{asbd_from_config, host_time_to_stream_instant}; use crate::{ - BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, - ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, OutputCallbackInfo, - OutputStreamTimestamp, ResultExt, SampleFormat, SampleRate, StreamConfig, StreamInstant, - SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + BufferSize, CallbackInfo, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, + DeviceId, Error, ErrorKind, FrameCount, ResultExt, SampleFormat, SampleRate, StreamConfig, + StreamInstant, StreamTimestamp, SupportedBufferSize, SupportedStreamConfig, + SupportedStreamConfigRange, host::{ ErrorCallbackArc, equilibrium::fill_equilibrium, frames_to_duration, latch::Latch, try_emit_error, @@ -176,7 +176,7 @@ impl DeviceTrait for Device { _timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { crate::validate_stream_config(&config)?; @@ -235,7 +235,7 @@ impl DeviceTrait for Device { _timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { crate::validate_stream_config(&config)?; @@ -613,7 +613,7 @@ fn setup_input_callback( mut error_callback: E, ) -> Result<(), Error> where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { let bytes_per_channel = sample_format.sample_size(); @@ -644,8 +644,18 @@ where }; let delay = frames_to_duration(latency_frames as FrameCount, sample_rate); let capture = callback.checked_sub(delay).unwrap_or(StreamInstant::ZERO); - let timestamp = InputStreamTimestamp { callback, capture }; - data_callback(&data, &InputCallbackInfo { timestamp }); + let timestamp = StreamTimestamp { + callback, + device: capture, + }; + // iOS exposes no xrun signal. + data_callback( + &data, + &CallbackInfo { + timestamp, + xrun: false, + }, + ); } Ok(()) })?; @@ -664,7 +674,7 @@ fn setup_output_callback( mut error_callback: E, ) -> Result<(), Error> where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { let bytes_per_channel = sample_format.sample_size(); @@ -708,9 +718,16 @@ where }; let delay = frames_to_duration(latency_frames as FrameCount, sample_rate); let playback = callback + delay; - let timestamp = OutputStreamTimestamp { callback, playback }; + let timestamp = StreamTimestamp { + callback, + device: playback, + }; - let info = OutputCallbackInfo { timestamp }; + // iOS exposes no xrun signal. + let info = CallbackInfo { + timestamp, + xrun: false, + }; data_callback(&mut data, &info); Ok(()) })?; @@ -735,7 +752,7 @@ mod tests { let result = device.build_output_stream( &config, - |_data: &mut [f32], _info: &OutputCallbackInfo| {}, + |_data: &mut [f32], _info: &CallbackInfo| {}, |_err| {}, None, ); diff --git a/src/host/coreaudio/macos/device.rs b/src/host/coreaudio/macos/device.rs index dc0f1bb4b..6e669dcad 100644 --- a/src/host/coreaudio/macos/device.rs +++ b/src/host/coreaudio/macos/device.rs @@ -45,10 +45,10 @@ use super::{ host_time_to_stream_instant, }; use crate::{ - BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, - ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, InterfaceType, - OutputCallbackInfo, OutputStreamTimestamp, ResultExt, SampleFormat, SampleRate, StreamConfig, - StreamInstant, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + BufferSize, CallbackInfo, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, + DeviceId, Error, ErrorKind, FrameCount, InterfaceType, ResultExt, SampleFormat, SampleRate, + StreamConfig, StreamInstant, StreamTimestamp, SupportedBufferSize, SupportedStreamConfig, + SupportedStreamConfigRange, host::{ ErrorCallbackArc, coreaudio::macos::{StreamInner, loopback::LoopbackDevice}, @@ -323,7 +323,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { Device::build_input_stream_raw( @@ -345,7 +345,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { Device::build_output_stream_raw( @@ -702,7 +702,7 @@ impl Device { timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { crate::validate_stream_config(&config)?; @@ -791,8 +791,17 @@ impl Device { device_buffer_frames.unwrap_or(buffer_frames) + extra_latency_frames; let delay = frames_to_duration(latency_frames as FrameCount, sample_rate); let capture = callback.checked_sub(delay).unwrap_or(StreamInstant::ZERO); - let timestamp = InputStreamTimestamp { callback, capture }; - data_callback(&data, &InputCallbackInfo { timestamp }); + let timestamp = StreamTimestamp { + callback, + device: capture, + }; + data_callback( + &data, + &CallbackInfo { + timestamp, + xrun: false, + }, + ); } Ok(()) })?; @@ -827,7 +836,7 @@ impl Device { timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { crate::validate_stream_config(&config)?; @@ -930,9 +939,15 @@ impl Device { }; let delay = frames_to_duration(latency_frames as FrameCount, sample_rate); let playback = callback + delay; - let timestamp = OutputStreamTimestamp { callback, playback }; + let timestamp = StreamTimestamp { + callback, + device: playback, + }; - let info = OutputCallbackInfo { timestamp }; + let info = CallbackInfo { + timestamp, + xrun: false, + }; data_callback(&mut data, &info); Ok(()) })?; diff --git a/src/host/coreaudio/macos/mod.rs b/src/host/coreaudio/macos/mod.rs index ce2f8c7e9..88eb7ba14 100644 --- a/src/host/coreaudio/macos/mod.rs +++ b/src/host/coreaudio/macos/mod.rs @@ -447,7 +447,7 @@ impl StreamTrait for Stream { #[cfg(test)] mod test { use crate::{ - InputCallbackInfo, OutputCallbackInfo, Sample, default_host, + CallbackInfo, Sample, default_host, traits::{DeviceTrait, HostTrait, StreamTrait}, }; @@ -495,7 +495,7 @@ mod test { let stream = device .build_input_stream( config, - move |data: &[f32], _: &InputCallbackInfo| { + move |data: &[f32], _: &CallbackInfo| { // react to stream events and read or write stream data here. println!("Got data: {:?}", &data[..25]); }, @@ -528,7 +528,7 @@ mod test { let stream = device .build_input_stream( config, - move |data: &[f32], _: &InputCallbackInfo| { + move |data: &[f32], _: &CallbackInfo| { // react to stream events and read or write stream data here. println!("Got data: {:?}", &data[..25]); }, @@ -540,7 +540,7 @@ mod test { std::thread::sleep(std::time::Duration::from_secs(1)); } - fn write_silence(data: &mut [T], _: &OutputCallbackInfo) { + fn write_silence(data: &mut [T], _: &CallbackInfo) { for sample in data.iter_mut() { *sample = Sample::EQUILIBRIUM; } diff --git a/src/host/custom/mod.rs b/src/host/custom/mod.rs index dfbee8094..2de8b5ff8 100644 --- a/src/host/custom/mod.rs +++ b/src/host/custom/mod.rs @@ -7,9 +7,8 @@ use core::time::Duration; use std::fmt; use crate::{ - Data, DeviceDescription, DeviceId, Error, ErrorKind, FrameCount, InputCallbackInfo, - OutputCallbackInfo, SampleFormat, StreamConfig, StreamInstant, SupportedStreamConfig, - SupportedStreamConfigRange, + CallbackInfo, Data, DeviceDescription, DeviceId, Error, ErrorKind, FrameCount, SampleFormat, + StreamConfig, StreamInstant, SupportedStreamConfig, SupportedStreamConfigRange, traits::{DeviceTrait, HostTrait, StreamTrait}, }; @@ -169,8 +168,8 @@ impl Clone for SupportedConfigs { } type ErrorCallback = Box; -type InputCallback = Box; -type OutputCallback = Box; +type InputCallback = Box; +type OutputCallback = Box; trait DeviceErased: Send + Sync { fn description(&self) -> Result; @@ -422,7 +421,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { self.0.build_input_stream_raw( @@ -443,7 +442,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { self.0.build_output_stream_raw( diff --git a/src/host/jack/device.rs b/src/host/jack/device.rs index 2496106d3..9bbb3e32c 100644 --- a/src/host/jack/device.rs +++ b/src/host/jack/device.rs @@ -7,10 +7,9 @@ use std::{ use super::{JACK_SAMPLE_FORMAT, stream::Stream}; pub use crate::iter::{SupportedInputConfigs, SupportedOutputConfigs}; use crate::{ - BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, - DeviceId, Error, ErrorKind, InputCallbackInfo, OutputCallbackInfo, SampleFormat, SampleRate, - StreamConfig, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, - traits::DeviceTrait, + BufferSize, CallbackInfo, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, + DeviceDirection, DeviceId, Error, ErrorKind, SampleFormat, SampleRate, StreamConfig, + SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, traits::DeviceTrait, }; const DEFAULT_NUM_CHANNELS: ChannelCount = 2; @@ -183,7 +182,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { if self.is_output() { @@ -267,7 +266,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { if self.is_input() { diff --git a/src/host/jack/stream.rs b/src/host/jack/stream.rs index f35851c6e..fa156d6ad 100644 --- a/src/host/jack/stream.rs +++ b/src/host/jack/stream.rs @@ -5,8 +5,8 @@ use std::sync::{ use super::JACK_SAMPLE_FORMAT; use crate::{ - ChannelCount, Data, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, - OutputCallbackInfo, OutputStreamTimestamp, ResultExt, Sample, SampleRate, StreamInstant, + CallbackInfo, ChannelCount, Data, Error, ErrorKind, FrameCount, ResultExt, Sample, SampleRate, + StreamInstant, StreamTimestamp, host::{ErrorCallbackArc, emit_error, frames_to_duration, try_emit_error}, traits::StreamTrait, }; @@ -49,7 +49,7 @@ impl Stream { error_callback: E, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { let mut ports = vec![]; @@ -105,7 +105,7 @@ impl Stream { error_callback: E, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { let mut ports = vec![]; @@ -259,8 +259,8 @@ impl StreamTrait for Stream { } } -type InputDataCallback = Box; -type OutputDataCallback = Box; +type InputDataCallback = Box; +type OutputDataCallback = Box; struct LocalProcessHandler { /// No new ports are allowed to be created after the creation of the LocalProcessHandler as that would invalidate the buffer sizes @@ -452,8 +452,14 @@ impl jack::ProcessHandler for LocalProcessHandler { let capture = start_cycle_instant .checked_sub(latency) .unwrap_or(StreamInstant::ZERO); - let timestamp = InputStreamTimestamp { callback, capture }; - let info = InputCallbackInfo { timestamp }; + let timestamp = StreamTimestamp { + callback, + device: capture, + }; + let info = CallbackInfo { + timestamp, + xrun: false, + }; input_callback(&data, &info); } @@ -488,8 +494,14 @@ impl jack::ProcessHandler for LocalProcessHandler { } }, }; - let timestamp = OutputStreamTimestamp { callback, playback }; - let info = OutputCallbackInfo { timestamp }; + let timestamp = StreamTimestamp { + callback, + device: playback, + }; + let info = CallbackInfo { + timestamp, + xrun: false, + }; output_callback(&mut data, &info); // Deinterlace diff --git a/src/host/mod.rs b/src/host/mod.rs index 25b59ff33..fdd823ede 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -258,15 +258,15 @@ fn non_decreasing(floor: &mut u64, instant: crate::StreamInstant) -> crate::Stre #[allow(dead_code)] pub(crate) fn monotonic_input_callback( mut data_callback: D, -) -> impl FnMut(&crate::Data, &crate::InputCallbackInfo) + Send + 'static +) -> impl FnMut(&crate::Data, &crate::CallbackInfo) + Send + 'static where - D: FnMut(&crate::Data, &crate::InputCallbackInfo) + Send + 'static, + D: FnMut(&crate::Data, &crate::CallbackInfo) + Send + 'static, { // FnMut runs on one thread at a time, so the floor needs no synchronization. let mut floor = 0u64; move |data, info| { let mut info = *info; - info.timestamp.capture = non_decreasing(&mut floor, info.timestamp.capture); + info.timestamp.device = non_decreasing(&mut floor, info.timestamp.device); data_callback(data, &info); } } @@ -275,14 +275,14 @@ where #[allow(dead_code)] pub(crate) fn monotonic_output_callback( mut data_callback: D, -) -> impl FnMut(&mut crate::Data, &crate::OutputCallbackInfo) + Send + 'static +) -> impl FnMut(&mut crate::Data, &crate::CallbackInfo) + Send + 'static where - D: FnMut(&mut crate::Data, &crate::OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut crate::Data, &crate::CallbackInfo) + Send + 'static, { let mut floor = 0u64; move |data, info| { let mut info = *info; - info.timestamp.playback = non_decreasing(&mut floor, info.timestamp.playback); + info.timestamp.device = non_decreasing(&mut floor, info.timestamp.device); data_callback(data, &info); } } diff --git a/src/host/null/mod.rs b/src/host/null/mod.rs index 157aa3d40..2cce813f3 100644 --- a/src/host/null/mod.rs +++ b/src/host/null/mod.rs @@ -6,9 +6,8 @@ use std::fmt; use std::time::Duration; use crate::{ - Data, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, FrameCount, - InputCallbackInfo, OutputCallbackInfo, SampleFormat, StreamConfig, StreamInstant, - SupportedStreamConfig, SupportedStreamConfigRange, + CallbackInfo, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, FrameCount, + SampleFormat, StreamConfig, StreamInstant, SupportedStreamConfig, SupportedStreamConfigRange, traits::{DeviceTrait, HostTrait, StreamTrait}, }; @@ -79,7 +78,7 @@ impl DeviceTrait for Device { _timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { unimplemented!() @@ -95,7 +94,7 @@ impl DeviceTrait for Device { _timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { unimplemented!() diff --git a/src/host/pipewire/device.rs b/src/host/pipewire/device.rs index 2c0c8241e..9402f69ec 100644 --- a/src/host/pipewire/device.rs +++ b/src/host/pipewire/device.rs @@ -27,10 +27,10 @@ use pipewire::{ use super::stream::Stream; use crate::{ - BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, - DeviceId, DeviceType, Error, ErrorKind, FrameCount, HostId, InputCallbackInfo, InterfaceType, - OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig, SupportedBufferSize, - SupportedStreamConfig, SupportedStreamConfigRange, + BufferSize, CallbackInfo, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, + DeviceDirection, DeviceId, DeviceType, Error, ErrorKind, FrameCount, HostId, InterfaceType, + SampleFormat, SampleRate, StreamConfig, SupportedBufferSize, SupportedStreamConfig, + SupportedStreamConfigRange, host::{ Notify, emit_error, latch::Latch, @@ -352,7 +352,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, + D: FnMut(&Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { crate::validate_stream_config(&config)?; @@ -544,7 +544,7 @@ impl DeviceTrait for Device { timeout: Option, ) -> Result where - D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, + D: FnMut(&mut Data, &CallbackInfo) + Send + 'static, E: FnMut(Error) + Send + 'static, { crate::validate_stream_config(&config)?; diff --git a/src/host/pipewire/stream.rs b/src/host/pipewire/stream.rs index 587cc3a34..895096129 100644 --- a/src/host/pipewire/stream.rs +++ b/src/host/pipewire/stream.rs @@ -33,8 +33,8 @@ use pipewire::{ }; use crate::{ - Data, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, - OutputCallbackInfo, OutputStreamTimestamp, SampleFormat, StreamConfig, StreamInstant, + CallbackInfo, Data, Error, ErrorKind, FrameCount, SampleFormat, StreamConfig, StreamInstant, + StreamTimestamp, host::{ ErrorCallbackArc, Notify, emit_error, equilibrium::fill_equilibrium, frames_to_duration, latch::Latch, try_emit_error, @@ -341,19 +341,18 @@ impl UserData { } } - fn check_xrun(&mut self) { + fn check_xrun(&mut self) -> bool { if self.spa_io_clock.is_null() { - return; + return false; } // spa/node/io.h; not present in bindgen output on all libspa versions. const SPA_IO_CLOCK_FLAG_XRUN_RECOVER: u32 = 1 << 1; // io_changed and process run on the same thread. let flags = unsafe { (*self.spa_io_clock).flags }; let recovering = flags & SPA_IO_CLOCK_FLAG_XRUN_RECOVER != 0; - if recovering && !self.xrun_recovering { - let _ = try_emit_error(&self.error_callback, ErrorKind::Xrun.into()); - } + let xrun = recovering && !self.xrun_recovering; self.xrun_recovering = recovering; + xrun } } @@ -370,9 +369,15 @@ fn pw_stream_time(stream: &pw::stream::Stream) -> Option