Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions asio-sys/src/bindings/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub enum AsioError {
HardwareStuck,
NoRate,
ASE_NoMemory,
InvalidBufferSize,
UnknownError,
}

Expand Down Expand Up @@ -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"),
}
}
Expand Down Expand Up @@ -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",
}
}
Expand Down
55 changes: 46 additions & 9 deletions asio-sys/src/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -431,8 +439,14 @@ 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
Expand All @@ -449,6 +463,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()?;
Expand All @@ -460,23 +485,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(),
Expand All @@ -489,7 +518,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 {
Expand All @@ -504,7 +533,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,
Expand All @@ -514,7 +543,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,
Expand All @@ -537,17 +566,21 @@ impl Driver {
///
/// `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.
Expand All @@ -559,17 +592,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.
Expand Down
1 change: 1 addition & 0 deletions examples/enumerate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
33 changes: 23 additions & 10 deletions src/host/alsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ extern crate libc;

use self::alsa::poll::Descriptors;
use crate::{
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError,
DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo, PauseStreamError,
PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError, SupportedStreamConfig,
SupportedStreamConfigRange, SupportedStreamConfigsError,
BackendSpecificError, BufferSize, BuildStreamError, ChannelCount, Data,
DefaultStreamConfigError, DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo,
PauseStreamError, PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError,
SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange,
SupportedStreamConfigsError,
};
use std::convert::TryInto;
use std::sync::Arc;
Expand Down Expand Up @@ -339,6 +340,14 @@ impl Device {
})
.collect::<Vec<_>>();

let min_buffer_size = hw_params.get_buffer_size_min()?;
let max_buffer_size = hw_params.get_buffer_size_max()?;

let buffer_size_range = SupportedBufferSize::Range {
min: min_buffer_size as u32,
max: max_buffer_size as u32,
};

let mut output = Vec::with_capacity(
supported_formats.len() * supported_channels.len() * sample_rates.len(),
);
Expand All @@ -349,6 +358,7 @@ impl Device {
channels: channels.clone(),
min_sample_rate: SampleRate(min_rate as u32),
max_sample_rate: SampleRate(max_rate as u32),
buffer_size: buffer_size_range.clone(),
sample_format: sample_format,
});
}
Expand Down Expand Up @@ -869,7 +879,7 @@ fn set_hw_params_from_format<'a>(
config: &StreamConfig,
sample_format: SampleFormat,
) -> Result<alsa::pcm::HwParams<'a>, BackendSpecificError> {
let mut hw_params = alsa::pcm::HwParams::any(pcm_handle)?;
let hw_params = alsa::pcm::HwParams::any(pcm_handle)?;
hw_params.set_access(alsa::pcm::Access::RWInterleaved)?;

let sample_format = if cfg!(target_endian = "big") {
Expand All @@ -890,11 +900,14 @@ fn set_hw_params_from_format<'a>(
hw_params.set_rate(config.sample_rate.0, alsa::ValueOr::Nearest)?;
hw_params.set_channels(config.channels as u32)?;

// If this isn't set manually a overlarge buffer may be used causing audio delay
let mut hw_params_copy = hw_params.clone();
if let Err(_) = hw_params.set_buffer_time_near(100_000, alsa::ValueOr::Nearest) {
// Swap out the params with errors for a snapshot taken before the error was introduced.
mem::swap(&mut hw_params_copy, &mut hw_params);
match config.buffer_size {
BufferSize::Fixed(v) => hw_params.set_buffer_size(v as i64)?,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be set_buffer_size_near.

Suggested change
BufferSize::Fixed(v) => hw_params.set_buffer_size(v as i64)?,
BufferSize::Fixed(v) => hw_params.set_buffer_size_near(v as i64)?,

Copy link
Member

@mitchmindtree mitchmindtree Jun 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using set_buffer_size is the correct approach here. If the user has specified a Fixed value and that exact value is unsupported, then we should return an error to the user to notify them.

Perhaps we could open an issue to discuss adding a Nearest(FrameCount) variant to the BufferSize enum in a follow-up PR that aligns with the semantics of set_buffer_size_near?

Copy link

@sniperrifle2004 sniperrifle2004 Jun 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I suggested set_buffer_size_near is because finding the Fixed values that actually work would pretty much rely on trial and error (Depending on the device). Especially since the possible range of buffer sizes is connected to the other hardware parameters. There are some Fixed values that should work most of the time, but relying on it will come to bite you. I think the user would benefit more from being able to query the precise buffer size afterwards (If that actually matters. It might). Nearest(Framecount) would of course also work for that though leaving the Fixed for the case in which the user really has to have that precise buffer size.

BufferSize::Default => {
// These values together represent a moderate latency and wakeup interval.
// Without them we are at the mercy of the device
hw_params.set_period_time_near(25_000, alsa::ValueOr::Nearest)?;
hw_params.set_buffer_time_near(100_000, alsa::ValueOr::Nearest)?;
}
}

pcm_handle.hw_params(&hw_params)?;
Expand Down
33 changes: 27 additions & 6 deletions src/host/asio/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use DeviceNameError;
use DevicesError;
use SampleFormat;
use SampleRate;
use SupportedBufferSize;
use SupportedStreamConfig;
use SupportedStreamConfigRange;
use SupportedStreamConfigsError;
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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())
Expand All @@ -122,13 +131,19 @@ 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 = SupportedBufferSize::Range {
min: min as u32,
max: max as u32,
};
// 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)
.ok_or(DefaultStreamConfigError::StreamTypeNotSupported)?;
Ok(SupportedStreamConfig {
channels,
sample_rate,
buffer_size,
sample_format,
})
}
Expand All @@ -137,12 +152,18 @@ 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 = SupportedBufferSize::Range {
min: min as u32,
max: max as u32,
};
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,
})
}
Expand Down
23 changes: 18 additions & 5 deletions src/host/asio/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -482,14 +482,20 @@ 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 {
Some(ref input) => Ok(input.buffer_size as usize),
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,
Expand Down Expand Up @@ -523,14 +529,20 @@ 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 {
Some(ref output) => Ok(output.buffer_size as usize),
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,
Expand Down Expand Up @@ -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();
Expand Down
Loading