Update ASIO backend for removal of `UnknownBufferType`

This commit is contained in:
mitchmindtree 2020-01-16 02:46:01 +11:00
parent b5bfb8d422
commit 9e832c6eb3
2 changed files with 94 additions and 83 deletions

View File

@ -1,17 +1,19 @@
extern crate asio_sys as sys; extern crate asio_sys as sys;
extern crate parking_lot; extern crate parking_lot;
use { use crate::{
BuildStreamError, BuildStreamError,
DefaultFormatError, DefaultFormatError,
DeviceNameError, DeviceNameError,
DevicesError, DevicesError,
Format, Format,
InputData,
OutputData,
PauseStreamError, PauseStreamError,
PlayStreamError, PlayStreamError,
SupportedFormatsError, Sample,
StreamData,
StreamError, StreamError,
SupportedFormatsError,
}; };
use traits::{ use traits::{
DeviceTrait, DeviceTrait,
@ -89,16 +91,30 @@ impl DeviceTrait for Device {
Device::default_output_format(self) Device::default_output_format(self)
} }
fn build_input_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError> fn build_input_stream<T, D, E>(
&self,
format: &Format,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
where where
D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static T: Sample,
D: FnMut(InputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static
{ {
Device::build_input_stream(self, format, data_callback, error_callback) Device::build_input_stream(self, format, data_callback, error_callback)
} }
fn build_output_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError> fn build_output_stream<T, D, E>(
&self,
format: &Format,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
where where
D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static T: Sample,
D: FnMut(OutputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static
{ {
Device::build_output_stream(self, format, data_callback, error_callback) Device::build_output_stream(self, format, data_callback, error_callback)
} }

View File

@ -10,12 +10,12 @@ use super::parking_lot::Mutex;
use BackendSpecificError; use BackendSpecificError;
use BuildStreamError; use BuildStreamError;
use Format; use Format;
use InputData;
use OutputData;
use PauseStreamError; use PauseStreamError;
use PlayStreamError; use PlayStreamError;
use Sample;
use SampleFormat; use SampleFormat;
use StreamData;
use UnknownTypeInputBuffer;
use UnknownTypeOutputBuffer;
use StreamError; use StreamError;
/// Sample types whose constant silent value is known. /// Sample types whose constant silent value is known.
@ -23,14 +23,11 @@ trait Silence {
const SILENCE: Self; const SILENCE: Self;
} }
/// Constraints on the interleaved sample buffer format required by the CPAL API.
trait InterleavedSample: Clone + Copy + Silence {
fn unknown_type_input_buffer(&[Self]) -> UnknownTypeInputBuffer;
fn unknown_type_output_buffer(&mut [Self]) -> UnknownTypeOutputBuffer;
}
/// Constraints on the ASIO sample types. /// Constraints on the ASIO sample types.
trait AsioSample: Clone + Copy + Silence + std::ops::Add<Self, Output = Self> {} trait AsioSample: Clone + Copy + Silence + std::ops::Add<Self, Output = Self> {
fn to_cpal_sample<T: Sample>(&self) -> T;
fn from_cpal_sample<T: Sample>(&T) -> Self;
}
// Used to keep track of whether or not the current current asio stream buffer requires // Used to keep track of whether or not the current current asio stream buffer requires
// being silencing before summing audio. // being silencing before summing audio.
@ -61,16 +58,18 @@ impl Stream {
} }
impl Device { impl Device {
pub fn build_input_stream<D, E>( pub fn build_input_stream<T, D, E>(
&self, &self,
format: &Format, format: &Format,
mut data_callback: D, mut data_callback: D,
_error_callback: E, _error_callback: E,
) -> Result<Stream, BuildStreamError> ) -> Result<Stream, BuildStreamError>
where where
D: FnMut(StreamData) + Send + 'static, T: Sample,
E: FnMut(StreamError) + Send + 'static D: FnMut(InputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{ {
assert_eq!(format.data_type, T::FORMAT, "sample type does not match `format.data_type`");
let stream_type = self.driver.input_data_type().map_err(build_stream_err)?; let stream_type = self.driver.input_data_type().map_err(build_stream_err)?;
// Ensure that the desired sample type is supported. // Ensure that the desired sample type is supported.
@ -109,20 +108,18 @@ impl Device {
/// 1. Write from the ASIO buffer to the interleaved CPAL buffer. /// 1. Write from the ASIO buffer to the interleaved CPAL buffer.
/// 2. Deliver the CPAL buffer to the user callback. /// 2. Deliver the CPAL buffer to the user callback.
unsafe fn process_input_callback<A, B, D, F, G>( unsafe fn process_input_callback<A, B, D, F>(
callback: &mut D, callback: &mut D,
interleaved: &mut [u8], interleaved: &mut [u8],
asio_stream: &sys::AsioStream, asio_stream: &sys::AsioStream,
buffer_index: usize, buffer_index: usize,
from_endianness: F, from_endianness: F,
to_cpal_sample: G,
) )
where where
A: AsioSample, A: AsioSample,
B: InterleavedSample, B: Sample,
D: FnMut(StreamData) + Send + 'static, D: FnMut(InputData<B>) + Send + 'static,
F: Fn(A) -> A, F: Fn(A) -> A,
G: Fn(A) -> B,
{ {
// 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);
@ -130,35 +127,32 @@ impl Device {
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) {
frame[ch_ix] = to_cpal_sample(from_endianness(*s_asio)); frame[ch_ix] = from_endianness(*s_asio).to_cpal_sample();
} }
} }
// 2. Deliver the interleaved buffer to the callback. // 2. Deliver the interleaved buffer to the callback.
callback( let data = InputData { buffer: interleaved };
StreamData::Input { buffer: B::unknown_type_input_buffer(interleaved) }, callback(data);
);
} }
match (&stream_type, data_type) { match (&stream_type, data_type) {
(&sys::AsioSampleType::ASIOSTInt16LSB, SampleFormat::I16) => { (&sys::AsioSampleType::ASIOSTInt16LSB, SampleFormat::I16) => {
process_input_callback::<i16, i16, _, _, _>( process_input_callback::<i16, T, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
from_le, from_le,
std::convert::identity::<i16>,
); );
} }
(&sys::AsioSampleType::ASIOSTInt16MSB, SampleFormat::I16) => { (&sys::AsioSampleType::ASIOSTInt16MSB, SampleFormat::I16) => {
process_input_callback::<i16, i16, _, _, _>( process_input_callback::<i16, T, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
from_be, from_be,
std::convert::identity::<i16>,
); );
} }
@ -166,13 +160,12 @@ impl Device {
// trait for the `to_le` and `to_be` methods, but this does not support floats. // trait for the `to_le` and `to_be` methods, but this does not support floats.
(&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32) | (&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32) |
(&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => { (&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => {
process_input_callback::<f32, f32, _, _, _>( process_input_callback::<f32, T, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
std::convert::identity::<f32>, std::convert::identity::<f32>,
std::convert::identity::<f32>,
); );
} }
@ -180,36 +173,33 @@ impl Device {
// `process_output_callback` function above by removing the unnecessary sample // `process_output_callback` function above by removing the unnecessary sample
// conversion function. // conversion function.
(&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I16) => { (&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I16) => {
process_input_callback::<i32, i16, _, _, _>( process_input_callback::<i32, T, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
from_le, from_le,
|s| (s >> 16) as i16,
); );
} }
(&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I16) => { (&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I16) => {
process_input_callback::<i32, i16, _, _, _>( process_input_callback::<i32, T, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
from_be, from_be,
|s| (s >> 16) as i16,
); );
} }
// TODO: Handle endianness conversion for floats? We currently use the `PrimInt` // TODO: Handle endianness conversion for floats? We currently use the `PrimInt`
// trait for the `to_le` and `to_be` methods, but this does not support floats. // trait for the `to_le` and `to_be` methods, but this does not support floats.
(&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F32) | (&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F32) |
(&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F32) => { (&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F32) => {
process_input_callback::<f64, f32, _, _, _>( process_input_callback::<f64, T, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
std::convert::identity::<f64>, std::convert::identity::<f64>,
|s| s as f32,
); );
} }
@ -234,16 +224,18 @@ impl Device {
}) })
} }
pub fn build_output_stream<D, E>( pub fn build_output_stream<T, D, E>(
&self, &self,
format: &Format, format: &Format,
mut data_callback: D, mut data_callback: D,
_error_callback: E, _error_callback: E,
) -> Result<Stream, BuildStreamError> ) -> Result<Stream, BuildStreamError>
where where
D: FnMut(StreamData) + Send + 'static, T: Sample,
D: FnMut(OutputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static, E: FnMut(StreamError) + Send + 'static,
{ {
assert_eq!(format.data_type, T::FORMAT, "sample type does not match `format.data_type`");
let stream_type = self.driver.output_data_type().map_err(build_stream_err)?; let stream_type = self.driver.output_data_type().map_err(build_stream_err)?;
// Ensure that the desired sample type is supported. // Ensure that the desired sample type is supported.
@ -305,26 +297,23 @@ impl Device {
/// 2. If required, silence the ASIO buffer. /// 2. If required, silence the ASIO buffer.
/// 3. Finally, write the interleaved data to the non-interleaved ASIO buffer, /// 3. Finally, write the interleaved data to the non-interleaved ASIO buffer,
/// performing endianness conversions as necessary. /// performing endianness conversions as necessary.
unsafe fn process_output_callback<A, B, D, F, G>( unsafe fn process_output_callback<A, B, D, F>(
callback: &mut D, callback: &mut D,
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, buffer_index: usize,
to_asio_sample: F, to_endianness: F,
to_endianness: G,
) )
where where
A: InterleavedSample, A: Sample,
B: AsioSample, B: AsioSample,
D: FnMut(StreamData) + Send + 'static, D: FnMut(OutputData<A>) + Send + 'static,
F: Fn(A) -> B, F: Fn(B) -> B,
G: Fn(B) -> B,
{ {
// 1. Render interleaved buffer from callback. // 1. Render interleaved buffer from callback.
let interleaved: &mut [A] = cast_slice_mut(interleaved); let interleaved: &mut [A] = cast_slice_mut(interleaved);
let buffer = A::unknown_type_output_buffer(interleaved); callback(OutputData { buffer: interleaved });
callback(StreamData::Output { buffer });
// 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() / asio_stream.buffer_size as usize;
@ -341,31 +330,29 @@ impl Device {
let asio_channel = let asio_channel =
asio_channel_slice_mut::<B>(asio_stream, buffer_index, ch_ix); asio_channel_slice_mut::<B>(asio_stream, buffer_index, ch_ix);
for (frame, s_asio) in interleaved.chunks(n_channels).zip(asio_channel) { for (frame, s_asio) in interleaved.chunks(n_channels).zip(asio_channel) {
*s_asio = *s_asio + to_endianness(to_asio_sample(frame[ch_ix])); *s_asio = *s_asio + to_endianness(B::from_cpal_sample(&frame[ch_ix]));
} }
} }
} }
match (data_type, &stream_type) { match (data_type, &stream_type) {
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16LSB) => { (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16LSB) => {
process_output_callback::<i16, i16, _, _, _>( process_output_callback::<T, i16, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
std::convert::identity::<i16>,
to_le, to_le,
); );
} }
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16MSB) => { (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16MSB) => {
process_output_callback::<i16, i16, _, _, _>( process_output_callback::<T, i16, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
std::convert::identity::<i16>,
to_be, to_be,
); );
} }
@ -374,14 +361,13 @@ impl Device {
// trait for the `to_le` and `to_be` methods, but this does not support floats. // trait for the `to_le` and `to_be` methods, but this does not support floats.
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB) | (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB) |
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => { (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => {
process_output_callback::<f32, f32, _, _, _>( process_output_callback::<T, f32, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
std::convert::identity::<f32>, std::convert::identity::<f32>,
std::convert::identity::<f32>,
); );
} }
@ -389,24 +375,22 @@ impl Device {
// `process_output_callback` function above by removing the unnecessary sample // `process_output_callback` function above by removing the unnecessary sample
// conversion function. // conversion function.
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32LSB) => { (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32LSB) => {
process_output_callback::<i16, i32, _, _, _>( process_output_callback::<T, i32, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
|s| (s as i32) << 16,
to_le, to_le,
); );
} }
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32MSB) => { (SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32MSB) => {
process_output_callback::<i16, i32, _, _, _>( process_output_callback::<T, i32, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
|s| (s as i32) << 16,
to_be, to_be,
); );
} }
@ -414,13 +398,12 @@ impl Device {
// trait for the `to_le` and `to_be` methods, but this does not support floats. // trait for the `to_le` and `to_be` methods, but this does not support floats.
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64LSB) | (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64LSB) |
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64MSB) => { (SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64MSB) => {
process_output_callback::<f32, f64, _, _, _>( process_output_callback::<T, f64, _, _>(
&mut data_callback, &mut data_callback,
&mut interleaved, &mut interleaved,
silence, silence,
asio_stream, asio_stream,
buffer_index as usize, buffer_index as usize,
|s| s as f64,
std::convert::identity::<f64>, std::convert::identity::<f64>,
); );
} }
@ -549,33 +532,45 @@ impl Silence for f64 {
const SILENCE: Self = 0.0; const SILENCE: Self = 0.0;
} }
impl InterleavedSample for i16 { impl AsioSample for i16 {
fn unknown_type_input_buffer(buffer: &[Self]) -> UnknownTypeInputBuffer { fn to_cpal_sample<T: Sample>(&self) -> T {
UnknownTypeInputBuffer::I16(::InputBuffer { buffer }) T::from(self)
} }
fn from_cpal_sample<T: Sample>(t: &T) -> Self {
fn unknown_type_output_buffer(buffer: &mut [Self]) -> UnknownTypeOutputBuffer { Sample::from(t)
UnknownTypeOutputBuffer::I16(::OutputBuffer { buffer })
} }
} }
impl InterleavedSample for f32 { impl AsioSample for i32 {
fn unknown_type_input_buffer(buffer: &[Self]) -> UnknownTypeInputBuffer { fn to_cpal_sample<T: Sample>(&self) -> T {
UnknownTypeInputBuffer::F32(::InputBuffer { buffer }) let s = (*self >> 16) as i16;
s.to_cpal_sample()
} }
fn from_cpal_sample<T: Sample>(t: &T) -> Self {
fn unknown_type_output_buffer(buffer: &mut [Self]) -> UnknownTypeOutputBuffer { let s = i16::from_cpal_sample(t);
UnknownTypeOutputBuffer::F32(::OutputBuffer { buffer }) (s as i32) << 16
} }
} }
impl AsioSample for i16 {} impl AsioSample for f32 {
fn to_cpal_sample<T: Sample>(&self) -> T {
T::from(self)
}
fn from_cpal_sample<T: Sample>(t: &T) -> Self {
Sample::from(t)
}
}
impl AsioSample for i32 {} impl AsioSample for f64 {
fn to_cpal_sample<T: Sample>(&self) -> T {
impl AsioSample for f32 {} let f = *self as f32;
f.to_cpal_sample()
impl AsioSample for f64 {} }
fn from_cpal_sample<T: Sample>(t: &T) -> Self {
let f = f32::from_cpal_sample(t);
f as f64
}
}
/// Check whether or not the desired format is supported by the stream. /// Check whether or not the desired format is supported by the stream.
/// ///