diff --git a/Cargo.toml b/Cargo.toml index b65ba44..65db108 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ libc = "0.2.65" [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] coreaudio-rs = { version = "0.9.1", default-features = false, features = ["audio_unit", "core_audio"] } core-foundation-sys = "0.6.2" # For linking to CoreFoundation.framework and handling device name `CFString`s. +mach = "0.3" # For access to mach_timebase type. [target.'cfg(target_os = "emscripten")'.dependencies] stdweb = { version = "0.1.3", default-features = false } diff --git a/src/host/coreaudio/mod.rs b/src/host/coreaudio/mod.rs index 10173af..14d8d93 100644 --- a/src/host/coreaudio/mod.rs +++ b/src/host/coreaudio/mod.rs @@ -483,7 +483,7 @@ impl Device { config: &StreamConfig, sample_format: SampleFormat, mut data_callback: D, - _error_callback: E, + mut error_callback: E, ) -> Result where D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, @@ -637,6 +637,7 @@ 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 = sample_format.sample_size(); + let sample_rate = config.sample_rate; 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; @@ -645,7 +646,7 @@ impl Device { // TODO: Perhaps loop over all buffers instead? let AudioBuffer { - mNumberChannels: _num_channels, + mNumberChannels: channels, mDataByteSize: data_byte_size, mData: data, } = buffers[0]; @@ -653,7 +654,23 @@ 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); - let info = InputCallbackInfo {}; + + // TODO: Need a better way to get delay, for now we assume a double-buffer offset. + let callback = match host_time_to_stream_instant(args.time_stamp.mHostTime) { + Err(err) => { + error_callback(err.into()); + return Err(()); + } + Ok(cb) => cb, + }; + let buffer_frames = len / channels as usize; + let delay = frames_to_duration(buffer_frames, sample_rate); + let capture = callback + .sub(delay) + .expect("`capture` occurs before origin of alsa `StreamInstant`"); + let timestamp = crate::InputStreamTimestamp { callback, capture }; + + let info = InputCallbackInfo { timestamp }; data_callback(&data, &info); Ok(()) })?; @@ -672,7 +689,7 @@ impl Device { config: &StreamConfig, sample_format: SampleFormat, mut data_callback: D, - _error_callback: E, + mut error_callback: E, ) -> Result where D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, @@ -691,13 +708,14 @@ 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 = sample_format.sample_size(); + let sample_rate = config.sample_rate; 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. // Otherwise, we just fill the buffer with zeroes and return. let AudioBuffer { - mNumberChannels: _num_channels, + mNumberChannels: channels, mDataByteSize: data_byte_size, mData: data, } = (*args.data.data).mBuffers[0]; @@ -705,7 +723,23 @@ 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); - let info = OutputCallbackInfo {}; + + let callback = match host_time_to_stream_instant(args.time_stamp.mHostTime) { + Err(err) => { + error_callback(err.into()); + return Err(()); + } + Ok(cb) => cb, + }; + // TODO: Need a better way to get delay, for now we assume a double-buffer offset. + let buffer_frames = len / channels as usize; + let delay = frames_to_duration(buffer_frames, sample_rate); + let playback = callback + .add(delay) + .expect("`playback` occurs beyond representation supported by `StreamInstant`"); + let timestamp = crate::OutputStreamTimestamp { callback, playback }; + + let info = OutputCallbackInfo { timestamp }; data_callback(&mut data, &info); Ok(()) })?; @@ -720,6 +754,26 @@ impl Device { } } +fn host_time_to_stream_instant( + m_host_time: u64, +) -> Result { + let mut info: mach::mach_time::mach_timebase_info = Default::default(); + let res = unsafe { mach::mach_time::mach_timebase_info(&mut info) }; + check_os_status(res)?; + let nanos = m_host_time * info.numer as u64 / info.denom as u64; + let secs = nanos / 1_000_000_000; + let subsec_nanos = nanos - secs * 1_000_000_000; + Ok(crate::StreamInstant::new(secs as i64, subsec_nanos as u32)) +} + +// Convert the given duration in frames at the given sample rate to a `std::time::Duration`. +fn frames_to_duration(frames: usize, rate: crate::SampleRate) -> std::time::Duration { + let secsf = frames as f64 / rate.0 as f64; + let secs = secsf as u64; + let nanos = ((secsf - secs as f64) * 1_000_000_000.0) as u32; + std::time::Duration::new(secs, nanos) +} + pub struct Stream { inner: RefCell, }