WIP Add timestamp implementation to ASIO backend
Currently unchecked and untested, though most of the groundwork should be laid.
This commit is contained in:
parent
4579daad7b
commit
22eef32898
|
@ -84,8 +84,15 @@ pub struct SampleRate {
|
||||||
pub rate: u32,
|
pub rate: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information provided to the BufferCallback.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CallbackInfo {
|
||||||
|
pub buffer_index: i32,
|
||||||
|
pub system_time: ai::ASIOTimeStamp,
|
||||||
|
}
|
||||||
|
|
||||||
/// Holds the pointer to the callbacks that come from cpal
|
/// Holds the pointer to the callbacks that come from cpal
|
||||||
struct BufferCallback(Box<dyn FnMut(i32) + Send>);
|
struct BufferCallback(Box<dyn FnMut(&CallbackInfo) + Send>);
|
||||||
|
|
||||||
/// Input and Output streams.
|
/// Input and Output streams.
|
||||||
///
|
///
|
||||||
|
@ -355,9 +362,9 @@ impl Asio {
|
||||||
|
|
||||||
impl BufferCallback {
|
impl BufferCallback {
|
||||||
/// Calls the inner callback.
|
/// Calls the inner callback.
|
||||||
fn run(&mut self, index: i32) {
|
fn run(&mut self, callback_info: &CallbackInfo) {
|
||||||
let cb = &mut self.0;
|
let cb = &mut self.0;
|
||||||
cb(index);
|
cb(callback_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,7 +617,7 @@ impl Driver {
|
||||||
/// Returns an ID uniquely associated with the given callback so that it may be removed later.
|
/// Returns an ID uniquely associated with the given callback so that it may be removed later.
|
||||||
pub fn add_callback<F>(&self, callback: F) -> CallbackId
|
pub fn add_callback<F>(&self, callback: F) -> CallbackId
|
||||||
where
|
where
|
||||||
F: 'static + FnMut(i32) + Send,
|
F: 'static + FnMut(&CallbackInfo) + Send,
|
||||||
{
|
{
|
||||||
let mut bc = BUFFER_CALLBACK.lock().unwrap();
|
let mut bc = BUFFER_CALLBACK.lock().unwrap();
|
||||||
let id = bc
|
let id = bc
|
||||||
|
@ -909,8 +916,15 @@ extern "C" fn buffer_switch_time_info(
|
||||||
) -> *mut ai::ASIOTime {
|
) -> *mut ai::ASIOTime {
|
||||||
// This lock is probably unavoidable, but locks in the audio stream are not great.
|
// This lock is probably unavoidable, but locks in the audio stream are not great.
|
||||||
let mut bcs = BUFFER_CALLBACK.lock().unwrap();
|
let mut bcs = BUFFER_CALLBACK.lock().unwrap();
|
||||||
|
let time: &mut AsioTime = unsafe {
|
||||||
|
&mut *(time as *mut AsioTime)
|
||||||
|
};
|
||||||
|
let callback_info = CallbackInfo {
|
||||||
|
buffer_index: double_buffer_index,
|
||||||
|
system_time: time.time_info.system_time,
|
||||||
|
};
|
||||||
for &mut (_, ref mut bc) in bcs.iter_mut() {
|
for &mut (_, ref mut bc) in bcs.iter_mut() {
|
||||||
bc.run(double_buffer_index);
|
bc.run(&callback_info);
|
||||||
}
|
}
|
||||||
time
|
time
|
||||||
}
|
}
|
||||||
|
@ -952,6 +966,10 @@ fn check_type_sizes() {
|
||||||
std::mem::size_of::<AsioTimeCode>(),
|
std::mem::size_of::<AsioTimeCode>(),
|
||||||
std::mem::size_of::<ai::ASIOTimeCode>()
|
std::mem::size_of::<ai::ASIOTimeCode>()
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
std::mem::size_of::<AsioTimeInfo>(),
|
||||||
|
std::mem::size_of::<ai::AsioTimeInfo>(),
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
std::mem::size_of::<AsioTime>(),
|
std::mem::size_of::<AsioTime>(),
|
||||||
std::mem::size_of::<ai::ASIOTime>()
|
std::mem::size_of::<ai::ASIOTime>()
|
||||||
|
|
|
@ -87,7 +87,7 @@ impl Device {
|
||||||
|
|
||||||
// Set the input callback.
|
// Set the input callback.
|
||||||
// This is most performance critical part of the ASIO bindings.
|
// This is most performance critical part of the ASIO bindings.
|
||||||
let callback_id = self.driver.add_callback(move |buffer_index| unsafe {
|
let callback_id = self.driver.add_callback(move |callback_info| unsafe {
|
||||||
// If not playing return early.
|
// If not playing return early.
|
||||||
if !playing.load(Ordering::SeqCst) {
|
if !playing.load(Ordering::SeqCst) {
|
||||||
return;
|
return;
|
||||||
|
@ -106,7 +106,8 @@ impl Device {
|
||||||
callback: &mut D,
|
callback: &mut D,
|
||||||
interleaved: &mut [u8],
|
interleaved: &mut [u8],
|
||||||
asio_stream: &sys::AsioStream,
|
asio_stream: &sys::AsioStream,
|
||||||
buffer_index: usize,
|
asio_info: &sys::CallbackInfo,
|
||||||
|
sample_rate: crate::SampleRate,
|
||||||
from_endianness: F,
|
from_endianness: F,
|
||||||
) where
|
) where
|
||||||
A: AsioSample,
|
A: AsioSample,
|
||||||
|
@ -116,7 +117,9 @@ impl Device {
|
||||||
{
|
{
|
||||||
// 1. Write the ASIO channels to the CPAL buffer.
|
// 1. Write the ASIO channels to the CPAL buffer.
|
||||||
let interleaved: &mut [B] = cast_slice_mut(interleaved);
|
let interleaved: &mut [B] = cast_slice_mut(interleaved);
|
||||||
let n_channels = interleaved.len() / asio_stream.buffer_size as usize;
|
let n_frames = asio_stream.buffer_size as usize;
|
||||||
|
let n_channels = interleaved.len() / n_frames;
|
||||||
|
let buffer_index = asio_info.buffer_index as usize;
|
||||||
for ch_ix in 0..n_channels {
|
for ch_ix in 0..n_channels {
|
||||||
let asio_channel = asio_channel_slice::<A>(asio_stream, buffer_index, ch_ix);
|
let asio_channel = asio_channel_slice::<A>(asio_stream, buffer_index, ch_ix);
|
||||||
for (frame, s_asio) in interleaved.chunks_mut(n_channels).zip(asio_channel) {
|
for (frame, s_asio) in interleaved.chunks_mut(n_channels).zip(asio_channel) {
|
||||||
|
@ -128,7 +131,13 @@ impl Device {
|
||||||
let data = interleaved.as_mut_ptr() as *mut ();
|
let data = interleaved.as_mut_ptr() as *mut ();
|
||||||
let len = interleaved.len();
|
let len = interleaved.len();
|
||||||
let data = Data::from_parts(data, len, B::FORMAT);
|
let data = Data::from_parts(data, len, B::FORMAT);
|
||||||
let info = InputCallbackInfo {};
|
let callback = system_time_to_stream_instant(asio_info.system_time);
|
||||||
|
let delay = frames_to_duration(n_frames, sample_rate);
|
||||||
|
let capture = callback
|
||||||
|
.sub(delay)
|
||||||
|
.expect("`capture` occurs before origin of alsa `StreamInstant`");
|
||||||
|
let timestamp = InputStreamTimestamp { callback, capture };
|
||||||
|
let info = InputCallbackInfo { timestamp };
|
||||||
callback(&data, &info);
|
callback(&data, &info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +147,8 @@ impl Device {
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
callback_info,
|
||||||
|
config.sample_rate,
|
||||||
from_le,
|
from_le,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +157,8 @@ impl Device {
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
callback_info,
|
||||||
|
config.sample_rate,
|
||||||
from_be,
|
from_be,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -160,7 +171,8 @@ impl Device {
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
callback_info,
|
||||||
|
config.sample_rate,
|
||||||
std::convert::identity::<f32>,
|
std::convert::identity::<f32>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -173,7 +185,8 @@ impl Device {
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
callback_info,
|
||||||
|
config.sample_rate,
|
||||||
from_le,
|
from_le,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -182,7 +195,8 @@ impl Device {
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
callback_info,
|
||||||
|
config.sample_rate,
|
||||||
from_be,
|
from_be,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -194,7 +208,8 @@ impl Device {
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
callback_info,
|
||||||
|
config.sample_rate,
|
||||||
std::convert::identity::<f64>,
|
std::convert::identity::<f64>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -254,7 +269,7 @@ impl Device {
|
||||||
let playing = Arc::clone(&stream_playing);
|
let playing = Arc::clone(&stream_playing);
|
||||||
let asio_streams = self.asio_streams.clone();
|
let asio_streams = self.asio_streams.clone();
|
||||||
|
|
||||||
let callback_id = self.driver.add_callback(move |buffer_index| unsafe {
|
let callback_id = self.driver.add_callback(move |callback_info| unsafe {
|
||||||
// If not playing, return early.
|
// If not playing, return early.
|
||||||
if !playing.load(Ordering::SeqCst) {
|
if !playing.load(Ordering::SeqCst) {
|
||||||
return;
|
return;
|
||||||
|
@ -298,7 +313,8 @@ impl Device {
|
||||||
interleaved: &mut [u8],
|
interleaved: &mut [u8],
|
||||||
silence_asio_buffer: bool,
|
silence_asio_buffer: bool,
|
||||||
asio_stream: &sys::AsioStream,
|
asio_stream: &sys::AsioStream,
|
||||||
buffer_index: usize,
|
asio_info: &sys::CallbackInfo,
|
||||||
|
sample_rate: crate::SampleRate,
|
||||||
to_endianness: F,
|
to_endianness: F,
|
||||||
) where
|
) where
|
||||||
A: Sample,
|
A: Sample,
|
||||||
|
@ -311,11 +327,19 @@ impl Device {
|
||||||
let data = interleaved.as_mut_ptr() as *mut ();
|
let data = interleaved.as_mut_ptr() as *mut ();
|
||||||
let len = interleaved.len();
|
let len = interleaved.len();
|
||||||
let mut data = Data::from_parts(data, len, A::FORMAT);
|
let mut data = Data::from_parts(data, len, A::FORMAT);
|
||||||
let info = OutputCallbackInfo {};
|
let callback = system_time_to_stream_instant(asio_info.system_time);
|
||||||
|
let n_frames = asio_stream.buffer_size as usize;
|
||||||
|
let delay = frames_to_duration(n_frames, sample_rate);
|
||||||
|
let playback = callback
|
||||||
|
.add(delay)
|
||||||
|
.expect("`playback` occurs beyond representation supported by `StreamInstant`");
|
||||||
|
let timestamp = OutputStreamTimestamp { callback, playback };
|
||||||
|
let info = OutputCallbackInfo { timestamp };
|
||||||
callback(&mut data, &info);
|
callback(&mut data, &info);
|
||||||
|
|
||||||
// 2. Silence ASIO channels if necessary.
|
// 2. Silence ASIO channels if necessary.
|
||||||
let n_channels = interleaved.len() / asio_stream.buffer_size as usize;
|
let n_channels = interleaved.len() / n_frames;
|
||||||
|
let buffer_index = asio_info.buffer_index as usize;
|
||||||
if silence_asio_buffer {
|
if silence_asio_buffer {
|
||||||
for ch_ix in 0..n_channels {
|
for ch_ix in 0..n_channels {
|
||||||
let asio_channel =
|
let asio_channel =
|
||||||
|
@ -343,7 +367,8 @@ impl Device {
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
silence,
|
silence,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
callback_info,
|
||||||
|
config.sample_rate,
|
||||||
to_le,
|
to_le,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -354,6 +379,7 @@ impl Device {
|
||||||
silence,
|
silence,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
buffer_index as usize,
|
||||||
|
config.sample_rate,
|
||||||
to_be,
|
to_be,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -367,7 +393,8 @@ impl Device {
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
silence,
|
silence,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
callback_info,
|
||||||
|
config.sample_rate,
|
||||||
std::convert::identity::<f32>,
|
std::convert::identity::<f32>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -381,7 +408,8 @@ impl Device {
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
silence,
|
silence,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
callback_info,
|
||||||
|
config.sample_rate,
|
||||||
to_le,
|
to_le,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -391,7 +419,8 @@ impl Device {
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
silence,
|
silence,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
callback_info,
|
||||||
|
config.sample_rate,
|
||||||
to_be,
|
to_be,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -404,7 +433,8 @@ impl Device {
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
silence,
|
silence,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
buffer_index as usize,
|
callback_info,
|
||||||
|
config.sample_rate,
|
||||||
std::convert::identity::<f64>,
|
std::convert::identity::<f64>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -578,6 +608,21 @@ impl AsioSample for f64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Asio retrieves system time via `timeGetTime` which returns the time in milliseconds.
|
||||||
|
fn system_time_to_stream_instant(system_time: ai::ASIOTimeStamp) -> crate::StreamInstant {
|
||||||
|
let secs = system_time as u64 / 1_000;
|
||||||
|
let nanos = ((system_time as u64 - secs * 1_000) * 1_000_000) as u32;
|
||||||
|
crate::StreamInstant::new(secs, nanos)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
}
|
||||||
|
|
||||||
/// Check whether or not the desired config is supported by the stream.
|
/// Check whether or not the desired config is supported by the stream.
|
||||||
///
|
///
|
||||||
/// Checks sample rate, data type and then finally the number of channels.
|
/// Checks sample rate, data type and then finally the number of channels.
|
||||||
|
|
Loading…
Reference in New Issue