Rename stream `Format` types to `Config`

This implements the changes described at #370.

This commit implements only the `null` and `alsa` backends - the rest
will be implemented in follow-up commits.

Closes #370.
This commit is contained in:
mitchmindtree 2020-01-27 21:28:07 +01:00
parent ae910e3fe5
commit 9c781bd381
10 changed files with 287 additions and 272 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(fmt) = device.default_input_config() {
println!(" Default input stream format:\n {:?}", fmt); println!(" Default input stream config:\n {:?}", fmt);
} }
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(fmt) = device.default_output_config() {
println!(" Default output stream format:\n {:?}", fmt); println!(" Default output stream config:\n {:?}", fmt);
} }
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.")]

View File

@ -2,9 +2,9 @@ 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, StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
}; };
use std::sync::Arc; use std::sync::Arc;
use std::thread::{self, JoinHandle}; use std::thread::{self, JoinHandle};
@ -14,8 +14,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 +52,37 @@ 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: &SupportedStreamConfig,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -90,14 +90,14 @@ 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, 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: &SupportedStreamConfig,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -105,7 +105,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_stream_inner(format, alsa::SND_PCM_STREAM_PLAYBACK)?; let stream_inner = self.build_stream_inner(conf, 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 +161,7 @@ pub struct Device(String);
impl Device { impl Device {
fn build_stream_inner( fn build_stream_inner(
&self, &self,
format: &Format, conf: &SupportedStreamConfig,
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 +185,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)
.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 +213,9 @@ impl Device {
let stream_inner = StreamInner { let stream_inner = StreamInner {
channel: handle, channel: handle,
sample_format: format.data_type, sample_format: conf.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 +235,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 +256,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 +402,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 +420,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 +468,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 +904,7 @@ 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, format: &SupportedStreamConfig,
) -> 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 +917,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 format.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 format.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,7 +934,7 @@ 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));
} }
@ -969,7 +973,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, format: &SupportedStreamConfig,
) -> 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)) {

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, 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,32 @@ 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, _format: &SupportedStreamConfig,
_data_callback: D, _data_callback: D,
_error_callback: E, _error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -77,7 +82,7 @@ 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, _format: &SupportedStreamConfig,
_data_callback: D, _data_callback: D,
_error_callback: E, _error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -129,20 +134,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

@ -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,11 @@
//! # 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 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, data_fn, err_fn).unwrap();
//! stream.play().unwrap(); //! stream.play().unwrap();
//! ``` //! ```
//! //!
@ -128,10 +131,11 @@
//! # 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 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, data_fn, err_fn).unwrap();
//! stream.pause().unwrap(); //! stream.pause().unwrap();
//! ``` //! ```
@ -149,7 +153,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 +176,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 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 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 max_sample_rate: SampleRate,
/// Type of data expected by the device. /// Type of data expected by the device.
pub data_type: SampleFormat, pub 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 {
pub channels: ChannelCount,
pub sample_rate: SampleRate,
pub 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 +218,17 @@ pub struct Data {
sample_format: SampleFormat, sample_format: SampleFormat,
} }
impl SupportedStreamConfig {
/// Construct a `SupportedStreamConfig` from an existing `StreamConfig`.
pub fn from_config(conf: &StreamConfig, fmt: SampleFormat) -> Self {
Self {
channels: conf.channels,
sample_rate: conf.sample_rate,
sample_format: fmt,
}
}
}
impl Data { impl Data {
// Internal constructor for host implementations to use. // Internal constructor for host implementations to use.
// //
@ -328,25 +322,25 @@ impl Data {
} }
} }
impl SupportedFormat { impl SupportedStreamConfigRange {
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate. /// Turns this `SupportedStreamConfigRange` into a `SupportedStreamConfig` corresponding to the maximum samples rate.
#[inline] #[inline]
pub fn with_max_sample_rate(self) -> Format { pub fn with_max_sample_rate(self) -> SupportedStreamConfig {
Format { SupportedStreamConfig {
channels: self.channels, channels: self.channels,
sample_rate: self.max_sample_rate, sample_rate: self.max_sample_rate,
data_type: self.data_type, sample_format: self.sample_format,
} }
} }
/// A comparison function which compares two `SupportedFormat`s in terms of their priority of /// 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 +376,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 +404,25 @@ impl SupportedFormat {
} }
} }
impl From<Format> for SupportedFormat { impl From<SupportedStreamConfig> for StreamConfig {
fn from(conf: SupportedStreamConfig) -> Self {
let channels = conf.channels;
let sample_rate = conf.sample_rate;
StreamConfig {
channels,
sample_rate,
}
}
}
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,49 @@ 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, format: &crate::SupportedStreamConfig,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, crate::BuildStreamError> ) -> Result<Self::Stream, crate::BuildStreamError>
@ -276,7 +276,7 @@ macro_rules! impl_platform_host {
fn build_output_stream_raw<D, E>( fn build_output_stream_raw<D, E>(
&self, &self,
format: &crate::Format, format: &crate::SupportedStreamConfig,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, crate::BuildStreamError> ) -> Result<Self::Stream, crate::BuildStreamError>
@ -436,8 +436,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 +454,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 +472,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 +491,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 +526,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, StreamConfig, StreamError,
SupportedFormat, SupportedFormatsError, 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,7 @@ 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), &SupportedStreamConfig::from_config(config, T::FORMAT),
move |data| { move |data| {
data_callback( data_callback(
data.as_slice() data.as_slice()
@ -139,7 +140,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 +150,7 @@ 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), &SupportedStreamConfig::from_config(config, T::FORMAT),
move |data| { move |data| {
data_callback( data_callback(
data.as_slice_mut() data.as_slice_mut()
@ -163,7 +164,7 @@ 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, format: &SupportedStreamConfig,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>
@ -174,7 +175,7 @@ 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, format: &SupportedStreamConfig,
data_callback: D, data_callback: D,
error_callback: E, error_callback: E,
) -> Result<Self::Stream, BuildStreamError> ) -> Result<Self::Stream, BuildStreamError>