Merge pull request #401 from JoshuaBatty/buffersize_range

Prototyping an API for getting the supported min and max buffersizes
This commit is contained in:
mitchmindtree 2020-07-14 14:35:46 +02:00 committed by GitHub
commit b78ff83c03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 405 additions and 106 deletions

View File

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

View File

@ -385,6 +385,14 @@ impl Driver {
Ok(channel) 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. /// Get current sample rate of the driver.
pub fn sample_rate(&self) -> Result<c_double, AsioError> { pub fn sample_rate(&self) -> Result<c_double, AsioError> {
let mut rate: c_double = 0.0; let mut rate: c_double = 0.0;
@ -431,8 +439,14 @@ impl Driver {
/// ///
/// This will destroy any already allocated buffers. /// This will destroy any already allocated buffers.
/// ///
/// The preferred buffer size from ASIO is used. /// If buffersize is None then the preferred buffer size from ASIO is used,
fn create_buffers(&self, buffer_infos: &mut [AsioBufferInfo]) -> Result<c_long, AsioError> { /// 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(); let num_channels = buffer_infos.len();
// To pass as ai::ASIOCallbacks // To pass as ai::ASIOCallbacks
@ -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. // Ensure the driver is in the `Initialized` state.
if let DriverState::Running = *state { if let DriverState::Running = *state {
state.stop()?; state.stop()?;
@ -460,23 +485,27 @@ impl Driver {
asio_result!(ai::ASIOCreateBuffers( asio_result!(ai::ASIOCreateBuffers(
buffer_infos.as_mut_ptr() as *mut _, buffer_infos.as_mut_ptr() as *mut _,
num_channels as i32, num_channels as i32,
buffer_sizes.pref, buffer_size,
&mut callbacks as *mut _ as *mut _, &mut callbacks as *mut _ as *mut _,
))?; ))?;
} }
*state = DriverState::Prepared; *state = DriverState::Prepared;
Ok(buffer_sizes.pref) Ok(buffer_size)
} }
/// Creates the streams. /// 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 /// Both input and output streams need to be created together as a single slice of
/// `ASIOBufferInfo`. /// `ASIOBufferInfo`.
fn create_streams( fn create_streams(
&self, &self,
mut input_buffer_infos: Vec<AsioBufferInfo>, mut input_buffer_infos: Vec<AsioBufferInfo>,
mut output_buffer_infos: Vec<AsioBufferInfo>, mut output_buffer_infos: Vec<AsioBufferInfo>,
buffer_size: Option<i32>,
) -> Result<AsioStreams, AsioError> { ) -> Result<AsioStreams, AsioError> {
let (input, output) = match ( let (input, output) = match (
input_buffer_infos.is_empty(), input_buffer_infos.is_empty(),
@ -489,7 +518,7 @@ impl Driver {
let mut all_buffer_infos = input_buffer_infos; let mut all_buffer_infos = input_buffer_infos;
all_buffer_infos.append(&mut output_buffer_infos); all_buffer_infos.append(&mut output_buffer_infos);
// Create the buffers. On success, split the output and input again. // 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 output_buffer_infos = all_buffer_infos.split_off(split_point);
let input_buffer_infos = all_buffer_infos; let input_buffer_infos = all_buffer_infos;
let input = Some(AsioStream { let input = Some(AsioStream {
@ -504,7 +533,7 @@ impl Driver {
} }
// Just input // Just input
(false, true) => { (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 { let input = Some(AsioStream {
buffer_infos: input_buffer_infos, buffer_infos: input_buffer_infos,
buffer_size, buffer_size,
@ -514,7 +543,7 @@ impl Driver {
} }
// Just output // Just output
(true, false) => { (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 input = None;
let output = Some(AsioStream { let output = Some(AsioStream {
buffer_infos: output_buffer_infos, buffer_infos: output_buffer_infos,
@ -537,17 +566,21 @@ impl Driver {
/// ///
/// `num_channels` is the desired number of input channels. /// `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. /// This returns a full AsioStreams with both input and output if output was active.
pub fn prepare_input_stream( pub fn prepare_input_stream(
&self, &self,
output: Option<AsioStream>, output: Option<AsioStream>,
num_channels: usize, num_channels: usize,
buffer_size: Option<i32>,
) -> Result<AsioStreams, AsioError> { ) -> Result<AsioStreams, AsioError> {
let input_buffer_infos = prepare_buffer_infos(true, num_channels); let input_buffer_infos = prepare_buffer_infos(true, num_channels);
let output_buffer_infos = output let output_buffer_infos = output
.map(|output| output.buffer_infos) .map(|output| output.buffer_infos)
.unwrap_or_else(Vec::new); .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. /// Prepare the output stream.
@ -559,17 +592,21 @@ impl Driver {
/// ///
/// `num_channels` is the desired number of output channels. /// `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. /// This returns a full AsioStreams with both input and output if input was active.
pub fn prepare_output_stream( pub fn prepare_output_stream(
&self, &self,
input: Option<AsioStream>, input: Option<AsioStream>,
num_channels: usize, num_channels: usize,
buffer_size: Option<i32>,
) -> Result<AsioStreams, AsioError> { ) -> Result<AsioStreams, AsioError> {
let input_buffer_infos = input let input_buffer_infos = input
.map(|input| input.buffer_infos) .map(|input| input.buffer_infos)
.unwrap_or_else(Vec::new); .unwrap_or_else(Vec::new);
let output_buffer_infos = prepare_buffer_infos(false, num_channels); 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. /// Releases buffers allocations.

View File

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

View File

@ -3,10 +3,11 @@ extern crate libc;
use self::alsa::poll::Descriptors; use self::alsa::poll::Descriptors;
use crate::{ use crate::{
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError, BackendSpecificError, BufferSize, BuildStreamError, ChannelCount, Data,
DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo, PauseStreamError, DefaultStreamConfigError, DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo,
PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError, SupportedStreamConfig, PauseStreamError, PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError,
SupportedStreamConfigRange, SupportedStreamConfigsError, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange,
SupportedStreamConfigsError,
}; };
use std::convert::TryInto; use std::convert::TryInto;
use std::sync::Arc; use std::sync::Arc;
@ -339,6 +340,14 @@ impl Device {
}) })
.collect::<Vec<_>>(); .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( let mut output = Vec::with_capacity(
supported_formats.len() * supported_channels.len() * sample_rates.len(), supported_formats.len() * supported_channels.len() * sample_rates.len(),
); );
@ -349,6 +358,7 @@ impl Device {
channels: channels.clone(), channels: channels.clone(),
min_sample_rate: SampleRate(min_rate as u32), min_sample_rate: SampleRate(min_rate as u32),
max_sample_rate: SampleRate(max_rate as u32), max_sample_rate: SampleRate(max_rate as u32),
buffer_size: buffer_size_range.clone(),
sample_format: sample_format, sample_format: sample_format,
}); });
} }
@ -869,7 +879,7 @@ fn set_hw_params_from_format<'a>(
config: &StreamConfig, config: &StreamConfig,
sample_format: SampleFormat, sample_format: SampleFormat,
) -> Result<alsa::pcm::HwParams<'a>, BackendSpecificError> { ) -> 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)?; hw_params.set_access(alsa::pcm::Access::RWInterleaved)?;
let sample_format = if cfg!(target_endian = "big") { let sample_format = if cfg!(target_endian = "big") {
@ -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_rate(config.sample_rate.0, alsa::ValueOr::Nearest)?;
hw_params.set_channels(config.channels as u32)?; hw_params.set_channels(config.channels as u32)?;
// If this isn't set manually a overlarge buffer may be used causing audio delay match config.buffer_size {
let mut hw_params_copy = hw_params.clone(); BufferSize::Fixed(v) => hw_params.set_buffer_size(v as i64)?,
if let Err(_) = hw_params.set_buffer_time_near(100_000, alsa::ValueOr::Nearest) { BufferSize::Default => {
// Swap out the params with errors for a snapshot taken before the error was introduced. // These values together represent a moderate latency and wakeup interval.
mem::swap(&mut hw_params_copy, &mut hw_params); // 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)?; pcm_handle.hw_params(&hw_params)?;

View File

@ -12,6 +12,7 @@ use DeviceNameError;
use DevicesError; use DevicesError;
use SampleFormat; use SampleFormat;
use SampleRate; use SampleRate;
use SupportedBufferSize;
use SupportedStreamConfig; use SupportedStreamConfig;
use SupportedStreamConfigRange; use SupportedStreamConfigRange;
use SupportedStreamConfigsError; use SupportedStreamConfigsError;
@ -77,9 +78,13 @@ impl Device {
continue; continue;
} }
for channels in 1..f.channels + 1 { for channels in 1..f.channels + 1 {
f.channels = channels; supported_configs.push(SupportedStreamConfigRange {
f.sample_rate = rate; channels,
supported_configs.push(SupportedStreamConfigRange::from(f.clone())); 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()) Ok(supported_configs.into_iter())
@ -110,9 +115,13 @@ impl Device {
continue; continue;
} }
for channels in 1..f.channels + 1 { for channels in 1..f.channels + 1 {
f.channels = channels; supported_configs.push(SupportedStreamConfigRange {
f.sample_rate = rate; channels,
supported_configs.push(SupportedStreamConfigRange::from(f.clone())); 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()) Ok(supported_configs.into_iter())
@ -122,6 +131,11 @@ impl Device {
pub fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> { pub fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
let channels = self.driver.channels().map_err(default_config_err)?.ins as u16; 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 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 // Map th ASIO sample type to a CPAL sample type
let data_type = self.driver.input_data_type().map_err(default_config_err)?; let data_type = self.driver.input_data_type().map_err(default_config_err)?;
let sample_format = convert_data_type(&data_type) let sample_format = convert_data_type(&data_type)
@ -129,6 +143,7 @@ impl Device {
Ok(SupportedStreamConfig { Ok(SupportedStreamConfig {
channels, channels,
sample_rate, sample_rate,
buffer_size,
sample_format, sample_format,
}) })
} }
@ -137,12 +152,18 @@ impl Device {
pub fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> { pub fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
let channels = self.driver.channels().map_err(default_config_err)?.outs as u16; 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 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 data_type = self.driver.output_data_type().map_err(default_config_err)?;
let sample_format = convert_data_type(&data_type) let sample_format = convert_data_type(&data_type)
.ok_or(DefaultStreamConfigError::StreamTypeNotSupported)?; .ok_or(DefaultStreamConfigError::StreamTypeNotSupported)?;
Ok(SupportedStreamConfig { Ok(SupportedStreamConfig {
channels, channels,
sample_rate, sample_rate,
buffer_size,
sample_format, sample_format,
}) })
} }

View File

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

View File

@ -5,7 +5,8 @@ use self::core_foundation_sys::string::{CFStringGetCString, CFStringGetCStringPt
use self::coreaudio::audio_unit::render_callback::{self, data}; use self::coreaudio::audio_unit::render_callback::{self, data};
use self::coreaudio::audio_unit::{AudioUnit, Element, Scope}; use self::coreaudio::audio_unit::{AudioUnit, Element, Scope};
use self::coreaudio::sys::{ use self::coreaudio::sys::{
kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyDeviceNameCFString, kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyBufferFrameSize,
kAudioDevicePropertyBufferFrameSizeRange, kAudioDevicePropertyDeviceNameCFString,
kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput, kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyScopeOutput,
kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyStreamFormat, kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyStreamFormat,
kAudioFormatFlagIsFloat, kAudioFormatFlagIsPacked, kAudioFormatLinearPCM, kAudioFormatFlagIsFloat, kAudioFormatFlagIsPacked, kAudioFormatLinearPCM,
@ -20,10 +21,11 @@ use self::coreaudio::sys::{
}; };
use crate::traits::{DeviceTrait, HostTrait, StreamTrait}; use crate::traits::{DeviceTrait, HostTrait, StreamTrait};
use crate::{ use crate::{
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError, BackendSpecificError, BufferSize, BuildStreamError, ChannelCount, Data,
DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo, PauseStreamError, DefaultStreamConfigError, DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo,
PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError, SupportedStreamConfig, PauseStreamError, PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError,
SupportedStreamConfigRange, SupportedStreamConfigsError, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange,
SupportedStreamConfigsError,
}; };
use std::cell::RefCell; use std::cell::RefCell;
use std::ffi::CStr; use std::ffi::CStr;
@ -276,6 +278,9 @@ impl Device {
let ranges: *mut AudioValueRange = ranges.as_mut_ptr() as *mut _; let ranges: *mut AudioValueRange = ranges.as_mut_ptr() as *mut _;
let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges); let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges);
let audio_unit = audio_unit_from_device(self, true)?;
let buffer_size = get_io_buffer_frame_size_range(&audio_unit)?;
// Collect the supported formats for the device. // Collect the supported formats for the device.
let mut fmts = vec![]; let mut fmts = vec![];
for range in ranges { for range in ranges {
@ -283,6 +288,7 @@ impl Device {
channels: n_channels as ChannelCount, channels: n_channels as ChannelCount,
min_sample_rate: SampleRate(range.mMinimum as _), min_sample_rate: SampleRate(range.mMinimum as _),
max_sample_rate: SampleRate(range.mMaximum as _), max_sample_rate: SampleRate(range.mMaximum as _),
buffer_size: buffer_size.clone(),
sample_format: sample_format, sample_format: sample_format,
}; };
fmts.push(fmt); fmts.push(fmt);
@ -374,9 +380,13 @@ impl Device {
} }
}; };
let audio_unit = audio_unit_from_device(self, true)?;
let buffer_size = get_io_buffer_frame_size_range(&audio_unit)?;
let config = SupportedStreamConfig { let config = SupportedStreamConfig {
sample_rate: SampleRate(asbd.mSampleRate as _), sample_rate: SampleRate(asbd.mSampleRate as _),
channels: asbd.mChannelsPerFrame as _, channels: asbd.mChannelsPerFrame as _,
buffer_size: buffer_size,
sample_format: sample_format, sample_format: sample_format,
}; };
Ok(config) Ok(config)
@ -426,6 +436,24 @@ impl From<coreaudio::Error> for BuildStreamError {
} }
} }
impl From<coreaudio::Error> for SupportedStreamConfigsError {
fn from(err: coreaudio::Error) -> SupportedStreamConfigsError {
let description = format!("{}", err);
let err = BackendSpecificError { description };
// Check for possible DeviceNotAvailable variant
SupportedStreamConfigsError::BackendSpecific { err }
}
}
impl From<coreaudio::Error> for DefaultStreamConfigError {
fn from(err: coreaudio::Error) -> DefaultStreamConfigError {
let description = format!("{}", err);
let err = BackendSpecificError { description };
// Check for possible DeviceNotAvailable variant
DefaultStreamConfigError::BackendSpecific { err }
}
}
// Create a coreaudio AudioStreamBasicDescription from a CPAL Format. // Create a coreaudio AudioStreamBasicDescription from a CPAL Format.
fn asbd_from_config( fn asbd_from_config(
config: &StreamConfig, config: &StreamConfig,
@ -656,6 +684,29 @@ impl Device {
let asbd = asbd_from_config(config, sample_format); let asbd = asbd_from_config(config, sample_format);
audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?; audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?;
// Set the buffersize
match config.buffer_size {
BufferSize::Fixed(v) => {
let buffer_size_range = get_io_buffer_frame_size_range(&audio_unit)?;
match buffer_size_range {
SupportedBufferSize::Range { min, max } => {
if v >= min && v <= max {
audio_unit.set_property(
kAudioDevicePropertyBufferFrameSize,
scope,
element,
Some(&v),
)?
} else {
return Err(BuildStreamError::StreamConfigNotSupported);
}
}
SupportedBufferSize::Unknown => (),
}
}
BufferSize::Default => (),
}
// Register the callback that is being called by coreaudio whenever it needs data to be // Register the callback that is being called by coreaudio whenever it needs data to be
// fed to the audio buffer. // fed to the audio buffer.
let bytes_per_channel = sample_format.sample_size(); let bytes_per_channel = sample_format.sample_size();
@ -727,6 +778,29 @@ impl Device {
let asbd = asbd_from_config(config, sample_format); let asbd = asbd_from_config(config, sample_format);
audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?; audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?;
// Set the buffersize
match config.buffer_size {
BufferSize::Fixed(v) => {
let buffer_size_range = get_io_buffer_frame_size_range(&audio_unit)?;
match buffer_size_range {
SupportedBufferSize::Range { min, max } => {
if v >= min && v <= max {
audio_unit.set_property(
kAudioDevicePropertyBufferFrameSize,
scope,
element,
Some(&v),
)?
} else {
return Err(BuildStreamError::StreamConfigNotSupported);
}
}
SupportedBufferSize::Unknown => (),
}
}
BufferSize::Default => (),
}
// Register the callback that is being called by coreaudio whenever it needs data to be // Register the callback that is being called by coreaudio whenever it needs data to be
// fed to the audio buffer. // fed to the audio buffer.
let bytes_per_channel = sample_format.sample_size(); let bytes_per_channel = sample_format.sample_size();
@ -848,3 +922,18 @@ fn check_os_status(os_status: OSStatus) -> Result<(), BackendSpecificError> {
} }
} }
} }
fn get_io_buffer_frame_size_range(
audio_unit: &AudioUnit,
) -> Result<SupportedBufferSize, coreaudio::Error> {
let buffer_size_range: AudioValueRange = audio_unit.get_property(
kAudioDevicePropertyBufferFrameSizeRange,
Scope::Global,
Element::Output,
)?;
Ok(SupportedBufferSize::Range {
min: buffer_size_range.mMinimum as u32,
max: buffer_size_range.mMaximum as u32,
})
}

View File

@ -8,10 +8,10 @@ use stdweb::web::TypedArray;
use stdweb::Reference; use stdweb::Reference;
use crate::{ use crate::{
BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, BufferSize, BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError,
InputCallbackInfo, OutputCallbackInfo, PauseStreamError, PlayStreamError, SampleFormat, InputCallbackInfo, OutputCallbackInfo, PauseStreamError, PlayStreamError, SampleFormat,
StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SampleRate, StreamConfig, StreamError, SupportedBufferSize, SupportedStreamConfig,
SupportedStreamConfigsError, SupportedStreamConfigRange, SupportedStreamConfigsError,
}; };
use traits::{DeviceTrait, HostTrait, StreamTrait}; use traits::{DeviceTrait, HostTrait, StreamTrait};
@ -41,6 +41,16 @@ pub struct StreamId(usize);
pub type SupportedInputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>; pub type SupportedInputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>;
pub type SupportedOutputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>; pub type SupportedOutputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>;
const MIN_CHANNELS: u16 = 1;
const MAX_CHANNELS: u16 = 32;
const MIN_SAMPLE_RATE: SampleRate = SampleRate(8_000);
const MAX_SAMPLE_RATE: SampleRate = SampleRate(96_000);
const DEFAULT_SAMPLE_RATE: SampleRate = SampleRate(44_100);
const MIN_BUFFER_SIZE: u32 = 1;
const MAX_BUFFER_SIZE: u32 = std::u32::MAX;
const DEFAULT_BUFFER_SIZE: usize = 2048;
const SUPPORTED_SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
impl Host { impl Host {
pub fn new() -> Result<Self, crate::HostUnavailable> { pub fn new() -> Result<Self, crate::HostUnavailable> {
stdweb::initialize(); stdweb::initialize();
@ -71,21 +81,20 @@ impl Device {
fn supported_output_configs( fn supported_output_configs(
&self, &self,
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> { ) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
// TODO: right now cpal's API doesn't allow flexibility here let buffer_size = SupportedBufferSize::Range {
// "44100" and "2" (channels) have also been hard-coded in the rest of the code ; if min: MIN_BUFFER_SIZE,
// this ever becomes more flexible, don't forget to change that max: MAX_BUFFER_SIZE,
// According to https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/createBuffer };
// browsers must support 1 to 32 channels at leats and 8,000 Hz to 96,000 Hz. let configs: Vec<_> = (MIN_CHANNELS..=MAX_CHANNELS)
// .map(|channels| SupportedStreamConfigRange {
// UPDATE: We can do this now. Might be best to use `crate::COMMON_SAMPLE_RATES` and channels,
// filter out those that lay outside the range specified above. min_sample_rate: MIN_SAMPLE_RATE,
Ok(vec![SupportedStreamConfigRange { max_sample_rate: MAX_SAMPLE_RATE,
channels: 2, buffer_size: buffer_size.clone(),
min_sample_rate: ::SampleRate(44100), sample_format: SUPPORTED_SAMPLE_FORMAT,
max_sample_rate: ::SampleRate(44100), })
sample_format: ::SampleFormat::F32, .collect();
}] Ok(configs.into_iter())
.into_iter())
} }
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> { fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
@ -93,12 +102,15 @@ impl Device {
} }
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> { fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
// TODO: because it is hard coded, see supported_output_configs. const EXPECT: &str = "expected at least one valid webaudio stream config";
Ok(SupportedStreamConfig { let mut configs: Vec<_> = self.supported_output_configs().expect(EXPECT).collect();
channels: 2, configs.sort_by(|a, b| a.cmp_default_heuristics(b));
sample_rate: ::SampleRate(44100), let config = configs
sample_format: ::SampleFormat::F32, .into_iter()
}) .next()
.expect(EXPECT)
.with_sample_rate(DEFAULT_SAMPLE_RATE);
Ok(config)
} }
} }
@ -169,7 +181,7 @@ impl DeviceTrait for Device {
fn build_output_stream_raw<D, E>( fn build_output_stream_raw<D, E>(
&self, &self,
_config: &StreamConfig, config: &StreamConfig,
sample_format: SampleFormat, sample_format: SampleFormat,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
@ -178,11 +190,20 @@ impl DeviceTrait for Device {
D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static,
E: FnMut(StreamError) + Send + 'static, E: FnMut(StreamError) + Send + 'static,
{ {
assert_eq!( if !valid_config(config, sample_format) {
sample_format, return Err(BuildStreamError::StreamConfigNotSupported);
SampleFormat::F32, }
"emscripten backend currently only supports `f32` data",
); let buffer_size_frames = match config.buffer_size {
BufferSize::Fixed(v) => {
if v == 0 {
return Err(BuildStreamError::StreamConfigNotSupported);
} else {
v as usize
}
}
BufferSize::Default => DEFAULT_BUFFER_SIZE,
};
// Create the stream. // Create the stream.
let audio_ctxt_ref = js!(return new AudioContext()).into_reference().unwrap(); let audio_ctxt_ref = js!(return new AudioContext()).into_reference().unwrap();
@ -199,7 +220,14 @@ impl DeviceTrait for Device {
// See also: The call to `set_timeout` at the end of the `audio_callback_fn` which creates // See also: The call to `set_timeout` at the end of the `audio_callback_fn` which creates
// the loop. // the loop.
set_timeout( set_timeout(
|| audio_callback_fn::<D, E>(user_data_ptr as *mut c_void), || {
audio_callback_fn::<D, E>(
user_data_ptr as *mut c_void,
config,
sample_format,
buffer_size_frames,
)
},
10, 10,
); );
@ -223,12 +251,18 @@ impl StreamTrait for Stream {
// The first argument of the callback function (a `void*`) is a casted pointer to `self` // The first argument of the callback function (a `void*`) is a casted pointer to `self`
// and to the `callback` parameter that was passed to `run`. // and to the `callback` parameter that was passed to `run`.
fn audio_callback_fn<D, E>(user_data_ptr: *mut c_void) fn audio_callback_fn<D, E>(
where user_data_ptr: *mut c_void,
config: &StreamConfig,
sample_format: SampleFormat,
buffer_size_frames: usize,
) where
D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static,
E: FnMut(StreamError) + Send + 'static, E: FnMut(StreamError) + Send + 'static,
{ {
const SAMPLE_RATE: usize = 44100; let num_channels = config.channels as usize;
let sample_rate = config.sample_rate.0;
let buffer_size_samples = buffer_size_frames * num_channels;
unsafe { unsafe {
let user_data_ptr2 = user_data_ptr as *mut (&Stream, D, E); let user_data_ptr2 = user_data_ptr as *mut (&Stream, D, E);
@ -237,12 +271,11 @@ where
let audio_ctxt = &stream.audio_ctxt_ref; let audio_ctxt = &stream.audio_ctxt_ref;
// TODO: We should be re-using a buffer. // TODO: We should be re-using a buffer.
let mut temporary_buffer = vec![0.0; SAMPLE_RATE * 2 / 3]; let mut temporary_buffer = vec![0f32; buffer_size_samples];
{ {
let len = temporary_buffer.len(); let len = temporary_buffer.len();
let data = temporary_buffer.as_mut_ptr() as *mut (); let data = temporary_buffer.as_mut_ptr() as *mut ();
let sample_format = SampleFormat::F32;
let mut data = Data::from_parts(data, len, sample_format); let mut data = Data::from_parts(data, len, sample_format);
let now_secs: f64 = js!(@{audio_ctxt}.getOutputTimestamp().currentTime) let now_secs: f64 = js!(@{audio_ctxt}.getOutputTimestamp().currentTime)
@ -253,7 +286,7 @@ where
// we estimate based on buffer size instead. Probably should use this, but it's only // we estimate based on buffer size instead. Probably should use this, but it's only
// supported by firefox (2020-04-28). // supported by firefox (2020-04-28).
// let latency_secs: f64 = js!(@{audio_ctxt}.outputLatency).try_into().unwrap(); // let latency_secs: f64 = js!(@{audio_ctxt}.outputLatency).try_into().unwrap();
let buffer_duration = frames_to_duration(len, SAMPLE_RATE); let buffer_duration = frames_to_duration(len, sample_rate as usize);
let playback = callback let playback = callback
.add(buffer_duration) .add(buffer_duration)
.expect("`playback` occurs beyond representation supported by `StreamInstant`"); .expect("`playback` occurs beyond representation supported by `StreamInstant`");
@ -273,19 +306,19 @@ where
typed_array typed_array
}; };
let num_channels = 2u32; // TODO: correct value
debug_assert_eq!(temporary_buffer.len() % num_channels as usize, 0); debug_assert_eq!(temporary_buffer.len() % num_channels as usize, 0);
js!( js!(
var src_buffer = new Float32Array(@{typed_array}.buffer); var src_buffer = new Float32Array(@{typed_array}.buffer);
var context = @{audio_ctxt}; var context = @{audio_ctxt};
var buf_len = @{temporary_buffer.len() as u32}; var buffer_size_frames = @{buffer_size_frames as u32};
var num_channels = @{num_channels}; var num_channels = @{num_channels as u32};
var sample_rate = sample_rate;
var buffer = context.createBuffer(num_channels, buf_len / num_channels, 44100); var buffer = context.createBuffer(num_channels, buffer_size_frames, sample_rate);
for (var channel = 0; channel < num_channels; ++channel) { for (var channel = 0; channel < num_channels; ++channel) {
var buffer_content = buffer.getChannelData(channel); var buffer_content = buffer.getChannelData(channel);
for (var i = 0; i < buf_len / num_channels; ++i) { for (var i = 0; i < buffer_size_frames; ++i) {
buffer_content[i] = src_buffer[i * num_channels + channel]; buffer_content[i] = src_buffer[i * num_channels + channel];
} }
} }
@ -299,7 +332,10 @@ where
// TODO: handle latency better ; right now we just use setInterval with the amount of sound // TODO: handle latency better ; right now we just use setInterval with the amount of sound
// data that is in each buffer ; this is obviously bad, and also the schedule is too tight // data that is in each buffer ; this is obviously bad, and also the schedule is too tight
// and there may be underflows // and there may be underflows
set_timeout(|| audio_callback_fn::<D, E>(user_data_ptr), 330); set_timeout(
|| audio_callback_fn::<D, E>(user_data_ptr, config, sample_format, buffer_size_frames),
buffer_size_frames as u32 * 1000 / sample_rate,
);
} }
} }
@ -348,6 +384,15 @@ fn is_webaudio_available() -> bool {
.unwrap() .unwrap()
} }
// Whether or not the given stream configuration is valid for building a stream.
fn valid_config(conf: &StreamConfig, sample_format: SampleFormat) -> bool {
conf.channels <= MAX_CHANNELS
&& conf.channels >= MIN_CHANNELS
&& conf.sample_rate <= MAX_SAMPLE_RATE
&& conf.sample_rate >= MIN_SAMPLE_RATE
&& sample_format == SUPPORTED_SAMPLE_FORMAT
}
// Convert the given duration in frames at the given sample rate to a `std::time::Duration`. // Convert the given duration in frames at the given sample rate to a `std::time::Duration`.
fn frames_to_duration(frames: usize, rate: usize) -> std::time::Duration { fn frames_to_duration(frames: usize, rate: usize) -> std::time::Duration {
let secsf = frames as f64 / rate as f64; let secsf = frames as f64 / rate as f64;

View File

@ -1,8 +1,8 @@
use crate::{ use crate::{
BackendSpecificError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, BackendSpecificError, BufferSize, Data, DefaultStreamConfigError, DeviceNameError,
InputCallbackInfo, OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig, DevicesError, InputCallbackInfo, OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig,
SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange,
COMMON_SAMPLE_RATES, SupportedStreamConfigsError, COMMON_SAMPLE_RATES,
}; };
use std; use std;
use std::ffi::OsString; use std::ffi::OsString;
@ -27,6 +27,7 @@ use super::winapi::shared::mmreg;
use super::winapi::shared::winerror; use super::winapi::shared::winerror;
use super::winapi::shared::wtypes; use super::winapi::shared::wtypes;
use super::winapi::Interface; use super::winapi::Interface;
// https://msdn.microsoft.com/en-us/library/cc230355.aspx // https://msdn.microsoft.com/en-us/library/cc230355.aspx
use super::winapi::um::audioclient::{ use super::winapi::um::audioclient::{
self, IAudioClient, IID_IAudioClient, AUDCLNT_E_DEVICE_INVALIDATED, self, IAudioClient, IID_IAudioClient, AUDCLNT_E_DEVICE_INVALIDATED,
@ -318,9 +319,11 @@ unsafe fn format_from_waveformatex_ptr(
// Unknown data format returned by GetMixFormat. // Unknown data format returned by GetMixFormat.
_ => return None, _ => return None,
}; };
let format = SupportedStreamConfig { let format = SupportedStreamConfig {
channels: (*waveformatex_ptr).nChannels as _, channels: (*waveformatex_ptr).nChannels as _,
sample_rate: SampleRate((*waveformatex_ptr).nSamplesPerSec), sample_rate: SampleRate((*waveformatex_ptr).nSamplesPerSec),
buffer_size: SupportedBufferSize::Unknown,
sample_format, sample_format,
}; };
Some(format) Some(format)
@ -513,7 +516,7 @@ impl Device {
// TODO: Test the different sample formats? // TODO: Test the different sample formats?
// Create the supported formats. // Create the supported formats.
let mut format = match format_from_waveformatex_ptr(default_waveformatex_ptr.0) { let format = match format_from_waveformatex_ptr(default_waveformatex_ptr.0) {
Some(fmt) => fmt, Some(fmt) => fmt,
None => { None => {
let description = let description =
@ -525,8 +528,13 @@ impl Device {
}; };
let mut supported_formats = Vec::with_capacity(supported_sample_rates.len()); let mut supported_formats = Vec::with_capacity(supported_sample_rates.len());
for rate in supported_sample_rates { for rate in supported_sample_rates {
format.sample_rate = SampleRate(rate as _); supported_formats.push(SupportedStreamConfigRange {
supported_formats.push(SupportedStreamConfigRange::from(format.clone())); channels: format.channels.clone(),
min_sample_rate: SampleRate(rate as _),
max_sample_rate: SampleRate(rate as _),
buffer_size: format.buffer_size.clone(),
sample_format: format.sample_format.clone(),
})
} }
Ok(supported_formats.into_iter()) Ok(supported_formats.into_iter())
} }
@ -639,6 +647,16 @@ impl Device {
} }
}; };
match config.buffer_size {
BufferSize::Fixed(_) => {
// TO DO: We need IAudioClient3 to get buffersize ranges first
// Otherwise the supported ranges are unknown. In the mean time
// the smallest buffersize is selected and used.
return Err(BuildStreamError::StreamConfigNotSupported);
}
BufferSize::Default => (),
};
// Computing the format and initializing the device. // Computing the format and initializing the device.
let waveformatex = { let waveformatex = {
let format_attempt = config_to_waveformatextensible(config, sample_format) let format_attempt = config_to_waveformatextensible(config, sample_format)
@ -791,6 +809,16 @@ impl Device {
} }
}; };
match config.buffer_size {
BufferSize::Fixed(_) => {
// TO DO: We need IAudioClient3 to get buffersize ranges first
// Otherwise the supported ranges are unknown. In the mean time
// the smallest buffersize is selected and used.
return Err(BuildStreamError::StreamConfigNotSupported);
}
BufferSize::Default => (),
};
// Computing the format and initializing the device. // Computing the format and initializing the device.
let waveformatex = { let waveformatex = {
let format_attempt = config_to_waveformatextensible(config, sample_format) let format_attempt = config_to_waveformatextensible(config, sample_format)
@ -813,6 +841,7 @@ impl Device {
&format_attempt.Format, &format_attempt.Format,
ptr::null(), ptr::null(),
); );
match check_result(hresult) { match check_result(hresult) {
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => { Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
(*audio_client).Release(); (*audio_client).Release();

View File

@ -7,10 +7,10 @@ use self::wasm_bindgen::prelude::*;
use self::wasm_bindgen::JsCast; use self::wasm_bindgen::JsCast;
use self::web_sys::{AudioContext, AudioContextOptions}; use self::web_sys::{AudioContext, AudioContextOptions};
use crate::{ use crate::{
BackendSpecificError, BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, BackendSpecificError, BufferSize, BuildStreamError, Data, DefaultStreamConfigError,
DevicesError, InputCallbackInfo, OutputCallbackInfo, PauseStreamError, PlayStreamError, DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo, PauseStreamError,
SampleFormat, SampleRate, StreamConfig, StreamError, SupportedStreamConfig, PlayStreamError, SampleFormat, SampleRate, StreamConfig, StreamError, SupportedBufferSize,
SupportedStreamConfigRange, SupportedStreamConfigsError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
}; };
use std::ops::DerefMut; use std::ops::DerefMut;
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
@ -39,6 +39,9 @@ const MAX_CHANNELS: u16 = 32;
const MIN_SAMPLE_RATE: SampleRate = SampleRate(8_000); const MIN_SAMPLE_RATE: SampleRate = SampleRate(8_000);
const MAX_SAMPLE_RATE: SampleRate = SampleRate(96_000); const MAX_SAMPLE_RATE: SampleRate = SampleRate(96_000);
const DEFAULT_SAMPLE_RATE: SampleRate = SampleRate(44_100); const DEFAULT_SAMPLE_RATE: SampleRate = SampleRate(44_100);
const MIN_BUFFER_SIZE: u32 = 1;
const MAX_BUFFER_SIZE: u32 = std::u32::MAX;
const DEFAULT_BUFFER_SIZE: usize = 2048;
const SUPPORTED_SAMPLE_FORMAT: SampleFormat = SampleFormat::F32; const SUPPORTED_SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
impl Host { impl Host {
@ -93,11 +96,16 @@ impl Device {
fn supported_output_configs( fn supported_output_configs(
&self, &self,
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> { ) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
let buffer_size = SupportedBufferSize::Range {
min: MIN_BUFFER_SIZE,
max: MAX_BUFFER_SIZE,
};
let configs: Vec<_> = (MIN_CHANNELS..=MAX_CHANNELS) let configs: Vec<_> = (MIN_CHANNELS..=MAX_CHANNELS)
.map(|channels| SupportedStreamConfigRange { .map(|channels| SupportedStreamConfigRange {
channels, channels,
min_sample_rate: MIN_SAMPLE_RATE, min_sample_rate: MIN_SAMPLE_RATE,
max_sample_rate: MAX_SAMPLE_RATE, max_sample_rate: MAX_SAMPLE_RATE,
buffer_size: buffer_size.clone(),
sample_format: SUPPORTED_SAMPLE_FORMAT, sample_format: SUPPORTED_SAMPLE_FORMAT,
}) })
.collect(); .collect();
@ -190,11 +198,20 @@ impl DeviceTrait for Device {
} }
let n_channels = config.channels as usize; let n_channels = config.channels as usize;
// Use a buffer period of 1/3s for this early proof of concept.
// TODO: Change this to the requested buffer size when updating for the buffer size API. let buffer_size_frames = match config.buffer_size {
let buffer_size_frames = (config.sample_rate.0 as f64 / 3.0).round() as usize; BufferSize::Fixed(v) => {
if v == 0 {
return Err(BuildStreamError::StreamConfigNotSupported);
} else {
v as usize
}
}
BufferSize::Default => DEFAULT_BUFFER_SIZE,
};
let buffer_size_samples = buffer_size_frames * n_channels; let buffer_size_samples = buffer_size_frames * n_channels;
let buffer_time_step_secs = buffer_time_step_secs(buffer_size_frames, config.sample_rate); let buffer_time_step_secs = buffer_time_step_secs(buffer_size_frames, config.sample_rate);
let data_callback = Arc::new(Mutex::new(Box::new(data_callback))); let data_callback = Arc::new(Mutex::new(Box::new(data_callback)));
// Create the WebAudio stream. // Create the WebAudio stream.

View File

@ -180,6 +180,22 @@ pub type ChannelCount = u16;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct SampleRate(pub u32); pub struct SampleRate(pub u32);
/// The desired number of frames for the hardware buffer.
pub type FrameCount = u32;
/// The buffer size used by the device.
///
/// Default is used when no specific buffer size is set and uses the default
/// behavior of the given host. Note, the default buffer size may be surprisingly
/// large, leading to latency issues. If low latency is desired, Fixed(BufferSize)
/// should be used in accordance with the SupportedBufferSize range produced by
/// the SupportedStreamConfig API.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BufferSize {
Default,
Fixed(FrameCount),
}
/// The set of parameters used to describe how to open a stream. /// The set of parameters used to describe how to open a stream.
/// ///
/// The sample format is omitted in favour of using a sample type. /// The sample format is omitted in favour of using a sample type.
@ -187,6 +203,19 @@ pub struct SampleRate(pub u32);
pub struct StreamConfig { pub struct StreamConfig {
pub channels: ChannelCount, pub channels: ChannelCount,
pub sample_rate: SampleRate, pub sample_rate: SampleRate,
pub buffer_size: BufferSize,
}
/// Describes the minimum and maximum supported buffer size for the device
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SupportedBufferSize {
Range {
min: FrameCount,
max: FrameCount,
},
/// In the case that the platform provides no way of getting the default
/// buffersize before starting a stream.
Unknown,
} }
/// Describes a range of supported stream configurations, retrieved via the /// Describes a range of supported stream configurations, retrieved via the
@ -198,6 +227,8 @@ pub struct SupportedStreamConfigRange {
pub(crate) min_sample_rate: SampleRate, pub(crate) min_sample_rate: SampleRate,
/// Maximum value for the samples rate of the supported formats. /// Maximum value for the samples rate of the supported formats.
pub(crate) max_sample_rate: SampleRate, pub(crate) max_sample_rate: SampleRate,
/// Buffersize ranges supported by the device
pub(crate) buffer_size: SupportedBufferSize,
/// Type of data expected by the device. /// Type of data expected by the device.
pub(crate) sample_format: SampleFormat, pub(crate) sample_format: SampleFormat,
} }
@ -208,6 +239,7 @@ pub struct SupportedStreamConfigRange {
pub struct SupportedStreamConfig { pub struct SupportedStreamConfig {
channels: ChannelCount, channels: ChannelCount,
sample_rate: SampleRate, sample_rate: SampleRate,
buffer_size: SupportedBufferSize,
sample_format: SampleFormat, sample_format: SampleFormat,
} }
@ -289,6 +321,10 @@ impl SupportedStreamConfig {
self.sample_rate self.sample_rate
} }
pub fn buffer_size(&self) -> &SupportedBufferSize {
&self.buffer_size
}
pub fn sample_format(&self) -> SampleFormat { pub fn sample_format(&self) -> SampleFormat {
self.sample_format self.sample_format
} }
@ -297,6 +333,7 @@ impl SupportedStreamConfig {
StreamConfig { StreamConfig {
channels: self.channels, channels: self.channels,
sample_rate: self.sample_rate, sample_rate: self.sample_rate,
buffer_size: BufferSize::Default,
} }
} }
} }
@ -492,11 +529,15 @@ impl SupportedStreamConfigRange {
self.max_sample_rate self.max_sample_rate
} }
pub fn buffer_size(&self) -> &SupportedBufferSize {
&self.buffer_size
}
pub fn sample_format(&self) -> SampleFormat { pub fn sample_format(&self) -> SampleFormat {
self.sample_format self.sample_format
} }
/// Retrieve a `SupportedStreamConfig` with the given sample rate. /// Retrieve a `SupportedStreamConfig` with the given sample rate and buffer size.
/// ///
/// **panic!**s if the given `sample_rate` is outside the range specified within this /// **panic!**s if the given `sample_rate` is outside the range specified within this
/// `SupportedStreamConfigRange` instance. /// `SupportedStreamConfigRange` instance.
@ -504,8 +545,9 @@ impl SupportedStreamConfigRange {
assert!(self.min_sample_rate <= sample_rate && sample_rate <= self.max_sample_rate); assert!(self.min_sample_rate <= sample_rate && sample_rate <= self.max_sample_rate);
SupportedStreamConfig { SupportedStreamConfig {
channels: self.channels, channels: self.channels,
sample_rate: self.max_sample_rate,
sample_format: self.sample_format, sample_format: self.sample_format,
sample_rate, buffer_size: self.buffer_size,
} }
} }
@ -516,6 +558,7 @@ impl SupportedStreamConfigRange {
channels: self.channels, channels: self.channels,
sample_rate: self.max_sample_rate, sample_rate: self.max_sample_rate,
sample_format: self.sample_format, sample_format: self.sample_format,
buffer_size: self.buffer_size,
} }
} }
@ -596,18 +639,6 @@ impl From<SupportedStreamConfig> for StreamConfig {
} }
} }
impl From<SupportedStreamConfig> for SupportedStreamConfigRange {
#[inline]
fn from(format: SupportedStreamConfig) -> SupportedStreamConfigRange {
SupportedStreamConfigRange {
channels: format.channels,
min_sample_rate: format.sample_rate,
max_sample_rate: format.sample_rate,
sample_format: format.sample_format,
}
}
}
// If a backend does not provide an API for retrieving supported formats, we query it with a bunch // If a backend does not provide an API for retrieving supported formats, we query it with a bunch
// of commonly used rates. This is always the case for wasapi and is sometimes the case for alsa. // of commonly used rates. This is always the case for wasapi and is sometimes the case for alsa.
// //