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:
parent
ae910e3fe5
commit
9c781bd381
|
@ -8,23 +8,23 @@ fn main() -> Result<(), anyhow::Error> {
|
|||
let device = host
|
||||
.default_output_device()
|
||||
.expect("failed to find a default output device");
|
||||
let format = device.default_output_format()?;
|
||||
let config = device.default_output_config()?;
|
||||
|
||||
match format.data_type {
|
||||
cpal::SampleFormat::F32 => run::<f32>(&device, &format.shape())?,
|
||||
cpal::SampleFormat::I16 => run::<i16>(&device, &format.shape())?,
|
||||
cpal::SampleFormat::U16 => run::<u16>(&device, &format.shape())?,
|
||||
match config.sample_format {
|
||||
cpal::SampleFormat::F32 => run::<f32>(&device, &config.into())?,
|
||||
cpal::SampleFormat::I16 => run::<i16>(&device, &config.into())?,
|
||||
cpal::SampleFormat::U16 => run::<u16>(&device, &config.into())?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run<T>(device: &cpal::Device, shape: &cpal::Shape) -> Result<(), anyhow::Error>
|
||||
fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error>
|
||||
where
|
||||
T: cpal::Sample,
|
||||
{
|
||||
let sample_rate = shape.sample_rate.0 as f32;
|
||||
let channels = shape.channels as usize;
|
||||
let sample_rate = config.sample_rate.0 as f32;
|
||||
let channels = config.channels as usize;
|
||||
|
||||
// Produce a sinusoid of maximum amplitude.
|
||||
let mut sample_clock = 0f32;
|
||||
|
@ -36,7 +36,7 @@ where
|
|||
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
|
||||
|
||||
let stream = device.build_output_stream(
|
||||
shape,
|
||||
config,
|
||||
move |data: &mut [T]| write_data(data, channels, &mut next_value),
|
||||
err_fn,
|
||||
)?;
|
||||
|
|
|
@ -21,48 +21,48 @@ fn main() -> Result<(), anyhow::Error> {
|
|||
for (device_index, device) in devices.enumerate() {
|
||||
println!(" {}. \"{}\"", device_index + 1, device.name()?);
|
||||
|
||||
// Input formats
|
||||
if let Ok(fmt) = device.default_input_format() {
|
||||
println!(" Default input stream format:\n {:?}", fmt);
|
||||
// Input configs
|
||||
if let Ok(fmt) = device.default_input_config() {
|
||||
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(),
|
||||
Err(e) => {
|
||||
println!("Error: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if input_formats.peek().is_some() {
|
||||
println!(" All supported input stream formats:");
|
||||
for (format_index, format) in input_formats.enumerate() {
|
||||
if input_configs.peek().is_some() {
|
||||
println!(" All supported input stream configs:");
|
||||
for (config_index, config) in input_configs.enumerate() {
|
||||
println!(
|
||||
" {}.{}. {:?}",
|
||||
device_index + 1,
|
||||
format_index + 1,
|
||||
format
|
||||
config_index + 1,
|
||||
config
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Output formats
|
||||
if let Ok(fmt) = device.default_output_format() {
|
||||
println!(" Default output stream format:\n {:?}", fmt);
|
||||
// Output configs
|
||||
if let Ok(fmt) = device.default_output_config() {
|
||||
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(),
|
||||
Err(e) => {
|
||||
println!("Error: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if output_formats.peek().is_some() {
|
||||
println!(" All supported output stream formats:");
|
||||
for (format_index, format) in output_formats.enumerate() {
|
||||
if output_configs.peek().is_some() {
|
||||
println!(" All supported output stream configs:");
|
||||
for (config_index, config) in output_configs.enumerate() {
|
||||
println!(
|
||||
" {}.{}. {:?}",
|
||||
device_index + 1,
|
||||
format_index + 1,
|
||||
format
|
||||
config_index + 1,
|
||||
config
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Feeds back the input stream directly into the output stream.
|
||||
//!
|
||||
//! Assumes that the input and output devices can use the same stream format and that they support
|
||||
//! the f32 sample format.
|
||||
//! Assumes that the input and output devices can use the same stream configuration and that they
|
||||
//! support the f32 sample format.
|
||||
//!
|
||||
//! Uses a delay of `LATENCY_MS` milliseconds in case the default input and output streams are not
|
||||
//! precisely synchronised.
|
||||
|
@ -28,12 +28,12 @@ fn main() -> Result<(), anyhow::Error> {
|
|||
println!("Using default input device: \"{}\"", input_device.name()?);
|
||||
println!("Using default output device: \"{}\"", output_device.name()?);
|
||||
|
||||
// We'll try and use the same format between streams to keep it simple
|
||||
let shape = input_device.default_input_format()?.shape();
|
||||
// We'll try and use the same configuration between streams to keep it simple.
|
||||
let config: cpal::StreamConfig = input_device.default_input_config()?.into();
|
||||
|
||||
// Create a delay in case the input and output devices aren't synced.
|
||||
let latency_frames = (LATENCY_MS / 1_000.0) * shape.sample_rate.0 as f32;
|
||||
let latency_samples = latency_frames as usize * shape.channels as usize;
|
||||
let latency_frames = (LATENCY_MS / 1_000.0) * config.sample_rate.0 as f32;
|
||||
let latency_samples = latency_frames as usize * config.channels as usize;
|
||||
|
||||
// The buffer to share samples
|
||||
let ring = RingBuffer::new(latency_samples * 2);
|
||||
|
@ -80,10 +80,10 @@ fn main() -> Result<(), anyhow::Error> {
|
|||
// Build streams.
|
||||
println!(
|
||||
"Attempting to build both streams with f32 samples and `{:?}`.",
|
||||
shape
|
||||
config
|
||||
);
|
||||
let input_stream = input_device.build_input_stream(&shape, input_data_fn, err_fn)?;
|
||||
let output_stream = output_device.build_output_stream(&shape, output_data_fn, err_fn)?;
|
||||
let input_stream = input_device.build_input_stream(&config, input_data_fn, err_fn)?;
|
||||
let output_stream = output_device.build_output_stream(&config, output_data_fn, err_fn)?;
|
||||
println!("Successfully built streams.");
|
||||
|
||||
// Play the streams.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Records a WAV file (roughly 3 seconds long) using the default input device and format.
|
||||
//! Records a WAV file (roughly 3 seconds long) using the default input device and config.
|
||||
//!
|
||||
//! The input data is recorded to "$CARGO_MANIFEST_DIR/recorded.wav".
|
||||
|
||||
|
@ -15,18 +15,18 @@ fn main() -> Result<(), anyhow::Error> {
|
|||
// Use the default host for working with audio devices.
|
||||
let host = cpal::default_host();
|
||||
|
||||
// Setup the default input device and stream with the default input format.
|
||||
// Setup the default input device and stream with the default input config.
|
||||
let device = host
|
||||
.default_input_device()
|
||||
.expect("Failed to get default input device");
|
||||
println!("Default input device: {}", device.name()?);
|
||||
let format = device
|
||||
.default_input_format()
|
||||
.expect("Failed to get default input format");
|
||||
println!("Default input format: {:?}", format);
|
||||
let config = device
|
||||
.default_input_config()
|
||||
.expect("Failed to get default input config");
|
||||
println!("Default input config: {:?}", config);
|
||||
// The WAV file we're recording to.
|
||||
const PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/recorded.wav");
|
||||
let spec = wav_spec_from_format(&format);
|
||||
let spec = wav_spec_from_config(&config);
|
||||
let writer = hound::WavWriter::create(PATH, spec)?;
|
||||
let writer = Arc::new(Mutex::new(Some(writer)));
|
||||
|
||||
|
@ -40,19 +40,19 @@ fn main() -> Result<(), anyhow::Error> {
|
|||
eprintln!("an error occurred on stream: {}", err);
|
||||
};
|
||||
|
||||
let stream = match format.data_type {
|
||||
let stream = match config.sample_format {
|
||||
cpal::SampleFormat::F32 => device.build_input_stream(
|
||||
&format.shape(),
|
||||
&config.into(),
|
||||
move |data| write_input_data::<f32, f32>(data, &writer_2),
|
||||
err_fn,
|
||||
)?,
|
||||
cpal::SampleFormat::I16 => device.build_input_stream(
|
||||
&format.shape(),
|
||||
&config.into(),
|
||||
move |data| write_input_data::<i16, i16>(data, &writer_2),
|
||||
err_fn,
|
||||
)?,
|
||||
cpal::SampleFormat::U16 => device.build_input_stream(
|
||||
&format.shape(),
|
||||
&config.into(),
|
||||
move |data| write_input_data::<u16, i16>(data, &writer_2),
|
||||
err_fn,
|
||||
)?,
|
||||
|
@ -76,12 +76,12 @@ fn sample_format(format: cpal::SampleFormat) -> hound::SampleFormat {
|
|||
}
|
||||
}
|
||||
|
||||
fn wav_spec_from_format(format: &cpal::Format) -> hound::WavSpec {
|
||||
fn wav_spec_from_config(config: &cpal::SupportedStreamConfig) -> hound::WavSpec {
|
||||
hound::WavSpec {
|
||||
channels: format.channels as _,
|
||||
sample_rate: format.sample_rate.0 as _,
|
||||
bits_per_sample: (format.data_type.sample_size() * 8) as _,
|
||||
sample_format: sample_format(format.data_type),
|
||||
channels: config.channels as _,
|
||||
sample_rate: config.sample_rate.0 as _,
|
||||
bits_per_sample: (config.sample_format.sample_size() * 8) as _,
|
||||
sample_format: sample_format(config.sample_format),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ pub enum DeviceNameError {
|
|||
|
||||
/// Error that can happen when enumerating the list of supported formats.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SupportedFormatsError {
|
||||
pub enum SupportedStreamConfigsError {
|
||||
/// The device no longer exists. This can happen if the device is disconnected while the
|
||||
/// program is running.
|
||||
#[error("The requested device is no longer available. For example, it has been unplugged.")]
|
||||
|
@ -67,7 +67,7 @@ pub enum SupportedFormatsError {
|
|||
|
||||
/// May occur when attempting to request the default input or output stream format from a `Device`.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DefaultFormatError {
|
||||
pub enum DefaultStreamConfigError {
|
||||
/// The device no longer exists. This can happen if the device is disconnected while the
|
||||
/// program is running.
|
||||
#[error("The requested device is no longer available. For example, it has been unplugged.")]
|
||||
|
|
|
@ -2,9 +2,9 @@ extern crate alsa_sys as alsa;
|
|||
extern crate libc;
|
||||
|
||||
use crate::{
|
||||
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultFormatError,
|
||||
DeviceNameError, DevicesError, Format, PauseStreamError, PlayStreamError, SampleFormat,
|
||||
SampleRate, StreamError, SupportedFormat, SupportedFormatsError,
|
||||
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError,
|
||||
DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate,
|
||||
StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
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 type SupportedInputFormats = VecIntoIter<SupportedFormat>;
|
||||
pub type SupportedOutputFormats = VecIntoIter<SupportedFormat>;
|
||||
pub type SupportedInputConfigs = VecIntoIter<SupportedStreamConfigRange>;
|
||||
pub type SupportedOutputConfigs = VecIntoIter<SupportedStreamConfigRange>;
|
||||
|
||||
mod enumerate;
|
||||
|
||||
|
@ -52,37 +52,37 @@ impl HostTrait for Host {
|
|||
}
|
||||
|
||||
impl DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
type SupportedInputConfigs = SupportedInputConfigs;
|
||||
type SupportedOutputConfigs = SupportedOutputConfigs;
|
||||
type Stream = Stream;
|
||||
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
Device::name(self)
|
||||
}
|
||||
|
||||
fn supported_input_formats(
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||
Device::supported_input_formats(self)
|
||||
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_input_configs(self)
|
||||
}
|
||||
|
||||
fn supported_output_formats(
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||
Device::supported_output_formats(self)
|
||||
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_output_configs(self)
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_input_format(self)
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_input_config(self)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_output_format(self)
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_output_config(self)
|
||||
}
|
||||
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
conf: &SupportedStreamConfig,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -90,14 +90,14 @@ impl DeviceTrait for Device {
|
|||
D: FnMut(&Data) + Send + 'static,
|
||||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_CAPTURE)?;
|
||||
let stream_inner = self.build_stream_inner(conf, alsa::SND_PCM_STREAM_CAPTURE)?;
|
||||
let stream = Stream::new_input(Arc::new(stream_inner), data_callback, error_callback);
|
||||
Ok(stream)
|
||||
}
|
||||
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
conf: &SupportedStreamConfig,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -105,7 +105,7 @@ impl DeviceTrait for Device {
|
|||
D: FnMut(&mut Data) + Send + 'static,
|
||||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_PLAYBACK)?;
|
||||
let stream_inner = self.build_stream_inner(conf, alsa::SND_PCM_STREAM_PLAYBACK)?;
|
||||
let stream = Stream::new_output(Arc::new(stream_inner), data_callback, error_callback);
|
||||
Ok(stream)
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ pub struct Device(String);
|
|||
impl Device {
|
||||
fn build_stream_inner(
|
||||
&self,
|
||||
format: &Format,
|
||||
conf: &SupportedStreamConfig,
|
||||
stream_type: alsa::snd_pcm_stream_t,
|
||||
) -> Result<StreamInner, BuildStreamError> {
|
||||
let name = ffi::CString::new(self.0.clone()).expect("unable to clone device");
|
||||
|
@ -185,13 +185,13 @@ impl Device {
|
|||
};
|
||||
let can_pause = unsafe {
|
||||
let hw_params = HwParams::alloc();
|
||||
set_hw_params_from_format(handle, &hw_params, format)
|
||||
set_hw_params_from_format(handle, &hw_params, conf)
|
||||
.map_err(|description| BackendSpecificError { description })?;
|
||||
|
||||
alsa::snd_pcm_hw_params_can_pause(hw_params.0) == 1
|
||||
};
|
||||
let (buffer_len, period_len) = unsafe {
|
||||
set_sw_params_from_format(handle, format)
|
||||
set_sw_params_from_format(handle, conf)
|
||||
.map_err(|description| BackendSpecificError { description })?
|
||||
};
|
||||
|
||||
|
@ -213,9 +213,9 @@ impl Device {
|
|||
|
||||
let stream_inner = StreamInner {
|
||||
channel: handle,
|
||||
sample_format: format.data_type,
|
||||
sample_format: conf.sample_format,
|
||||
num_descriptors,
|
||||
num_channels: format.channels as u16,
|
||||
num_channels: conf.channels as u16,
|
||||
buffer_len,
|
||||
period_len,
|
||||
can_pause,
|
||||
|
@ -235,10 +235,10 @@ impl Device {
|
|||
Ok(self.0.clone())
|
||||
}
|
||||
|
||||
unsafe fn supported_formats(
|
||||
unsafe fn supported_configs(
|
||||
&self,
|
||||
stream_t: alsa::snd_pcm_stream_t,
|
||||
) -> Result<VecIntoIter<SupportedFormat>, SupportedFormatsError> {
|
||||
) -> Result<VecIntoIter<SupportedStreamConfigRange>, SupportedStreamConfigsError> {
|
||||
let mut handle = ptr::null_mut();
|
||||
let device_name = match ffi::CString::new(&self.0[..]) {
|
||||
Ok(name) => name,
|
||||
|
@ -256,8 +256,8 @@ impl Device {
|
|||
alsa::SND_PCM_NONBLOCK,
|
||||
) {
|
||||
-2 |
|
||||
-16 /* determined empirically */ => return Err(SupportedFormatsError::DeviceNotAvailable),
|
||||
-22 => return Err(SupportedFormatsError::InvalidArgument),
|
||||
-16 /* determined empirically */ => return Err(SupportedStreamConfigsError::DeviceNotAvailable),
|
||||
-22 => return Err(SupportedStreamConfigsError::InvalidArgument),
|
||||
e => if let Err(description) = check_errors(e) {
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into())
|
||||
|
@ -402,14 +402,14 @@ impl Device {
|
|||
let mut output = Vec::with_capacity(
|
||||
supported_formats.len() * supported_channels.len() * sample_rates.len(),
|
||||
);
|
||||
for &data_type in supported_formats.iter() {
|
||||
for &sample_format in supported_formats.iter() {
|
||||
for channels in supported_channels.iter() {
|
||||
for &(min_rate, max_rate) in sample_rates.iter() {
|
||||
output.push(SupportedFormat {
|
||||
output.push(SupportedStreamConfigRange {
|
||||
channels: channels.clone(),
|
||||
min_sample_rate: SampleRate(min_rate as u32),
|
||||
max_sample_rate: SampleRate(max_rate as u32),
|
||||
data_type: data_type,
|
||||
sample_format: sample_format,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -420,31 +420,35 @@ impl Device {
|
|||
Ok(output.into_iter())
|
||||
}
|
||||
|
||||
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
unsafe { self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE) }
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
unsafe { self.supported_configs(alsa::SND_PCM_STREAM_CAPTURE) }
|
||||
}
|
||||
|
||||
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
unsafe { self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK) }
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
unsafe { self.supported_configs(alsa::SND_PCM_STREAM_PLAYBACK) }
|
||||
}
|
||||
|
||||
// ALSA does not offer default stream formats, so instead we compare all supported formats by
|
||||
// the `SupportedFormat::cmp_default_heuristics` order and select the greatest.
|
||||
fn default_format(
|
||||
// the `SupportedStreamConfigRange::cmp_default_heuristics` order and select the greatest.
|
||||
fn default_config(
|
||||
&self,
|
||||
stream_t: alsa::snd_pcm_stream_t,
|
||||
) -> Result<Format, DefaultFormatError> {
|
||||
) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
let mut formats: Vec<_> = unsafe {
|
||||
match self.supported_formats(stream_t) {
|
||||
Err(SupportedFormatsError::DeviceNotAvailable) => {
|
||||
return Err(DefaultFormatError::DeviceNotAvailable);
|
||||
match self.supported_configs(stream_t) {
|
||||
Err(SupportedStreamConfigsError::DeviceNotAvailable) => {
|
||||
return Err(DefaultStreamConfigError::DeviceNotAvailable);
|
||||
}
|
||||
Err(SupportedFormatsError::InvalidArgument) => {
|
||||
Err(SupportedStreamConfigsError::InvalidArgument) => {
|
||||
// this happens sometimes when querying for input and output capabilities but
|
||||
// the device supports only one
|
||||
return Err(DefaultFormatError::StreamTypeNotSupported);
|
||||
return Err(DefaultStreamConfigError::StreamTypeNotSupported);
|
||||
}
|
||||
Err(SupportedFormatsError::BackendSpecific { err }) => {
|
||||
Err(SupportedStreamConfigsError::BackendSpecific { err }) => {
|
||||
return Err(err.into());
|
||||
}
|
||||
Ok(fmts) => fmts.collect(),
|
||||
|
@ -464,16 +468,16 @@ impl Device {
|
|||
}
|
||||
Ok(format)
|
||||
}
|
||||
None => Err(DefaultFormatError::StreamTypeNotSupported),
|
||||
None => Err(DefaultStreamConfigError::StreamTypeNotSupported),
|
||||
}
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.default_format(alsa::SND_PCM_STREAM_CAPTURE)
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
self.default_config(alsa::SND_PCM_STREAM_CAPTURE)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.default_format(alsa::SND_PCM_STREAM_PLAYBACK)
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
self.default_config(alsa::SND_PCM_STREAM_PLAYBACK)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -900,7 +904,7 @@ fn get_available_samples(stream: &StreamInner) -> Result<usize, BackendSpecificE
|
|||
unsafe fn set_hw_params_from_format(
|
||||
pcm_handle: *mut alsa::snd_pcm_t,
|
||||
hw_params: &HwParams,
|
||||
format: &Format,
|
||||
format: &SupportedStreamConfig,
|
||||
) -> Result<(), String> {
|
||||
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_any(pcm_handle, hw_params.0)) {
|
||||
return Err(format!("errors on pcm handle: {}", e));
|
||||
|
@ -913,14 +917,14 @@ unsafe fn set_hw_params_from_format(
|
|||
return Err(format!("handle not acessible: {}", e));
|
||||
}
|
||||
|
||||
let data_type = if cfg!(target_endian = "big") {
|
||||
match format.data_type {
|
||||
let sample_format = if cfg!(target_endian = "big") {
|
||||
match format.sample_format {
|
||||
SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_BE,
|
||||
SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_BE,
|
||||
SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_BE,
|
||||
}
|
||||
} else {
|
||||
match format.data_type {
|
||||
match format.sample_format {
|
||||
SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_LE,
|
||||
SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_LE,
|
||||
SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_LE,
|
||||
|
@ -930,7 +934,7 @@ unsafe fn set_hw_params_from_format(
|
|||
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_format(
|
||||
pcm_handle,
|
||||
hw_params.0,
|
||||
data_type,
|
||||
sample_format,
|
||||
)) {
|
||||
return Err(format!("format could not be set: {}", e));
|
||||
}
|
||||
|
@ -969,7 +973,7 @@ unsafe fn set_hw_params_from_format(
|
|||
|
||||
unsafe fn set_sw_params_from_format(
|
||||
pcm_handle: *mut alsa::snd_pcm_t,
|
||||
format: &Format,
|
||||
format: &SupportedStreamConfig,
|
||||
) -> Result<(usize, usize), String> {
|
||||
let mut sw_params = ptr::null_mut(); // TODO: RAII
|
||||
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_malloc(&mut sw_params)) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||
PauseStreamError, PlayStreamError, StreamError, SupportedFormat, SupportedFormatsError,
|
||||
BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError,
|
||||
PauseStreamError, PlayStreamError, StreamError, SupportedStreamConfig,
|
||||
SupportedStreamConfigRange, SupportedStreamConfigsError,
|
||||
};
|
||||
use traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
|
||||
|
@ -15,8 +16,8 @@ pub struct Host;
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Stream;
|
||||
|
||||
pub struct SupportedInputFormats;
|
||||
pub struct SupportedOutputFormats;
|
||||
pub struct SupportedInputConfigs;
|
||||
pub struct SupportedOutputConfigs;
|
||||
|
||||
impl Host {
|
||||
#[allow(dead_code)]
|
||||
|
@ -32,8 +33,8 @@ impl Devices {
|
|||
}
|
||||
|
||||
impl DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
type SupportedInputConfigs = SupportedInputConfigs;
|
||||
type SupportedOutputConfigs = SupportedOutputConfigs;
|
||||
type Stream = Stream;
|
||||
|
||||
#[inline]
|
||||
|
@ -42,28 +43,32 @@ impl DeviceTrait for Device {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
_format: &Format,
|
||||
_format: &SupportedStreamConfig,
|
||||
_data_callback: D,
|
||||
_error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -77,7 +82,7 @@ impl DeviceTrait for Device {
|
|||
/// Create an output stream.
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
_format: &Format,
|
||||
_format: &SupportedStreamConfig,
|
||||
_data_callback: D,
|
||||
_error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -129,20 +134,20 @@ impl Iterator for Devices {
|
|||
}
|
||||
}
|
||||
|
||||
impl Iterator for SupportedInputFormats {
|
||||
type Item = SupportedFormat;
|
||||
impl Iterator for SupportedInputConfigs {
|
||||
type Item = SupportedStreamConfigRange;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<SupportedFormat> {
|
||||
fn next(&mut self) -> Option<SupportedStreamConfigRange> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SupportedOutputFormats {
|
||||
type Item = SupportedFormat;
|
||||
impl Iterator for SupportedOutputConfigs {
|
||||
type Item = SupportedStreamConfigRange;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<SupportedFormat> {
|
||||
fn next(&mut self) -> Option<SupportedStreamConfigRange> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
161
src/lib.rs
161
src/lib.rs
|
@ -31,24 +31,25 @@
|
|||
//! let device = host.default_output_device().expect("no output device available");
|
||||
//! ```
|
||||
//!
|
||||
//! Before we can create a stream, we must decide what the format of the audio samples is going to
|
||||
//! be. You can query all the supported formats with the `supported_input_formats()` and
|
||||
//! `supported_output_formats()` methods. These produce a list of `SupportedFormat` structs which
|
||||
//! can later be turned into actual `Format` structs. If you don't want to query the list of
|
||||
//! formats, you can also build your own `Format` manually, but doing so could lead to an error
|
||||
//! when building the stream if the format is not supported by the device.
|
||||
//! Before we can create a stream, we must decide what the configuration of the audio stream is
|
||||
//! going to be. You can query all the supported configurations with the
|
||||
//! `supported_input_configs()` and `supported_output_configs()` methods. These produce a list of
|
||||
//! `SupportedStreamConfigRange` structs which can later be turned into actual
|
||||
//! `SupportedStreamConfig` structs. If you don't want to query the list of configs, you can also
|
||||
//! build your own `StreamConfig` manually, but doing so could lead to an error when building the
|
||||
//! stream if the config is not supported by the device.
|
||||
//!
|
||||
//! > **Note**: the `supported_formats()` method could return an error for example if the device
|
||||
//! > has been disconnected.
|
||||
//! > **Note**: the `supported_input/output_configs()` methods could return an error for example if
|
||||
//! > the device has been disconnected.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use cpal::traits::{DeviceTrait, HostTrait};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let device = host.default_output_device().unwrap();
|
||||
//! let mut supported_formats_range = device.supported_output_formats()
|
||||
//! .expect("error while querying formats");
|
||||
//! let format = supported_formats_range.next()
|
||||
//! .expect("no supported format?!")
|
||||
//! let mut supported_configs_range = device.supported_output_configs()
|
||||
//! .expect("error while querying configs");
|
||||
//! let supported_config = supported_configs_range.next()
|
||||
//! .expect("no supported config?!")
|
||||
//! .with_max_sample_rate();
|
||||
//! ```
|
||||
//!
|
||||
|
@ -59,9 +60,9 @@
|
|||
//! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let device = host.default_output_device().unwrap();
|
||||
//! # let shape = device.default_output_format().unwrap().shape();
|
||||
//! # let config = device.default_output_config().unwrap().into();
|
||||
//! let stream = device.build_output_stream(
|
||||
//! &shape,
|
||||
//! &config,
|
||||
//! move |data: &mut [f32]| {
|
||||
//! // react to stream events and read or write stream data here.
|
||||
//! },
|
||||
|
@ -91,13 +92,14 @@
|
|||
//! use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let device = host.default_output_device().unwrap();
|
||||
//! # let format = device.default_output_format().unwrap();
|
||||
//! # let supported_config = device.default_output_config().unwrap();
|
||||
//! let err_fn = |err| eprintln!("an error occurred on the output audio stream: {}", err);
|
||||
//! let shape = format.shape();
|
||||
//! let stream = match format.data_type {
|
||||
//! SampleFormat::F32 => device.build_output_stream(&shape, write_silence::<f32>, err_fn),
|
||||
//! SampleFormat::I16 => device.build_output_stream(&shape, write_silence::<i16>, err_fn),
|
||||
//! SampleFormat::U16 => device.build_output_stream(&shape, write_silence::<u16>, err_fn),
|
||||
//! let sample_format = supported_config.sample_format;
|
||||
//! let config = supported_config.into();
|
||||
//! let stream = match sample_format {
|
||||
//! SampleFormat::F32 => device.build_output_stream(&config, write_silence::<f32>, err_fn),
|
||||
//! SampleFormat::I16 => device.build_output_stream(&config, write_silence::<i16>, err_fn),
|
||||
//! SampleFormat::U16 => device.build_output_stream(&config, write_silence::<u16>, err_fn),
|
||||
//! }.unwrap();
|
||||
//!
|
||||
//! fn write_silence<T: Sample>(data: &mut [T]) {
|
||||
|
@ -114,10 +116,11 @@
|
|||
//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let device = host.default_output_device().unwrap();
|
||||
//! # let format = device.default_output_format().unwrap();
|
||||
//! # let supported_config = device.default_output_config().unwrap();
|
||||
//! # let config = supported_config.into();
|
||||
//! # let data_fn = move |_data: &mut cpal::Data| {};
|
||||
//! # let err_fn = move |_err| {};
|
||||
//! # let stream = device.build_output_stream_raw(&format, data_fn, err_fn).unwrap();
|
||||
//! # let stream = device.build_output_stream_raw(&config, data_fn, err_fn).unwrap();
|
||||
//! stream.play().unwrap();
|
||||
//! ```
|
||||
//!
|
||||
|
@ -128,10 +131,11 @@
|
|||
//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let device = host.default_output_device().unwrap();
|
||||
//! # let format = device.default_output_format().unwrap();
|
||||
//! # let supported_config = device.default_output_config().unwrap();
|
||||
//! # let config = supported_config.into();
|
||||
//! # let data_fn = move |_data: &mut cpal::Data| {};
|
||||
//! # let err_fn = move |_err| {};
|
||||
//! # let stream = device.build_output_stream_raw(&format, data_fn, err_fn).unwrap();
|
||||
//! # let stream = device.build_output_stream_raw(&config, data_fn, err_fn).unwrap();
|
||||
//! stream.pause().unwrap();
|
||||
//! ```
|
||||
|
||||
|
@ -149,7 +153,7 @@ extern crate thiserror;
|
|||
pub use error::*;
|
||||
pub use platform::{
|
||||
available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream,
|
||||
SupportedInputFormats, SupportedOutputFormats, ALL_HOSTS,
|
||||
SupportedInputConfigs, SupportedOutputConfigs, ALL_HOSTS,
|
||||
};
|
||||
pub use samples_formats::{Sample, SampleFormat};
|
||||
|
||||
|
@ -172,56 +176,35 @@ pub type ChannelCount = u16;
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct SampleRate(pub u32);
|
||||
|
||||
/// The format of an input or output audio stream.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Format {
|
||||
pub channels: ChannelCount,
|
||||
pub sample_rate: SampleRate,
|
||||
pub data_type: SampleFormat,
|
||||
}
|
||||
|
||||
impl Format {
|
||||
/// Construct a format having a particular `shape` and `data_type`.
|
||||
pub fn with_shape(shape: &Shape, data_type: SampleFormat) -> Self {
|
||||
Self {
|
||||
channels: shape.channels,
|
||||
sample_rate: shape.sample_rate,
|
||||
data_type,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract aspects of the format independent of the data type.
|
||||
pub fn shape(&self) -> Shape {
|
||||
self.clone().into()
|
||||
}
|
||||
}
|
||||
|
||||
/// The properties of an input or output audio stream excluding its data type.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Shape {
|
||||
/// The set of parameters used to describe how to open a stream.
|
||||
///
|
||||
/// The sample format is omitted in favour of using a sample type.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct StreamConfig {
|
||||
pub channels: ChannelCount,
|
||||
pub sample_rate: SampleRate,
|
||||
}
|
||||
|
||||
impl From<Format> for Shape {
|
||||
fn from(x: Format) -> Self {
|
||||
Self {
|
||||
channels: x.channels,
|
||||
sample_rate: x.sample_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a range of supported stream formats.
|
||||
/// Describes a range of supported stream configurations, retrieved via the
|
||||
/// `Device::supported_input/output_configs` method.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SupportedFormat {
|
||||
pub struct SupportedStreamConfigRange {
|
||||
pub channels: ChannelCount,
|
||||
/// Minimum value for the samples rate of the supported formats.
|
||||
pub min_sample_rate: SampleRate,
|
||||
/// Maximum value for the samples rate of the supported formats.
|
||||
pub max_sample_rate: SampleRate,
|
||||
/// 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.
|
||||
|
@ -235,6 +218,17 @@ pub struct Data {
|
|||
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 {
|
||||
// Internal constructor for host implementations to use.
|
||||
//
|
||||
|
@ -328,25 +322,25 @@ impl Data {
|
|||
}
|
||||
}
|
||||
|
||||
impl SupportedFormat {
|
||||
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate.
|
||||
impl SupportedStreamConfigRange {
|
||||
/// Turns this `SupportedStreamConfigRange` into a `SupportedStreamConfig` corresponding to the maximum samples rate.
|
||||
#[inline]
|
||||
pub fn with_max_sample_rate(self) -> Format {
|
||||
Format {
|
||||
pub fn with_max_sample_rate(self) -> SupportedStreamConfig {
|
||||
SupportedStreamConfig {
|
||||
channels: self.channels,
|
||||
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.
|
||||
///
|
||||
/// Some backends do not provide a default stream format for their audio devices. In these
|
||||
/// cases, CPAL attempts to decide on a reasonable default format for the user. To do this we
|
||||
/// use the "greatest" of all supported stream formats when compared with this method.
|
||||
///
|
||||
/// Formats are prioritised by the following heuristics:
|
||||
/// SupportedStreamConfigs are prioritised by the following heuristics:
|
||||
///
|
||||
/// **Channels**:
|
||||
///
|
||||
|
@ -382,17 +376,17 @@ impl SupportedFormat {
|
|||
return cmp_channels;
|
||||
}
|
||||
|
||||
let cmp_f32 = (self.data_type == F32).cmp(&(other.data_type == F32));
|
||||
let cmp_f32 = (self.sample_format == F32).cmp(&(other.sample_format == F32));
|
||||
if cmp_f32 != Equal {
|
||||
return cmp_f32;
|
||||
}
|
||||
|
||||
let cmp_i16 = (self.data_type == I16).cmp(&(other.data_type == I16));
|
||||
let cmp_i16 = (self.sample_format == I16).cmp(&(other.sample_format == I16));
|
||||
if cmp_i16 != Equal {
|
||||
return cmp_i16;
|
||||
}
|
||||
|
||||
let cmp_u16 = (self.data_type == U16).cmp(&(other.data_type == U16));
|
||||
let cmp_u16 = (self.sample_format == U16).cmp(&(other.sample_format == U16));
|
||||
if cmp_u16 != Equal {
|
||||
return cmp_u16;
|
||||
}
|
||||
|
@ -410,14 +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]
|
||||
fn from(format: Format) -> SupportedFormat {
|
||||
SupportedFormat {
|
||||
fn from(format: SupportedStreamConfig) -> SupportedStreamConfigRange {
|
||||
SupportedStreamConfigRange {
|
||||
channels: format.channels,
|
||||
min_sample_rate: format.sample_rate,
|
||||
max_sample_rate: format.sample_rate,
|
||||
data_type: format.data_type,
|
||||
sample_format: format.sample_format,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ pub use self::platform_impl::*;
|
|||
// }
|
||||
// ```
|
||||
//
|
||||
// And so on for Device, Devices, EventLoop, Host, StreamId, SupportedInputFormats,
|
||||
// SupportedOutputFormats and all their necessary trait implementations.
|
||||
// And so on for Device, Devices, EventLoop, Host, StreamId, SupportedInputConfigs,
|
||||
// SupportedOutputConfigs and all their necessary trait implementations.
|
||||
// ```
|
||||
macro_rules! impl_platform_host {
|
||||
($($HostVariant:ident $host_mod:ident $host_name:literal),*) => {
|
||||
|
@ -67,13 +67,13 @@ macro_rules! impl_platform_host {
|
|||
// TODO: Confirm this and add more specific detail and references.
|
||||
pub struct Stream(StreamInner, crate::platform::NotSendSyncAcrossAllPlatforms);
|
||||
|
||||
/// The **SupportedInputFormats** iterator associated with the platform's dynamically
|
||||
/// The **SupportedInputConfigs** iterator associated with the platform's dynamically
|
||||
/// dispatched **Host** type.
|
||||
pub struct SupportedInputFormats(SupportedInputFormatsInner);
|
||||
pub struct SupportedInputConfigs(SupportedInputConfigsInner);
|
||||
|
||||
/// The **SupportedOutputFormats** iterator associated with the platform's dynamically
|
||||
/// The **SupportedOutputConfigs** iterator associated with the platform's dynamically
|
||||
/// dispatched **Host** type.
|
||||
pub struct SupportedOutputFormats(SupportedOutputFormatsInner);
|
||||
pub struct SupportedOutputConfigs(SupportedOutputConfigsInner);
|
||||
|
||||
/// Unique identifier for available hosts on the platform.
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
|
@ -107,15 +107,15 @@ macro_rules! impl_platform_host {
|
|||
)*
|
||||
}
|
||||
|
||||
enum SupportedInputFormatsInner {
|
||||
enum SupportedInputConfigsInner {
|
||||
$(
|
||||
$HostVariant(crate::host::$host_mod::SupportedInputFormats),
|
||||
$HostVariant(crate::host::$host_mod::SupportedInputConfigs),
|
||||
)*
|
||||
}
|
||||
|
||||
enum SupportedOutputFormatsInner {
|
||||
enum SupportedOutputConfigsInner {
|
||||
$(
|
||||
$HostVariant(crate::host::$host_mod::SupportedOutputFormats),
|
||||
$HostVariant(crate::host::$host_mod::SupportedOutputConfigs),
|
||||
)*
|
||||
}
|
||||
|
||||
|
@ -162,13 +162,13 @@ macro_rules! impl_platform_host {
|
|||
}
|
||||
}
|
||||
|
||||
impl Iterator for SupportedInputFormats {
|
||||
type Item = crate::SupportedFormat;
|
||||
impl Iterator for SupportedInputConfigs {
|
||||
type Item = crate::SupportedStreamConfigRange;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.0 {
|
||||
$(
|
||||
SupportedInputFormatsInner::$HostVariant(ref mut s) => s.next(),
|
||||
SupportedInputConfigsInner::$HostVariant(ref mut s) => s.next(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
@ -176,19 +176,19 @@ macro_rules! impl_platform_host {
|
|||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self.0 {
|
||||
$(
|
||||
SupportedInputFormatsInner::$HostVariant(ref d) => d.size_hint(),
|
||||
SupportedInputConfigsInner::$HostVariant(ref d) => d.size_hint(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SupportedOutputFormats {
|
||||
type Item = crate::SupportedFormat;
|
||||
impl Iterator for SupportedOutputConfigs {
|
||||
type Item = crate::SupportedStreamConfigRange;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.0 {
|
||||
$(
|
||||
SupportedOutputFormatsInner::$HostVariant(ref mut s) => s.next(),
|
||||
SupportedOutputConfigsInner::$HostVariant(ref mut s) => s.next(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
@ -196,15 +196,15 @@ macro_rules! impl_platform_host {
|
|||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self.0 {
|
||||
$(
|
||||
SupportedOutputFormatsInner::$HostVariant(ref d) => d.size_hint(),
|
||||
SupportedOutputConfigsInner::$HostVariant(ref d) => d.size_hint(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::traits::DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
type SupportedInputConfigs = SupportedInputConfigs;
|
||||
type SupportedOutputConfigs = SupportedOutputConfigs;
|
||||
type Stream = Stream;
|
||||
|
||||
fn name(&self) -> Result<String, crate::DeviceNameError> {
|
||||
|
@ -215,49 +215,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 {
|
||||
$(
|
||||
DeviceInner::$HostVariant(ref d) => {
|
||||
d.supported_input_formats()
|
||||
.map(SupportedInputFormatsInner::$HostVariant)
|
||||
.map(SupportedInputFormats)
|
||||
d.supported_input_configs()
|
||||
.map(SupportedInputConfigsInner::$HostVariant)
|
||||
.map(SupportedInputConfigs)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, crate::SupportedFormatsError> {
|
||||
fn supported_output_configs(&self) -> Result<Self::SupportedOutputConfigs, crate::SupportedStreamConfigsError> {
|
||||
match self.0 {
|
||||
$(
|
||||
DeviceInner::$HostVariant(ref d) => {
|
||||
d.supported_output_formats()
|
||||
.map(SupportedOutputFormatsInner::$HostVariant)
|
||||
.map(SupportedOutputFormats)
|
||||
d.supported_output_configs()
|
||||
.map(SupportedOutputConfigsInner::$HostVariant)
|
||||
.map(SupportedOutputConfigs)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<crate::Format, crate::DefaultFormatError> {
|
||||
fn default_input_config(&self) -> Result<crate::SupportedStreamConfig, crate::DefaultStreamConfigError> {
|
||||
match self.0 {
|
||||
$(
|
||||
DeviceInner::$HostVariant(ref d) => d.default_input_format(),
|
||||
DeviceInner::$HostVariant(ref d) => d.default_input_config(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<crate::Format, crate::DefaultFormatError> {
|
||||
fn default_output_config(&self) -> Result<crate::SupportedStreamConfig, crate::DefaultStreamConfigError> {
|
||||
match self.0 {
|
||||
$(
|
||||
DeviceInner::$HostVariant(ref d) => d.default_output_format(),
|
||||
DeviceInner::$HostVariant(ref d) => d.default_output_config(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &crate::Format,
|
||||
format: &crate::SupportedStreamConfig,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, crate::BuildStreamError>
|
||||
|
@ -276,7 +276,7 @@ macro_rules! impl_platform_host {
|
|||
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &crate::Format,
|
||||
format: &crate::SupportedStreamConfig,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, crate::BuildStreamError>
|
||||
|
@ -436,8 +436,8 @@ macro_rules! impl_platform_host {
|
|||
mod platform_impl {
|
||||
pub use crate::host::alsa::{
|
||||
Device as AlsaDevice, Devices as AlsaDevices, Host as AlsaHost, Stream as AlsaStream,
|
||||
SupportedInputFormats as AlsaSupportedInputFormats,
|
||||
SupportedOutputFormats as AlsaSupportedOutputFormats,
|
||||
SupportedInputConfigs as AlsaSupportedInputConfigs,
|
||||
SupportedOutputConfigs as AlsaSupportedOutputConfigs,
|
||||
};
|
||||
|
||||
impl_platform_host!(Alsa alsa "ALSA");
|
||||
|
@ -454,8 +454,8 @@ mod platform_impl {
|
|||
mod platform_impl {
|
||||
pub use crate::host::coreaudio::{
|
||||
Device as CoreAudioDevice, Devices as CoreAudioDevices, Host as CoreAudioHost,
|
||||
Stream as CoreAudioStream, SupportedInputFormats as CoreAudioSupportedInputFormats,
|
||||
SupportedOutputFormats as CoreAudioSupportedOutputFormats,
|
||||
Stream as CoreAudioStream, SupportedInputConfigs as CoreAudioSupportedInputConfigs,
|
||||
SupportedOutputConfigs as CoreAudioSupportedOutputConfigs,
|
||||
};
|
||||
|
||||
impl_platform_host!(CoreAudio coreaudio "CoreAudio");
|
||||
|
@ -472,8 +472,8 @@ mod platform_impl {
|
|||
mod platform_impl {
|
||||
pub use crate::host::emscripten::{
|
||||
Device as EmscriptenDevice, Devices as EmscriptenDevices, Host as EmscriptenHost,
|
||||
Stream as EmscriptenStream, SupportedInputFormats as EmscriptenSupportedInputFormats,
|
||||
SupportedOutputFormats as EmscriptenSupportedOutputFormats,
|
||||
Stream as EmscriptenStream, SupportedInputConfigs as EmscriptenSupportedInputConfigs,
|
||||
SupportedOutputConfigs as EmscriptenSupportedOutputConfigs,
|
||||
};
|
||||
|
||||
impl_platform_host!(Emscripten emscripten "Emscripten");
|
||||
|
@ -491,13 +491,13 @@ mod platform_impl {
|
|||
#[cfg(feature = "asio")]
|
||||
pub use crate::host::asio::{
|
||||
Device as AsioDevice, Devices as AsioDevices, Host as AsioHost, Stream as AsioStream,
|
||||
SupportedInputFormats as AsioSupportedInputFormats,
|
||||
SupportedOutputFormats as AsioSupportedOutputFormats,
|
||||
SupportedInputConfigs as AsioSupportedInputConfigs,
|
||||
SupportedOutputConfigs as AsioSupportedOutputConfigs,
|
||||
};
|
||||
pub use crate::host::wasapi::{
|
||||
Device as WasapiDevice, Devices as WasapiDevices, Host as WasapiHost,
|
||||
Stream as WasapiStream, SupportedInputFormats as WasapiSupportedInputFormats,
|
||||
SupportedOutputFormats as WasapiSupportedOutputFormats,
|
||||
Stream as WasapiStream, SupportedInputConfigs as WasapiSupportedInputConfigs,
|
||||
SupportedOutputConfigs as WasapiSupportedOutputConfigs,
|
||||
};
|
||||
|
||||
#[cfg(feature = "asio")]
|
||||
|
@ -526,8 +526,8 @@ mod platform_impl {
|
|||
mod platform_impl {
|
||||
pub use crate::host::null::{
|
||||
Device as NullDevice, Devices as NullDevices, EventLoop as NullEventLoop, Host as NullHost,
|
||||
StreamId as NullStreamId, SupportedInputFormats as NullSupportedInputFormats,
|
||||
SupportedOutputFormats as NullSupportedOutputFormats,
|
||||
StreamId as NullStreamId, SupportedInputConfigs as NullSupportedInputConfigs,
|
||||
SupportedOutputConfigs as NullSupportedOutputConfigs,
|
||||
};
|
||||
|
||||
impl_platform_host!(Null null "Null");
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
//! The suite of traits allowing CPAL to abstract over hosts, devices, event loops and stream IDs.
|
||||
|
||||
use {
|
||||
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||
InputDevices, OutputDevices, PauseStreamError, PlayStreamError, Sample, Shape, StreamError,
|
||||
SupportedFormat, SupportedFormatsError,
|
||||
BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, InputDevices,
|
||||
OutputDevices, PauseStreamError, PlayStreamError, Sample, StreamConfig, StreamError,
|
||||
SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
|
||||
};
|
||||
|
||||
/// A **Host** provides access to the available audio devices on the system.
|
||||
|
@ -56,7 +56,7 @@ pub trait HostTrait {
|
|||
fn input_devices(&self) -> Result<InputDevices<Self::Devices>, DevicesError> {
|
||||
fn supports_input<D: DeviceTrait>(device: &D) -> bool {
|
||||
device
|
||||
.supported_input_formats()
|
||||
.supported_input_configs()
|
||||
.map(|mut iter| iter.next().is_some())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ pub trait HostTrait {
|
|||
fn output_devices(&self) -> Result<OutputDevices<Self::Devices>, DevicesError> {
|
||||
fn supports_output<D: DeviceTrait>(device: &D) -> bool {
|
||||
device
|
||||
.supported_output_formats()
|
||||
.supported_output_configs()
|
||||
.map(|mut iter| iter.next().is_some())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
@ -84,9 +84,9 @@ pub trait HostTrait {
|
|||
/// methods that involve a device return a `Result` allowing the user to handle this case.
|
||||
pub trait DeviceTrait {
|
||||
/// The iterator type yielding supported input stream formats.
|
||||
type SupportedInputFormats: Iterator<Item = SupportedFormat>;
|
||||
type SupportedInputConfigs: Iterator<Item = SupportedStreamConfigRange>;
|
||||
/// The iterator type yielding supported output stream formats.
|
||||
type SupportedOutputFormats: Iterator<Item = SupportedFormat>;
|
||||
type SupportedOutputConfigs: Iterator<Item = SupportedStreamConfigRange>;
|
||||
/// The stream type created by `build_input_stream_raw` and `build_output_stream_raw`.
|
||||
type Stream: StreamTrait;
|
||||
|
||||
|
@ -96,26 +96,27 @@ pub trait DeviceTrait {
|
|||
/// An iterator yielding formats that are supported by the backend.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
||||
fn supported_input_formats(&self)
|
||||
-> Result<Self::SupportedInputFormats, SupportedFormatsError>;
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError>;
|
||||
|
||||
/// An iterator yielding output stream formats that are supported by the device.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
||||
fn supported_output_formats(
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError>;
|
||||
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError>;
|
||||
|
||||
/// The default input stream format for the device.
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError>;
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError>;
|
||||
|
||||
/// The default output stream format for the device.
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError>;
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError>;
|
||||
|
||||
/// Create an input stream.
|
||||
fn build_input_stream<T, D, E>(
|
||||
&self,
|
||||
shape: &Shape,
|
||||
config: &StreamConfig,
|
||||
mut data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -125,7 +126,7 @@ pub trait DeviceTrait {
|
|||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
self.build_input_stream_raw(
|
||||
&Format::with_shape(shape, T::FORMAT),
|
||||
&SupportedStreamConfig::from_config(config, T::FORMAT),
|
||||
move |data| {
|
||||
data_callback(
|
||||
data.as_slice()
|
||||
|
@ -139,7 +140,7 @@ pub trait DeviceTrait {
|
|||
/// Create an output stream.
|
||||
fn build_output_stream<T, D, E>(
|
||||
&self,
|
||||
shape: &Shape,
|
||||
config: &StreamConfig,
|
||||
mut data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -149,7 +150,7 @@ pub trait DeviceTrait {
|
|||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
self.build_output_stream_raw(
|
||||
&Format::with_shape(shape, T::FORMAT),
|
||||
&SupportedStreamConfig::from_config(config, T::FORMAT),
|
||||
move |data| {
|
||||
data_callback(
|
||||
data.as_slice_mut()
|
||||
|
@ -163,7 +164,7 @@ pub trait DeviceTrait {
|
|||
/// Create a dynamically typed input stream.
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
format: &SupportedStreamConfig,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -174,7 +175,7 @@ pub trait DeviceTrait {
|
|||
/// Create a dynamically typed output stream.
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
format: &SupportedStreamConfig,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
|
Loading…
Reference in New Issue