implemented buffersizes for asio

This commit is contained in:
JoshuaBatty 2020-05-22 13:30:13 +02:00
parent 6edee6c6c2
commit 9e660da433
11 changed files with 142 additions and 65 deletions

View File

@ -19,6 +19,7 @@ pub enum AsioError {
HardwareStuck,
NoRate,
ASE_NoMemory,
InvalidBufferSize,
UnknownError,
}
@ -63,6 +64,7 @@ impl fmt::Display for AsioError {
"sample clock or rate cannot be determined or is not present"
),
AsioError::ASE_NoMemory => write!(f, "not enough memory for completing the request"),
AsioError::InvalidBufferSize => write!(f, "buffersize out of range for device"),
AsioError::UnknownError => write!(f, "Error not in SDK"),
}
}
@ -94,6 +96,7 @@ impl Error for AsioError {
AsioError::HardwareStuck => "hardware is not running when sample position is inquired",
AsioError::NoRate => "sample clock or rate cannot be determined or is not present",
AsioError::ASE_NoMemory => "not enough memory for completing the request",
AsioError::InvalidBufferSize => "buffersize out of range for device",
AsioError::UnknownError => "Error not in SDK",
}
}

View File

@ -385,6 +385,14 @@ impl Driver {
Ok(channel)
}
/// Get the min and max supported buffersize of the driver.
pub fn buffersize_range(&self) -> Result<(c_long, c_long), AsioError> {
let buffer_sizes = asio_get_buffer_sizes()?;
let min = buffer_sizes.min;
let max = buffer_sizes.max;
Ok((min, max))
}
/// Get current sample rate of the driver.
pub fn sample_rate(&self) -> Result<c_double, AsioError> {
let mut rate: c_double = 0.0;
@ -431,8 +439,10 @@ impl Driver {
///
/// This will destroy any already allocated buffers.
///
/// The preferred buffer size from ASIO is used.
fn create_buffers(&self, buffer_infos: &mut [AsioBufferInfo]) -> Result<c_long, AsioError> {
/// If buffersize is None then the preferred buffer size from ASIO is used,
/// otherwise the desired buffersize is used if the requeted size is within
/// the range of accepted buffersizes for the device.
fn create_buffers(&self, buffer_infos: &mut [AsioBufferInfo], buffer_size: Option<i32>) -> Result<c_long, AsioError> {
let num_channels = buffer_infos.len();
// To pass as ai::ASIOCallbacks
@ -449,6 +459,17 @@ impl Driver {
);
}
let buffer_size = match buffer_size {
Some(v) => {
if v <= buffer_sizes.max {
v
} else {
return Err(AsioError::InvalidBufferSize)
}
},
None => buffer_sizes.pref,
};
// Ensure the driver is in the `Initialized` state.
if let DriverState::Running = *state {
state.stop()?;
@ -460,23 +481,27 @@ impl Driver {
asio_result!(ai::ASIOCreateBuffers(
buffer_infos.as_mut_ptr() as *mut _,
num_channels as i32,
buffer_sizes.pref,
buffer_size,
&mut callbacks as *mut _ as *mut _,
))?;
}
*state = DriverState::Prepared;
Ok(buffer_sizes.pref)
Ok(buffer_size)
}
/// Creates the streams.
///
/// `buffer_size` sets the desired buffer_size. If None is passed in, then the
/// default buffersize for the device is used.
///
/// Both input and output streams need to be created together as a single slice of
/// `ASIOBufferInfo`.
fn create_streams(
&self,
mut input_buffer_infos: Vec<AsioBufferInfo>,
mut output_buffer_infos: Vec<AsioBufferInfo>,
buffer_size: Option<i32>,
) -> Result<AsioStreams, AsioError> {
let (input, output) = match (
input_buffer_infos.is_empty(),
@ -489,7 +514,7 @@ impl Driver {
let mut all_buffer_infos = input_buffer_infos;
all_buffer_infos.append(&mut output_buffer_infos);
// Create the buffers. On success, split the output and input again.
let buffer_size = self.create_buffers(&mut all_buffer_infos)?;
let buffer_size = self.create_buffers(&mut all_buffer_infos, buffer_size)?;
let output_buffer_infos = all_buffer_infos.split_off(split_point);
let input_buffer_infos = all_buffer_infos;
let input = Some(AsioStream {
@ -504,7 +529,7 @@ impl Driver {
}
// Just input
(false, true) => {
let buffer_size = self.create_buffers(&mut input_buffer_infos)?;
let buffer_size = self.create_buffers(&mut input_buffer_infos, buffer_size)?;
let input = Some(AsioStream {
buffer_infos: input_buffer_infos,
buffer_size,
@ -514,7 +539,7 @@ impl Driver {
}
// Just output
(true, false) => {
let buffer_size = self.create_buffers(&mut output_buffer_infos)?;
let buffer_size = self.create_buffers(&mut output_buffer_infos, buffer_size)?;
let input = None;
let output = Some(AsioStream {
buffer_infos: output_buffer_infos,
@ -536,18 +561,22 @@ impl Driver {
/// For this reason we take the output stream if it exists.
///
/// `num_channels` is the desired number of input channels.
///
/// `buffer_size` sets the desired buffer_size. If None is passed in, then the
/// default buffersize for the device is used.
///
/// This returns a full AsioStreams with both input and output if output was active.
pub fn prepare_input_stream(
&self,
output: Option<AsioStream>,
num_channels: usize,
buffer_size: Option<i32>,
) -> Result<AsioStreams, AsioError> {
let input_buffer_infos = prepare_buffer_infos(true, num_channels);
let output_buffer_infos = output
.map(|output| output.buffer_infos)
.unwrap_or_else(Vec::new);
self.create_streams(input_buffer_infos, output_buffer_infos)
self.create_streams(input_buffer_infos, output_buffer_infos, buffer_size)
}
/// Prepare the output stream.
@ -559,17 +588,21 @@ impl Driver {
///
/// `num_channels` is the desired number of output channels.
///
/// `buffer_size` sets the desired buffer_size. If None is passed in, then the
/// default buffersize for the device is used.
///
/// This returns a full AsioStreams with both input and output if input was active.
pub fn prepare_output_stream(
&self,
input: Option<AsioStream>,
num_channels: usize,
buffer_size: Option<i32>,
) -> Result<AsioStreams, AsioError> {
let input_buffer_infos = input
.map(|input| input.buffer_infos)
.unwrap_or_else(Vec::new);
let output_buffer_infos = prepare_buffer_infos(false, num_channels);
self.create_streams(input_buffer_infos, output_buffer_infos)
self.create_streams(input_buffer_infos, output_buffer_infos, buffer_size)
}
/// Releases buffers allocations.

View File

@ -4,7 +4,9 @@ extern crate cpal;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
fn main() -> Result<(), anyhow::Error> {
let host = cpal::default_host();
//let host = cpal::default_host();
let host =
cpal::host_from_id(cpal::platform::HostId::Asio).expect("failed to initialise ASIO host");
let device = host
.default_output_device()
.expect("failed to find a default output device");
@ -23,6 +25,9 @@ fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyh
where
T: cpal::Sample,
{
let mut config: cpal::StreamConfig = config.clone();
config.buffer_size = cpal::BufferSize::Fixed(256);
let sample_rate = config.sample_rate.0 as f32;
let channels = config.channels as usize;
@ -36,7 +41,7 @@ where
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
let stream = device.build_output_stream(
config,
&config,
move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
write_data(data, channels, &mut next_value)
},

View File

@ -11,6 +11,7 @@ fn main() -> Result<(), anyhow::Error> {
for host_id in available_hosts {
println!("{}", host_id.name());
let host = cpal::host_from_id(host_id)?;
let default_in = host.default_input_device().map(|e| e.name().unwrap());
let default_out = host.default_output_device().map(|e| e.name().unwrap());
println!(" Default Input Device:\n {:?}", default_in);

View File

@ -16,8 +16,9 @@ use ringbuf::RingBuffer;
const LATENCY_MS: f32 = 150.0;
fn main() -> Result<(), anyhow::Error> {
let host = cpal::default_host();
//let host = cpal::default_host();
let host =
cpal::host_from_id(cpal::platform::HostId::Asio).expect("failed to initialise ASIO host");
// Default devices.
let input_device = host
.default_input_device()
@ -30,8 +31,8 @@ fn main() -> Result<(), anyhow::Error> {
// We'll try and use the same configuration between streams to keep it simple.
let mut config: cpal::StreamConfig = input_device.default_input_config()?.into();
config.buffer_size = cpal::BufferSize::Fixed(1024);
//config.buffer_size = cpal::BufferSize::Fixed(1024);
// Create a delay in case the input and output devices aren't synced.
let latency_frames = (LATENCY_MS / 1_000.0) * config.sample_rate.0 as f32;
let latency_samples = latency_frames as usize * config.channels as usize;
@ -44,10 +45,10 @@ fn main() -> Result<(), anyhow::Error> {
for _ in 0..latency_samples {
// The ring buffer has twice as much space as necessary to add latency here,
// so this should never fail
producer.push(0.0).unwrap();
producer.push(0).unwrap();
}
let input_data_fn = move |data: &[f32], _: &cpal::InputCallbackInfo| {
let input_data_fn = move |data: &[i16], _: &cpal::InputCallbackInfo| {
println!("data len = {}", data.len());
let mut output_fell_behind = false;
for &sample in data {
@ -60,14 +61,14 @@ fn main() -> Result<(), anyhow::Error> {
}
};
let output_data_fn = move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
let output_data_fn = move |data: &mut [i16], _: &cpal::OutputCallbackInfo| {
let mut input_fell_behind = None;
for sample in data {
*sample = match consumer.pop() {
Ok(s) => s,
Err(err) => {
input_fell_behind = Some(err);
0.0
0
}
};
}

View File

@ -3,10 +3,11 @@ extern crate libc;
use self::alsa::poll::Descriptors;
use crate::{
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError,
BufferSize, DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo, PauseStreamError,
PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError, SupportedBufferSizeRange,
SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
BackendSpecificError, BufferSize, BuildStreamError, ChannelCount, Data,
DefaultStreamConfigError, DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo,
PauseStreamError, PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError,
SupportedBufferSizeRange, SupportedStreamConfig, SupportedStreamConfigRange,
SupportedStreamConfigsError,
};
use std::convert::TryInto;
use std::sync::Arc;

View File

@ -12,6 +12,7 @@ use DeviceNameError;
use DevicesError;
use SampleFormat;
use SampleRate;
use SupportedBufferSizeRange;
use SupportedStreamConfig;
use SupportedStreamConfigRange;
use SupportedStreamConfigsError;
@ -77,9 +78,13 @@ impl Device {
continue;
}
for channels in 1..f.channels + 1 {
f.channels = channels;
f.sample_rate = rate;
supported_configs.push(SupportedStreamConfigRange::from(f.clone()));
supported_configs.push(SupportedStreamConfigRange {
channels,
min_sample_rate: rate,
max_sample_rate: rate,
buffer_size: f.buffer_size.clone(),
sample_format: f.sample_format.clone(),
})
}
}
Ok(supported_configs.into_iter())
@ -110,9 +115,13 @@ impl Device {
continue;
}
for channels in 1..f.channels + 1 {
f.channels = channels;
f.sample_rate = rate;
supported_configs.push(SupportedStreamConfigRange::from(f.clone()));
supported_configs.push(SupportedStreamConfigRange {
channels,
min_sample_rate: rate,
max_sample_rate: rate,
buffer_size: f.buffer_size.clone(),
sample_format: f.sample_format.clone(),
})
}
}
Ok(supported_configs.into_iter())
@ -122,6 +131,12 @@ impl Device {
pub fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
let channels = self.driver.channels().map_err(default_config_err)?.ins as u16;
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_config_err)? as _);
let (min, max) = self.driver.buffersize_range().map_err(default_config_err)?;
let buffer_size = SupportedBufferSizeRange {
min: min as u32,
max: max as u32,
requires_power_of_two: false,
};
// Map th ASIO sample type to a CPAL sample type
let data_type = self.driver.input_data_type().map_err(default_config_err)?;
let sample_format = convert_data_type(&data_type)
@ -129,6 +144,7 @@ impl Device {
Ok(SupportedStreamConfig {
channels,
sample_rate,
buffer_size,
sample_format,
})
}
@ -137,12 +153,19 @@ impl Device {
pub fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
let channels = self.driver.channels().map_err(default_config_err)?.outs as u16;
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_config_err)? as _);
let (min, max) = self.driver.buffersize_range().map_err(default_config_err)?;
let buffer_size = SupportedBufferSizeRange {
min: min as u32,
max: max as u32,
requires_power_of_two: false,
};
let data_type = self.driver.output_data_type().map_err(default_config_err)?;
let sample_format = convert_data_type(&data_type)
.ok_or(DefaultStreamConfigError::StreamTypeNotSupported)?;
Ok(SupportedStreamConfig {
channels,
sample_rate,
buffer_size,
sample_format,
})
}

View File

@ -5,9 +5,9 @@ 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,
BackendSpecificError, BufferSize, BuildStreamError, Data, InputCallbackInfo,
OutputCallbackInfo, PauseStreamError, PlayStreamError, Sample, SampleFormat, StreamConfig,
StreamError,
};
use std;
use std::sync::atomic::{AtomicBool, Ordering};
@ -482,6 +482,12 @@ impl Device {
}?;
let num_channels = config.channels as usize;
let ref mut streams = *self.asio_streams.lock();
let buffer_size = match config.buffer_size {
BufferSize::Fixed(v) => Some(v as i32),
BufferSize::Default => None,
};
// Either create a stream if thers none or had back the
// size of the current one.
match streams.input {
@ -489,7 +495,7 @@ impl Device {
None => {
let output = streams.output.take();
self.driver
.prepare_input_stream(output, num_channels)
.prepare_input_stream(output, num_channels, buffer_size)
.map(|new_streams| {
let bs = match new_streams.input {
Some(ref inp) => inp.buffer_size as usize,
@ -523,6 +529,12 @@ impl Device {
}?;
let num_channels = config.channels as usize;
let ref mut streams = *self.asio_streams.lock();
let buffer_size = match config.buffer_size {
BufferSize::Fixed(v) => Some(v as i32),
BufferSize::Default => None,
};
// Either create a stream if thers none or had back the
// size of the current one.
match streams.output {
@ -530,7 +542,7 @@ impl Device {
None => {
let output = streams.output.take();
self.driver
.prepare_output_stream(output, num_channels)
.prepare_output_stream(output, num_channels, buffer_size)
.map(|new_streams| {
let bs = match new_streams.output {
Some(ref out) => out.buffer_size as usize,
@ -645,6 +657,7 @@ fn check_config(
let StreamConfig {
channels,
sample_rate,
buffer_size,
} = config;
// Try and set the sample rate to what the user selected.
let sample_rate = sample_rate.0.into();

View File

@ -5,9 +5,10 @@ use self::core_foundation_sys::string::{CFStringGetCString, CFStringGetCStringPt
use self::coreaudio::audio_unit::render_callback::{self, data};
use self::coreaudio::audio_unit::{AudioUnit, Element, Scope};
use self::coreaudio::sys::{
kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceNameCFString,
kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyBufferFrameSizeRange,kAudioDevicePropertyNominalSampleRate,
kAudioDevicePropertyScopeOutput, kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyStreamFormat,
kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyBufferFrameSize,
kAudioDevicePropertyBufferFrameSizeRange, kAudioDevicePropertyDeviceNameCFString,
kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput,
kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyStreamFormat,
kAudioFormatFlagIsFloat, kAudioFormatFlagIsPacked, kAudioFormatLinearPCM,
kAudioObjectPropertyElementMaster, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeOutput,
@ -20,10 +21,11 @@ use self::coreaudio::sys::{
};
use crate::traits::{DeviceTrait, HostTrait, StreamTrait};
use crate::{
BackendSpecificError, BufferSize, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError,
DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo, PauseStreamError,
PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError, SupportedBufferSizeRange,
SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
BackendSpecificError, BufferSize, BuildStreamError, ChannelCount, Data,
DefaultStreamConfigError, DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo,
PauseStreamError, PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError,
SupportedBufferSizeRange, SupportedStreamConfig, SupportedStreamConfigRange,
SupportedStreamConfigsError,
};
use std::cell::RefCell;
use std::ffi::CStr;
@ -667,16 +669,14 @@ impl Device {
let asbd = asbd_from_config(config, sample_format);
audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?;
// Set the buffersize
// Set the buffersize
match config.buffer_size {
BufferSize::Fixed(v) => {
audio_unit.set_property(
kAudioDevicePropertyBufferFrameSize,
scope,
element,
Some(&v),
)?
},
BufferSize::Fixed(v) => audio_unit.set_property(
kAudioDevicePropertyBufferFrameSize,
scope,
element,
Some(&v),
)?,
BufferSize::Default => (),
}
@ -751,16 +751,14 @@ impl Device {
let asbd = asbd_from_config(config, sample_format);
audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?;
// Set the buffersize
// Set the buffersize
match config.buffer_size {
BufferSize::Fixed(v) => {
audio_unit.set_property(
kAudioDevicePropertyBufferFrameSize,
scope,
element,
Some(&v),
)?
},
BufferSize::Fixed(v) => audio_unit.set_property(
kAudioDevicePropertyBufferFrameSize,
scope,
element,
Some(&v),
)?,
BufferSize::Default => (),
}
@ -900,4 +898,4 @@ fn get_io_buffer_frame_size_range(
max: buffer_size_range.mMaximum as u32,
requires_power_of_two: false,
})
}
}

View File

@ -319,6 +319,7 @@ unsafe fn format_from_waveformatex_ptr(
_ => return None,
};
let format = SupportedStreamConfig {
buffer_size: unimplemented!(),
channels: (*waveformatex_ptr).nChannels as _,
sample_rate: SampleRate((*waveformatex_ptr).nSamplesPerSec),
sample_format,
@ -526,7 +527,8 @@ impl Device {
let mut supported_formats = Vec::with_capacity(supported_sample_rates.len());
for rate in supported_sample_rates {
format.sample_rate = SampleRate(rate as _);
supported_formats.push(SupportedStreamConfigRange::from(format.clone()));
unimplemented!();
//supported_formats.push(SupportedStreamConfigRange::from(format.clone()));
}
Ok(supported_formats.into_iter())
}

View File

@ -531,10 +531,7 @@ impl SupportedStreamConfigRange {
///
/// **panic!**s if the given `sample_rate` is outside the range specified within this
/// `SupportedStreamConfigRange` instance.
pub fn with_sample_rate(
self,
sample_rate: SampleRate,
) -> SupportedStreamConfig {
pub fn with_sample_rate(self, sample_rate: SampleRate) -> SupportedStreamConfig {
assert!(self.min_sample_rate <= sample_rate && sample_rate <= self.max_sample_rate);
SupportedStreamConfig {
channels: self.channels,