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

View File

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

View File

@ -1,7 +1,7 @@
//! Feeds back the input stream directly into the output stream. //! 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 //! Assumes that the input and output devices can use the same stream configuration and that they
//! the f32 sample format. //! support the f32 sample format.
//! //!
//! Uses a delay of `LATENCY_MS` milliseconds in case the default input and output streams are not //! Uses a delay of `LATENCY_MS` milliseconds in case the default input and output streams are not
//! precisely synchronised. //! precisely synchronised.
@ -28,12 +28,12 @@ fn main() -> Result<(), anyhow::Error> {
println!("Using default input device: \"{}\"", input_device.name()?); println!("Using default input device: \"{}\"", input_device.name()?);
println!("Using default output device: \"{}\"", output_device.name()?); println!("Using default output device: \"{}\"", output_device.name()?);
// We'll try and use the same format between streams to keep it simple // We'll try and use the same configuration between streams to keep it simple.
let shape = input_device.default_input_format()?.shape(); let config: cpal::StreamConfig = input_device.default_input_config()?.into();
// Create a delay in case the input and output devices aren't synced. // 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_frames = (LATENCY_MS / 1_000.0) * config.sample_rate.0 as f32;
let latency_samples = latency_frames as usize * shape.channels as usize; let latency_samples = latency_frames as usize * config.channels as usize;
// The buffer to share samples // The buffer to share samples
let ring = RingBuffer::new(latency_samples * 2); let ring = RingBuffer::new(latency_samples * 2);
@ -80,10 +80,10 @@ fn main() -> Result<(), anyhow::Error> {
// Build streams. // Build streams.
println!( println!(
"Attempting to build both streams with f32 samples and `{:?}`.", "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 input_stream = input_device.build_input_stream(&config, input_data_fn, err_fn)?;
let output_stream = output_device.build_output_stream(&shape, output_data_fn, err_fn)?; let output_stream = output_device.build_output_stream(&config, output_data_fn, err_fn)?;
println!("Successfully built streams."); println!("Successfully built streams.");
// Play the 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". //! 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. // Use the default host for working with audio devices.
let host = cpal::default_host(); 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 let device = host
.default_input_device() .default_input_device()
.expect("Failed to get default input device"); .expect("Failed to get default input device");
println!("Default input device: {}", device.name()?); println!("Default input device: {}", device.name()?);
let format = device let config = device
.default_input_format() .default_input_config()
.expect("Failed to get default input format"); .expect("Failed to get default input config");
println!("Default input format: {:?}", format); println!("Default input config: {:?}", config);
// The WAV file we're recording to. // The WAV file we're recording to.
const PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/recorded.wav"); 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 = hound::WavWriter::create(PATH, spec)?;
let writer = Arc::new(Mutex::new(Some(writer))); let writer = Arc::new(Mutex::new(Some(writer)));
@ -40,19 +40,19 @@ fn main() -> Result<(), anyhow::Error> {
eprintln!("an error occurred on stream: {}", err); 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( cpal::SampleFormat::F32 => device.build_input_stream(
&format.shape(), &config.into(),
move |data| write_input_data::<f32, f32>(data, &writer_2), move |data| write_input_data::<f32, f32>(data, &writer_2),
err_fn, err_fn,
)?, )?,
cpal::SampleFormat::I16 => device.build_input_stream( cpal::SampleFormat::I16 => device.build_input_stream(
&format.shape(), &config.into(),
move |data| write_input_data::<i16, i16>(data, &writer_2), move |data| write_input_data::<i16, i16>(data, &writer_2),
err_fn, err_fn,
)?, )?,
cpal::SampleFormat::U16 => device.build_input_stream( cpal::SampleFormat::U16 => device.build_input_stream(
&format.shape(), &config.into(),
move |data| write_input_data::<u16, i16>(data, &writer_2), move |data| write_input_data::<u16, i16>(data, &writer_2),
err_fn, 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 { hound::WavSpec {
channels: format.channels as _, channels: config.channels() as _,
sample_rate: format.sample_rate.0 as _, sample_rate: config.sample_rate().0 as _,
bits_per_sample: (format.data_type.sample_size() * 8) as _, bits_per_sample: (config.sample_format().sample_size() * 8) as _,
sample_format: sample_format(format.data_type), 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. /// Error that can happen when enumerating the list of supported formats.
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum SupportedFormatsError { pub enum SupportedStreamConfigsError {
/// The device no longer exists. This can happen if the device is disconnected while the /// The device no longer exists. This can happen if the device is disconnected while the
/// program is running. /// program is running.
#[error("The requested device is no longer available. For example, it has been unplugged.")] #[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`. /// May occur when attempting to request the default input or output stream format from a `Device`.
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum DefaultFormatError { pub enum DefaultStreamConfigError {
/// The device no longer exists. This can happen if the device is disconnected while the /// The device no longer exists. This can happen if the device is disconnected while the
/// program is running. /// program is running.
#[error("The requested device is no longer available. For example, it has been unplugged.")] #[error("The requested device is no longer available. For example, it has been unplugged.")]
@ -90,9 +90,9 @@ pub enum BuildStreamError {
/// program is running. /// program is running.
#[error("The requested device is no longer available. For example, it has been unplugged.")] #[error("The requested device is no longer available. For example, it has been unplugged.")]
DeviceNotAvailable, DeviceNotAvailable,
/// The required format is not supported. /// The specified stream configuration is not supported.
#[error("The requested stream format is not supported by the device.")] #[error("The requested stream configuration is not supported by the device.")]
FormatNotSupported, StreamConfigNotSupported,
/// We called something the C-Layer did not understand /// 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. /// 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; extern crate libc;
use crate::{ use crate::{
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultFormatError, BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError,
DeviceNameError, DevicesError, Format, PauseStreamError, PlayStreamError, SampleFormat, DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate,
SampleRate, StreamError, SupportedFormat, SupportedFormatsError, StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange,
SupportedStreamConfigsError,
}; };
use std::sync::Arc; use std::sync::Arc;
use std::thread::{self, JoinHandle}; 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 use self::enumerate::{default_input_device, default_output_device, Devices};
pub type SupportedInputFormats = VecIntoIter<SupportedFormat>; pub type SupportedInputConfigs = VecIntoIter<SupportedStreamConfigRange>;
pub type SupportedOutputFormats = VecIntoIter<SupportedFormat>; pub type SupportedOutputConfigs = VecIntoIter<SupportedStreamConfigRange>;
mod enumerate; mod enumerate;
@ -52,37 +53,38 @@ impl HostTrait for Host {
} }
impl DeviceTrait for Device { impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats; type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputFormats = SupportedOutputFormats; type SupportedOutputConfigs = SupportedOutputConfigs;
type Stream = Stream; type Stream = Stream;
fn name(&self) -> Result<String, DeviceNameError> { fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self) Device::name(self)
} }
fn supported_input_formats( fn supported_input_configs(
&self, &self,
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> { ) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
Device::supported_input_formats(self) Device::supported_input_configs(self)
} }
fn supported_output_formats( fn supported_output_configs(
&self, &self,
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> { ) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
Device::supported_output_formats(self) Device::supported_output_configs(self)
} }
fn default_input_format(&self) -> Result<Format, DefaultFormatError> { fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_input_format(self) Device::default_input_config(self)
} }
fn default_output_format(&self) -> Result<Format, DefaultFormatError> { fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_output_format(self) Device::default_output_config(self)
} }
fn build_input_stream_raw<D, E>( fn build_input_stream_raw<D, E>(
&self, &self,
format: &Format, conf: &StreamConfig,
sample_format: SampleFormat,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -90,14 +92,16 @@ impl DeviceTrait for Device {
D: FnMut(&Data) + Send + 'static, D: FnMut(&Data) + Send + 'static,
E: FnMut(StreamError) + 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); let stream = Stream::new_input(Arc::new(stream_inner), data_callback, error_callback);
Ok(stream) Ok(stream)
} }
fn build_output_stream_raw<D, E>( fn build_output_stream_raw<D, E>(
&self, &self,
format: &Format, conf: &StreamConfig,
sample_format: SampleFormat,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -105,7 +109,8 @@ impl DeviceTrait for Device {
D: FnMut(&mut Data) + Send + 'static, D: FnMut(&mut Data) + Send + 'static,
E: FnMut(StreamError) + 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); let stream = Stream::new_output(Arc::new(stream_inner), data_callback, error_callback);
Ok(stream) Ok(stream)
} }
@ -161,7 +166,8 @@ pub struct Device(String);
impl Device { impl Device {
fn build_stream_inner( fn build_stream_inner(
&self, &self,
format: &Format, conf: &StreamConfig,
sample_format: SampleFormat,
stream_type: alsa::snd_pcm_stream_t, stream_type: alsa::snd_pcm_stream_t,
) -> Result<StreamInner, BuildStreamError> { ) -> Result<StreamInner, BuildStreamError> {
let name = ffi::CString::new(self.0.clone()).expect("unable to clone device"); let name = ffi::CString::new(self.0.clone()).expect("unable to clone device");
@ -185,13 +191,13 @@ impl Device {
}; };
let can_pause = unsafe { let can_pause = unsafe {
let hw_params = HwParams::alloc(); 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 })?; .map_err(|description| BackendSpecificError { description })?;
alsa::snd_pcm_hw_params_can_pause(hw_params.0) == 1 alsa::snd_pcm_hw_params_can_pause(hw_params.0) == 1
}; };
let (buffer_len, period_len) = unsafe { 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 })? .map_err(|description| BackendSpecificError { description })?
}; };
@ -213,9 +219,9 @@ impl Device {
let stream_inner = StreamInner { let stream_inner = StreamInner {
channel: handle, channel: handle,
sample_format: format.data_type, sample_format,
num_descriptors, num_descriptors,
num_channels: format.channels as u16, num_channels: conf.channels as u16,
buffer_len, buffer_len,
period_len, period_len,
can_pause, can_pause,
@ -235,10 +241,10 @@ impl Device {
Ok(self.0.clone()) Ok(self.0.clone())
} }
unsafe fn supported_formats( unsafe fn supported_configs(
&self, &self,
stream_t: alsa::snd_pcm_stream_t, stream_t: alsa::snd_pcm_stream_t,
) -> Result<VecIntoIter<SupportedFormat>, SupportedFormatsError> { ) -> Result<VecIntoIter<SupportedStreamConfigRange>, SupportedStreamConfigsError> {
let mut handle = ptr::null_mut(); let mut handle = ptr::null_mut();
let device_name = match ffi::CString::new(&self.0[..]) { let device_name = match ffi::CString::new(&self.0[..]) {
Ok(name) => name, Ok(name) => name,
@ -256,8 +262,8 @@ impl Device {
alsa::SND_PCM_NONBLOCK, alsa::SND_PCM_NONBLOCK,
) { ) {
-2 | -2 |
-16 /* determined empirically */ => return Err(SupportedFormatsError::DeviceNotAvailable), -16 /* determined empirically */ => return Err(SupportedStreamConfigsError::DeviceNotAvailable),
-22 => return Err(SupportedFormatsError::InvalidArgument), -22 => return Err(SupportedStreamConfigsError::InvalidArgument),
e => if let Err(description) = check_errors(e) { e => if let Err(description) = check_errors(e) {
let err = BackendSpecificError { description }; let err = BackendSpecificError { description };
return Err(err.into()) return Err(err.into())
@ -402,14 +408,14 @@ impl Device {
let mut output = Vec::with_capacity( let mut output = Vec::with_capacity(
supported_formats.len() * supported_channels.len() * sample_rates.len(), supported_formats.len() * supported_channels.len() * sample_rates.len(),
); );
for &data_type in supported_formats.iter() { for &sample_format in supported_formats.iter() {
for channels in supported_channels.iter() { for channels in supported_channels.iter() {
for &(min_rate, max_rate) in sample_rates.iter() { for &(min_rate, max_rate) in sample_rates.iter() {
output.push(SupportedFormat { output.push(SupportedStreamConfigRange {
channels: channels.clone(), channels: channels.clone(),
min_sample_rate: SampleRate(min_rate as u32), min_sample_rate: SampleRate(min_rate as u32),
max_sample_rate: SampleRate(max_rate as u32), max_sample_rate: SampleRate(max_rate as u32),
data_type: data_type, sample_format: sample_format,
}); });
} }
} }
@ -420,31 +426,35 @@ impl Device {
Ok(output.into_iter()) Ok(output.into_iter())
} }
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> { fn supported_input_configs(
unsafe { self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE) } &self,
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
unsafe { self.supported_configs(alsa::SND_PCM_STREAM_CAPTURE) }
} }
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> { fn supported_output_configs(
unsafe { self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK) } &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 // 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. // the `SupportedStreamConfigRange::cmp_default_heuristics` order and select the greatest.
fn default_format( fn default_config(
&self, &self,
stream_t: alsa::snd_pcm_stream_t, stream_t: alsa::snd_pcm_stream_t,
) -> Result<Format, DefaultFormatError> { ) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
let mut formats: Vec<_> = unsafe { let mut formats: Vec<_> = unsafe {
match self.supported_formats(stream_t) { match self.supported_configs(stream_t) {
Err(SupportedFormatsError::DeviceNotAvailable) => { Err(SupportedStreamConfigsError::DeviceNotAvailable) => {
return Err(DefaultFormatError::DeviceNotAvailable); return Err(DefaultStreamConfigError::DeviceNotAvailable);
} }
Err(SupportedFormatsError::InvalidArgument) => { Err(SupportedStreamConfigsError::InvalidArgument) => {
// this happens sometimes when querying for input and output capabilities but // this happens sometimes when querying for input and output capabilities but
// the device supports only one // 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()); return Err(err.into());
} }
Ok(fmts) => fmts.collect(), Ok(fmts) => fmts.collect(),
@ -464,16 +474,16 @@ impl Device {
} }
Ok(format) Ok(format)
} }
None => Err(DefaultFormatError::StreamTypeNotSupported), None => Err(DefaultStreamConfigError::StreamTypeNotSupported),
} }
} }
fn default_input_format(&self) -> Result<Format, DefaultFormatError> { fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
self.default_format(alsa::SND_PCM_STREAM_CAPTURE) self.default_config(alsa::SND_PCM_STREAM_CAPTURE)
} }
fn default_output_format(&self) -> Result<Format, DefaultFormatError> { fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
self.default_format(alsa::SND_PCM_STREAM_PLAYBACK) 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( unsafe fn set_hw_params_from_format(
pcm_handle: *mut alsa::snd_pcm_t, pcm_handle: *mut alsa::snd_pcm_t,
hw_params: &HwParams, hw_params: &HwParams,
format: &Format, config: &StreamConfig,
sample_format: SampleFormat,
) -> Result<(), String> { ) -> Result<(), String> {
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_any(pcm_handle, hw_params.0)) { 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)); 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)); return Err(format!("handle not acessible: {}", e));
} }
let data_type = if cfg!(target_endian = "big") { let sample_format = if cfg!(target_endian = "big") {
match format.data_type { match sample_format {
SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_BE, SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_BE,
SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_BE, SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_BE,
SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_BE, SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_BE,
} }
} else { } else {
match format.data_type { match sample_format {
SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_LE, SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_LE,
SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_LE, SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_LE,
SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_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( if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_format(
pcm_handle, pcm_handle,
hw_params.0, hw_params.0,
data_type, sample_format,
)) { )) {
return Err(format!("format could not be set: {}", e)); return Err(format!("format could not be set: {}", e));
} }
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_rate( if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_rate(
pcm_handle, pcm_handle,
hw_params.0, hw_params.0,
format.sample_rate.0 as libc::c_uint, config.sample_rate.0 as libc::c_uint,
0, 0,
)) { )) {
return Err(format!("sample rate could not be set: {}", e)); 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( if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_channels(
pcm_handle, pcm_handle,
hw_params.0, 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)); 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( unsafe fn set_sw_params_from_format(
pcm_handle: *mut alsa::snd_pcm_t, pcm_handle: *mut alsa::snd_pcm_t,
format: &Format, config: &StreamConfig,
) -> Result<(usize, usize), String> { ) -> Result<(usize, usize), String> {
let mut sw_params = ptr::null_mut(); // TODO: RAII let mut sw_params = ptr::null_mut(); // TODO: RAII
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_malloc(&mut sw_params)) { 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)); return Err(format!("snd_pcm_sw_params_set_avail_min failed: {}", e));
} }
let buffer = buffer as usize * format.channels as usize; let buffer = buffer as usize * config.channels as usize;
let period = period as usize * format.channels as usize; let period = period as usize * config.channels as usize;
(buffer, period) (buffer, period)
}; };

View File

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

View File

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

View File

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

View File

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

View File

@ -8,9 +8,9 @@ use stdweb::web::TypedArray;
use stdweb::Reference; use stdweb::Reference;
use crate::{ use crate::{
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format, BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError,
PauseStreamError, PlayStreamError, SampleFormat, StreamError, SupportedFormat, PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError,
SupportedFormatsError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
}; };
use traits::{DeviceTrait, HostTrait, StreamTrait}; use traits::{DeviceTrait, HostTrait, StreamTrait};
@ -37,8 +37,8 @@ pub struct Stream {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StreamId(usize); pub struct StreamId(usize);
pub type SupportedInputFormats = ::std::vec::IntoIter<SupportedFormat>; pub type SupportedInputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>;
pub type SupportedOutputFormats = ::std::vec::IntoIter<SupportedFormat>; pub type SupportedOutputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>;
impl Host { impl Host {
pub fn new() -> Result<Self, crate::HostUnavailable> { pub fn new() -> Result<Self, crate::HostUnavailable> {
@ -60,12 +60,16 @@ impl Device {
} }
#[inline] #[inline]
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> { fn supported_input_configs(
&self,
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
unimplemented!(); unimplemented!();
} }
#[inline] #[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 // 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 // "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 // 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 // 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. // filter out those that lay outside the range specified above.
Ok(vec![SupportedFormat { Ok(vec![SupportedStreamConfigRange {
channels: 2, channels: 2,
min_sample_rate: ::SampleRate(44100), min_sample_rate: ::SampleRate(44100),
max_sample_rate: ::SampleRate(44100), max_sample_rate: ::SampleRate(44100),
data_type: ::SampleFormat::F32, sample_format: ::SampleFormat::F32,
}] }]
.into_iter()) .into_iter())
} }
fn default_input_format(&self) -> Result<Format, DefaultFormatError> { fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
unimplemented!(); unimplemented!();
} }
fn default_output_format(&self) -> Result<Format, DefaultFormatError> { fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
// TODO: because it is hard coded, see supported_output_formats. // TODO: because it is hard coded, see supported_output_configs.
Ok(Format { Ok(SupportedStreamConfig {
channels: 2, channels: 2,
sample_rate: ::SampleRate(44100), sample_rate: ::SampleRate(44100),
data_type: ::SampleFormat::F32, sample_format: ::SampleFormat::F32,
}) })
} }
} }
@ -120,37 +124,38 @@ impl HostTrait for Host {
} }
impl DeviceTrait for Device { impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats; type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputFormats = SupportedOutputFormats; type SupportedOutputConfigs = SupportedOutputConfigs;
type Stream = Stream; type Stream = Stream;
fn name(&self) -> Result<String, DeviceNameError> { fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self) Device::name(self)
} }
fn supported_input_formats( fn supported_input_configs(
&self, &self,
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> { ) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
Device::supported_input_formats(self) Device::supported_input_configs(self)
} }
fn supported_output_formats( fn supported_output_configs(
&self, &self,
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> { ) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
Device::supported_output_formats(self) Device::supported_output_configs(self)
} }
fn default_input_format(&self) -> Result<Format, DefaultFormatError> { fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_input_format(self) Device::default_input_config(self)
} }
fn default_output_format(&self) -> Result<Format, DefaultFormatError> { fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
Device::default_output_format(self) Device::default_output_config(self)
} }
fn build_input_stream_raw<D, E>( fn build_input_stream_raw<D, E>(
&self, &self,
_format: &Format, _config: &StreamConfig,
_sample_format: SampleFormat,
_data_callback: D, _data_callback: D,
_error_callback: E, _error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -163,7 +168,8 @@ impl DeviceTrait for Device {
fn build_output_stream_raw<D, E>( fn build_output_stream_raw<D, E>(
&self, &self,
format: &Format, _config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -172,7 +178,7 @@ impl DeviceTrait for Device {
E: FnMut(StreamError) + Send + 'static, E: FnMut(StreamError) + Send + 'static,
{ {
assert_eq!( assert_eq!(
format.data_type, sample_format,
SampleFormat::F32, SampleFormat::F32,
"emscripten backend currently only supports `f32` data", "emscripten backend currently only supports `f32` data",
); );

View File

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

View File

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

View File

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

View File

@ -31,24 +31,25 @@
//! let device = host.default_output_device().expect("no output device available"); //! 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 //! Before we can create a stream, we must decide what the configuration of the audio stream is
//! be. You can query all the supported formats with the `supported_input_formats()` and //! going to be. You can query all the supported configurations with the
//! `supported_output_formats()` methods. These produce a list of `SupportedFormat` structs which //! `supported_input_configs()` and `supported_output_configs()` methods. These produce a list of
//! can later be turned into actual `Format` structs. If you don't want to query the list of //! `SupportedStreamConfigRange` structs which can later be turned into actual
//! formats, you can also build your own `Format` manually, but doing so could lead to an error //! `SupportedStreamConfig` structs. If you don't want to query the list of configs, you can also
//! when building the stream if the format is not supported by the device. //! 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 //! > **Note**: the `supported_input/output_configs()` methods could return an error for example if
//! > has been disconnected. //! > the device has been disconnected.
//! //!
//! ```no_run //! ```no_run
//! use cpal::traits::{DeviceTrait, HostTrait}; //! use cpal::traits::{DeviceTrait, HostTrait};
//! # let host = cpal::default_host(); //! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap(); //! # let device = host.default_output_device().unwrap();
//! let mut supported_formats_range = device.supported_output_formats() //! let mut supported_configs_range = device.supported_output_configs()
//! .expect("error while querying formats"); //! .expect("error while querying configs");
//! let format = supported_formats_range.next() //! let supported_config = supported_configs_range.next()
//! .expect("no supported format?!") //! .expect("no supported config?!")
//! .with_max_sample_rate(); //! .with_max_sample_rate();
//! ``` //! ```
//! //!
@ -59,9 +60,9 @@
//! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; //! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
//! # let host = cpal::default_host(); //! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap(); //! # 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( //! let stream = device.build_output_stream(
//! &shape, //! &config,
//! move |data: &mut [f32]| { //! move |data: &mut [f32]| {
//! // react to stream events and read or write stream data here. //! // react to stream events and read or write stream data here.
//! }, //! },
@ -91,13 +92,14 @@
//! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; //! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
//! # let host = cpal::default_host(); //! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap(); //! # 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 err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err);
//! let shape = format.shape(); //! let sample_format = supported_config.sample_format();
//! let stream = match format.data_type { //! let config = supported_config.into();
//! SampleFormat::F32 => device.build_output_stream(&shape, write_silence::<f32>, err_fn), //! let stream = match sample_format {
//! SampleFormat::I16 => device.build_output_stream(&shape, write_silence::<i16>, err_fn), //! SampleFormat::F32 => device.build_output_stream(&config, write_silence::<f32>, err_fn),
//! SampleFormat::U16 => device.build_output_stream(&shape, write_silence::<u16>, 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(); //! }.unwrap();
//! //!
//! fn write_silence<T: Sample>(data: &mut [T]) { //! fn write_silence<T: Sample>(data: &mut [T]) {
@ -114,10 +116,12 @@
//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; //! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
//! # let host = cpal::default_host(); //! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap(); //! # 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 data_fn = move |_data: &mut cpal::Data| {};
//! # let err_fn = move |_err| {}; //! # 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(); //! stream.play().unwrap();
//! ``` //! ```
//! //!
@ -128,10 +132,12 @@
//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; //! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
//! # let host = cpal::default_host(); //! # let host = cpal::default_host();
//! # let device = host.default_output_device().unwrap(); //! # 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 data_fn = move |_data: &mut cpal::Data| {};
//! # let err_fn = move |_err| {}; //! # 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(); //! stream.pause().unwrap();
//! ``` //! ```
@ -149,7 +155,7 @@ extern crate thiserror;
pub use error::*; pub use error::*;
pub use platform::{ pub use platform::{
available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream, 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}; pub use samples_formats::{Sample, SampleFormat};
@ -172,56 +178,35 @@ pub type ChannelCount = u16;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct SampleRate(pub u32); pub struct SampleRate(pub u32);
/// The format of an input or output audio stream. /// The set of parameters used to describe how to open a stream.
#[derive(Debug, Clone, PartialEq, Eq)] ///
pub struct Format { /// The sample format is omitted in favour of using a sample type.
pub channels: ChannelCount, #[derive(Clone, Debug, Eq, PartialEq)]
pub sample_rate: SampleRate, pub struct StreamConfig {
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 {
pub channels: ChannelCount, pub channels: ChannelCount,
pub sample_rate: SampleRate, pub sample_rate: SampleRate,
} }
impl From<Format> for Shape { /// Describes a range of supported stream configurations, retrieved via the
fn from(x: Format) -> Self { /// `Device::supported_input/output_configs` method.
Self {
channels: x.channels,
sample_rate: x.sample_rate,
}
}
}
/// Describes a range of supported stream formats.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct SupportedFormat { pub struct SupportedStreamConfigRange {
pub channels: ChannelCount, pub(crate) channels: ChannelCount,
/// Minimum value for the samples rate of the supported formats. /// 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. /// 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. /// 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. /// A buffer of dynamically typed audio data, passed to raw stream callbacks.
@ -235,6 +220,27 @@ pub struct Data {
sample_format: SampleFormat, 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 { impl Data {
// Internal constructor for host implementations to use. // Internal constructor for host implementations to use.
// //
@ -328,25 +334,54 @@ impl Data {
} }
} }
impl SupportedFormat { impl SupportedStreamConfigRange {
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate. pub fn channels(&self) -> ChannelCount {
#[inline] self.channels
pub fn with_max_sample_rate(self) -> Format { }
Format {
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, channels: self.channels,
sample_rate: self.max_sample_rate, sample_format: self.sample_format,
data_type: self.data_type, 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. /// use as a default stream format.
/// ///
/// Some backends do not provide a default stream format for their audio devices. In these /// 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 /// 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. /// 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**: /// **Channels**:
/// ///
@ -382,17 +417,17 @@ impl SupportedFormat {
return cmp_channels; 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 { if cmp_f32 != Equal {
return cmp_f32; 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 { if cmp_i16 != Equal {
return cmp_i16; 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 { if cmp_u16 != Equal {
return cmp_u16; 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] #[inline]
fn from(format: Format) -> SupportedFormat { fn from(format: SupportedStreamConfig) -> SupportedStreamConfigRange {
SupportedFormat { SupportedStreamConfigRange {
channels: format.channels, channels: format.channels,
min_sample_rate: format.sample_rate, min_sample_rate: format.sample_rate,
max_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, // And so on for Device, Devices, EventLoop, Host, StreamId, SupportedInputConfigs,
// SupportedOutputFormats and all their necessary trait implementations. // SupportedOutputConfigs and all their necessary trait implementations.
// ``` // ```
macro_rules! impl_platform_host { macro_rules! impl_platform_host {
($($HostVariant:ident $host_mod:ident $host_name:literal),*) => { ($($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. // TODO: Confirm this and add more specific detail and references.
pub struct Stream(StreamInner, crate::platform::NotSendSyncAcrossAllPlatforms); 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. /// 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. /// dispatched **Host** type.
pub struct SupportedOutputFormats(SupportedOutputFormatsInner); pub struct SupportedOutputConfigs(SupportedOutputConfigsInner);
/// Unique identifier for available hosts on the platform. /// Unique identifier for available hosts on the platform.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] #[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 { impl Iterator for SupportedInputConfigs {
type Item = crate::SupportedFormat; type Item = crate::SupportedStreamConfigRange;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
match self.0 { 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>) { fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 { match self.0 {
$( $(
SupportedInputFormatsInner::$HostVariant(ref d) => d.size_hint(), SupportedInputConfigsInner::$HostVariant(ref d) => d.size_hint(),
)* )*
} }
} }
} }
impl Iterator for SupportedOutputFormats { impl Iterator for SupportedOutputConfigs {
type Item = crate::SupportedFormat; type Item = crate::SupportedStreamConfigRange;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
match self.0 { 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>) { fn size_hint(&self) -> (usize, Option<usize>) {
match self.0 { match self.0 {
$( $(
SupportedOutputFormatsInner::$HostVariant(ref d) => d.size_hint(), SupportedOutputConfigsInner::$HostVariant(ref d) => d.size_hint(),
)* )*
} }
} }
} }
impl crate::traits::DeviceTrait for Device { impl crate::traits::DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats; type SupportedInputConfigs = SupportedInputConfigs;
type SupportedOutputFormats = SupportedOutputFormats; type SupportedOutputConfigs = SupportedOutputConfigs;
type Stream = Stream; type Stream = Stream;
fn name(&self) -> Result<String, crate::DeviceNameError> { 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 { match self.0 {
$( $(
DeviceInner::$HostVariant(ref d) => { DeviceInner::$HostVariant(ref d) => {
d.supported_input_formats() d.supported_input_configs()
.map(SupportedInputFormatsInner::$HostVariant) .map(SupportedInputConfigsInner::$HostVariant)
.map(SupportedInputFormats) .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 { match self.0 {
$( $(
DeviceInner::$HostVariant(ref d) => { DeviceInner::$HostVariant(ref d) => {
d.supported_output_formats() d.supported_output_configs()
.map(SupportedOutputFormatsInner::$HostVariant) .map(SupportedOutputConfigsInner::$HostVariant)
.map(SupportedOutputFormats) .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 { 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 { 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>( fn build_input_stream_raw<D, E>(
&self, &self,
format: &crate::Format, config: &crate::StreamConfig,
sample_format: crate::SampleFormat,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, crate::BuildStreamError> ) -> Result<Self::Stream, crate::BuildStreamError>
@ -267,7 +268,13 @@ macro_rules! impl_platform_host {
{ {
match self.0 { 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(StreamInner::$HostVariant)
.map(Stream::from), .map(Stream::from),
)* )*
@ -276,7 +283,8 @@ macro_rules! impl_platform_host {
fn build_output_stream_raw<D, E>( fn build_output_stream_raw<D, E>(
&self, &self,
format: &crate::Format, config: &crate::StreamConfig,
sample_format: crate::SampleFormat,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, crate::BuildStreamError> ) -> Result<Self::Stream, crate::BuildStreamError>
@ -286,7 +294,13 @@ macro_rules! impl_platform_host {
{ {
match self.0 { 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(StreamInner::$HostVariant)
.map(Stream::from), .map(Stream::from),
)* )*
@ -436,8 +450,8 @@ macro_rules! impl_platform_host {
mod platform_impl { mod platform_impl {
pub use crate::host::alsa::{ pub use crate::host::alsa::{
Device as AlsaDevice, Devices as AlsaDevices, Host as AlsaHost, Stream as AlsaStream, Device as AlsaDevice, Devices as AlsaDevices, Host as AlsaHost, Stream as AlsaStream,
SupportedInputFormats as AlsaSupportedInputFormats, SupportedInputConfigs as AlsaSupportedInputConfigs,
SupportedOutputFormats as AlsaSupportedOutputFormats, SupportedOutputConfigs as AlsaSupportedOutputConfigs,
}; };
impl_platform_host!(Alsa alsa "ALSA"); impl_platform_host!(Alsa alsa "ALSA");
@ -454,8 +468,8 @@ mod platform_impl {
mod platform_impl { mod platform_impl {
pub use crate::host::coreaudio::{ pub use crate::host::coreaudio::{
Device as CoreAudioDevice, Devices as CoreAudioDevices, Host as CoreAudioHost, Device as CoreAudioDevice, Devices as CoreAudioDevices, Host as CoreAudioHost,
Stream as CoreAudioStream, SupportedInputFormats as CoreAudioSupportedInputFormats, Stream as CoreAudioStream, SupportedInputConfigs as CoreAudioSupportedInputConfigs,
SupportedOutputFormats as CoreAudioSupportedOutputFormats, SupportedOutputConfigs as CoreAudioSupportedOutputConfigs,
}; };
impl_platform_host!(CoreAudio coreaudio "CoreAudio"); impl_platform_host!(CoreAudio coreaudio "CoreAudio");
@ -472,8 +486,8 @@ mod platform_impl {
mod platform_impl { mod platform_impl {
pub use crate::host::emscripten::{ pub use crate::host::emscripten::{
Device as EmscriptenDevice, Devices as EmscriptenDevices, Host as EmscriptenHost, Device as EmscriptenDevice, Devices as EmscriptenDevices, Host as EmscriptenHost,
Stream as EmscriptenStream, SupportedInputFormats as EmscriptenSupportedInputFormats, Stream as EmscriptenStream, SupportedInputConfigs as EmscriptenSupportedInputConfigs,
SupportedOutputFormats as EmscriptenSupportedOutputFormats, SupportedOutputConfigs as EmscriptenSupportedOutputConfigs,
}; };
impl_platform_host!(Emscripten emscripten "Emscripten"); impl_platform_host!(Emscripten emscripten "Emscripten");
@ -491,13 +505,13 @@ mod platform_impl {
#[cfg(feature = "asio")] #[cfg(feature = "asio")]
pub use crate::host::asio::{ pub use crate::host::asio::{
Device as AsioDevice, Devices as AsioDevices, Host as AsioHost, Stream as AsioStream, Device as AsioDevice, Devices as AsioDevices, Host as AsioHost, Stream as AsioStream,
SupportedInputFormats as AsioSupportedInputFormats, SupportedInputConfigs as AsioSupportedInputConfigs,
SupportedOutputFormats as AsioSupportedOutputFormats, SupportedOutputConfigs as AsioSupportedOutputConfigs,
}; };
pub use crate::host::wasapi::{ pub use crate::host::wasapi::{
Device as WasapiDevice, Devices as WasapiDevices, Host as WasapiHost, Device as WasapiDevice, Devices as WasapiDevices, Host as WasapiHost,
Stream as WasapiStream, SupportedInputFormats as WasapiSupportedInputFormats, Stream as WasapiStream, SupportedInputConfigs as WasapiSupportedInputConfigs,
SupportedOutputFormats as WasapiSupportedOutputFormats, SupportedOutputConfigs as WasapiSupportedOutputConfigs,
}; };
#[cfg(feature = "asio")] #[cfg(feature = "asio")]
@ -526,8 +540,8 @@ mod platform_impl {
mod platform_impl { mod platform_impl {
pub use crate::host::null::{ pub use crate::host::null::{
Device as NullDevice, Devices as NullDevices, EventLoop as NullEventLoop, Host as NullHost, Device as NullDevice, Devices as NullDevices, EventLoop as NullEventLoop, Host as NullHost,
StreamId as NullStreamId, SupportedInputFormats as NullSupportedInputFormats, StreamId as NullStreamId, SupportedInputConfigs as NullSupportedInputConfigs,
SupportedOutputFormats as NullSupportedOutputFormats, SupportedOutputConfigs as NullSupportedOutputConfigs,
}; };
impl_platform_host!(Null null "Null"); 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. //! The suite of traits allowing CPAL to abstract over hosts, devices, event loops and stream IDs.
use { use {
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format, BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, InputDevices,
InputDevices, OutputDevices, PauseStreamError, PlayStreamError, Sample, Shape, StreamError, OutputDevices, PauseStreamError, PlayStreamError, Sample, SampleFormat, StreamConfig,
SupportedFormat, SupportedFormatsError, StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
}; };
/// A **Host** provides access to the available audio devices on the system. /// 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 input_devices(&self) -> Result<InputDevices<Self::Devices>, DevicesError> {
fn supports_input<D: DeviceTrait>(device: &D) -> bool { fn supports_input<D: DeviceTrait>(device: &D) -> bool {
device device
.supported_input_formats() .supported_input_configs()
.map(|mut iter| iter.next().is_some()) .map(|mut iter| iter.next().is_some())
.unwrap_or(false) .unwrap_or(false)
} }
@ -70,7 +70,7 @@ pub trait HostTrait {
fn output_devices(&self) -> Result<OutputDevices<Self::Devices>, DevicesError> { fn output_devices(&self) -> Result<OutputDevices<Self::Devices>, DevicesError> {
fn supports_output<D: DeviceTrait>(device: &D) -> bool { fn supports_output<D: DeviceTrait>(device: &D) -> bool {
device device
.supported_output_formats() .supported_output_configs()
.map(|mut iter| iter.next().is_some()) .map(|mut iter| iter.next().is_some())
.unwrap_or(false) .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. /// methods that involve a device return a `Result` allowing the user to handle this case.
pub trait DeviceTrait { pub trait DeviceTrait {
/// The iterator type yielding supported input stream formats. /// 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. /// 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`. /// The stream type created by `build_input_stream_raw` and `build_output_stream_raw`.
type Stream: StreamTrait; type Stream: StreamTrait;
@ -96,26 +96,27 @@ pub trait DeviceTrait {
/// An iterator yielding formats that are supported by the backend. /// 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). /// Can return an error if the device is no longer valid (eg. it has been disconnected).
fn supported_input_formats(&self) fn supported_input_configs(
-> Result<Self::SupportedInputFormats, SupportedFormatsError>; &self,
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError>;
/// An iterator yielding output stream formats that are supported by the device. /// 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). /// 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, &self,
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError>; ) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError>;
/// The default input stream format for the device. /// 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. /// 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. /// Create an input stream.
fn build_input_stream<T, D, E>( fn build_input_stream<T, D, E>(
&self, &self,
shape: &Shape, config: &StreamConfig,
mut data_callback: D, mut data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -125,7 +126,8 @@ pub trait DeviceTrait {
E: FnMut(StreamError) + Send + 'static, E: FnMut(StreamError) + Send + 'static,
{ {
self.build_input_stream_raw( self.build_input_stream_raw(
&Format::with_shape(shape, T::FORMAT), config,
T::FORMAT,
move |data| { move |data| {
data_callback( data_callback(
data.as_slice() data.as_slice()
@ -139,7 +141,7 @@ pub trait DeviceTrait {
/// Create an output stream. /// Create an output stream.
fn build_output_stream<T, D, E>( fn build_output_stream<T, D, E>(
&self, &self,
shape: &Shape, config: &StreamConfig,
mut data_callback: D, mut data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -149,7 +151,8 @@ pub trait DeviceTrait {
E: FnMut(StreamError) + Send + 'static, E: FnMut(StreamError) + Send + 'static,
{ {
self.build_output_stream_raw( self.build_output_stream_raw(
&Format::with_shape(shape, T::FORMAT), config,
T::FORMAT,
move |data| { move |data| {
data_callback( data_callback(
data.as_slice_mut() data.as_slice_mut()
@ -163,7 +166,8 @@ pub trait DeviceTrait {
/// Create a dynamically typed input stream. /// Create a dynamically typed input stream.
fn build_input_stream_raw<D, E>( fn build_input_stream_raw<D, E>(
&self, &self,
format: &Format, config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -174,7 +178,8 @@ pub trait DeviceTrait {
/// Create a dynamically typed output stream. /// Create a dynamically typed output stream.
fn build_output_stream_raw<D, E>( fn build_output_stream_raw<D, E>(
&self, &self,
format: &Format, config: &StreamConfig,
sample_format: SampleFormat,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>