Merge pull request #371 from mitchmindtree/stream_config

Rename stream `Format` types to `StreamConfig` and other related renamings.
This commit is contained in:
mitchmindtree 2020-02-04 17:07:15 +01:00 committed by GitHub
commit e4df8b277f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 689 additions and 567 deletions

View File

@ -8,23 +8,23 @@ fn main() -> Result<(), anyhow::Error> {
let device = host
.default_output_device()
.expect("failed to find a default output device");
let format = device.default_output_format()?;
let config = device.default_output_config()?;
match format.data_type {
cpal::SampleFormat::F32 => run::<f32>(&device, &format.shape())?,
cpal::SampleFormat::I16 => run::<i16>(&device, &format.shape())?,
cpal::SampleFormat::U16 => run::<u16>(&device, &format.shape())?,
match config.sample_format() {
cpal::SampleFormat::F32 => run::<f32>(&device, &config.into())?,
cpal::SampleFormat::I16 => run::<i16>(&device, &config.into())?,
cpal::SampleFormat::U16 => run::<u16>(&device, &config.into())?,
}
Ok(())
}
fn run<T>(device: &cpal::Device, shape: &cpal::Shape) -> Result<(), anyhow::Error>
fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error>
where
T: cpal::Sample,
{
let sample_rate = shape.sample_rate.0 as f32;
let channels = shape.channels as usize;
let sample_rate = config.sample_rate.0 as f32;
let channels = config.channels as usize;
// Produce a sinusoid of maximum amplitude.
let mut sample_clock = 0f32;
@ -36,7 +36,7 @@ where
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
let stream = device.build_output_stream(
shape,
config,
move |data: &mut [T]| write_data(data, channels, &mut next_value),
err_fn,
)?;

View File

@ -21,48 +21,48 @@ fn main() -> Result<(), anyhow::Error> {
for (device_index, device) in devices.enumerate() {
println!(" {}. \"{}\"", device_index + 1, device.name()?);
// Input formats
if let Ok(fmt) = device.default_input_format() {
println!(" Default input stream format:\n {:?}", fmt);
// Input configs
if let Ok(conf) = device.default_input_config() {
println!(" Default input stream config:\n {:?}", conf);
}
let mut input_formats = match device.supported_input_formats() {
let mut input_configs = match device.supported_input_configs() {
Ok(f) => f.peekable(),
Err(e) => {
println!("Error: {:?}", e);
continue;
}
};
if input_formats.peek().is_some() {
println!(" All supported input stream formats:");
for (format_index, format) in input_formats.enumerate() {
if input_configs.peek().is_some() {
println!(" All supported input stream configs:");
for (config_index, config) in input_configs.enumerate() {
println!(
" {}.{}. {:?}",
device_index + 1,
format_index + 1,
format
config_index + 1,
config
);
}
}
// Output formats
if let Ok(fmt) = device.default_output_format() {
println!(" Default output stream format:\n {:?}", fmt);
// Output configs
if let Ok(conf) = device.default_output_config() {
println!(" Default output stream config:\n {:?}", conf);
}
let mut output_formats = match device.supported_output_formats() {
let mut output_configs = match device.supported_output_configs() {
Ok(f) => f.peekable(),
Err(e) => {
println!("Error: {:?}", e);
continue;
}
};
if output_formats.peek().is_some() {
println!(" All supported output stream formats:");
for (format_index, format) in output_formats.enumerate() {
if output_configs.peek().is_some() {
println!(" All supported output stream configs:");
for (config_index, config) in output_configs.enumerate() {
println!(
" {}.{}. {:?}",
device_index + 1,
format_index + 1,
format
config_index + 1,
config
);
}
}

View File

@ -1,7 +1,7 @@
//! Feeds back the input stream directly into the output stream.
//!
//! Assumes that the input and output devices can use the same stream format and that they support
//! the f32 sample format.
//! Assumes that the input and output devices can use the same stream configuration and that they
//! support the f32 sample format.
//!
//! Uses a delay of `LATENCY_MS` milliseconds in case the default input and output streams are not
//! precisely synchronised.
@ -28,12 +28,12 @@ fn main() -> Result<(), anyhow::Error> {
println!("Using default input device: \"{}\"", input_device.name()?);
println!("Using default output device: \"{}\"", output_device.name()?);
// We'll try and use the same format between streams to keep it simple
let shape = input_device.default_input_format()?.shape();
// We'll try and use the same configuration between streams to keep it simple.
let config: cpal::StreamConfig = input_device.default_input_config()?.into();
// Create a delay in case the input and output devices aren't synced.
let latency_frames = (LATENCY_MS / 1_000.0) * shape.sample_rate.0 as f32;
let latency_samples = latency_frames as usize * shape.channels as usize;
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;
// The buffer to share samples
let ring = RingBuffer::new(latency_samples * 2);
@ -80,10 +80,10 @@ fn main() -> Result<(), anyhow::Error> {
// Build streams.
println!(
"Attempting to build both streams with f32 samples and `{:?}`.",
shape
config
);
let input_stream = input_device.build_input_stream(&shape, input_data_fn, err_fn)?;
let output_stream = output_device.build_output_stream(&shape, output_data_fn, err_fn)?;
let input_stream = input_device.build_input_stream(&config, input_data_fn, err_fn)?;
let output_stream = output_device.build_output_stream(&config, output_data_fn, err_fn)?;
println!("Successfully built streams.");
// Play the streams.

View File

@ -1,4 +1,4 @@
//! Records a WAV file (roughly 3 seconds long) using the default input device and format.
//! Records a WAV file (roughly 3 seconds long) using the default input device and config.
//!
//! The input data is recorded to "$CARGO_MANIFEST_DIR/recorded.wav".
@ -15,18 +15,18 @@ fn main() -> Result<(), anyhow::Error> {
// Use the default host for working with audio devices.
let host = cpal::default_host();
// Setup the default input device and stream with the default input format.
// Setup the default input device and stream with the default input config.
let device = host
.default_input_device()
.expect("Failed to get default input device");
println!("Default input device: {}", device.name()?);
let format = device
.default_input_format()
.expect("Failed to get default input format");
println!("Default input format: {:?}", format);
let config = device
.default_input_config()
.expect("Failed to get default input config");
println!("Default input config: {:?}", config);
// The WAV file we're recording to.
const PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/recorded.wav");
let spec = wav_spec_from_format(&format);
let spec = wav_spec_from_config(&config);
let writer = hound::WavWriter::create(PATH, spec)?;
let writer = Arc::new(Mutex::new(Some(writer)));
@ -40,19 +40,19 @@ fn main() -> Result<(), anyhow::Error> {
eprintln!("an error occurred on stream: {}", err);
};
let stream = match format.data_type {
let stream = match config.sample_format() {
cpal::SampleFormat::F32 => device.build_input_stream(
&format.shape(),
&config.into(),
move |data| write_input_data::<f32, f32>(data, &writer_2),
err_fn,
)?,
cpal::SampleFormat::I16 => device.build_input_stream(
&format.shape(),
&config.into(),
move |data| write_input_data::<i16, i16>(data, &writer_2),
err_fn,
)?,
cpal::SampleFormat::U16 => device.build_input_stream(
&format.shape(),
&config.into(),
move |data| write_input_data::<u16, i16>(data, &writer_2),
err_fn,
)?,
@ -76,12 +76,12 @@ fn sample_format(format: cpal::SampleFormat) -> hound::SampleFormat {
}
}
fn wav_spec_from_format(format: &cpal::Format) -> hound::WavSpec {
fn wav_spec_from_config(config: &cpal::SupportedStreamConfig) -> hound::WavSpec {
hound::WavSpec {
channels: format.channels as _,
sample_rate: format.sample_rate.0 as _,
bits_per_sample: (format.data_type.sample_size() * 8) as _,
sample_format: sample_format(format.data_type),
channels: config.channels() as _,
sample_rate: config.sample_rate().0 as _,
bits_per_sample: (config.sample_format().sample_size() * 8) as _,
sample_format: sample_format(config.sample_format()),
}
}

View File

@ -47,7 +47,7 @@ pub enum DeviceNameError {
/// Error that can happen when enumerating the list of supported formats.
#[derive(Debug, Error)]
pub enum SupportedFormatsError {
pub enum SupportedStreamConfigsError {
/// The device no longer exists. This can happen if the device is disconnected while the
/// program is running.
#[error("The requested device is no longer available. For example, it has been unplugged.")]
@ -67,7 +67,7 @@ pub enum SupportedFormatsError {
/// May occur when attempting to request the default input or output stream format from a `Device`.
#[derive(Debug, Error)]
pub enum DefaultFormatError {
pub enum DefaultStreamConfigError {
/// The device no longer exists. This can happen if the device is disconnected while the
/// program is running.
#[error("The requested device is no longer available. For example, it has been unplugged.")]
@ -90,9 +90,9 @@ pub enum BuildStreamError {
/// program is running.
#[error("The requested device is no longer available. For example, it has been unplugged.")]
DeviceNotAvailable,
/// The required format is not supported.
#[error("The requested stream format is not supported by the device.")]
FormatNotSupported,
/// The specified stream configuration is not supported.
#[error("The requested stream configuration is not supported by the device.")]
StreamConfigNotSupported,
/// We called something the C-Layer did not understand
///
/// On ALSA device functions called with a feature they do not support will yield this. E.g.

View File

@ -2,9 +2,10 @@ extern crate alsa_sys as alsa;
extern crate libc;
use crate::{
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultFormatError,
DeviceNameError, DevicesError, Format, PauseStreamError, PlayStreamError, SampleFormat,
SampleRate, StreamError, SupportedFormat, SupportedFormatsError,
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError,
DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate,
StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange,
SupportedStreamConfigsError,
};
use std::sync::Arc;
use std::thread::{self, JoinHandle};
@ -14,8 +15,8 @@ use traits::{DeviceTrait, HostTrait, StreamTrait};
pub use self::enumerate::{default_input_device, default_output_device, Devices};
pub type SupportedInputFormats = VecIntoIter<SupportedFormat>;
pub type SupportedOutputFormats = VecIntoIter<SupportedFormat>;
pub type SupportedInputConfigs = VecIntoIter<SupportedStreamConfigRange>;
pub type SupportedOutputConfigs = VecIntoIter<SupportedStreamConfigRange>;
mod enumerate;
@ -52,37 +53,38 @@ impl HostTrait for Host {
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputConfigs = SupportedOutputConfigs;
type Stream = Stream;
fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self)
}
fn supported_input_formats(
fn supported_input_configs(
&self,
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
Device::supported_input_formats(self)
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
Device::supported_input_configs(self)
}
fn supported_output_formats(
fn supported_output_configs(
&self,
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
Device::supported_output_formats(self)
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
Device::supported_output_configs(self)
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_input_format(self)
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_input_config(self)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_output_format(self)
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_output_config(self)
}
fn build_input_stream_raw<D, E>(
&self,
format: &Format,
conf: &StreamConfig,
sample_format: SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -90,14 +92,16 @@ impl DeviceTrait for Device {
D: FnMut(&Data) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_CAPTURE)?;
let stream_inner =
self.build_stream_inner(conf, sample_format, alsa::SND_PCM_STREAM_CAPTURE)?;
let stream = Stream::new_input(Arc::new(stream_inner), data_callback, error_callback);
Ok(stream)
}
fn build_output_stream_raw<D, E>(
&self,
format: &Format,
conf: &StreamConfig,
sample_format: SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -105,7 +109,8 @@ impl DeviceTrait for Device {
D: FnMut(&mut Data) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_PLAYBACK)?;
let stream_inner =
self.build_stream_inner(conf, sample_format, alsa::SND_PCM_STREAM_PLAYBACK)?;
let stream = Stream::new_output(Arc::new(stream_inner), data_callback, error_callback);
Ok(stream)
}
@ -161,7 +166,8 @@ pub struct Device(String);
impl Device {
fn build_stream_inner(
&self,
format: &Format,
conf: &StreamConfig,
sample_format: SampleFormat,
stream_type: alsa::snd_pcm_stream_t,
) -> Result<StreamInner, BuildStreamError> {
let name = ffi::CString::new(self.0.clone()).expect("unable to clone device");
@ -185,13 +191,13 @@ impl Device {
};
let can_pause = unsafe {
let hw_params = HwParams::alloc();
set_hw_params_from_format(handle, &hw_params, format)
set_hw_params_from_format(handle, &hw_params, conf, sample_format)
.map_err(|description| BackendSpecificError { description })?;
alsa::snd_pcm_hw_params_can_pause(hw_params.0) == 1
};
let (buffer_len, period_len) = unsafe {
set_sw_params_from_format(handle, format)
set_sw_params_from_format(handle, conf)
.map_err(|description| BackendSpecificError { description })?
};
@ -213,9 +219,9 @@ impl Device {
let stream_inner = StreamInner {
channel: handle,
sample_format: format.data_type,
sample_format,
num_descriptors,
num_channels: format.channels as u16,
num_channels: conf.channels as u16,
buffer_len,
period_len,
can_pause,
@ -235,10 +241,10 @@ impl Device {
Ok(self.0.clone())
}
unsafe fn supported_formats(
unsafe fn supported_configs(
&self,
stream_t: alsa::snd_pcm_stream_t,
) -> Result<VecIntoIter<SupportedFormat>, SupportedFormatsError> {
) -> Result<VecIntoIter<SupportedStreamConfigRange>, SupportedStreamConfigsError> {
let mut handle = ptr::null_mut();
let device_name = match ffi::CString::new(&self.0[..]) {
Ok(name) => name,
@ -256,8 +262,8 @@ impl Device {
alsa::SND_PCM_NONBLOCK,
) {
-2 |
-16 /* determined empirically */ => return Err(SupportedFormatsError::DeviceNotAvailable),
-22 => return Err(SupportedFormatsError::InvalidArgument),
-16 /* determined empirically */ => return Err(SupportedStreamConfigsError::DeviceNotAvailable),
-22 => return Err(SupportedStreamConfigsError::InvalidArgument),
e => if let Err(description) = check_errors(e) {
let err = BackendSpecificError { description };
return Err(err.into())
@ -402,14 +408,14 @@ impl Device {
let mut output = Vec::with_capacity(
supported_formats.len() * supported_channels.len() * sample_rates.len(),
);
for &data_type in supported_formats.iter() {
for &sample_format in supported_formats.iter() {
for channels in supported_channels.iter() {
for &(min_rate, max_rate) in sample_rates.iter() {
output.push(SupportedFormat {
output.push(SupportedStreamConfigRange {
channels: channels.clone(),
min_sample_rate: SampleRate(min_rate as u32),
max_sample_rate: SampleRate(max_rate as u32),
data_type: data_type,
sample_format: sample_format,
});
}
}
@ -420,31 +426,35 @@ impl Device {
Ok(output.into_iter())
}
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
unsafe { self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE) }
fn supported_input_configs(
&self,
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
unsafe { self.supported_configs(alsa::SND_PCM_STREAM_CAPTURE) }
}
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
unsafe { self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK) }
fn supported_output_configs(
&self,
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
unsafe { self.supported_configs(alsa::SND_PCM_STREAM_PLAYBACK) }
}
// ALSA does not offer default stream formats, so instead we compare all supported formats by
// the `SupportedFormat::cmp_default_heuristics` order and select the greatest.
fn default_format(
// the `SupportedStreamConfigRange::cmp_default_heuristics` order and select the greatest.
fn default_config(
&self,
stream_t: alsa::snd_pcm_stream_t,
) -> Result<Format, DefaultFormatError> {
) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
let mut formats: Vec<_> = unsafe {
match self.supported_formats(stream_t) {
Err(SupportedFormatsError::DeviceNotAvailable) => {
return Err(DefaultFormatError::DeviceNotAvailable);
match self.supported_configs(stream_t) {
Err(SupportedStreamConfigsError::DeviceNotAvailable) => {
return Err(DefaultStreamConfigError::DeviceNotAvailable);
}
Err(SupportedFormatsError::InvalidArgument) => {
Err(SupportedStreamConfigsError::InvalidArgument) => {
// this happens sometimes when querying for input and output capabilities but
// the device supports only one
return Err(DefaultFormatError::StreamTypeNotSupported);
return Err(DefaultStreamConfigError::StreamTypeNotSupported);
}
Err(SupportedFormatsError::BackendSpecific { err }) => {
Err(SupportedStreamConfigsError::BackendSpecific { err }) => {
return Err(err.into());
}
Ok(fmts) => fmts.collect(),
@ -464,16 +474,16 @@ impl Device {
}
Ok(format)
}
None => Err(DefaultFormatError::StreamTypeNotSupported),
None => Err(DefaultStreamConfigError::StreamTypeNotSupported),
}
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
self.default_format(alsa::SND_PCM_STREAM_CAPTURE)
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
self.default_config(alsa::SND_PCM_STREAM_CAPTURE)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
self.default_format(alsa::SND_PCM_STREAM_PLAYBACK)
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
self.default_config(alsa::SND_PCM_STREAM_PLAYBACK)
}
}
@ -900,7 +910,8 @@ fn get_available_samples(stream: &StreamInner) -> Result<usize, BackendSpecificE
unsafe fn set_hw_params_from_format(
pcm_handle: *mut alsa::snd_pcm_t,
hw_params: &HwParams,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
) -> Result<(), String> {
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_any(pcm_handle, hw_params.0)) {
return Err(format!("errors on pcm handle: {}", e));
@ -913,14 +924,14 @@ unsafe fn set_hw_params_from_format(
return Err(format!("handle not acessible: {}", e));
}
let data_type = if cfg!(target_endian = "big") {
match format.data_type {
let sample_format = if cfg!(target_endian = "big") {
match sample_format {
SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_BE,
SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_BE,
SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_BE,
}
} else {
match format.data_type {
match sample_format {
SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_LE,
SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_LE,
SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_LE,
@ -930,14 +941,14 @@ unsafe fn set_hw_params_from_format(
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_format(
pcm_handle,
hw_params.0,
data_type,
sample_format,
)) {
return Err(format!("format could not be set: {}", e));
}
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_rate(
pcm_handle,
hw_params.0,
format.sample_rate.0 as libc::c_uint,
config.sample_rate.0 as libc::c_uint,
0,
)) {
return Err(format!("sample rate could not be set: {}", e));
@ -945,7 +956,7 @@ unsafe fn set_hw_params_from_format(
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_channels(
pcm_handle,
hw_params.0,
format.channels as libc::c_uint,
config.channels as libc::c_uint,
)) {
return Err(format!("channel count could not be set: {}", e));
}
@ -969,7 +980,7 @@ unsafe fn set_hw_params_from_format(
unsafe fn set_sw_params_from_format(
pcm_handle: *mut alsa::snd_pcm_t,
format: &Format,
config: &StreamConfig,
) -> Result<(usize, usize), String> {
let mut sw_params = ptr::null_mut(); // TODO: RAII
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_malloc(&mut sw_params)) {
@ -1005,8 +1016,8 @@ unsafe fn set_sw_params_from_format(
)) {
return Err(format!("snd_pcm_sw_params_set_avail_min failed: {}", e));
}
let buffer = buffer as usize * format.channels as usize;
let period = period as usize * format.channels as usize;
let buffer = buffer as usize * config.channels as usize;
let period = period as usize * config.channels as usize;
(buffer, period)
};

View File

@ -1,20 +1,20 @@
use std;
pub type SupportedInputFormats = std::vec::IntoIter<SupportedFormat>;
pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>;
pub type SupportedInputConfigs = std::vec::IntoIter<SupportedStreamConfigRange>;
pub type SupportedOutputConfigs = std::vec::IntoIter<SupportedStreamConfigRange>;
use super::parking_lot::Mutex;
use super::sys;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use BackendSpecificError;
use DefaultFormatError;
use DefaultStreamConfigError;
use DeviceNameError;
use DevicesError;
use Format;
use SampleFormat;
use SampleRate;
use SupportedFormat;
use SupportedFormatsError;
use SupportedStreamConfig;
use SupportedStreamConfigRange;
use SupportedStreamConfigsError;
/// A ASIO Device
pub struct Device {
@ -52,52 +52,21 @@ impl Device {
Ok(self.driver.name().to_string())
}
/// Gets the supported input formats.
/// Gets the supported input configs.
/// TODO currently only supports the default.
/// Need to find all possible formats.
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
// Retrieve the default format for the total supported channels and supported sample
// format.
let mut f = match self.default_input_format() {
Err(_) => return Err(SupportedFormatsError::DeviceNotAvailable),
Ok(f) => f,
};
// Collect a format for every combination of supported sample rate and number of channels.
let mut supported_formats = vec![];
for &rate in ::COMMON_SAMPLE_RATES {
if !self
.driver
.can_sample_rate(rate.0.into())
.ok()
.unwrap_or(false)
{
continue;
}
for channels in 1..f.channels + 1 {
f.channels = channels;
f.sample_rate = rate;
supported_formats.push(SupportedFormat::from(f.clone()));
}
}
Ok(supported_formats.into_iter())
}
/// Gets the supported output formats.
/// TODO currently only supports the default.
/// Need to find all possible formats.
pub fn supported_output_formats(
/// Need to find all possible configs.
pub fn supported_input_configs(
&self,
) -> Result<SupportedOutputFormats, SupportedFormatsError> {
// Retrieve the default format for the total supported channels and supported sample
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
// Retrieve the default config for the total supported channels and supported sample
// format.
let mut f = match self.default_output_format() {
Err(_) => return Err(SupportedFormatsError::DeviceNotAvailable),
let mut f = match self.default_input_config() {
Err(_) => return Err(SupportedStreamConfigsError::DeviceNotAvailable),
Ok(f) => f,
};
// Collect a format for every combination of supported sample rate and number of channels.
let mut supported_formats = vec![];
// Collect a config for every combination of supported sample rate and number of channels.
let mut supported_configs = vec![];
for &rate in ::COMMON_SAMPLE_RATES {
if !self
.driver
@ -110,38 +79,71 @@ impl Device {
for channels in 1..f.channels + 1 {
f.channels = channels;
f.sample_rate = rate;
supported_formats.push(SupportedFormat::from(f.clone()));
supported_configs.push(SupportedStreamConfigRange::from(f.clone()));
}
}
Ok(supported_formats.into_iter())
Ok(supported_configs.into_iter())
}
/// Returns the default input format
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
let channels = self.driver.channels().map_err(default_format_err)?.ins as u16;
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _);
/// Gets the supported output configs.
/// TODO currently only supports the default.
/// Need to find all possible configs.
pub fn supported_output_configs(
&self,
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
// Retrieve the default config for the total supported channels and supported sample
// format.
let mut f = match self.default_output_config() {
Err(_) => return Err(SupportedStreamConfigsError::DeviceNotAvailable),
Ok(f) => f,
};
// Collect a config for every combination of supported sample rate and number of channels.
let mut supported_configs = vec![];
for &rate in ::COMMON_SAMPLE_RATES {
if !self
.driver
.can_sample_rate(rate.0.into())
.ok()
.unwrap_or(false)
{
continue;
}
for channels in 1..f.channels + 1 {
f.channels = channels;
f.sample_rate = rate;
supported_configs.push(SupportedStreamConfigRange::from(f.clone()));
}
}
Ok(supported_configs.into_iter())
}
/// Returns the default input config
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 _);
// Map th ASIO sample type to a CPAL sample type
let data_type = self.driver.input_data_type().map_err(default_format_err)?;
let data_type =
convert_data_type(&data_type).ok_or(DefaultFormatError::StreamTypeNotSupported)?;
Ok(Format {
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,
data_type,
sample_format,
})
}
/// Returns the default output format
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
let channels = self.driver.channels().map_err(default_format_err)?.outs as u16;
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _);
let data_type = self.driver.output_data_type().map_err(default_format_err)?;
let data_type =
convert_data_type(&data_type).ok_or(DefaultFormatError::StreamTypeNotSupported)?;
Ok(Format {
/// Returns the default output config
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 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,
data_type,
sample_format,
})
}
}
@ -202,12 +204,12 @@ pub(crate) fn convert_data_type(ty: &sys::AsioSampleType) -> Option<SampleFormat
Some(fmt)
}
fn default_format_err(e: sys::AsioError) -> DefaultFormatError {
fn default_config_err(e: sys::AsioError) -> DefaultStreamConfigError {
match e {
sys::AsioError::NoDrivers | sys::AsioError::HardwareMalfunction => {
DefaultFormatError::DeviceNotAvailable
DefaultStreamConfigError::DeviceNotAvailable
}
sys::AsioError::NoRate => DefaultFormatError::StreamTypeNotSupported,
sys::AsioError::NoRate => DefaultStreamConfigError::StreamTypeNotSupported,
err => {
let description = format!("{}", err);
BackendSpecificError { description }.into()

View File

@ -2,12 +2,13 @@ extern crate asio_sys as sys;
extern crate parking_lot;
use crate::{
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
PauseStreamError, PlayStreamError, StreamError, SupportedFormatsError,
BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError,
PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError,
SupportedStreamConfig, SupportedStreamConfigsError,
};
use traits::{DeviceTrait, HostTrait, StreamTrait};
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats};
pub use self::device::{Device, Devices, SupportedInputConfigs, SupportedOutputConfigs};
pub use self::stream::Stream;
use std::sync::Arc;
@ -53,37 +54,38 @@ impl HostTrait for Host {
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputConfigs = SupportedOutputConfigs;
type Stream = Stream;
fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self)
}
fn supported_input_formats(
fn supported_input_configs(
&self,
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
Device::supported_input_formats(self)
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
Device::supported_input_configs(self)
}
fn supported_output_formats(
fn supported_output_configs(
&self,
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
Device::supported_output_formats(self)
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
Device::supported_output_configs(self)
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_input_format(self)
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_input_config(self)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_output_format(self)
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_output_config(self)
}
fn build_input_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -91,12 +93,13 @@ impl DeviceTrait for Device {
D: FnMut(&Data) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
Device::build_input_stream_raw(self, format, data_callback, error_callback)
Device::build_input_stream_raw(self, config, sample_format, data_callback, error_callback)
}
fn build_output_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -104,7 +107,7 @@ impl DeviceTrait for Device {
D: FnMut(&mut Data) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
Device::build_output_stream_raw(self, format, data_callback, error_callback)
Device::build_output_stream_raw(self, config, sample_format, data_callback, error_callback)
}
}

View File

@ -10,12 +10,13 @@ use std::sync::Arc;
use BackendSpecificError;
use BuildStreamError;
use Data;
use Format;
use PauseStreamError;
use PlayStreamError;
use Sample;
use SampleFormat;
use StreamConfig;
use StreamError;
use SupportedStreamConfig;
/// Sample types whose constant silent value is known.
trait Silence {
@ -59,7 +60,8 @@ impl Stream {
impl Device {
pub fn build_input_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
mut data_callback: D,
_error_callback: E,
) -> Result<Stream, BuildStreamError>
@ -70,18 +72,18 @@ impl Device {
let stream_type = self.driver.input_data_type().map_err(build_stream_err)?;
// Ensure that the desired sample type is supported.
let data_type = super::device::convert_data_type(&stream_type)
.ok_or(BuildStreamError::FormatNotSupported)?;
if format.data_type != data_type {
return Err(BuildStreamError::FormatNotSupported);
let expected_sample_format = super::device::convert_data_type(&stream_type)
.ok_or(BuildStreamError::StreamConfigNotSupported)?;
if sample_format != expected_sample_format {
return Err(BuildStreamError::StreamConfigNotSupported);
}
let num_channels = format.channels.clone();
let buffer_size = self.get_or_create_input_stream(format)?;
let num_channels = config.channels.clone();
let buffer_size = self.get_or_create_input_stream(config, sample_format)?;
let cpal_num_samples = buffer_size * num_channels as usize;
// Create the buffer depending on the size of the data type.
let len_bytes = cpal_num_samples * data_type.sample_size();
let len_bytes = cpal_num_samples * sample_format.sample_size();
let mut interleaved = vec![0u8; len_bytes];
let stream_playing = Arc::new(AtomicBool::new(false));
@ -134,7 +136,7 @@ impl Device {
callback(&data);
}
match (&stream_type, data_type) {
match (&stream_type, sample_format) {
(&sys::AsioSampleType::ASIOSTInt16LSB, SampleFormat::I16) => {
process_input_callback::<i16, i16, _, _>(
&mut data_callback,
@ -225,7 +227,8 @@ impl Device {
pub fn build_output_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
mut data_callback: D,
_error_callback: E,
) -> Result<Stream, BuildStreamError>
@ -236,18 +239,18 @@ impl Device {
let stream_type = self.driver.output_data_type().map_err(build_stream_err)?;
// Ensure that the desired sample type is supported.
let data_type = super::device::convert_data_type(&stream_type)
.ok_or(BuildStreamError::FormatNotSupported)?;
if format.data_type != data_type {
return Err(BuildStreamError::FormatNotSupported);
let expected_sample_format = super::device::convert_data_type(&stream_type)
.ok_or(BuildStreamError::StreamConfigNotSupported)?;
if sample_format != expected_sample_format {
return Err(BuildStreamError::StreamConfigNotSupported);
}
let num_channels = format.channels.clone();
let buffer_size = self.get_or_create_output_stream(format)?;
let num_channels = config.channels.clone();
let buffer_size = self.get_or_create_output_stream(config, sample_format)?;
let cpal_num_samples = buffer_size * num_channels as usize;
// Create buffers depending on data type.
let len_bytes = cpal_num_samples * data_type.sample_size();
let len_bytes = cpal_num_samples * sample_format.sample_size();
let mut interleaved = vec![0u8; len_bytes];
let mut silence_asio_buffer = SilenceAsioBuffer::default();
@ -336,7 +339,7 @@ impl Device {
}
}
match (data_type, &stream_type) {
match (sample_format, &stream_type) {
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16LSB) => {
process_output_callback::<i16, i16, _, _>(
&mut data_callback,
@ -436,15 +439,19 @@ impl Device {
/// If there is no existing ASIO Input Stream it will be created.
///
/// On success, the buffer size of the stream is returned.
fn get_or_create_input_stream(&self, format: &Format) -> Result<usize, BuildStreamError> {
match self.default_input_format() {
fn get_or_create_input_stream(
&self,
config: &StreamConfig,
sample_format: SampleFormat,
) -> Result<usize, BuildStreamError> {
match self.default_input_config() {
Ok(f) => {
let num_asio_channels = f.channels;
check_format(&self.driver, format, num_asio_channels)
check_config(&self.driver, config, sample_format, num_asio_channels)
}
Err(_) => Err(BuildStreamError::FormatNotSupported),
Err(_) => Err(BuildStreamError::StreamConfigNotSupported),
}?;
let num_channels = format.channels as usize;
let num_channels = config.channels as usize;
let ref mut streams = *self.asio_streams.lock();
// Either create a stream if thers none or had back the
// size of the current one.
@ -473,15 +480,19 @@ impl Device {
/// Create a new CPAL Output Stream.
///
/// If there is no existing ASIO Output Stream it will be created.
fn get_or_create_output_stream(&self, format: &Format) -> Result<usize, BuildStreamError> {
match self.default_output_format() {
fn get_or_create_output_stream(
&self,
config: &StreamConfig,
sample_format: SampleFormat,
) -> Result<usize, BuildStreamError> {
match self.default_output_config() {
Ok(f) => {
let num_asio_channels = f.channels;
check_format(&self.driver, format, num_asio_channels)
check_config(&self.driver, config, sample_format, num_asio_channels)
}
Err(_) => Err(BuildStreamError::FormatNotSupported),
Err(_) => Err(BuildStreamError::StreamConfigNotSupported),
}?;
let num_channels = format.channels as usize;
let num_channels = config.channels as usize;
let ref mut streams = *self.asio_streams.lock();
// Either create a stream if thers none or had back the
// size of the current one.
@ -570,19 +581,19 @@ impl AsioSample for f64 {
}
}
/// Check whether or not the desired format is supported by the stream.
/// Check whether or not the desired config is supported by the stream.
///
/// Checks sample rate, data type and then finally the number of channels.
fn check_format(
fn check_config(
driver: &sys::Driver,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
num_asio_channels: u16,
) -> Result<(), BuildStreamError> {
let Format {
let StreamConfig {
channels,
sample_rate,
data_type,
} = format;
} = config;
// Try and set the sample rate to what the user selected.
let sample_rate = sample_rate.0.into();
if sample_rate != driver.sample_rate().map_err(build_stream_err)? {
@ -594,16 +605,16 @@ fn check_format(
.set_sample_rate(sample_rate)
.map_err(build_stream_err)?;
} else {
return Err(BuildStreamError::FormatNotSupported);
return Err(BuildStreamError::StreamConfigNotSupported);
}
}
// unsigned formats are not supported by asio
match data_type {
match sample_format {
SampleFormat::I16 | SampleFormat::F32 => (),
SampleFormat::U16 => return Err(BuildStreamError::FormatNotSupported),
SampleFormat::U16 => return Err(BuildStreamError::StreamConfigNotSupported),
}
if *channels > num_asio_channels {
return Err(BuildStreamError::FormatNotSupported);
return Err(BuildStreamError::StreamConfigNotSupported);
}
Ok(())
}

View File

@ -9,7 +9,7 @@ use super::Device;
use std::mem;
use std::ptr::null;
use std::vec::IntoIter as VecIntoIter;
use {BackendSpecificError, DevicesError, SupportedFormat};
use {BackendSpecificError, DevicesError, SupportedStreamConfigRange};
unsafe fn audio_devices() -> Result<Vec<AudioDeviceID>, OSStatus> {
let property_address = AudioObjectPropertyAddress {
@ -143,5 +143,5 @@ pub fn default_output_device() -> Option<Device> {
Some(device)
}
pub type SupportedInputFormats = VecIntoIter<SupportedFormat>;
pub type SupportedOutputFormats = VecIntoIter<SupportedFormat>;
pub type SupportedInputConfigs = VecIntoIter<SupportedStreamConfigRange>;
pub type SupportedOutputConfigs = VecIntoIter<SupportedStreamConfigRange>;

View File

@ -20,9 +20,10 @@ use self::coreaudio::sys::{
};
use crate::traits::{DeviceTrait, HostTrait, StreamTrait};
use crate::{
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultFormatError,
DeviceNameError, DevicesError, Format, PauseStreamError, PlayStreamError, SampleFormat,
SampleRate, StreamError, SupportedFormat, SupportedFormatsError,
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError,
DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate,
StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange,
SupportedStreamConfigsError,
};
use std::cell::RefCell;
use std::ffi::CStr;
@ -37,8 +38,8 @@ use std::time::Duration;
mod enumerate;
pub use self::enumerate::{
default_input_device, default_output_device, Devices, SupportedInputFormats,
SupportedOutputFormats,
default_input_device, default_output_device, Devices, SupportedInputConfigs,
SupportedOutputConfigs,
};
/// Coreaudio host, the default host on macOS and iOS.
@ -74,37 +75,38 @@ impl HostTrait for Host {
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputConfigs = SupportedOutputConfigs;
type Stream = Stream;
fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self)
}
fn supported_input_formats(
fn supported_input_configs(
&self,
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
Device::supported_input_formats(self)
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
Device::supported_input_configs(self)
}
fn supported_output_formats(
fn supported_output_configs(
&self,
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
Device::supported_output_formats(self)
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
Device::supported_output_configs(self)
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_input_format(self)
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_input_config(self)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_output_format(self)
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_output_config(self)
}
fn build_input_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -112,12 +114,13 @@ impl DeviceTrait for Device {
D: FnMut(&Data) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
Device::build_input_stream_raw(self, format, data_callback, error_callback)
Device::build_input_stream_raw(self, config, sample_format, data_callback, error_callback)
}
fn build_output_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -125,7 +128,7 @@ impl DeviceTrait for Device {
D: FnMut(&mut Data) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
Device::build_output_stream_raw(self, format, data_callback, error_callback)
Device::build_output_stream_raw(self, config, sample_format, data_callback, error_callback)
}
}
@ -165,11 +168,11 @@ impl Device {
Ok(c_str.to_string_lossy().into_owned())
}
// Logic re-used between `supported_input_formats` and `supported_output_formats`.
fn supported_formats(
// Logic re-used between `supported_input_configs` and `supported_output_configs`.
fn supported_configs(
&self,
scope: AudioObjectPropertyScope,
) -> Result<SupportedOutputFormats, SupportedFormatsError> {
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
let mut property_address = AudioObjectPropertyAddress {
mSelector: kAudioDevicePropertyStreamConfiguration,
mScope: scope,
@ -255,11 +258,11 @@ impl Device {
// Collect the supported formats for the device.
let mut fmts = vec![];
for range in ranges {
let fmt = SupportedFormat {
let fmt = SupportedStreamConfigRange {
channels: n_channels as ChannelCount,
min_sample_rate: SampleRate(range.mMinimum as _),
max_sample_rate: SampleRate(range.mMaximum as _),
data_type: sample_format,
sample_format: sample_format,
};
fmts.push(fmt);
}
@ -268,19 +271,25 @@ impl Device {
}
}
fn supported_input_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
self.supported_formats(kAudioObjectPropertyScopeInput)
fn supported_input_configs(
&self,
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
self.supported_configs(kAudioObjectPropertyScopeInput)
}
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
self.supported_formats(kAudioObjectPropertyScopeOutput)
fn supported_output_configs(
&self,
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
self.supported_configs(kAudioObjectPropertyScopeOutput)
}
fn default_format(
fn default_config(
&self,
scope: AudioObjectPropertyScope,
) -> Result<Format, DefaultFormatError> {
fn default_format_error_from_os_status(status: OSStatus) -> Result<(), DefaultFormatError> {
) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
fn default_config_error_from_os_status(
status: OSStatus,
) -> Result<(), DefaultStreamConfigError> {
let err = match coreaudio::Error::from_os_status(status) {
Err(err) => err,
Ok(_) => return Ok(()),
@ -291,10 +300,10 @@ impl Device {
)
| coreaudio::Error::AudioCodec(_)
| coreaudio::Error::AudioFormat(_) => {
Err(DefaultFormatError::StreamTypeNotSupported)
Err(DefaultStreamConfigError::StreamTypeNotSupported)
}
coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::NoConnection) => {
Err(DefaultFormatError::DeviceNotAvailable)
Err(DefaultStreamConfigError::DeviceNotAvailable)
}
err => {
let description = format!("{}", std::error::Error::description(&err));
@ -321,7 +330,7 @@ impl Device {
&data_size as *const _ as *mut _,
&asbd as *const _ as *mut _,
);
default_format_error_from_os_status(status)?;
default_config_error_from_os_status(status)?;
let sample_format = {
let audio_format = coreaudio::audio_unit::AudioFormat::from_format_and_flag(
@ -330,7 +339,7 @@ impl Device {
);
let flags = match audio_format {
Some(coreaudio::audio_unit::AudioFormat::LinearPCM(flags)) => flags,
_ => return Err(DefaultFormatError::StreamTypeNotSupported),
_ => return Err(DefaultStreamConfigError::StreamTypeNotSupported),
};
let maybe_sample_format =
coreaudio::audio_unit::SampleFormat::from_flags_and_bytes_per_frame(
@ -340,25 +349,25 @@ impl Device {
match maybe_sample_format {
Some(coreaudio::audio_unit::SampleFormat::F32) => SampleFormat::F32,
Some(coreaudio::audio_unit::SampleFormat::I16) => SampleFormat::I16,
_ => return Err(DefaultFormatError::StreamTypeNotSupported),
_ => return Err(DefaultStreamConfigError::StreamTypeNotSupported),
}
};
let format = Format {
let config = SupportedStreamConfig {
sample_rate: SampleRate(asbd.mSampleRate as _),
channels: asbd.mChannelsPerFrame as _,
data_type: sample_format,
sample_format: sample_format,
};
Ok(format)
Ok(config)
}
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
self.default_format(kAudioObjectPropertyScopeInput)
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
self.default_config(kAudioObjectPropertyScopeInput)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
self.default_format(kAudioObjectPropertyScopeOutput)
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
self.default_config(kAudioObjectPropertyScopeOutput)
}
}
@ -389,22 +398,24 @@ impl From<coreaudio::Error> for BuildStreamError {
| coreaudio::Error::NoKnownSubtype
| coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported)
| coreaudio::Error::AudioCodec(_)
| coreaudio::Error::AudioFormat(_) => BuildStreamError::FormatNotSupported,
| coreaudio::Error::AudioFormat(_) => BuildStreamError::StreamConfigNotSupported,
_ => BuildStreamError::DeviceNotAvailable,
}
}
}
// Create a coreaudio AudioStreamBasicDescription from a CPAL Format.
fn asbd_from_format(format: &Format) -> AudioStreamBasicDescription {
let n_channels = format.channels as usize;
let sample_rate = format.sample_rate.0;
let bytes_per_channel = format.data_type.sample_size();
fn asbd_from_config(
config: &StreamConfig,
sample_format: SampleFormat,
) -> AudioStreamBasicDescription {
let n_channels = config.channels as usize;
let sample_rate = config.sample_rate.0;
let bytes_per_channel = sample_format.sample_size();
let bits_per_channel = bytes_per_channel * 8;
let bytes_per_frame = n_channels * bytes_per_channel;
let frames_per_packet = 1;
let bytes_per_packet = frames_per_packet * bytes_per_frame;
let sample_format = format.data_type;
let format_flags = match sample_format {
SampleFormat::F32 => (kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked) as u32,
_ => kAudioFormatFlagIsPacked as u32,
@ -469,7 +480,8 @@ fn audio_unit_from_device(device: &Device, input: bool) -> Result<AudioUnit, cor
impl Device {
fn build_input_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
mut data_callback: D,
_error_callback: E,
) -> Result<Stream, BuildStreamError>
@ -502,7 +514,7 @@ impl Device {
coreaudio::Error::from_os_status(status)?;
// If the requested sample rate is different to the device sample rate, update the device.
if sample_rate as u32 != format.sample_rate.0 {
if sample_rate as u32 != config.sample_rate.0 {
// Get available sample rate ranges.
property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
let data_size = 0u32;
@ -530,12 +542,12 @@ impl Device {
let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges);
// Now that we have the available ranges, pick the one matching the desired rate.
let sample_rate = format.sample_rate.0;
let sample_rate = config.sample_rate.0;
let maybe_index = ranges.iter().position(|r| {
r.mMinimum as u32 == sample_rate && r.mMaximum as u32 == sample_rate
});
let range_index = match maybe_index {
None => return Err(BuildStreamError::FormatNotSupported),
None => return Err(BuildStreamError::StreamConfigNotSupported),
Some(i) => i,
};
@ -619,12 +631,11 @@ impl Device {
let mut audio_unit = audio_unit_from_device(self, true)?;
// Set the stream in interleaved mode.
let asbd = asbd_from_format(format);
let asbd = asbd_from_config(config, sample_format);
audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?;
// Register the callback that is being called by coreaudio whenever it needs data to be
// fed to the audio buffer.
let sample_format = format.data_type;
let bytes_per_channel = sample_format.sample_size();
type Args = render_callback::Args<data::Raw>;
audio_unit.set_input_callback(move |args: Args| unsafe {
@ -657,7 +668,8 @@ impl Device {
fn build_output_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
mut data_callback: D,
_error_callback: E,
) -> Result<Stream, BuildStreamError>
@ -672,12 +684,11 @@ impl Device {
let element = Element::Output;
// Set the stream in interleaved mode.
let asbd = asbd_from_format(format);
let asbd = asbd_from_config(config, sample_format);
audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?;
// Register the callback that is being called by coreaudio whenever it needs data to be
// fed to the audio buffer.
let sample_format = format.data_type;
let bytes_per_channel = sample_format.sample_size();
type Args = render_callback::Args<data::Raw>;
audio_unit.set_render_callback(move |args: Args| unsafe {

View File

@ -8,9 +8,9 @@ use stdweb::web::TypedArray;
use stdweb::Reference;
use crate::{
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
PauseStreamError, PlayStreamError, SampleFormat, StreamError, SupportedFormat,
SupportedFormatsError,
BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError,
PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError,
SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
};
use traits::{DeviceTrait, HostTrait, StreamTrait};
@ -37,8 +37,8 @@ pub struct Stream {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StreamId(usize);
pub type SupportedInputFormats = ::std::vec::IntoIter<SupportedFormat>;
pub type SupportedOutputFormats = ::std::vec::IntoIter<SupportedFormat>;
pub type SupportedInputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>;
pub type SupportedOutputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>;
impl Host {
pub fn new() -> Result<Self, crate::HostUnavailable> {
@ -60,12 +60,16 @@ impl Device {
}
#[inline]
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
fn supported_input_configs(
&self,
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
unimplemented!();
}
#[inline]
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
fn supported_output_configs(
&self,
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
// TODO: right now cpal's API doesn't allow flexibility here
// "44100" and "2" (channels) have also been hard-coded in the rest of the code ; if
// this ever becomes more flexible, don't forget to change that
@ -74,25 +78,25 @@ impl Device {
//
// UPDATE: We can do this now. Might be best to use `crate::COMMON_SAMPLE_RATES` and
// filter out those that lay outside the range specified above.
Ok(vec![SupportedFormat {
Ok(vec![SupportedStreamConfigRange {
channels: 2,
min_sample_rate: ::SampleRate(44100),
max_sample_rate: ::SampleRate(44100),
data_type: ::SampleFormat::F32,
sample_format: ::SampleFormat::F32,
}]
.into_iter())
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
unimplemented!();
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
// TODO: because it is hard coded, see supported_output_formats.
Ok(Format {
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
// TODO: because it is hard coded, see supported_output_configs.
Ok(SupportedStreamConfig {
channels: 2,
sample_rate: ::SampleRate(44100),
data_type: ::SampleFormat::F32,
sample_format: ::SampleFormat::F32,
})
}
}
@ -120,37 +124,38 @@ impl HostTrait for Host {
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputConfigs = SupportedOutputConfigs;
type Stream = Stream;
fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self)
}
fn supported_input_formats(
fn supported_input_configs(
&self,
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
Device::supported_input_formats(self)
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
Device::supported_input_configs(self)
}
fn supported_output_formats(
fn supported_output_configs(
&self,
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
Device::supported_output_formats(self)
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
Device::supported_output_configs(self)
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_input_format(self)
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_input_config(self)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_output_format(self)
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_output_config(self)
}
fn build_input_stream_raw<D, E>(
&self,
_format: &Format,
_config: &StreamConfig,
_sample_format: SampleFormat,
_data_callback: D,
_error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -163,7 +168,8 @@ impl DeviceTrait for Device {
fn build_output_stream_raw<D, E>(
&self,
format: &Format,
_config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -172,7 +178,7 @@ impl DeviceTrait for Device {
E: FnMut(StreamError) + Send + 'static,
{
assert_eq!(
format.data_type,
sample_format,
SampleFormat::F32,
"emscripten backend currently only supports `f32` data",
);

View File

@ -1,6 +1,7 @@
use crate::{
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
PauseStreamError, PlayStreamError, StreamError, SupportedFormat, SupportedFormatsError,
BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError,
PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError,
SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
};
use traits::{DeviceTrait, HostTrait, StreamTrait};
@ -15,8 +16,8 @@ pub struct Host;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Stream;
pub struct SupportedInputFormats;
pub struct SupportedOutputFormats;
pub struct SupportedInputConfigs;
pub struct SupportedOutputConfigs;
impl Host {
#[allow(dead_code)]
@ -32,8 +33,8 @@ impl Devices {
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputConfigs = SupportedOutputConfigs;
type Stream = Stream;
#[inline]
@ -42,28 +43,33 @@ impl DeviceTrait for Device {
}
#[inline]
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
fn supported_input_configs(
&self,
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
unimplemented!()
}
#[inline]
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
fn supported_output_configs(
&self,
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
unimplemented!()
}
#[inline]
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
unimplemented!()
}
#[inline]
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
unimplemented!()
}
fn build_input_stream_raw<D, E>(
&self,
_format: &Format,
_config: &StreamConfig,
_sample_format: SampleFormat,
_data_callback: D,
_error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -77,7 +83,8 @@ impl DeviceTrait for Device {
/// Create an output stream.
fn build_output_stream_raw<D, E>(
&self,
_format: &Format,
_config: &StreamConfig,
_sample_format: SampleFormat,
_data_callback: D,
_error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -129,20 +136,20 @@ impl Iterator for Devices {
}
}
impl Iterator for SupportedInputFormats {
type Item = SupportedFormat;
impl Iterator for SupportedInputConfigs {
type Item = SupportedStreamConfigRange;
#[inline]
fn next(&mut self) -> Option<SupportedFormat> {
fn next(&mut self) -> Option<SupportedStreamConfigRange> {
None
}
}
impl Iterator for SupportedOutputFormats {
type Item = SupportedFormat;
impl Iterator for SupportedOutputConfigs {
type Item = SupportedStreamConfigRange;
#[inline]
fn next(&mut self) -> Option<SupportedFormat> {
fn next(&mut self) -> Option<SupportedStreamConfigRange> {
None
}
}

View File

@ -1,6 +1,7 @@
use crate::{
BackendSpecificError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
SampleFormat, SampleRate, SupportedFormat, SupportedFormatsError, COMMON_SAMPLE_RATES,
BackendSpecificError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError,
SampleFormat, SampleRate, StreamConfig, SupportedStreamConfig, SupportedStreamConfigRange,
SupportedStreamConfigsError, COMMON_SAMPLE_RATES,
};
use std;
use std::ffi::OsString;
@ -49,8 +50,8 @@ use super::{
};
use crate::{traits::DeviceTrait, BuildStreamError, StreamError};
pub type SupportedInputFormats = std::vec::IntoIter<SupportedFormat>;
pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>;
pub type SupportedInputConfigs = std::vec::IntoIter<SupportedStreamConfigRange>;
pub type SupportedOutputConfigs = std::vec::IntoIter<SupportedStreamConfigRange>;
/// Wrapper because of that stupid decision to remove `Send` and `Sync` from raw pointers.
#[derive(Copy, Clone)]
@ -67,37 +68,38 @@ pub struct Device {
}
impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputConfigs = SupportedOutputConfigs;
type Stream = Stream;
fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self)
}
fn supported_input_formats(
fn supported_input_configs(
&self,
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
Device::supported_input_formats(self)
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
Device::supported_input_configs(self)
}
fn supported_output_formats(
fn supported_output_configs(
&self,
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
Device::supported_output_formats(self)
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
Device::supported_output_configs(self)
}
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_input_format(self)
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_input_config(self)
}
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_output_format(self)
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_output_config(self)
}
fn build_input_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -105,7 +107,7 @@ impl DeviceTrait for Device {
D: FnMut(&Data) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
let stream_inner = self.build_input_stream_raw_inner(format)?;
let stream_inner = self.build_input_stream_raw_inner(config, sample_format)?;
Ok(Stream::new_input(
stream_inner,
data_callback,
@ -115,7 +117,8 @@ impl DeviceTrait for Device {
fn build_output_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -123,7 +126,7 @@ impl DeviceTrait for Device {
D: FnMut(&mut Data) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
let stream_inner = self.build_output_stream_raw_inner(format)?;
let stream_inner = self.build_output_stream_raw_inner(config, sample_format)?;
Ok(Stream::new_output(
stream_inner,
data_callback,
@ -214,7 +217,7 @@ unsafe fn data_flow_from_immendpoint(endpoint: *const IMMEndpoint) -> EDataFlow
pub unsafe fn is_format_supported(
client: *const IAudioClient,
waveformatex_ptr: *const mmreg::WAVEFORMATEX,
) -> Result<bool, SupportedFormatsError> {
) -> Result<bool, SupportedStreamConfigsError> {
/*
// `IsFormatSupported` checks whether the format is supported and fills
// a `WAVEFORMATEX`
@ -241,7 +244,7 @@ pub unsafe fn is_format_supported(
},
(winerror::S_FALSE, _) => {
(*audio_client).Release();
return Err(BuildStreamError::FormatNotSupported);
return Err(BuildStreamError::StreamConfigNotSupported);
},
(_, Ok(())) => (),
};
@ -258,7 +261,7 @@ pub unsafe fn is_format_supported(
// has been found, but not an exact match) so we also treat this as unsupported.
match (result, check_result(result)) {
(_, Err(ref e)) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
Err(SupportedFormatsError::DeviceNotAvailable)
Err(SupportedStreamConfigsError::DeviceNotAvailable)
}
(_, Err(_)) => Ok(false),
(winerror::S_FALSE, _) => Ok(false),
@ -291,11 +294,11 @@ pub unsafe fn is_format_supported(
// Get a cpal Format from a WAVEFORMATEX.
unsafe fn format_from_waveformatex_ptr(
waveformatex_ptr: *const mmreg::WAVEFORMATEX,
) -> Option<Format> {
) -> Option<SupportedStreamConfig> {
fn cmp_guid(a: &GUID, b: &GUID) -> bool {
a.Data1 == b.Data1 && a.Data2 == b.Data2 && a.Data3 == b.Data3 && a.Data4 == b.Data4
}
let data_type = match (
let sample_format = match (
(*waveformatex_ptr).wBitsPerSample,
(*waveformatex_ptr).wFormatTag,
) {
@ -315,10 +318,10 @@ unsafe fn format_from_waveformatex_ptr(
// Unknown data format returned by GetMixFormat.
_ => return None,
};
let format = Format {
let format = SupportedStreamConfig {
channels: (*waveformatex_ptr).nChannels as _,
sample_rate: SampleRate((*waveformatex_ptr).nSamplesPerSec),
data_type,
sample_format,
};
Some(format)
}
@ -433,7 +436,7 @@ impl Device {
// number of channels seems to be supported. Any more or less returns an invalid
// parameter error. Thus we just assume that the default number of channels is the only
// number supported.
fn supported_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
fn supported_formats(&self) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
// initializing COM because we call `CoTaskMemFree` to release the format.
com::com_initialized();
@ -441,7 +444,7 @@ impl Device {
let lock = match self.ensure_future_audio_client() {
Ok(lock) => lock,
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
return Err(SupportedFormatsError::DeviceNotAvailable)
return Err(SupportedStreamConfigsError::DeviceNotAvailable)
}
Err(e) => {
let description = format!("{}", e);
@ -457,7 +460,7 @@ impl Device {
match check_result((*client).GetMixFormat(&mut default_waveformatex_ptr.0)) {
Ok(()) => (),
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
return Err(SupportedFormatsError::DeviceNotAvailable);
return Err(SupportedStreamConfigsError::DeviceNotAvailable);
}
Err(e) => {
let description = format!("{}", e);
@ -514,7 +517,8 @@ impl Device {
Some(fmt) => fmt,
None => {
let description =
"could not create a `cpal::Format` from a `WAVEFORMATEX`".to_string();
"could not create a `cpal::SupportedStreamConfig` from a `WAVEFORMATEX`"
.to_string();
let err = BackendSpecificError { description };
return Err(err.into());
}
@ -522,13 +526,15 @@ 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(SupportedFormat::from(format.clone()));
supported_formats.push(SupportedStreamConfigRange::from(format.clone()));
}
Ok(supported_formats.into_iter())
}
}
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
pub fn supported_input_configs(
&self,
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
if self.data_flow() == eCapture {
self.supported_formats()
// If it's an output device, assume no input formats.
@ -537,9 +543,9 @@ impl Device {
}
}
pub fn supported_output_formats(
pub fn supported_output_configs(
&self,
) -> Result<SupportedOutputFormats, SupportedFormatsError> {
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
if self.data_flow() == eRender {
self.supported_formats()
// If it's an input device, assume no output formats.
@ -552,14 +558,14 @@ impl Device {
// processor to mix them together.
//
// One format is guaranteed to be supported, the one returned by `GetMixFormat`.
fn default_format(&self) -> Result<Format, DefaultFormatError> {
fn default_format(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
// initializing COM because we call `CoTaskMemFree`
com::com_initialized();
let lock = match self.ensure_future_audio_client() {
Ok(lock) => lock,
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
return Err(DefaultFormatError::DeviceNotAvailable)
return Err(DefaultStreamConfigError::DeviceNotAvailable)
}
Err(e) => {
let description = format!("{}", e);
@ -573,7 +579,7 @@ impl Device {
let mut format_ptr = WaveFormatExPtr(ptr::null_mut());
match check_result((*client).GetMixFormat(&mut format_ptr.0)) {
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
return Err(DefaultFormatError::DeviceNotAvailable);
return Err(DefaultStreamConfigError::DeviceNotAvailable);
}
Err(e) => {
let description = format!("{}", e);
@ -584,7 +590,7 @@ impl Device {
};
format_from_waveformatex_ptr(format_ptr.0)
.ok_or(DefaultFormatError::StreamTypeNotSupported)
.ok_or(DefaultStreamConfigError::StreamTypeNotSupported)
}
}
@ -593,26 +599,27 @@ impl Device {
endpoint.data_flow()
}
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
pub fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
if self.data_flow() == eCapture {
self.default_format()
} else {
Err(DefaultFormatError::StreamTypeNotSupported)
Err(DefaultStreamConfigError::StreamTypeNotSupported)
}
}
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
pub fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
let data_flow = self.data_flow();
if data_flow == eRender {
self.default_format()
} else {
Err(DefaultFormatError::StreamTypeNotSupported)
Err(DefaultStreamConfigError::StreamTypeNotSupported)
}
}
pub(crate) fn build_input_stream_raw_inner(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
) -> Result<StreamInner, BuildStreamError> {
unsafe {
// Making sure that COM is initialized.
@ -634,13 +641,13 @@ impl Device {
// Computing the format and initializing the device.
let waveformatex = {
let format_attempt = format_to_waveformatextensible(format)
.ok_or(BuildStreamError::FormatNotSupported)?;
let format_attempt = config_to_waveformatextensible(config, sample_format)
.ok_or(BuildStreamError::StreamConfigNotSupported)?;
let share_mode = AUDCLNT_SHAREMODE_SHARED;
// Ensure the format is supported.
match super::device::is_format_supported(audio_client, &format_attempt.Format) {
Ok(false) => return Err(BuildStreamError::FormatNotSupported),
Ok(false) => return Err(BuildStreamError::StreamConfigNotSupported),
Err(_) => return Err(BuildStreamError::DeviceNotAvailable),
_ => (),
}
@ -749,14 +756,15 @@ impl Device {
playing: false,
max_frames_in_buffer,
bytes_per_frame: waveformatex.nBlockAlign,
sample_format: format.data_type,
sample_format,
})
}
}
pub(crate) fn build_output_stream_raw_inner(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
) -> Result<StreamInner, BuildStreamError> {
unsafe {
// Making sure that COM is initialized.
@ -778,13 +786,13 @@ impl Device {
// Computing the format and initializing the device.
let waveformatex = {
let format_attempt = format_to_waveformatextensible(format)
.ok_or(BuildStreamError::FormatNotSupported)?;
let format_attempt = config_to_waveformatextensible(config, sample_format)
.ok_or(BuildStreamError::StreamConfigNotSupported)?;
let share_mode = AUDCLNT_SHAREMODE_SHARED;
// Ensure the format is supported.
match super::device::is_format_supported(audio_client, &format_attempt.Format) {
Ok(false) => return Err(BuildStreamError::FormatNotSupported),
Ok(false) => return Err(BuildStreamError::StreamConfigNotSupported),
Err(_) => return Err(BuildStreamError::DeviceNotAvailable),
_ => (),
}
@ -893,7 +901,7 @@ impl Device {
playing: false,
max_frames_in_buffer,
bytes_per_frame: waveformatex.nBlockAlign,
sample_format: format.data_type,
sample_format,
})
}
}
@ -1141,19 +1149,22 @@ pub fn default_output_device() -> Option<Device> {
// Turns a `Format` into a `WAVEFORMATEXTENSIBLE`.
//
// Returns `None` if the WAVEFORMATEXTENSIBLE does not support the given format.
fn format_to_waveformatextensible(format: &Format) -> Option<mmreg::WAVEFORMATEXTENSIBLE> {
let format_tag = match format.data_type {
fn config_to_waveformatextensible(
config: &StreamConfig,
sample_format: SampleFormat,
) -> Option<mmreg::WAVEFORMATEXTENSIBLE> {
let format_tag = match sample_format {
SampleFormat::I16 => mmreg::WAVE_FORMAT_PCM,
SampleFormat::F32 => mmreg::WAVE_FORMAT_EXTENSIBLE,
SampleFormat::U16 => return None,
};
let channels = format.channels as WORD;
let sample_rate = format.sample_rate.0 as DWORD;
let sample_bytes = format.data_type.sample_size() as WORD;
let channels = config.channels as WORD;
let sample_rate = config.sample_rate.0 as DWORD;
let sample_bytes = sample_format.sample_size() as WORD;
let avg_bytes_per_sec = u32::from(channels) * sample_rate * u32::from(sample_bytes);
let block_align = channels * sample_bytes;
let bits_per_sample = 8 * sample_bytes;
let cb_size = match format.data_type {
let cb_size = match sample_format {
SampleFormat::I16 => 0,
SampleFormat::F32 => {
let extensible_size = mem::size_of::<mmreg::WAVEFORMATEXTENSIBLE>();
@ -1177,7 +1188,7 @@ fn format_to_waveformatextensible(format: &Format) -> Option<mmreg::WAVEFORMATEX
const KSAUDIO_SPEAKER_DIRECTOUT: DWORD = 0;
let channel_mask = KSAUDIO_SPEAKER_DIRECTOUT;
let sub_format = match format.data_type {
let sub_format = match sample_format {
SampleFormat::I16 => ksmedia::KSDATAFORMAT_SUBTYPE_PCM,
SampleFormat::F32 => ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
SampleFormat::U16 => return None,

View File

@ -1,8 +1,8 @@
extern crate winapi;
pub use self::device::{
default_input_device, default_output_device, Device, Devices, SupportedInputFormats,
SupportedOutputFormats,
default_input_device, default_output_device, Device, Devices, SupportedInputConfigs,
SupportedOutputConfigs,
};
pub use self::stream::Stream;
use self::winapi::um::winnt::HRESULT;

View File

@ -31,24 +31,25 @@
//! let device = host.default_output_device().expect("no output device available");
//! ```
//!
//! Before we can create a stream, we must decide what the format of the audio samples is going to
//! be. You can query all the supported formats with the `supported_input_formats()` and
//! `supported_output_formats()` methods. These produce a list of `SupportedFormat` structs which
//! can later be turned into actual `Format` structs. If you don't want to query the list of
//! formats, you can also build your own `Format` manually, but doing so could lead to an error
//! when building the stream if the format is not supported by the device.
//! Before we can create a stream, we must decide what the configuration of the audio stream is
//! going to be. You can query all the supported configurations with the
//! `supported_input_configs()` and `supported_output_configs()` methods. These produce a list of
//! `SupportedStreamConfigRange` structs which can later be turned into actual
//! `SupportedStreamConfig` structs. If you don't want to query the list of configs, you can also
//! build your own `StreamConfig` manually, but doing so could lead to an error when building the
//! stream if the config is not supported by the device.
//!
//! > **Note**: the `supported_formats()` method could return an error for example if the device
//! > has been disconnected.
//! > **Note**: the `supported_input/output_configs()` methods could return an error for example if
//! > the device has been disconnected.
//!
//! ```no_run
//! use cpal::traits::{DeviceTrait, HostTrait};
//! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap();
//! let mut supported_formats_range = device.supported_output_formats()
//! .expect("error while querying formats");
//! let format = supported_formats_range.next()
//! .expect("no supported format?!")
//! let mut supported_configs_range = device.supported_output_configs()
//! .expect("error while querying configs");
//! let supported_config = supported_configs_range.next()
//! .expect("no supported config?!")
//! .with_max_sample_rate();
//! ```
//!
@ -59,9 +60,9 @@
//! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
//! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap();
//! # let shape = device.default_output_format().unwrap().shape();
//! # let config = device.default_output_config().unwrap().into();
//! let stream = device.build_output_stream(
//! &shape,
//! &config,
//! move |data: &mut [f32]| {
//! // react to stream events and read or write stream data here.
//! },
@ -91,13 +92,14 @@
//! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
//! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap();
//! # let format = device.default_output_format().unwrap();
//! # let supported_config = device.default_output_config().unwrap();
//! let err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err);
//! let shape = format.shape();
//! let stream = match format.data_type {
//! SampleFormat::F32 => device.build_output_stream(&shape, write_silence::<f32>, err_fn),
//! SampleFormat::I16 => device.build_output_stream(&shape, write_silence::<i16>, err_fn),
//! SampleFormat::U16 => device.build_output_stream(&shape, write_silence::<u16>, err_fn),
//! let sample_format = supported_config.sample_format();
//! let config = supported_config.into();
//! let stream = match sample_format {
//! SampleFormat::F32 => device.build_output_stream(&config, write_silence::<f32>, err_fn),
//! SampleFormat::I16 => device.build_output_stream(&config, write_silence::<i16>, err_fn),
//! SampleFormat::U16 => device.build_output_stream(&config, write_silence::<u16>, err_fn),
//! }.unwrap();
//!
//! fn write_silence<T: Sample>(data: &mut [T]) {
@ -114,10 +116,12 @@
//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
//! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap();
//! # let format = device.default_output_format().unwrap();
//! # let supported_config = device.default_output_config().unwrap();
//! # let sample_format = supported_config.sample_format();
//! # let config = supported_config.into();
//! # let data_fn = move |_data: &mut cpal::Data| {};
//! # let err_fn = move |_err| {};
//! # let stream = device.build_output_stream_raw(&format, data_fn, err_fn).unwrap();
//! # let stream = device.build_output_stream_raw(&config, sample_format, data_fn, err_fn).unwrap();
//! stream.play().unwrap();
//! ```
//!
@ -128,10 +132,12 @@
//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
//! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap();
//! # let format = device.default_output_format().unwrap();
//! # let supported_config = device.default_output_config().unwrap();
//! # let sample_format = supported_config.sample_format();
//! # let config = supported_config.into();
//! # let data_fn = move |_data: &mut cpal::Data| {};
//! # let err_fn = move |_err| {};
//! # let stream = device.build_output_stream_raw(&format, data_fn, err_fn).unwrap();
//! # let stream = device.build_output_stream_raw(&config, sample_format, data_fn, err_fn).unwrap();
//! stream.pause().unwrap();
//! ```
@ -149,7 +155,7 @@ extern crate thiserror;
pub use error::*;
pub use platform::{
available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream,
SupportedInputFormats, SupportedOutputFormats, ALL_HOSTS,
SupportedInputConfigs, SupportedOutputConfigs, ALL_HOSTS,
};
pub use samples_formats::{Sample, SampleFormat};
@ -172,56 +178,35 @@ pub type ChannelCount = u16;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct SampleRate(pub u32);
/// The format of an input or output audio stream.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Format {
pub channels: ChannelCount,
pub sample_rate: SampleRate,
pub data_type: SampleFormat,
}
impl Format {
/// Construct a format having a particular `shape` and `data_type`.
pub fn with_shape(shape: &Shape, data_type: SampleFormat) -> Self {
Self {
channels: shape.channels,
sample_rate: shape.sample_rate,
data_type,
}
}
/// Extract aspects of the format independent of the data type.
pub fn shape(&self) -> Shape {
self.clone().into()
}
}
/// The properties of an input or output audio stream excluding its data type.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Shape {
/// The set of parameters used to describe how to open a stream.
///
/// The sample format is omitted in favour of using a sample type.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StreamConfig {
pub channels: ChannelCount,
pub sample_rate: SampleRate,
}
impl From<Format> for Shape {
fn from(x: Format) -> Self {
Self {
channels: x.channels,
sample_rate: x.sample_rate,
}
}
}
/// Describes a range of supported stream formats.
/// Describes a range of supported stream configurations, retrieved via the
/// `Device::supported_input/output_configs` method.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SupportedFormat {
pub channels: ChannelCount,
pub struct SupportedStreamConfigRange {
pub(crate) channels: ChannelCount,
/// Minimum value for the samples rate of the supported formats.
pub min_sample_rate: SampleRate,
pub(crate) min_sample_rate: SampleRate,
/// Maximum value for the samples rate of the supported formats.
pub max_sample_rate: SampleRate,
pub(crate) max_sample_rate: SampleRate,
/// Type of data expected by the device.
pub data_type: SampleFormat,
pub(crate) sample_format: SampleFormat,
}
/// Describes a single supported stream configuration, retrieved via either a
/// `SupportedStreamConfigRange` instance or one of the `Device::default_input/output_config` methods.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SupportedStreamConfig {
channels: ChannelCount,
sample_rate: SampleRate,
sample_format: SampleFormat,
}
/// A buffer of dynamically typed audio data, passed to raw stream callbacks.
@ -235,6 +220,27 @@ pub struct Data {
sample_format: SampleFormat,
}
impl SupportedStreamConfig {
pub fn channels(&self) -> ChannelCount {
self.channels
}
pub fn sample_rate(&self) -> SampleRate {
self.sample_rate
}
pub fn sample_format(&self) -> SampleFormat {
self.sample_format
}
pub fn config(&self) -> StreamConfig {
StreamConfig {
channels: self.channels,
sample_rate: self.sample_rate,
}
}
}
impl Data {
// Internal constructor for host implementations to use.
//
@ -328,25 +334,54 @@ impl Data {
}
}
impl SupportedFormat {
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate.
#[inline]
pub fn with_max_sample_rate(self) -> Format {
Format {
impl SupportedStreamConfigRange {
pub fn channels(&self) -> ChannelCount {
self.channels
}
pub fn min_sample_rate(&self) -> SampleRate {
self.min_sample_rate
}
pub fn max_sample_rate(&self) -> SampleRate {
self.max_sample_rate
}
pub fn sample_format(&self) -> SampleFormat {
self.sample_format
}
/// Retrieve a `SupportedStreamConfig` with the given sample rate.
///
/// **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 {
assert!(sample_rate <= self.min_sample_rate && sample_rate <= self.max_sample_rate);
SupportedStreamConfig {
channels: self.channels,
sample_rate: self.max_sample_rate,
data_type: self.data_type,
sample_format: self.sample_format,
sample_rate,
}
}
/// A comparison function which compares two `SupportedFormat`s in terms of their priority of
/// Turns this `SupportedStreamConfigRange` into a `SupportedStreamConfig` corresponding to the maximum samples rate.
#[inline]
pub fn with_max_sample_rate(self) -> SupportedStreamConfig {
SupportedStreamConfig {
channels: self.channels,
sample_rate: self.max_sample_rate,
sample_format: self.sample_format,
}
}
/// A comparison function which compares two `SupportedStreamConfigRange`s in terms of their priority of
/// use as a default stream format.
///
/// Some backends do not provide a default stream format for their audio devices. In these
/// cases, CPAL attempts to decide on a reasonable default format for the user. To do this we
/// use the "greatest" of all supported stream formats when compared with this method.
///
/// Formats are prioritised by the following heuristics:
/// SupportedStreamConfigs are prioritised by the following heuristics:
///
/// **Channels**:
///
@ -382,17 +417,17 @@ impl SupportedFormat {
return cmp_channels;
}
let cmp_f32 = (self.data_type == F32).cmp(&(other.data_type == F32));
let cmp_f32 = (self.sample_format == F32).cmp(&(other.sample_format == F32));
if cmp_f32 != Equal {
return cmp_f32;
}
let cmp_i16 = (self.data_type == I16).cmp(&(other.data_type == I16));
let cmp_i16 = (self.sample_format == I16).cmp(&(other.sample_format == I16));
if cmp_i16 != Equal {
return cmp_i16;
}
let cmp_u16 = (self.data_type == U16).cmp(&(other.data_type == U16));
let cmp_u16 = (self.sample_format == U16).cmp(&(other.sample_format == U16));
if cmp_u16 != Equal {
return cmp_u16;
}
@ -410,14 +445,20 @@ impl SupportedFormat {
}
}
impl From<Format> for SupportedFormat {
impl From<SupportedStreamConfig> for StreamConfig {
fn from(conf: SupportedStreamConfig) -> Self {
conf.config()
}
}
impl From<SupportedStreamConfig> for SupportedStreamConfigRange {
#[inline]
fn from(format: Format) -> SupportedFormat {
SupportedFormat {
fn from(format: SupportedStreamConfig) -> SupportedStreamConfigRange {
SupportedStreamConfigRange {
channels: format.channels,
min_sample_rate: format.sample_rate,
max_sample_rate: format.sample_rate,
data_type: format.data_type,
sample_format: format.sample_format,
}
}
}

View File

@ -27,8 +27,8 @@ pub use self::platform_impl::*;
// }
// ```
//
// And so on for Device, Devices, EventLoop, Host, StreamId, SupportedInputFormats,
// SupportedOutputFormats and all their necessary trait implementations.
// And so on for Device, Devices, EventLoop, Host, StreamId, SupportedInputConfigs,
// SupportedOutputConfigs and all their necessary trait implementations.
// ```
macro_rules! impl_platform_host {
($($HostVariant:ident $host_mod:ident $host_name:literal),*) => {
@ -67,13 +67,13 @@ macro_rules! impl_platform_host {
// TODO: Confirm this and add more specific detail and references.
pub struct Stream(StreamInner, crate::platform::NotSendSyncAcrossAllPlatforms);
/// The **SupportedInputFormats** iterator associated with the platform's dynamically
/// The **SupportedInputConfigs** iterator associated with the platform's dynamically
/// dispatched **Host** type.
pub struct SupportedInputFormats(SupportedInputFormatsInner);
pub struct SupportedInputConfigs(SupportedInputConfigsInner);
/// The **SupportedOutputFormats** iterator associated with the platform's dynamically
/// The **SupportedOutputConfigs** iterator associated with the platform's dynamically
/// dispatched **Host** type.
pub struct SupportedOutputFormats(SupportedOutputFormatsInner);
pub struct SupportedOutputConfigs(SupportedOutputConfigsInner);
/// Unique identifier for available hosts on the platform.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
@ -107,15 +107,15 @@ macro_rules! impl_platform_host {
)*
}
enum SupportedInputFormatsInner {
enum SupportedInputConfigsInner {
$(
$HostVariant(crate::host::$host_mod::SupportedInputFormats),
$HostVariant(crate::host::$host_mod::SupportedInputConfigs),
)*
}
enum SupportedOutputFormatsInner {
enum SupportedOutputConfigsInner {
$(
$HostVariant(crate::host::$host_mod::SupportedOutputFormats),
$HostVariant(crate::host::$host_mod::SupportedOutputConfigs),
)*
}
@ -162,13 +162,13 @@ macro_rules! impl_platform_host {
}
}
impl Iterator for SupportedInputFormats {
type Item = crate::SupportedFormat;
impl Iterator for SupportedInputConfigs {
type Item = crate::SupportedStreamConfigRange;
fn next(&mut self) -> Option<Self::Item> {
match self.0 {
$(
SupportedInputFormatsInner::$HostVariant(ref mut s) => s.next(),
SupportedInputConfigsInner::$HostVariant(ref mut s) => s.next(),
)*
}
}
@ -176,19 +176,19 @@ macro_rules! impl_platform_host {
fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 {
$(
SupportedInputFormatsInner::$HostVariant(ref d) => d.size_hint(),
SupportedInputConfigsInner::$HostVariant(ref d) => d.size_hint(),
)*
}
}
}
impl Iterator for SupportedOutputFormats {
type Item = crate::SupportedFormat;
impl Iterator for SupportedOutputConfigs {
type Item = crate::SupportedStreamConfigRange;
fn next(&mut self) -> Option<Self::Item> {
match self.0 {
$(
SupportedOutputFormatsInner::$HostVariant(ref mut s) => s.next(),
SupportedOutputConfigsInner::$HostVariant(ref mut s) => s.next(),
)*
}
}
@ -196,15 +196,15 @@ macro_rules! impl_platform_host {
fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 {
$(
SupportedOutputFormatsInner::$HostVariant(ref d) => d.size_hint(),
SupportedOutputConfigsInner::$HostVariant(ref d) => d.size_hint(),
)*
}
}
}
impl crate::traits::DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats;
type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputConfigs = SupportedOutputConfigs;
type Stream = Stream;
fn name(&self) -> Result<String, crate::DeviceNameError> {
@ -215,49 +215,50 @@ macro_rules! impl_platform_host {
}
}
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, crate::SupportedFormatsError> {
fn supported_input_configs(&self) -> Result<Self::SupportedInputConfigs, crate::SupportedStreamConfigsError> {
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => {
d.supported_input_formats()
.map(SupportedInputFormatsInner::$HostVariant)
.map(SupportedInputFormats)
d.supported_input_configs()
.map(SupportedInputConfigsInner::$HostVariant)
.map(SupportedInputConfigs)
}
)*
}
}
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, crate::SupportedFormatsError> {
fn supported_output_configs(&self) -> Result<Self::SupportedOutputConfigs, crate::SupportedStreamConfigsError> {
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => {
d.supported_output_formats()
.map(SupportedOutputFormatsInner::$HostVariant)
.map(SupportedOutputFormats)
d.supported_output_configs()
.map(SupportedOutputConfigsInner::$HostVariant)
.map(SupportedOutputConfigs)
}
)*
}
}
fn default_input_format(&self) -> Result<crate::Format, crate::DefaultFormatError> {
fn default_input_config(&self) -> Result<crate::SupportedStreamConfig, crate::DefaultStreamConfigError> {
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => d.default_input_format(),
DeviceInner::$HostVariant(ref d) => d.default_input_config(),
)*
}
}
fn default_output_format(&self) -> Result<crate::Format, crate::DefaultFormatError> {
fn default_output_config(&self) -> Result<crate::SupportedStreamConfig, crate::DefaultStreamConfigError> {
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => d.default_output_format(),
DeviceInner::$HostVariant(ref d) => d.default_output_config(),
)*
}
}
fn build_input_stream_raw<D, E>(
&self,
format: &crate::Format,
config: &crate::StreamConfig,
sample_format: crate::SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, crate::BuildStreamError>
@ -267,7 +268,13 @@ macro_rules! impl_platform_host {
{
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => d.build_input_stream_raw(format, data_callback, error_callback)
DeviceInner::$HostVariant(ref d) => d
.build_input_stream_raw(
config,
sample_format,
data_callback,
error_callback,
)
.map(StreamInner::$HostVariant)
.map(Stream::from),
)*
@ -276,7 +283,8 @@ macro_rules! impl_platform_host {
fn build_output_stream_raw<D, E>(
&self,
format: &crate::Format,
config: &crate::StreamConfig,
sample_format: crate::SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, crate::BuildStreamError>
@ -286,7 +294,13 @@ macro_rules! impl_platform_host {
{
match self.0 {
$(
DeviceInner::$HostVariant(ref d) => d.build_output_stream_raw(format, data_callback, error_callback)
DeviceInner::$HostVariant(ref d) => d
.build_output_stream_raw(
config,
sample_format,
data_callback,
error_callback,
)
.map(StreamInner::$HostVariant)
.map(Stream::from),
)*
@ -436,8 +450,8 @@ macro_rules! impl_platform_host {
mod platform_impl {
pub use crate::host::alsa::{
Device as AlsaDevice, Devices as AlsaDevices, Host as AlsaHost, Stream as AlsaStream,
SupportedInputFormats as AlsaSupportedInputFormats,
SupportedOutputFormats as AlsaSupportedOutputFormats,
SupportedInputConfigs as AlsaSupportedInputConfigs,
SupportedOutputConfigs as AlsaSupportedOutputConfigs,
};
impl_platform_host!(Alsa alsa "ALSA");
@ -454,8 +468,8 @@ mod platform_impl {
mod platform_impl {
pub use crate::host::coreaudio::{
Device as CoreAudioDevice, Devices as CoreAudioDevices, Host as CoreAudioHost,
Stream as CoreAudioStream, SupportedInputFormats as CoreAudioSupportedInputFormats,
SupportedOutputFormats as CoreAudioSupportedOutputFormats,
Stream as CoreAudioStream, SupportedInputConfigs as CoreAudioSupportedInputConfigs,
SupportedOutputConfigs as CoreAudioSupportedOutputConfigs,
};
impl_platform_host!(CoreAudio coreaudio "CoreAudio");
@ -472,8 +486,8 @@ mod platform_impl {
mod platform_impl {
pub use crate::host::emscripten::{
Device as EmscriptenDevice, Devices as EmscriptenDevices, Host as EmscriptenHost,
Stream as EmscriptenStream, SupportedInputFormats as EmscriptenSupportedInputFormats,
SupportedOutputFormats as EmscriptenSupportedOutputFormats,
Stream as EmscriptenStream, SupportedInputConfigs as EmscriptenSupportedInputConfigs,
SupportedOutputConfigs as EmscriptenSupportedOutputConfigs,
};
impl_platform_host!(Emscripten emscripten "Emscripten");
@ -491,13 +505,13 @@ mod platform_impl {
#[cfg(feature = "asio")]
pub use crate::host::asio::{
Device as AsioDevice, Devices as AsioDevices, Host as AsioHost, Stream as AsioStream,
SupportedInputFormats as AsioSupportedInputFormats,
SupportedOutputFormats as AsioSupportedOutputFormats,
SupportedInputConfigs as AsioSupportedInputConfigs,
SupportedOutputConfigs as AsioSupportedOutputConfigs,
};
pub use crate::host::wasapi::{
Device as WasapiDevice, Devices as WasapiDevices, Host as WasapiHost,
Stream as WasapiStream, SupportedInputFormats as WasapiSupportedInputFormats,
SupportedOutputFormats as WasapiSupportedOutputFormats,
Stream as WasapiStream, SupportedInputConfigs as WasapiSupportedInputConfigs,
SupportedOutputConfigs as WasapiSupportedOutputConfigs,
};
#[cfg(feature = "asio")]
@ -526,8 +540,8 @@ mod platform_impl {
mod platform_impl {
pub use crate::host::null::{
Device as NullDevice, Devices as NullDevices, EventLoop as NullEventLoop, Host as NullHost,
StreamId as NullStreamId, SupportedInputFormats as NullSupportedInputFormats,
SupportedOutputFormats as NullSupportedOutputFormats,
StreamId as NullStreamId, SupportedInputConfigs as NullSupportedInputConfigs,
SupportedOutputConfigs as NullSupportedOutputConfigs,
};
impl_platform_host!(Null null "Null");

View File

@ -1,9 +1,9 @@
//! The suite of traits allowing CPAL to abstract over hosts, devices, event loops and stream IDs.
use {
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
InputDevices, OutputDevices, PauseStreamError, PlayStreamError, Sample, Shape, StreamError,
SupportedFormat, SupportedFormatsError,
BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, InputDevices,
OutputDevices, PauseStreamError, PlayStreamError, Sample, SampleFormat, StreamConfig,
StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
};
/// A **Host** provides access to the available audio devices on the system.
@ -56,7 +56,7 @@ pub trait HostTrait {
fn input_devices(&self) -> Result<InputDevices<Self::Devices>, DevicesError> {
fn supports_input<D: DeviceTrait>(device: &D) -> bool {
device
.supported_input_formats()
.supported_input_configs()
.map(|mut iter| iter.next().is_some())
.unwrap_or(false)
}
@ -70,7 +70,7 @@ pub trait HostTrait {
fn output_devices(&self) -> Result<OutputDevices<Self::Devices>, DevicesError> {
fn supports_output<D: DeviceTrait>(device: &D) -> bool {
device
.supported_output_formats()
.supported_output_configs()
.map(|mut iter| iter.next().is_some())
.unwrap_or(false)
}
@ -84,9 +84,9 @@ pub trait HostTrait {
/// methods that involve a device return a `Result` allowing the user to handle this case.
pub trait DeviceTrait {
/// The iterator type yielding supported input stream formats.
type SupportedInputFormats: Iterator<Item = SupportedFormat>;
type SupportedInputConfigs: Iterator<Item = SupportedStreamConfigRange>;
/// The iterator type yielding supported output stream formats.
type SupportedOutputFormats: Iterator<Item = SupportedFormat>;
type SupportedOutputConfigs: Iterator<Item = SupportedStreamConfigRange>;
/// The stream type created by `build_input_stream_raw` and `build_output_stream_raw`.
type Stream: StreamTrait;
@ -96,26 +96,27 @@ pub trait DeviceTrait {
/// An iterator yielding formats that are supported by the backend.
///
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
fn supported_input_formats(&self)
-> Result<Self::SupportedInputFormats, SupportedFormatsError>;
fn supported_input_configs(
&self,
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError>;
/// An iterator yielding output stream formats that are supported by the device.
///
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
fn supported_output_formats(
fn supported_output_configs(
&self,
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError>;
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError>;
/// The default input stream format for the device.
fn default_input_format(&self) -> Result<Format, DefaultFormatError>;
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError>;
/// The default output stream format for the device.
fn default_output_format(&self) -> Result<Format, DefaultFormatError>;
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError>;
/// Create an input stream.
fn build_input_stream<T, D, E>(
&self,
shape: &Shape,
config: &StreamConfig,
mut data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -125,7 +126,8 @@ pub trait DeviceTrait {
E: FnMut(StreamError) + Send + 'static,
{
self.build_input_stream_raw(
&Format::with_shape(shape, T::FORMAT),
config,
T::FORMAT,
move |data| {
data_callback(
data.as_slice()
@ -139,7 +141,7 @@ pub trait DeviceTrait {
/// Create an output stream.
fn build_output_stream<T, D, E>(
&self,
shape: &Shape,
config: &StreamConfig,
mut data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -149,7 +151,8 @@ pub trait DeviceTrait {
E: FnMut(StreamError) + Send + 'static,
{
self.build_output_stream_raw(
&Format::with_shape(shape, T::FORMAT),
config,
T::FORMAT,
move |data| {
data_callback(
data.as_slice_mut()
@ -163,7 +166,8 @@ pub trait DeviceTrait {
/// Create a dynamically typed input stream.
fn build_input_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
@ -174,7 +178,8 @@ pub trait DeviceTrait {
/// Create a dynamically typed output stream.
fn build_output_stream_raw<D, E>(
&self,
format: &Format,
config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>