Merge pull request #371 from mitchmindtree/stream_config
Rename stream `Format` types to `StreamConfig` and other related renamings.
This commit is contained in:
commit
e4df8b277f
|
@ -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(conf) = device.default_input_config() {
|
||||
println!(" Default input stream config:\n {:?}", conf);
|
||||
}
|
||||
let mut input_formats = match device.supported_input_formats() {
|
||||
let mut input_configs = match device.supported_input_configs() {
|
||||
Ok(f) => f.peekable(),
|
||||
Err(e) => {
|
||||
println!("Error: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if input_formats.peek().is_some() {
|
||||
println!(" All supported input stream formats:");
|
||||
for (format_index, format) in input_formats.enumerate() {
|
||||
if input_configs.peek().is_some() {
|
||||
println!(" All supported input stream configs:");
|
||||
for (config_index, config) in input_configs.enumerate() {
|
||||
println!(
|
||||
" {}.{}. {:?}",
|
||||
device_index + 1,
|
||||
format_index + 1,
|
||||
format
|
||||
config_index + 1,
|
||||
config
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Output formats
|
||||
if let Ok(fmt) = device.default_output_format() {
|
||||
println!(" Default output stream format:\n {:?}", fmt);
|
||||
// Output configs
|
||||
if let Ok(conf) = device.default_output_config() {
|
||||
println!(" Default output stream config:\n {:?}", conf);
|
||||
}
|
||||
let mut output_formats = match device.supported_output_formats() {
|
||||
let mut output_configs = match device.supported_output_configs() {
|
||||
Ok(f) => f.peekable(),
|
||||
Err(e) => {
|
||||
println!("Error: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if output_formats.peek().is_some() {
|
||||
println!(" All supported output stream formats:");
|
||||
for (format_index, format) in output_formats.enumerate() {
|
||||
if output_configs.peek().is_some() {
|
||||
println!(" All supported output stream configs:");
|
||||
for (config_index, config) in output_configs.enumerate() {
|
||||
println!(
|
||||
" {}.{}. {:?}",
|
||||
device_index + 1,
|
||||
format_index + 1,
|
||||
format
|
||||
config_index + 1,
|
||||
config
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
10
src/error.rs
10
src/error.rs
|
@ -47,7 +47,7 @@ pub enum DeviceNameError {
|
|||
|
||||
/// Error that can happen when enumerating the list of supported formats.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SupportedFormatsError {
|
||||
pub enum SupportedStreamConfigsError {
|
||||
/// The device no longer exists. This can happen if the device is disconnected while the
|
||||
/// program is running.
|
||||
#[error("The requested device is no longer available. For example, it has been unplugged.")]
|
||||
|
@ -67,7 +67,7 @@ pub enum SupportedFormatsError {
|
|||
|
||||
/// May occur when attempting to request the default input or output stream format from a `Device`.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DefaultFormatError {
|
||||
pub enum DefaultStreamConfigError {
|
||||
/// The device no longer exists. This can happen if the device is disconnected while the
|
||||
/// program is running.
|
||||
#[error("The requested device is no longer available. For example, it has been unplugged.")]
|
||||
|
@ -90,9 +90,9 @@ pub enum BuildStreamError {
|
|||
/// program is running.
|
||||
#[error("The requested device is no longer available. For example, it has been unplugged.")]
|
||||
DeviceNotAvailable,
|
||||
/// The required format is not supported.
|
||||
#[error("The requested stream format is not supported by the device.")]
|
||||
FormatNotSupported,
|
||||
/// The specified stream configuration is not supported.
|
||||
#[error("The requested stream configuration is not supported by the device.")]
|
||||
StreamConfigNotSupported,
|
||||
/// We called something the C-Layer did not understand
|
||||
///
|
||||
/// On ALSA device functions called with a feature they do not support will yield this. E.g.
|
||||
|
|
|
@ -2,9 +2,10 @@ extern crate alsa_sys as alsa;
|
|||
extern crate libc;
|
||||
|
||||
use crate::{
|
||||
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultFormatError,
|
||||
DeviceNameError, DevicesError, Format, PauseStreamError, PlayStreamError, SampleFormat,
|
||||
SampleRate, StreamError, SupportedFormat, SupportedFormatsError,
|
||||
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError,
|
||||
DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate,
|
||||
StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange,
|
||||
SupportedStreamConfigsError,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::thread::{self, JoinHandle};
|
||||
|
@ -14,8 +15,8 @@ use traits::{DeviceTrait, HostTrait, StreamTrait};
|
|||
|
||||
pub use self::enumerate::{default_input_device, default_output_device, Devices};
|
||||
|
||||
pub type SupportedInputFormats = VecIntoIter<SupportedFormat>;
|
||||
pub type SupportedOutputFormats = VecIntoIter<SupportedFormat>;
|
||||
pub type SupportedInputConfigs = VecIntoIter<SupportedStreamConfigRange>;
|
||||
pub type SupportedOutputConfigs = VecIntoIter<SupportedStreamConfigRange>;
|
||||
|
||||
mod enumerate;
|
||||
|
||||
|
@ -52,37 +53,38 @@ impl HostTrait for Host {
|
|||
}
|
||||
|
||||
impl DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
type SupportedInputConfigs = SupportedInputConfigs;
|
||||
type SupportedOutputConfigs = SupportedOutputConfigs;
|
||||
type Stream = Stream;
|
||||
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
Device::name(self)
|
||||
}
|
||||
|
||||
fn supported_input_formats(
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||
Device::supported_input_formats(self)
|
||||
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_input_configs(self)
|
||||
}
|
||||
|
||||
fn supported_output_formats(
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||
Device::supported_output_formats(self)
|
||||
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_output_configs(self)
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_input_format(self)
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_input_config(self)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_output_format(self)
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_output_config(self)
|
||||
}
|
||||
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
conf: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -90,14 +92,16 @@ impl DeviceTrait for Device {
|
|||
D: FnMut(&Data) + Send + 'static,
|
||||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_CAPTURE)?;
|
||||
let stream_inner =
|
||||
self.build_stream_inner(conf, sample_format, alsa::SND_PCM_STREAM_CAPTURE)?;
|
||||
let stream = Stream::new_input(Arc::new(stream_inner), data_callback, error_callback);
|
||||
Ok(stream)
|
||||
}
|
||||
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
conf: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -105,7 +109,8 @@ impl DeviceTrait for Device {
|
|||
D: FnMut(&mut Data) + Send + 'static,
|
||||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_PLAYBACK)?;
|
||||
let stream_inner =
|
||||
self.build_stream_inner(conf, sample_format, alsa::SND_PCM_STREAM_PLAYBACK)?;
|
||||
let stream = Stream::new_output(Arc::new(stream_inner), data_callback, error_callback);
|
||||
Ok(stream)
|
||||
}
|
||||
|
@ -161,7 +166,8 @@ pub struct Device(String);
|
|||
impl Device {
|
||||
fn build_stream_inner(
|
||||
&self,
|
||||
format: &Format,
|
||||
conf: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
stream_type: alsa::snd_pcm_stream_t,
|
||||
) -> Result<StreamInner, BuildStreamError> {
|
||||
let name = ffi::CString::new(self.0.clone()).expect("unable to clone device");
|
||||
|
@ -185,13 +191,13 @@ impl Device {
|
|||
};
|
||||
let can_pause = unsafe {
|
||||
let hw_params = HwParams::alloc();
|
||||
set_hw_params_from_format(handle, &hw_params, format)
|
||||
set_hw_params_from_format(handle, &hw_params, conf, sample_format)
|
||||
.map_err(|description| BackendSpecificError { description })?;
|
||||
|
||||
alsa::snd_pcm_hw_params_can_pause(hw_params.0) == 1
|
||||
};
|
||||
let (buffer_len, period_len) = unsafe {
|
||||
set_sw_params_from_format(handle, format)
|
||||
set_sw_params_from_format(handle, conf)
|
||||
.map_err(|description| BackendSpecificError { description })?
|
||||
};
|
||||
|
||||
|
@ -213,9 +219,9 @@ impl Device {
|
|||
|
||||
let stream_inner = StreamInner {
|
||||
channel: handle,
|
||||
sample_format: format.data_type,
|
||||
sample_format,
|
||||
num_descriptors,
|
||||
num_channels: format.channels as u16,
|
||||
num_channels: conf.channels as u16,
|
||||
buffer_len,
|
||||
period_len,
|
||||
can_pause,
|
||||
|
@ -235,10 +241,10 @@ impl Device {
|
|||
Ok(self.0.clone())
|
||||
}
|
||||
|
||||
unsafe fn supported_formats(
|
||||
unsafe fn supported_configs(
|
||||
&self,
|
||||
stream_t: alsa::snd_pcm_stream_t,
|
||||
) -> Result<VecIntoIter<SupportedFormat>, SupportedFormatsError> {
|
||||
) -> Result<VecIntoIter<SupportedStreamConfigRange>, SupportedStreamConfigsError> {
|
||||
let mut handle = ptr::null_mut();
|
||||
let device_name = match ffi::CString::new(&self.0[..]) {
|
||||
Ok(name) => name,
|
||||
|
@ -256,8 +262,8 @@ impl Device {
|
|||
alsa::SND_PCM_NONBLOCK,
|
||||
) {
|
||||
-2 |
|
||||
-16 /* determined empirically */ => return Err(SupportedFormatsError::DeviceNotAvailable),
|
||||
-22 => return Err(SupportedFormatsError::InvalidArgument),
|
||||
-16 /* determined empirically */ => return Err(SupportedStreamConfigsError::DeviceNotAvailable),
|
||||
-22 => return Err(SupportedStreamConfigsError::InvalidArgument),
|
||||
e => if let Err(description) = check_errors(e) {
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into())
|
||||
|
@ -402,14 +408,14 @@ impl Device {
|
|||
let mut output = Vec::with_capacity(
|
||||
supported_formats.len() * supported_channels.len() * sample_rates.len(),
|
||||
);
|
||||
for &data_type in supported_formats.iter() {
|
||||
for &sample_format in supported_formats.iter() {
|
||||
for channels in supported_channels.iter() {
|
||||
for &(min_rate, max_rate) in sample_rates.iter() {
|
||||
output.push(SupportedFormat {
|
||||
output.push(SupportedStreamConfigRange {
|
||||
channels: channels.clone(),
|
||||
min_sample_rate: SampleRate(min_rate as u32),
|
||||
max_sample_rate: SampleRate(max_rate as u32),
|
||||
data_type: data_type,
|
||||
sample_format: sample_format,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -420,31 +426,35 @@ impl Device {
|
|||
Ok(output.into_iter())
|
||||
}
|
||||
|
||||
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
unsafe { self.supported_formats(alsa::SND_PCM_STREAM_CAPTURE) }
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
unsafe { self.supported_configs(alsa::SND_PCM_STREAM_CAPTURE) }
|
||||
}
|
||||
|
||||
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
unsafe { self.supported_formats(alsa::SND_PCM_STREAM_PLAYBACK) }
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
unsafe { self.supported_configs(alsa::SND_PCM_STREAM_PLAYBACK) }
|
||||
}
|
||||
|
||||
// ALSA does not offer default stream formats, so instead we compare all supported formats by
|
||||
// the `SupportedFormat::cmp_default_heuristics` order and select the greatest.
|
||||
fn default_format(
|
||||
// the `SupportedStreamConfigRange::cmp_default_heuristics` order and select the greatest.
|
||||
fn default_config(
|
||||
&self,
|
||||
stream_t: alsa::snd_pcm_stream_t,
|
||||
) -> Result<Format, DefaultFormatError> {
|
||||
) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
let mut formats: Vec<_> = unsafe {
|
||||
match self.supported_formats(stream_t) {
|
||||
Err(SupportedFormatsError::DeviceNotAvailable) => {
|
||||
return Err(DefaultFormatError::DeviceNotAvailable);
|
||||
match self.supported_configs(stream_t) {
|
||||
Err(SupportedStreamConfigsError::DeviceNotAvailable) => {
|
||||
return Err(DefaultStreamConfigError::DeviceNotAvailable);
|
||||
}
|
||||
Err(SupportedFormatsError::InvalidArgument) => {
|
||||
Err(SupportedStreamConfigsError::InvalidArgument) => {
|
||||
// this happens sometimes when querying for input and output capabilities but
|
||||
// the device supports only one
|
||||
return Err(DefaultFormatError::StreamTypeNotSupported);
|
||||
return Err(DefaultStreamConfigError::StreamTypeNotSupported);
|
||||
}
|
||||
Err(SupportedFormatsError::BackendSpecific { err }) => {
|
||||
Err(SupportedStreamConfigsError::BackendSpecific { err }) => {
|
||||
return Err(err.into());
|
||||
}
|
||||
Ok(fmts) => fmts.collect(),
|
||||
|
@ -464,16 +474,16 @@ impl Device {
|
|||
}
|
||||
Ok(format)
|
||||
}
|
||||
None => Err(DefaultFormatError::StreamTypeNotSupported),
|
||||
None => Err(DefaultStreamConfigError::StreamTypeNotSupported),
|
||||
}
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.default_format(alsa::SND_PCM_STREAM_CAPTURE)
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
self.default_config(alsa::SND_PCM_STREAM_CAPTURE)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.default_format(alsa::SND_PCM_STREAM_PLAYBACK)
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
self.default_config(alsa::SND_PCM_STREAM_PLAYBACK)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -900,7 +910,8 @@ fn get_available_samples(stream: &StreamInner) -> Result<usize, BackendSpecificE
|
|||
unsafe fn set_hw_params_from_format(
|
||||
pcm_handle: *mut alsa::snd_pcm_t,
|
||||
hw_params: &HwParams,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
) -> Result<(), String> {
|
||||
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_any(pcm_handle, hw_params.0)) {
|
||||
return Err(format!("errors on pcm handle: {}", e));
|
||||
|
@ -913,14 +924,14 @@ unsafe fn set_hw_params_from_format(
|
|||
return Err(format!("handle not acessible: {}", e));
|
||||
}
|
||||
|
||||
let data_type = if cfg!(target_endian = "big") {
|
||||
match format.data_type {
|
||||
let sample_format = if cfg!(target_endian = "big") {
|
||||
match sample_format {
|
||||
SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_BE,
|
||||
SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_BE,
|
||||
SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_BE,
|
||||
}
|
||||
} else {
|
||||
match format.data_type {
|
||||
match sample_format {
|
||||
SampleFormat::I16 => alsa::SND_PCM_FORMAT_S16_LE,
|
||||
SampleFormat::U16 => alsa::SND_PCM_FORMAT_U16_LE,
|
||||
SampleFormat::F32 => alsa::SND_PCM_FORMAT_FLOAT_LE,
|
||||
|
@ -930,14 +941,14 @@ unsafe fn set_hw_params_from_format(
|
|||
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_format(
|
||||
pcm_handle,
|
||||
hw_params.0,
|
||||
data_type,
|
||||
sample_format,
|
||||
)) {
|
||||
return Err(format!("format could not be set: {}", e));
|
||||
}
|
||||
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_rate(
|
||||
pcm_handle,
|
||||
hw_params.0,
|
||||
format.sample_rate.0 as libc::c_uint,
|
||||
config.sample_rate.0 as libc::c_uint,
|
||||
0,
|
||||
)) {
|
||||
return Err(format!("sample rate could not be set: {}", e));
|
||||
|
@ -945,7 +956,7 @@ unsafe fn set_hw_params_from_format(
|
|||
if let Err(e) = check_errors(alsa::snd_pcm_hw_params_set_channels(
|
||||
pcm_handle,
|
||||
hw_params.0,
|
||||
format.channels as libc::c_uint,
|
||||
config.channels as libc::c_uint,
|
||||
)) {
|
||||
return Err(format!("channel count could not be set: {}", e));
|
||||
}
|
||||
|
@ -969,7 +980,7 @@ unsafe fn set_hw_params_from_format(
|
|||
|
||||
unsafe fn set_sw_params_from_format(
|
||||
pcm_handle: *mut alsa::snd_pcm_t,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
) -> Result<(usize, usize), String> {
|
||||
let mut sw_params = ptr::null_mut(); // TODO: RAII
|
||||
if let Err(e) = check_errors(alsa::snd_pcm_sw_params_malloc(&mut sw_params)) {
|
||||
|
@ -1005,8 +1016,8 @@ unsafe fn set_sw_params_from_format(
|
|||
)) {
|
||||
return Err(format!("snd_pcm_sw_params_set_avail_min failed: {}", e));
|
||||
}
|
||||
let buffer = buffer as usize * format.channels as usize;
|
||||
let period = period as usize * format.channels as usize;
|
||||
let buffer = buffer as usize * config.channels as usize;
|
||||
let period = period as usize * config.channels as usize;
|
||||
(buffer, period)
|
||||
};
|
||||
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
use std;
|
||||
pub type SupportedInputFormats = std::vec::IntoIter<SupportedFormat>;
|
||||
pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>;
|
||||
pub type SupportedInputConfigs = std::vec::IntoIter<SupportedStreamConfigRange>;
|
||||
pub type SupportedOutputConfigs = std::vec::IntoIter<SupportedStreamConfigRange>;
|
||||
|
||||
use super::parking_lot::Mutex;
|
||||
use super::sys;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::Arc;
|
||||
use BackendSpecificError;
|
||||
use DefaultFormatError;
|
||||
use DefaultStreamConfigError;
|
||||
use DeviceNameError;
|
||||
use DevicesError;
|
||||
use Format;
|
||||
use SampleFormat;
|
||||
use SampleRate;
|
||||
use SupportedFormat;
|
||||
use SupportedFormatsError;
|
||||
use SupportedStreamConfig;
|
||||
use SupportedStreamConfigRange;
|
||||
use SupportedStreamConfigsError;
|
||||
|
||||
/// A ASIO Device
|
||||
pub struct Device {
|
||||
|
@ -52,52 +52,21 @@ impl Device {
|
|||
Ok(self.driver.name().to_string())
|
||||
}
|
||||
|
||||
/// Gets the supported input formats.
|
||||
/// Gets the supported input configs.
|
||||
/// TODO currently only supports the default.
|
||||
/// Need to find all possible formats.
|
||||
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
// Retrieve the default format for the total supported channels and supported sample
|
||||
// format.
|
||||
let mut f = match self.default_input_format() {
|
||||
Err(_) => return Err(SupportedFormatsError::DeviceNotAvailable),
|
||||
Ok(f) => f,
|
||||
};
|
||||
|
||||
// Collect a format for every combination of supported sample rate and number of channels.
|
||||
let mut supported_formats = vec![];
|
||||
for &rate in ::COMMON_SAMPLE_RATES {
|
||||
if !self
|
||||
.driver
|
||||
.can_sample_rate(rate.0.into())
|
||||
.ok()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for channels in 1..f.channels + 1 {
|
||||
f.channels = channels;
|
||||
f.sample_rate = rate;
|
||||
supported_formats.push(SupportedFormat::from(f.clone()));
|
||||
}
|
||||
}
|
||||
Ok(supported_formats.into_iter())
|
||||
}
|
||||
|
||||
/// Gets the supported output formats.
|
||||
/// TODO currently only supports the default.
|
||||
/// Need to find all possible formats.
|
||||
pub fn supported_output_formats(
|
||||
/// Need to find all possible configs.
|
||||
pub fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
// Retrieve the default format for the total supported channels and supported sample
|
||||
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
// Retrieve the default config for the total supported channels and supported sample
|
||||
// format.
|
||||
let mut f = match self.default_output_format() {
|
||||
Err(_) => return Err(SupportedFormatsError::DeviceNotAvailable),
|
||||
let mut f = match self.default_input_config() {
|
||||
Err(_) => return Err(SupportedStreamConfigsError::DeviceNotAvailable),
|
||||
Ok(f) => f,
|
||||
};
|
||||
|
||||
// Collect a format for every combination of supported sample rate and number of channels.
|
||||
let mut supported_formats = vec![];
|
||||
// Collect a config for every combination of supported sample rate and number of channels.
|
||||
let mut supported_configs = vec![];
|
||||
for &rate in ::COMMON_SAMPLE_RATES {
|
||||
if !self
|
||||
.driver
|
||||
|
@ -110,38 +79,71 @@ impl Device {
|
|||
for channels in 1..f.channels + 1 {
|
||||
f.channels = channels;
|
||||
f.sample_rate = rate;
|
||||
supported_formats.push(SupportedFormat::from(f.clone()));
|
||||
supported_configs.push(SupportedStreamConfigRange::from(f.clone()));
|
||||
}
|
||||
}
|
||||
Ok(supported_formats.into_iter())
|
||||
Ok(supported_configs.into_iter())
|
||||
}
|
||||
|
||||
/// Returns the default input format
|
||||
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
let channels = self.driver.channels().map_err(default_format_err)?.ins as u16;
|
||||
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _);
|
||||
/// Gets the supported output configs.
|
||||
/// TODO currently only supports the default.
|
||||
/// Need to find all possible configs.
|
||||
pub fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
// Retrieve the default config for the total supported channels and supported sample
|
||||
// format.
|
||||
let mut f = match self.default_output_config() {
|
||||
Err(_) => return Err(SupportedStreamConfigsError::DeviceNotAvailable),
|
||||
Ok(f) => f,
|
||||
};
|
||||
|
||||
// Collect a config for every combination of supported sample rate and number of channels.
|
||||
let mut supported_configs = vec![];
|
||||
for &rate in ::COMMON_SAMPLE_RATES {
|
||||
if !self
|
||||
.driver
|
||||
.can_sample_rate(rate.0.into())
|
||||
.ok()
|
||||
.unwrap_or(false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for channels in 1..f.channels + 1 {
|
||||
f.channels = channels;
|
||||
f.sample_rate = rate;
|
||||
supported_configs.push(SupportedStreamConfigRange::from(f.clone()));
|
||||
}
|
||||
}
|
||||
Ok(supported_configs.into_iter())
|
||||
}
|
||||
|
||||
/// Returns the default input config
|
||||
pub fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
let channels = self.driver.channels().map_err(default_config_err)?.ins as u16;
|
||||
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_config_err)? as _);
|
||||
// Map th ASIO sample type to a CPAL sample type
|
||||
let data_type = self.driver.input_data_type().map_err(default_format_err)?;
|
||||
let data_type =
|
||||
convert_data_type(&data_type).ok_or(DefaultFormatError::StreamTypeNotSupported)?;
|
||||
Ok(Format {
|
||||
let data_type = self.driver.input_data_type().map_err(default_config_err)?;
|
||||
let sample_format = convert_data_type(&data_type)
|
||||
.ok_or(DefaultStreamConfigError::StreamTypeNotSupported)?;
|
||||
Ok(SupportedStreamConfig {
|
||||
channels,
|
||||
sample_rate,
|
||||
data_type,
|
||||
sample_format,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the default output format
|
||||
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
let channels = self.driver.channels().map_err(default_format_err)?.outs as u16;
|
||||
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_format_err)? as _);
|
||||
let data_type = self.driver.output_data_type().map_err(default_format_err)?;
|
||||
let data_type =
|
||||
convert_data_type(&data_type).ok_or(DefaultFormatError::StreamTypeNotSupported)?;
|
||||
Ok(Format {
|
||||
/// Returns the default output config
|
||||
pub fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
let channels = self.driver.channels().map_err(default_config_err)?.outs as u16;
|
||||
let sample_rate = SampleRate(self.driver.sample_rate().map_err(default_config_err)? as _);
|
||||
let data_type = self.driver.output_data_type().map_err(default_config_err)?;
|
||||
let sample_format = convert_data_type(&data_type)
|
||||
.ok_or(DefaultStreamConfigError::StreamTypeNotSupported)?;
|
||||
Ok(SupportedStreamConfig {
|
||||
channels,
|
||||
sample_rate,
|
||||
data_type,
|
||||
sample_format,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -202,12 +204,12 @@ pub(crate) fn convert_data_type(ty: &sys::AsioSampleType) -> Option<SampleFormat
|
|||
Some(fmt)
|
||||
}
|
||||
|
||||
fn default_format_err(e: sys::AsioError) -> DefaultFormatError {
|
||||
fn default_config_err(e: sys::AsioError) -> DefaultStreamConfigError {
|
||||
match e {
|
||||
sys::AsioError::NoDrivers | sys::AsioError::HardwareMalfunction => {
|
||||
DefaultFormatError::DeviceNotAvailable
|
||||
DefaultStreamConfigError::DeviceNotAvailable
|
||||
}
|
||||
sys::AsioError::NoRate => DefaultFormatError::StreamTypeNotSupported,
|
||||
sys::AsioError::NoRate => DefaultStreamConfigError::StreamTypeNotSupported,
|
||||
err => {
|
||||
let description = format!("{}", err);
|
||||
BackendSpecificError { description }.into()
|
||||
|
|
|
@ -2,12 +2,13 @@ extern crate asio_sys as sys;
|
|||
extern crate parking_lot;
|
||||
|
||||
use crate::{
|
||||
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||
PauseStreamError, PlayStreamError, StreamError, SupportedFormatsError,
|
||||
BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError,
|
||||
PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError,
|
||||
SupportedStreamConfig, SupportedStreamConfigsError,
|
||||
};
|
||||
use traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
|
||||
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats};
|
||||
pub use self::device::{Device, Devices, SupportedInputConfigs, SupportedOutputConfigs};
|
||||
pub use self::stream::Stream;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -53,37 +54,38 @@ impl HostTrait for Host {
|
|||
}
|
||||
|
||||
impl DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
type SupportedInputConfigs = SupportedInputConfigs;
|
||||
type SupportedOutputConfigs = SupportedOutputConfigs;
|
||||
type Stream = Stream;
|
||||
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
Device::name(self)
|
||||
}
|
||||
|
||||
fn supported_input_formats(
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||
Device::supported_input_formats(self)
|
||||
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_input_configs(self)
|
||||
}
|
||||
|
||||
fn supported_output_formats(
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||
Device::supported_output_formats(self)
|
||||
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_output_configs(self)
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_input_format(self)
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_input_config(self)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_output_format(self)
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_output_config(self)
|
||||
}
|
||||
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -91,12 +93,13 @@ impl DeviceTrait for Device {
|
|||
D: FnMut(&Data) + Send + 'static,
|
||||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
Device::build_input_stream_raw(self, format, data_callback, error_callback)
|
||||
Device::build_input_stream_raw(self, config, sample_format, data_callback, error_callback)
|
||||
}
|
||||
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -104,7 +107,7 @@ impl DeviceTrait for Device {
|
|||
D: FnMut(&mut Data) + Send + 'static,
|
||||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
Device::build_output_stream_raw(self, format, data_callback, error_callback)
|
||||
Device::build_output_stream_raw(self, config, sample_format, data_callback, error_callback)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,12 +10,13 @@ use std::sync::Arc;
|
|||
use BackendSpecificError;
|
||||
use BuildStreamError;
|
||||
use Data;
|
||||
use Format;
|
||||
use PauseStreamError;
|
||||
use PlayStreamError;
|
||||
use Sample;
|
||||
use SampleFormat;
|
||||
use StreamConfig;
|
||||
use StreamError;
|
||||
use SupportedStreamConfig;
|
||||
|
||||
/// Sample types whose constant silent value is known.
|
||||
trait Silence {
|
||||
|
@ -59,7 +60,8 @@ impl Stream {
|
|||
impl Device {
|
||||
pub fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
mut data_callback: D,
|
||||
_error_callback: E,
|
||||
) -> Result<Stream, BuildStreamError>
|
||||
|
@ -70,18 +72,18 @@ impl Device {
|
|||
let stream_type = self.driver.input_data_type().map_err(build_stream_err)?;
|
||||
|
||||
// Ensure that the desired sample type is supported.
|
||||
let data_type = super::device::convert_data_type(&stream_type)
|
||||
.ok_or(BuildStreamError::FormatNotSupported)?;
|
||||
if format.data_type != data_type {
|
||||
return Err(BuildStreamError::FormatNotSupported);
|
||||
let expected_sample_format = super::device::convert_data_type(&stream_type)
|
||||
.ok_or(BuildStreamError::StreamConfigNotSupported)?;
|
||||
if sample_format != expected_sample_format {
|
||||
return Err(BuildStreamError::StreamConfigNotSupported);
|
||||
}
|
||||
|
||||
let num_channels = format.channels.clone();
|
||||
let buffer_size = self.get_or_create_input_stream(format)?;
|
||||
let num_channels = config.channels.clone();
|
||||
let buffer_size = self.get_or_create_input_stream(config, sample_format)?;
|
||||
let cpal_num_samples = buffer_size * num_channels as usize;
|
||||
|
||||
// Create the buffer depending on the size of the data type.
|
||||
let len_bytes = cpal_num_samples * data_type.sample_size();
|
||||
let len_bytes = cpal_num_samples * sample_format.sample_size();
|
||||
let mut interleaved = vec![0u8; len_bytes];
|
||||
|
||||
let stream_playing = Arc::new(AtomicBool::new(false));
|
||||
|
@ -134,7 +136,7 @@ impl Device {
|
|||
callback(&data);
|
||||
}
|
||||
|
||||
match (&stream_type, data_type) {
|
||||
match (&stream_type, sample_format) {
|
||||
(&sys::AsioSampleType::ASIOSTInt16LSB, SampleFormat::I16) => {
|
||||
process_input_callback::<i16, i16, _, _>(
|
||||
&mut data_callback,
|
||||
|
@ -225,7 +227,8 @@ impl Device {
|
|||
|
||||
pub fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
mut data_callback: D,
|
||||
_error_callback: E,
|
||||
) -> Result<Stream, BuildStreamError>
|
||||
|
@ -236,18 +239,18 @@ impl Device {
|
|||
let stream_type = self.driver.output_data_type().map_err(build_stream_err)?;
|
||||
|
||||
// Ensure that the desired sample type is supported.
|
||||
let data_type = super::device::convert_data_type(&stream_type)
|
||||
.ok_or(BuildStreamError::FormatNotSupported)?;
|
||||
if format.data_type != data_type {
|
||||
return Err(BuildStreamError::FormatNotSupported);
|
||||
let expected_sample_format = super::device::convert_data_type(&stream_type)
|
||||
.ok_or(BuildStreamError::StreamConfigNotSupported)?;
|
||||
if sample_format != expected_sample_format {
|
||||
return Err(BuildStreamError::StreamConfigNotSupported);
|
||||
}
|
||||
|
||||
let num_channels = format.channels.clone();
|
||||
let buffer_size = self.get_or_create_output_stream(format)?;
|
||||
let num_channels = config.channels.clone();
|
||||
let buffer_size = self.get_or_create_output_stream(config, sample_format)?;
|
||||
let cpal_num_samples = buffer_size * num_channels as usize;
|
||||
|
||||
// Create buffers depending on data type.
|
||||
let len_bytes = cpal_num_samples * data_type.sample_size();
|
||||
let len_bytes = cpal_num_samples * sample_format.sample_size();
|
||||
let mut interleaved = vec![0u8; len_bytes];
|
||||
let mut silence_asio_buffer = SilenceAsioBuffer::default();
|
||||
|
||||
|
@ -336,7 +339,7 @@ impl Device {
|
|||
}
|
||||
}
|
||||
|
||||
match (data_type, &stream_type) {
|
||||
match (sample_format, &stream_type) {
|
||||
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16LSB) => {
|
||||
process_output_callback::<i16, i16, _, _>(
|
||||
&mut data_callback,
|
||||
|
@ -436,15 +439,19 @@ impl Device {
|
|||
/// If there is no existing ASIO Input Stream it will be created.
|
||||
///
|
||||
/// On success, the buffer size of the stream is returned.
|
||||
fn get_or_create_input_stream(&self, format: &Format) -> Result<usize, BuildStreamError> {
|
||||
match self.default_input_format() {
|
||||
fn get_or_create_input_stream(
|
||||
&self,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
) -> Result<usize, BuildStreamError> {
|
||||
match self.default_input_config() {
|
||||
Ok(f) => {
|
||||
let num_asio_channels = f.channels;
|
||||
check_format(&self.driver, format, num_asio_channels)
|
||||
check_config(&self.driver, config, sample_format, num_asio_channels)
|
||||
}
|
||||
Err(_) => Err(BuildStreamError::FormatNotSupported),
|
||||
Err(_) => Err(BuildStreamError::StreamConfigNotSupported),
|
||||
}?;
|
||||
let num_channels = format.channels as usize;
|
||||
let num_channels = config.channels as usize;
|
||||
let ref mut streams = *self.asio_streams.lock();
|
||||
// Either create a stream if thers none or had back the
|
||||
// size of the current one.
|
||||
|
@ -473,15 +480,19 @@ impl Device {
|
|||
/// Create a new CPAL Output Stream.
|
||||
///
|
||||
/// If there is no existing ASIO Output Stream it will be created.
|
||||
fn get_or_create_output_stream(&self, format: &Format) -> Result<usize, BuildStreamError> {
|
||||
match self.default_output_format() {
|
||||
fn get_or_create_output_stream(
|
||||
&self,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
) -> Result<usize, BuildStreamError> {
|
||||
match self.default_output_config() {
|
||||
Ok(f) => {
|
||||
let num_asio_channels = f.channels;
|
||||
check_format(&self.driver, format, num_asio_channels)
|
||||
check_config(&self.driver, config, sample_format, num_asio_channels)
|
||||
}
|
||||
Err(_) => Err(BuildStreamError::FormatNotSupported),
|
||||
Err(_) => Err(BuildStreamError::StreamConfigNotSupported),
|
||||
}?;
|
||||
let num_channels = format.channels as usize;
|
||||
let num_channels = config.channels as usize;
|
||||
let ref mut streams = *self.asio_streams.lock();
|
||||
// Either create a stream if thers none or had back the
|
||||
// size of the current one.
|
||||
|
@ -570,19 +581,19 @@ impl AsioSample for f64 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check whether or not the desired format is supported by the stream.
|
||||
/// Check whether or not the desired config is supported by the stream.
|
||||
///
|
||||
/// Checks sample rate, data type and then finally the number of channels.
|
||||
fn check_format(
|
||||
fn check_config(
|
||||
driver: &sys::Driver,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
num_asio_channels: u16,
|
||||
) -> Result<(), BuildStreamError> {
|
||||
let Format {
|
||||
let StreamConfig {
|
||||
channels,
|
||||
sample_rate,
|
||||
data_type,
|
||||
} = format;
|
||||
} = config;
|
||||
// Try and set the sample rate to what the user selected.
|
||||
let sample_rate = sample_rate.0.into();
|
||||
if sample_rate != driver.sample_rate().map_err(build_stream_err)? {
|
||||
|
@ -594,16 +605,16 @@ fn check_format(
|
|||
.set_sample_rate(sample_rate)
|
||||
.map_err(build_stream_err)?;
|
||||
} else {
|
||||
return Err(BuildStreamError::FormatNotSupported);
|
||||
return Err(BuildStreamError::StreamConfigNotSupported);
|
||||
}
|
||||
}
|
||||
// unsigned formats are not supported by asio
|
||||
match data_type {
|
||||
match sample_format {
|
||||
SampleFormat::I16 | SampleFormat::F32 => (),
|
||||
SampleFormat::U16 => return Err(BuildStreamError::FormatNotSupported),
|
||||
SampleFormat::U16 => return Err(BuildStreamError::StreamConfigNotSupported),
|
||||
}
|
||||
if *channels > num_asio_channels {
|
||||
return Err(BuildStreamError::FormatNotSupported);
|
||||
return Err(BuildStreamError::StreamConfigNotSupported);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use super::Device;
|
|||
use std::mem;
|
||||
use std::ptr::null;
|
||||
use std::vec::IntoIter as VecIntoIter;
|
||||
use {BackendSpecificError, DevicesError, SupportedFormat};
|
||||
use {BackendSpecificError, DevicesError, SupportedStreamConfigRange};
|
||||
|
||||
unsafe fn audio_devices() -> Result<Vec<AudioDeviceID>, OSStatus> {
|
||||
let property_address = AudioObjectPropertyAddress {
|
||||
|
@ -143,5 +143,5 @@ pub fn default_output_device() -> Option<Device> {
|
|||
Some(device)
|
||||
}
|
||||
|
||||
pub type SupportedInputFormats = VecIntoIter<SupportedFormat>;
|
||||
pub type SupportedOutputFormats = VecIntoIter<SupportedFormat>;
|
||||
pub type SupportedInputConfigs = VecIntoIter<SupportedStreamConfigRange>;
|
||||
pub type SupportedOutputConfigs = VecIntoIter<SupportedStreamConfigRange>;
|
||||
|
|
|
@ -20,9 +20,10 @@ use self::coreaudio::sys::{
|
|||
};
|
||||
use crate::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
use crate::{
|
||||
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultFormatError,
|
||||
DeviceNameError, DevicesError, Format, PauseStreamError, PlayStreamError, SampleFormat,
|
||||
SampleRate, StreamError, SupportedFormat, SupportedFormatsError,
|
||||
BackendSpecificError, BuildStreamError, ChannelCount, Data, DefaultStreamConfigError,
|
||||
DeviceNameError, DevicesError, PauseStreamError, PlayStreamError, SampleFormat, SampleRate,
|
||||
StreamConfig, StreamError, SupportedStreamConfig, SupportedStreamConfigRange,
|
||||
SupportedStreamConfigsError,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::CStr;
|
||||
|
@ -37,8 +38,8 @@ use std::time::Duration;
|
|||
mod enumerate;
|
||||
|
||||
pub use self::enumerate::{
|
||||
default_input_device, default_output_device, Devices, SupportedInputFormats,
|
||||
SupportedOutputFormats,
|
||||
default_input_device, default_output_device, Devices, SupportedInputConfigs,
|
||||
SupportedOutputConfigs,
|
||||
};
|
||||
|
||||
/// Coreaudio host, the default host on macOS and iOS.
|
||||
|
@ -74,37 +75,38 @@ impl HostTrait for Host {
|
|||
}
|
||||
|
||||
impl DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
type SupportedInputConfigs = SupportedInputConfigs;
|
||||
type SupportedOutputConfigs = SupportedOutputConfigs;
|
||||
type Stream = Stream;
|
||||
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
Device::name(self)
|
||||
}
|
||||
|
||||
fn supported_input_formats(
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||
Device::supported_input_formats(self)
|
||||
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_input_configs(self)
|
||||
}
|
||||
|
||||
fn supported_output_formats(
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||
Device::supported_output_formats(self)
|
||||
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_output_configs(self)
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_input_format(self)
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_input_config(self)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_output_format(self)
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_output_config(self)
|
||||
}
|
||||
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -112,12 +114,13 @@ impl DeviceTrait for Device {
|
|||
D: FnMut(&Data) + Send + 'static,
|
||||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
Device::build_input_stream_raw(self, format, data_callback, error_callback)
|
||||
Device::build_input_stream_raw(self, config, sample_format, data_callback, error_callback)
|
||||
}
|
||||
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -125,7 +128,7 @@ impl DeviceTrait for Device {
|
|||
D: FnMut(&mut Data) + Send + 'static,
|
||||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
Device::build_output_stream_raw(self, format, data_callback, error_callback)
|
||||
Device::build_output_stream_raw(self, config, sample_format, data_callback, error_callback)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,11 +168,11 @@ impl Device {
|
|||
Ok(c_str.to_string_lossy().into_owned())
|
||||
}
|
||||
|
||||
// Logic re-used between `supported_input_formats` and `supported_output_formats`.
|
||||
fn supported_formats(
|
||||
// Logic re-used between `supported_input_configs` and `supported_output_configs`.
|
||||
fn supported_configs(
|
||||
&self,
|
||||
scope: AudioObjectPropertyScope,
|
||||
) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
let mut property_address = AudioObjectPropertyAddress {
|
||||
mSelector: kAudioDevicePropertyStreamConfiguration,
|
||||
mScope: scope,
|
||||
|
@ -255,11 +258,11 @@ impl Device {
|
|||
// Collect the supported formats for the device.
|
||||
let mut fmts = vec![];
|
||||
for range in ranges {
|
||||
let fmt = SupportedFormat {
|
||||
let fmt = SupportedStreamConfigRange {
|
||||
channels: n_channels as ChannelCount,
|
||||
min_sample_rate: SampleRate(range.mMinimum as _),
|
||||
max_sample_rate: SampleRate(range.mMaximum as _),
|
||||
data_type: sample_format,
|
||||
sample_format: sample_format,
|
||||
};
|
||||
fmts.push(fmt);
|
||||
}
|
||||
|
@ -268,19 +271,25 @@ impl Device {
|
|||
}
|
||||
}
|
||||
|
||||
fn supported_input_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
self.supported_formats(kAudioObjectPropertyScopeInput)
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
self.supported_configs(kAudioObjectPropertyScopeInput)
|
||||
}
|
||||
|
||||
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
self.supported_formats(kAudioObjectPropertyScopeOutput)
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
self.supported_configs(kAudioObjectPropertyScopeOutput)
|
||||
}
|
||||
|
||||
fn default_format(
|
||||
fn default_config(
|
||||
&self,
|
||||
scope: AudioObjectPropertyScope,
|
||||
) -> Result<Format, DefaultFormatError> {
|
||||
fn default_format_error_from_os_status(status: OSStatus) -> Result<(), DefaultFormatError> {
|
||||
) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
fn default_config_error_from_os_status(
|
||||
status: OSStatus,
|
||||
) -> Result<(), DefaultStreamConfigError> {
|
||||
let err = match coreaudio::Error::from_os_status(status) {
|
||||
Err(err) => err,
|
||||
Ok(_) => return Ok(()),
|
||||
|
@ -291,10 +300,10 @@ impl Device {
|
|||
)
|
||||
| coreaudio::Error::AudioCodec(_)
|
||||
| coreaudio::Error::AudioFormat(_) => {
|
||||
Err(DefaultFormatError::StreamTypeNotSupported)
|
||||
Err(DefaultStreamConfigError::StreamTypeNotSupported)
|
||||
}
|
||||
coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::NoConnection) => {
|
||||
Err(DefaultFormatError::DeviceNotAvailable)
|
||||
Err(DefaultStreamConfigError::DeviceNotAvailable)
|
||||
}
|
||||
err => {
|
||||
let description = format!("{}", std::error::Error::description(&err));
|
||||
|
@ -321,7 +330,7 @@ impl Device {
|
|||
&data_size as *const _ as *mut _,
|
||||
&asbd as *const _ as *mut _,
|
||||
);
|
||||
default_format_error_from_os_status(status)?;
|
||||
default_config_error_from_os_status(status)?;
|
||||
|
||||
let sample_format = {
|
||||
let audio_format = coreaudio::audio_unit::AudioFormat::from_format_and_flag(
|
||||
|
@ -330,7 +339,7 @@ impl Device {
|
|||
);
|
||||
let flags = match audio_format {
|
||||
Some(coreaudio::audio_unit::AudioFormat::LinearPCM(flags)) => flags,
|
||||
_ => return Err(DefaultFormatError::StreamTypeNotSupported),
|
||||
_ => return Err(DefaultStreamConfigError::StreamTypeNotSupported),
|
||||
};
|
||||
let maybe_sample_format =
|
||||
coreaudio::audio_unit::SampleFormat::from_flags_and_bytes_per_frame(
|
||||
|
@ -340,25 +349,25 @@ impl Device {
|
|||
match maybe_sample_format {
|
||||
Some(coreaudio::audio_unit::SampleFormat::F32) => SampleFormat::F32,
|
||||
Some(coreaudio::audio_unit::SampleFormat::I16) => SampleFormat::I16,
|
||||
_ => return Err(DefaultFormatError::StreamTypeNotSupported),
|
||||
_ => return Err(DefaultStreamConfigError::StreamTypeNotSupported),
|
||||
}
|
||||
};
|
||||
|
||||
let format = Format {
|
||||
let config = SupportedStreamConfig {
|
||||
sample_rate: SampleRate(asbd.mSampleRate as _),
|
||||
channels: asbd.mChannelsPerFrame as _,
|
||||
data_type: sample_format,
|
||||
sample_format: sample_format,
|
||||
};
|
||||
Ok(format)
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.default_format(kAudioObjectPropertyScopeInput)
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
self.default_config(kAudioObjectPropertyScopeInput)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
self.default_format(kAudioObjectPropertyScopeOutput)
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
self.default_config(kAudioObjectPropertyScopeOutput)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -389,22 +398,24 @@ impl From<coreaudio::Error> for BuildStreamError {
|
|||
| coreaudio::Error::NoKnownSubtype
|
||||
| coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported)
|
||||
| coreaudio::Error::AudioCodec(_)
|
||||
| coreaudio::Error::AudioFormat(_) => BuildStreamError::FormatNotSupported,
|
||||
| coreaudio::Error::AudioFormat(_) => BuildStreamError::StreamConfigNotSupported,
|
||||
_ => BuildStreamError::DeviceNotAvailable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a coreaudio AudioStreamBasicDescription from a CPAL Format.
|
||||
fn asbd_from_format(format: &Format) -> AudioStreamBasicDescription {
|
||||
let n_channels = format.channels as usize;
|
||||
let sample_rate = format.sample_rate.0;
|
||||
let bytes_per_channel = format.data_type.sample_size();
|
||||
fn asbd_from_config(
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
) -> AudioStreamBasicDescription {
|
||||
let n_channels = config.channels as usize;
|
||||
let sample_rate = config.sample_rate.0;
|
||||
let bytes_per_channel = sample_format.sample_size();
|
||||
let bits_per_channel = bytes_per_channel * 8;
|
||||
let bytes_per_frame = n_channels * bytes_per_channel;
|
||||
let frames_per_packet = 1;
|
||||
let bytes_per_packet = frames_per_packet * bytes_per_frame;
|
||||
let sample_format = format.data_type;
|
||||
let format_flags = match sample_format {
|
||||
SampleFormat::F32 => (kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked) as u32,
|
||||
_ => kAudioFormatFlagIsPacked as u32,
|
||||
|
@ -469,7 +480,8 @@ fn audio_unit_from_device(device: &Device, input: bool) -> Result<AudioUnit, cor
|
|||
impl Device {
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
mut data_callback: D,
|
||||
_error_callback: E,
|
||||
) -> Result<Stream, BuildStreamError>
|
||||
|
@ -502,7 +514,7 @@ impl Device {
|
|||
coreaudio::Error::from_os_status(status)?;
|
||||
|
||||
// If the requested sample rate is different to the device sample rate, update the device.
|
||||
if sample_rate as u32 != format.sample_rate.0 {
|
||||
if sample_rate as u32 != config.sample_rate.0 {
|
||||
// Get available sample rate ranges.
|
||||
property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
|
||||
let data_size = 0u32;
|
||||
|
@ -530,12 +542,12 @@ impl Device {
|
|||
let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges);
|
||||
|
||||
// Now that we have the available ranges, pick the one matching the desired rate.
|
||||
let sample_rate = format.sample_rate.0;
|
||||
let sample_rate = config.sample_rate.0;
|
||||
let maybe_index = ranges.iter().position(|r| {
|
||||
r.mMinimum as u32 == sample_rate && r.mMaximum as u32 == sample_rate
|
||||
});
|
||||
let range_index = match maybe_index {
|
||||
None => return Err(BuildStreamError::FormatNotSupported),
|
||||
None => return Err(BuildStreamError::StreamConfigNotSupported),
|
||||
Some(i) => i,
|
||||
};
|
||||
|
||||
|
@ -619,12 +631,11 @@ impl Device {
|
|||
let mut audio_unit = audio_unit_from_device(self, true)?;
|
||||
|
||||
// Set the stream in interleaved mode.
|
||||
let asbd = asbd_from_format(format);
|
||||
let asbd = asbd_from_config(config, sample_format);
|
||||
audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?;
|
||||
|
||||
// Register the callback that is being called by coreaudio whenever it needs data to be
|
||||
// fed to the audio buffer.
|
||||
let sample_format = format.data_type;
|
||||
let bytes_per_channel = sample_format.sample_size();
|
||||
type Args = render_callback::Args<data::Raw>;
|
||||
audio_unit.set_input_callback(move |args: Args| unsafe {
|
||||
|
@ -657,7 +668,8 @@ impl Device {
|
|||
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
mut data_callback: D,
|
||||
_error_callback: E,
|
||||
) -> Result<Stream, BuildStreamError>
|
||||
|
@ -672,12 +684,11 @@ impl Device {
|
|||
let element = Element::Output;
|
||||
|
||||
// Set the stream in interleaved mode.
|
||||
let asbd = asbd_from_format(format);
|
||||
let asbd = asbd_from_config(config, sample_format);
|
||||
audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?;
|
||||
|
||||
// Register the callback that is being called by coreaudio whenever it needs data to be
|
||||
// fed to the audio buffer.
|
||||
let sample_format = format.data_type;
|
||||
let bytes_per_channel = sample_format.sample_size();
|
||||
type Args = render_callback::Args<data::Raw>;
|
||||
audio_unit.set_render_callback(move |args: Args| unsafe {
|
||||
|
|
|
@ -8,9 +8,9 @@ use stdweb::web::TypedArray;
|
|||
use stdweb::Reference;
|
||||
|
||||
use crate::{
|
||||
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||
PauseStreamError, PlayStreamError, SampleFormat, StreamError, SupportedFormat,
|
||||
SupportedFormatsError,
|
||||
BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError,
|
||||
PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError,
|
||||
SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
|
||||
};
|
||||
use traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
|
||||
|
@ -37,8 +37,8 @@ pub struct Stream {
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct StreamId(usize);
|
||||
|
||||
pub type SupportedInputFormats = ::std::vec::IntoIter<SupportedFormat>;
|
||||
pub type SupportedOutputFormats = ::std::vec::IntoIter<SupportedFormat>;
|
||||
pub type SupportedInputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>;
|
||||
pub type SupportedOutputConfigs = ::std::vec::IntoIter<SupportedStreamConfigRange>;
|
||||
|
||||
impl Host {
|
||||
pub fn new() -> Result<Self, crate::HostUnavailable> {
|
||||
|
@ -60,12 +60,16 @@ impl Device {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
// TODO: right now cpal's API doesn't allow flexibility here
|
||||
// "44100" and "2" (channels) have also been hard-coded in the rest of the code ; if
|
||||
// this ever becomes more flexible, don't forget to change that
|
||||
|
@ -74,25 +78,25 @@ impl Device {
|
|||
//
|
||||
// UPDATE: We can do this now. Might be best to use `crate::COMMON_SAMPLE_RATES` and
|
||||
// filter out those that lay outside the range specified above.
|
||||
Ok(vec![SupportedFormat {
|
||||
Ok(vec![SupportedStreamConfigRange {
|
||||
channels: 2,
|
||||
min_sample_rate: ::SampleRate(44100),
|
||||
max_sample_rate: ::SampleRate(44100),
|
||||
data_type: ::SampleFormat::F32,
|
||||
sample_format: ::SampleFormat::F32,
|
||||
}]
|
||||
.into_iter())
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
// TODO: because it is hard coded, see supported_output_formats.
|
||||
Ok(Format {
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
// TODO: because it is hard coded, see supported_output_configs.
|
||||
Ok(SupportedStreamConfig {
|
||||
channels: 2,
|
||||
sample_rate: ::SampleRate(44100),
|
||||
data_type: ::SampleFormat::F32,
|
||||
sample_format: ::SampleFormat::F32,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -120,37 +124,38 @@ impl HostTrait for Host {
|
|||
}
|
||||
|
||||
impl DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
type SupportedInputConfigs = SupportedInputConfigs;
|
||||
type SupportedOutputConfigs = SupportedOutputConfigs;
|
||||
type Stream = Stream;
|
||||
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
Device::name(self)
|
||||
}
|
||||
|
||||
fn supported_input_formats(
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||
Device::supported_input_formats(self)
|
||||
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_input_configs(self)
|
||||
}
|
||||
|
||||
fn supported_output_formats(
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||
Device::supported_output_formats(self)
|
||||
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_output_configs(self)
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_input_format(self)
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_input_config(self)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_output_format(self)
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_output_config(self)
|
||||
}
|
||||
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
_format: &Format,
|
||||
_config: &StreamConfig,
|
||||
_sample_format: SampleFormat,
|
||||
_data_callback: D,
|
||||
_error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -163,7 +168,8 @@ impl DeviceTrait for Device {
|
|||
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
_config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -172,7 +178,7 @@ impl DeviceTrait for Device {
|
|||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
assert_eq!(
|
||||
format.data_type,
|
||||
sample_format,
|
||||
SampleFormat::F32,
|
||||
"emscripten backend currently only supports `f32` data",
|
||||
);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||
PauseStreamError, PlayStreamError, StreamError, SupportedFormat, SupportedFormatsError,
|
||||
BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError,
|
||||
PauseStreamError, PlayStreamError, SampleFormat, StreamConfig, StreamError,
|
||||
SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
|
||||
};
|
||||
use traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
|
||||
|
@ -15,8 +16,8 @@ pub struct Host;
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Stream;
|
||||
|
||||
pub struct SupportedInputFormats;
|
||||
pub struct SupportedOutputFormats;
|
||||
pub struct SupportedInputConfigs;
|
||||
pub struct SupportedOutputConfigs;
|
||||
|
||||
impl Host {
|
||||
#[allow(dead_code)]
|
||||
|
@ -32,8 +33,8 @@ impl Devices {
|
|||
}
|
||||
|
||||
impl DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
type SupportedInputConfigs = SupportedInputConfigs;
|
||||
type SupportedOutputConfigs = SupportedOutputConfigs;
|
||||
type Stream = Stream;
|
||||
|
||||
#[inline]
|
||||
|
@ -42,28 +43,33 @@ impl DeviceTrait for Device {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn supported_output_formats(&self) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
_format: &Format,
|
||||
_config: &StreamConfig,
|
||||
_sample_format: SampleFormat,
|
||||
_data_callback: D,
|
||||
_error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -77,7 +83,8 @@ impl DeviceTrait for Device {
|
|||
/// Create an output stream.
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
_format: &Format,
|
||||
_config: &StreamConfig,
|
||||
_sample_format: SampleFormat,
|
||||
_data_callback: D,
|
||||
_error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -129,20 +136,20 @@ impl Iterator for Devices {
|
|||
}
|
||||
}
|
||||
|
||||
impl Iterator for SupportedInputFormats {
|
||||
type Item = SupportedFormat;
|
||||
impl Iterator for SupportedInputConfigs {
|
||||
type Item = SupportedStreamConfigRange;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<SupportedFormat> {
|
||||
fn next(&mut self) -> Option<SupportedStreamConfigRange> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SupportedOutputFormats {
|
||||
type Item = SupportedFormat;
|
||||
impl Iterator for SupportedOutputConfigs {
|
||||
type Item = SupportedStreamConfigRange;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<SupportedFormat> {
|
||||
fn next(&mut self) -> Option<SupportedStreamConfigRange> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
BackendSpecificError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||
SampleFormat, SampleRate, SupportedFormat, SupportedFormatsError, COMMON_SAMPLE_RATES,
|
||||
BackendSpecificError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError,
|
||||
SampleFormat, SampleRate, StreamConfig, SupportedStreamConfig, SupportedStreamConfigRange,
|
||||
SupportedStreamConfigsError, COMMON_SAMPLE_RATES,
|
||||
};
|
||||
use std;
|
||||
use std::ffi::OsString;
|
||||
|
@ -49,8 +50,8 @@ use super::{
|
|||
};
|
||||
use crate::{traits::DeviceTrait, BuildStreamError, StreamError};
|
||||
|
||||
pub type SupportedInputFormats = std::vec::IntoIter<SupportedFormat>;
|
||||
pub type SupportedOutputFormats = std::vec::IntoIter<SupportedFormat>;
|
||||
pub type SupportedInputConfigs = std::vec::IntoIter<SupportedStreamConfigRange>;
|
||||
pub type SupportedOutputConfigs = std::vec::IntoIter<SupportedStreamConfigRange>;
|
||||
|
||||
/// Wrapper because of that stupid decision to remove `Send` and `Sync` from raw pointers.
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -67,37 +68,38 @@ pub struct Device {
|
|||
}
|
||||
|
||||
impl DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
type SupportedInputConfigs = SupportedInputConfigs;
|
||||
type SupportedOutputConfigs = SupportedOutputConfigs;
|
||||
type Stream = Stream;
|
||||
|
||||
fn name(&self) -> Result<String, DeviceNameError> {
|
||||
Device::name(self)
|
||||
}
|
||||
|
||||
fn supported_input_formats(
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedInputFormats, SupportedFormatsError> {
|
||||
Device::supported_input_formats(self)
|
||||
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_input_configs(self)
|
||||
}
|
||||
|
||||
fn supported_output_formats(
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError> {
|
||||
Device::supported_output_formats(self)
|
||||
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
Device::supported_output_configs(self)
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_input_format(self)
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_input_config(self)
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
Device::default_output_format(self)
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
Device::default_output_config(self)
|
||||
}
|
||||
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -105,7 +107,7 @@ impl DeviceTrait for Device {
|
|||
D: FnMut(&Data) + Send + 'static,
|
||||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
let stream_inner = self.build_input_stream_raw_inner(format)?;
|
||||
let stream_inner = self.build_input_stream_raw_inner(config, sample_format)?;
|
||||
Ok(Stream::new_input(
|
||||
stream_inner,
|
||||
data_callback,
|
||||
|
@ -115,7 +117,8 @@ impl DeviceTrait for Device {
|
|||
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -123,7 +126,7 @@ impl DeviceTrait for Device {
|
|||
D: FnMut(&mut Data) + Send + 'static,
|
||||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
let stream_inner = self.build_output_stream_raw_inner(format)?;
|
||||
let stream_inner = self.build_output_stream_raw_inner(config, sample_format)?;
|
||||
Ok(Stream::new_output(
|
||||
stream_inner,
|
||||
data_callback,
|
||||
|
@ -214,7 +217,7 @@ unsafe fn data_flow_from_immendpoint(endpoint: *const IMMEndpoint) -> EDataFlow
|
|||
pub unsafe fn is_format_supported(
|
||||
client: *const IAudioClient,
|
||||
waveformatex_ptr: *const mmreg::WAVEFORMATEX,
|
||||
) -> Result<bool, SupportedFormatsError> {
|
||||
) -> Result<bool, SupportedStreamConfigsError> {
|
||||
/*
|
||||
// `IsFormatSupported` checks whether the format is supported and fills
|
||||
// a `WAVEFORMATEX`
|
||||
|
@ -241,7 +244,7 @@ pub unsafe fn is_format_supported(
|
|||
},
|
||||
(winerror::S_FALSE, _) => {
|
||||
(*audio_client).Release();
|
||||
return Err(BuildStreamError::FormatNotSupported);
|
||||
return Err(BuildStreamError::StreamConfigNotSupported);
|
||||
},
|
||||
(_, Ok(())) => (),
|
||||
};
|
||||
|
@ -258,7 +261,7 @@ pub unsafe fn is_format_supported(
|
|||
// has been found, but not an exact match) so we also treat this as unsupported.
|
||||
match (result, check_result(result)) {
|
||||
(_, Err(ref e)) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||
Err(SupportedFormatsError::DeviceNotAvailable)
|
||||
Err(SupportedStreamConfigsError::DeviceNotAvailable)
|
||||
}
|
||||
(_, Err(_)) => Ok(false),
|
||||
(winerror::S_FALSE, _) => Ok(false),
|
||||
|
@ -291,11 +294,11 @@ pub unsafe fn is_format_supported(
|
|||
// Get a cpal Format from a WAVEFORMATEX.
|
||||
unsafe fn format_from_waveformatex_ptr(
|
||||
waveformatex_ptr: *const mmreg::WAVEFORMATEX,
|
||||
) -> Option<Format> {
|
||||
) -> Option<SupportedStreamConfig> {
|
||||
fn cmp_guid(a: &GUID, b: &GUID) -> bool {
|
||||
a.Data1 == b.Data1 && a.Data2 == b.Data2 && a.Data3 == b.Data3 && a.Data4 == b.Data4
|
||||
}
|
||||
let data_type = match (
|
||||
let sample_format = match (
|
||||
(*waveformatex_ptr).wBitsPerSample,
|
||||
(*waveformatex_ptr).wFormatTag,
|
||||
) {
|
||||
|
@ -315,10 +318,10 @@ unsafe fn format_from_waveformatex_ptr(
|
|||
// Unknown data format returned by GetMixFormat.
|
||||
_ => return None,
|
||||
};
|
||||
let format = Format {
|
||||
let format = SupportedStreamConfig {
|
||||
channels: (*waveformatex_ptr).nChannels as _,
|
||||
sample_rate: SampleRate((*waveformatex_ptr).nSamplesPerSec),
|
||||
data_type,
|
||||
sample_format,
|
||||
};
|
||||
Some(format)
|
||||
}
|
||||
|
@ -433,7 +436,7 @@ impl Device {
|
|||
// number of channels seems to be supported. Any more or less returns an invalid
|
||||
// parameter error. Thus we just assume that the default number of channels is the only
|
||||
// number supported.
|
||||
fn supported_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
fn supported_formats(&self) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
// initializing COM because we call `CoTaskMemFree` to release the format.
|
||||
com::com_initialized();
|
||||
|
||||
|
@ -441,7 +444,7 @@ impl Device {
|
|||
let lock = match self.ensure_future_audio_client() {
|
||||
Ok(lock) => lock,
|
||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||
return Err(SupportedFormatsError::DeviceNotAvailable)
|
||||
return Err(SupportedStreamConfigsError::DeviceNotAvailable)
|
||||
}
|
||||
Err(e) => {
|
||||
let description = format!("{}", e);
|
||||
|
@ -457,7 +460,7 @@ impl Device {
|
|||
match check_result((*client).GetMixFormat(&mut default_waveformatex_ptr.0)) {
|
||||
Ok(()) => (),
|
||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||
return Err(SupportedFormatsError::DeviceNotAvailable);
|
||||
return Err(SupportedStreamConfigsError::DeviceNotAvailable);
|
||||
}
|
||||
Err(e) => {
|
||||
let description = format!("{}", e);
|
||||
|
@ -514,7 +517,8 @@ impl Device {
|
|||
Some(fmt) => fmt,
|
||||
None => {
|
||||
let description =
|
||||
"could not create a `cpal::Format` from a `WAVEFORMATEX`".to_string();
|
||||
"could not create a `cpal::SupportedStreamConfig` from a `WAVEFORMATEX`"
|
||||
.to_string();
|
||||
let err = BackendSpecificError { description };
|
||||
return Err(err.into());
|
||||
}
|
||||
|
@ -522,13 +526,15 @@ impl Device {
|
|||
let mut supported_formats = Vec::with_capacity(supported_sample_rates.len());
|
||||
for rate in supported_sample_rates {
|
||||
format.sample_rate = SampleRate(rate as _);
|
||||
supported_formats.push(SupportedFormat::from(format.clone()));
|
||||
supported_formats.push(SupportedStreamConfigRange::from(format.clone()));
|
||||
}
|
||||
Ok(supported_formats.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn supported_input_formats(&self) -> Result<SupportedInputFormats, SupportedFormatsError> {
|
||||
pub fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<SupportedInputConfigs, SupportedStreamConfigsError> {
|
||||
if self.data_flow() == eCapture {
|
||||
self.supported_formats()
|
||||
// If it's an output device, assume no input formats.
|
||||
|
@ -537,9 +543,9 @@ impl Device {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn supported_output_formats(
|
||||
pub fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<SupportedOutputFormats, SupportedFormatsError> {
|
||||
) -> Result<SupportedOutputConfigs, SupportedStreamConfigsError> {
|
||||
if self.data_flow() == eRender {
|
||||
self.supported_formats()
|
||||
// If it's an input device, assume no output formats.
|
||||
|
@ -552,14 +558,14 @@ impl Device {
|
|||
// processor to mix them together.
|
||||
//
|
||||
// One format is guaranteed to be supported, the one returned by `GetMixFormat`.
|
||||
fn default_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
fn default_format(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
// initializing COM because we call `CoTaskMemFree`
|
||||
com::com_initialized();
|
||||
|
||||
let lock = match self.ensure_future_audio_client() {
|
||||
Ok(lock) => lock,
|
||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||
return Err(DefaultFormatError::DeviceNotAvailable)
|
||||
return Err(DefaultStreamConfigError::DeviceNotAvailable)
|
||||
}
|
||||
Err(e) => {
|
||||
let description = format!("{}", e);
|
||||
|
@ -573,7 +579,7 @@ impl Device {
|
|||
let mut format_ptr = WaveFormatExPtr(ptr::null_mut());
|
||||
match check_result((*client).GetMixFormat(&mut format_ptr.0)) {
|
||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => {
|
||||
return Err(DefaultFormatError::DeviceNotAvailable);
|
||||
return Err(DefaultStreamConfigError::DeviceNotAvailable);
|
||||
}
|
||||
Err(e) => {
|
||||
let description = format!("{}", e);
|
||||
|
@ -584,7 +590,7 @@ impl Device {
|
|||
};
|
||||
|
||||
format_from_waveformatex_ptr(format_ptr.0)
|
||||
.ok_or(DefaultFormatError::StreamTypeNotSupported)
|
||||
.ok_or(DefaultStreamConfigError::StreamTypeNotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -593,26 +599,27 @@ impl Device {
|
|||
endpoint.data_flow()
|
||||
}
|
||||
|
||||
pub fn default_input_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
pub fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
if self.data_flow() == eCapture {
|
||||
self.default_format()
|
||||
} else {
|
||||
Err(DefaultFormatError::StreamTypeNotSupported)
|
||||
Err(DefaultStreamConfigError::StreamTypeNotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
|
||||
pub fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError> {
|
||||
let data_flow = self.data_flow();
|
||||
if data_flow == eRender {
|
||||
self.default_format()
|
||||
} else {
|
||||
Err(DefaultFormatError::StreamTypeNotSupported)
|
||||
Err(DefaultStreamConfigError::StreamTypeNotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build_input_stream_raw_inner(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
) -> Result<StreamInner, BuildStreamError> {
|
||||
unsafe {
|
||||
// Making sure that COM is initialized.
|
||||
|
@ -634,13 +641,13 @@ impl Device {
|
|||
|
||||
// Computing the format and initializing the device.
|
||||
let waveformatex = {
|
||||
let format_attempt = format_to_waveformatextensible(format)
|
||||
.ok_or(BuildStreamError::FormatNotSupported)?;
|
||||
let format_attempt = config_to_waveformatextensible(config, sample_format)
|
||||
.ok_or(BuildStreamError::StreamConfigNotSupported)?;
|
||||
let share_mode = AUDCLNT_SHAREMODE_SHARED;
|
||||
|
||||
// Ensure the format is supported.
|
||||
match super::device::is_format_supported(audio_client, &format_attempt.Format) {
|
||||
Ok(false) => return Err(BuildStreamError::FormatNotSupported),
|
||||
Ok(false) => return Err(BuildStreamError::StreamConfigNotSupported),
|
||||
Err(_) => return Err(BuildStreamError::DeviceNotAvailable),
|
||||
_ => (),
|
||||
}
|
||||
|
@ -749,14 +756,15 @@ impl Device {
|
|||
playing: false,
|
||||
max_frames_in_buffer,
|
||||
bytes_per_frame: waveformatex.nBlockAlign,
|
||||
sample_format: format.data_type,
|
||||
sample_format,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build_output_stream_raw_inner(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
) -> Result<StreamInner, BuildStreamError> {
|
||||
unsafe {
|
||||
// Making sure that COM is initialized.
|
||||
|
@ -778,13 +786,13 @@ impl Device {
|
|||
|
||||
// Computing the format and initializing the device.
|
||||
let waveformatex = {
|
||||
let format_attempt = format_to_waveformatextensible(format)
|
||||
.ok_or(BuildStreamError::FormatNotSupported)?;
|
||||
let format_attempt = config_to_waveformatextensible(config, sample_format)
|
||||
.ok_or(BuildStreamError::StreamConfigNotSupported)?;
|
||||
let share_mode = AUDCLNT_SHAREMODE_SHARED;
|
||||
|
||||
// Ensure the format is supported.
|
||||
match super::device::is_format_supported(audio_client, &format_attempt.Format) {
|
||||
Ok(false) => return Err(BuildStreamError::FormatNotSupported),
|
||||
Ok(false) => return Err(BuildStreamError::StreamConfigNotSupported),
|
||||
Err(_) => return Err(BuildStreamError::DeviceNotAvailable),
|
||||
_ => (),
|
||||
}
|
||||
|
@ -893,7 +901,7 @@ impl Device {
|
|||
playing: false,
|
||||
max_frames_in_buffer,
|
||||
bytes_per_frame: waveformatex.nBlockAlign,
|
||||
sample_format: format.data_type,
|
||||
sample_format,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1141,19 +1149,22 @@ pub fn default_output_device() -> Option<Device> {
|
|||
// Turns a `Format` into a `WAVEFORMATEXTENSIBLE`.
|
||||
//
|
||||
// Returns `None` if the WAVEFORMATEXTENSIBLE does not support the given format.
|
||||
fn format_to_waveformatextensible(format: &Format) -> Option<mmreg::WAVEFORMATEXTENSIBLE> {
|
||||
let format_tag = match format.data_type {
|
||||
fn config_to_waveformatextensible(
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
) -> Option<mmreg::WAVEFORMATEXTENSIBLE> {
|
||||
let format_tag = match sample_format {
|
||||
SampleFormat::I16 => mmreg::WAVE_FORMAT_PCM,
|
||||
SampleFormat::F32 => mmreg::WAVE_FORMAT_EXTENSIBLE,
|
||||
SampleFormat::U16 => return None,
|
||||
};
|
||||
let channels = format.channels as WORD;
|
||||
let sample_rate = format.sample_rate.0 as DWORD;
|
||||
let sample_bytes = format.data_type.sample_size() as WORD;
|
||||
let channels = config.channels as WORD;
|
||||
let sample_rate = config.sample_rate.0 as DWORD;
|
||||
let sample_bytes = sample_format.sample_size() as WORD;
|
||||
let avg_bytes_per_sec = u32::from(channels) * sample_rate * u32::from(sample_bytes);
|
||||
let block_align = channels * sample_bytes;
|
||||
let bits_per_sample = 8 * sample_bytes;
|
||||
let cb_size = match format.data_type {
|
||||
let cb_size = match sample_format {
|
||||
SampleFormat::I16 => 0,
|
||||
SampleFormat::F32 => {
|
||||
let extensible_size = mem::size_of::<mmreg::WAVEFORMATEXTENSIBLE>();
|
||||
|
@ -1177,7 +1188,7 @@ fn format_to_waveformatextensible(format: &Format) -> Option<mmreg::WAVEFORMATEX
|
|||
const KSAUDIO_SPEAKER_DIRECTOUT: DWORD = 0;
|
||||
let channel_mask = KSAUDIO_SPEAKER_DIRECTOUT;
|
||||
|
||||
let sub_format = match format.data_type {
|
||||
let sub_format = match sample_format {
|
||||
SampleFormat::I16 => ksmedia::KSDATAFORMAT_SUBTYPE_PCM,
|
||||
SampleFormat::F32 => ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
|
||||
SampleFormat::U16 => return None,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
extern crate winapi;
|
||||
|
||||
pub use self::device::{
|
||||
default_input_device, default_output_device, Device, Devices, SupportedInputFormats,
|
||||
SupportedOutputFormats,
|
||||
default_input_device, default_output_device, Device, Devices, SupportedInputConfigs,
|
||||
SupportedOutputConfigs,
|
||||
};
|
||||
pub use self::stream::Stream;
|
||||
use self::winapi::um::winnt::HRESULT;
|
||||
|
|
207
src/lib.rs
207
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,12 @@
|
|||
//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let device = host.default_output_device().unwrap();
|
||||
//! # let format = device.default_output_format().unwrap();
|
||||
//! # let supported_config = device.default_output_config().unwrap();
|
||||
//! # let sample_format = supported_config.sample_format();
|
||||
//! # let config = supported_config.into();
|
||||
//! # let data_fn = move |_data: &mut cpal::Data| {};
|
||||
//! # let err_fn = move |_err| {};
|
||||
//! # let stream = device.build_output_stream_raw(&format, data_fn, err_fn).unwrap();
|
||||
//! # let stream = device.build_output_stream_raw(&config, sample_format, data_fn, err_fn).unwrap();
|
||||
//! stream.play().unwrap();
|
||||
//! ```
|
||||
//!
|
||||
|
@ -128,10 +132,12 @@
|
|||
//! # use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
//! # let host = cpal::default_host();
|
||||
//! # let device = host.default_output_device().unwrap();
|
||||
//! # let format = device.default_output_format().unwrap();
|
||||
//! # let supported_config = device.default_output_config().unwrap();
|
||||
//! # let sample_format = supported_config.sample_format();
|
||||
//! # let config = supported_config.into();
|
||||
//! # let data_fn = move |_data: &mut cpal::Data| {};
|
||||
//! # let err_fn = move |_err| {};
|
||||
//! # let stream = device.build_output_stream_raw(&format, data_fn, err_fn).unwrap();
|
||||
//! # let stream = device.build_output_stream_raw(&config, sample_format, data_fn, err_fn).unwrap();
|
||||
//! stream.pause().unwrap();
|
||||
//! ```
|
||||
|
||||
|
@ -149,7 +155,7 @@ extern crate thiserror;
|
|||
pub use error::*;
|
||||
pub use platform::{
|
||||
available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream,
|
||||
SupportedInputFormats, SupportedOutputFormats, ALL_HOSTS,
|
||||
SupportedInputConfigs, SupportedOutputConfigs, ALL_HOSTS,
|
||||
};
|
||||
pub use samples_formats::{Sample, SampleFormat};
|
||||
|
||||
|
@ -172,56 +178,35 @@ pub type ChannelCount = u16;
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct SampleRate(pub u32);
|
||||
|
||||
/// The format of an input or output audio stream.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Format {
|
||||
pub channels: ChannelCount,
|
||||
pub sample_rate: SampleRate,
|
||||
pub data_type: SampleFormat,
|
||||
}
|
||||
|
||||
impl Format {
|
||||
/// Construct a format having a particular `shape` and `data_type`.
|
||||
pub fn with_shape(shape: &Shape, data_type: SampleFormat) -> Self {
|
||||
Self {
|
||||
channels: shape.channels,
|
||||
sample_rate: shape.sample_rate,
|
||||
data_type,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract aspects of the format independent of the data type.
|
||||
pub fn shape(&self) -> Shape {
|
||||
self.clone().into()
|
||||
}
|
||||
}
|
||||
|
||||
/// The properties of an input or output audio stream excluding its data type.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Shape {
|
||||
/// The set of parameters used to describe how to open a stream.
|
||||
///
|
||||
/// The sample format is omitted in favour of using a sample type.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct StreamConfig {
|
||||
pub channels: ChannelCount,
|
||||
pub sample_rate: SampleRate,
|
||||
}
|
||||
|
||||
impl From<Format> for Shape {
|
||||
fn from(x: Format) -> Self {
|
||||
Self {
|
||||
channels: x.channels,
|
||||
sample_rate: x.sample_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a range of supported stream formats.
|
||||
/// Describes a range of supported stream configurations, retrieved via the
|
||||
/// `Device::supported_input/output_configs` method.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SupportedFormat {
|
||||
pub channels: ChannelCount,
|
||||
pub struct SupportedStreamConfigRange {
|
||||
pub(crate) channels: ChannelCount,
|
||||
/// Minimum value for the samples rate of the supported formats.
|
||||
pub min_sample_rate: SampleRate,
|
||||
pub(crate) min_sample_rate: SampleRate,
|
||||
/// Maximum value for the samples rate of the supported formats.
|
||||
pub max_sample_rate: SampleRate,
|
||||
pub(crate) max_sample_rate: SampleRate,
|
||||
/// Type of data expected by the device.
|
||||
pub data_type: SampleFormat,
|
||||
pub(crate) sample_format: SampleFormat,
|
||||
}
|
||||
|
||||
/// Describes a single supported stream configuration, retrieved via either a
|
||||
/// `SupportedStreamConfigRange` instance or one of the `Device::default_input/output_config` methods.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SupportedStreamConfig {
|
||||
channels: ChannelCount,
|
||||
sample_rate: SampleRate,
|
||||
sample_format: SampleFormat,
|
||||
}
|
||||
|
||||
/// A buffer of dynamically typed audio data, passed to raw stream callbacks.
|
||||
|
@ -235,6 +220,27 @@ pub struct Data {
|
|||
sample_format: SampleFormat,
|
||||
}
|
||||
|
||||
impl SupportedStreamConfig {
|
||||
pub fn channels(&self) -> ChannelCount {
|
||||
self.channels
|
||||
}
|
||||
|
||||
pub fn sample_rate(&self) -> SampleRate {
|
||||
self.sample_rate
|
||||
}
|
||||
|
||||
pub fn sample_format(&self) -> SampleFormat {
|
||||
self.sample_format
|
||||
}
|
||||
|
||||
pub fn config(&self) -> StreamConfig {
|
||||
StreamConfig {
|
||||
channels: self.channels,
|
||||
sample_rate: self.sample_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Data {
|
||||
// Internal constructor for host implementations to use.
|
||||
//
|
||||
|
@ -328,25 +334,54 @@ impl Data {
|
|||
}
|
||||
}
|
||||
|
||||
impl SupportedFormat {
|
||||
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate.
|
||||
#[inline]
|
||||
pub fn with_max_sample_rate(self) -> Format {
|
||||
Format {
|
||||
impl SupportedStreamConfigRange {
|
||||
pub fn channels(&self) -> ChannelCount {
|
||||
self.channels
|
||||
}
|
||||
|
||||
pub fn min_sample_rate(&self) -> SampleRate {
|
||||
self.min_sample_rate
|
||||
}
|
||||
|
||||
pub fn max_sample_rate(&self) -> SampleRate {
|
||||
self.max_sample_rate
|
||||
}
|
||||
|
||||
pub fn sample_format(&self) -> SampleFormat {
|
||||
self.sample_format
|
||||
}
|
||||
|
||||
/// Retrieve a `SupportedStreamConfig` with the given sample rate.
|
||||
///
|
||||
/// **panic!**s if the given `sample_rate` is outside the range specified within this
|
||||
/// `SupportedStreamConfigRange` instance.
|
||||
pub fn with_sample_rate(self, sample_rate: SampleRate) -> SupportedStreamConfig {
|
||||
assert!(sample_rate <= self.min_sample_rate && sample_rate <= self.max_sample_rate);
|
||||
SupportedStreamConfig {
|
||||
channels: self.channels,
|
||||
sample_rate: self.max_sample_rate,
|
||||
data_type: self.data_type,
|
||||
sample_format: self.sample_format,
|
||||
sample_rate,
|
||||
}
|
||||
}
|
||||
|
||||
/// A comparison function which compares two `SupportedFormat`s in terms of their priority of
|
||||
/// Turns this `SupportedStreamConfigRange` into a `SupportedStreamConfig` corresponding to the maximum samples rate.
|
||||
#[inline]
|
||||
pub fn with_max_sample_rate(self) -> SupportedStreamConfig {
|
||||
SupportedStreamConfig {
|
||||
channels: self.channels,
|
||||
sample_rate: self.max_sample_rate,
|
||||
sample_format: self.sample_format,
|
||||
}
|
||||
}
|
||||
|
||||
/// A comparison function which compares two `SupportedStreamConfigRange`s in terms of their priority of
|
||||
/// use as a default stream format.
|
||||
///
|
||||
/// Some backends do not provide a default stream format for their audio devices. In these
|
||||
/// cases, CPAL attempts to decide on a reasonable default format for the user. To do this we
|
||||
/// use the "greatest" of all supported stream formats when compared with this method.
|
||||
///
|
||||
/// Formats are prioritised by the following heuristics:
|
||||
/// SupportedStreamConfigs are prioritised by the following heuristics:
|
||||
///
|
||||
/// **Channels**:
|
||||
///
|
||||
|
@ -382,17 +417,17 @@ impl SupportedFormat {
|
|||
return cmp_channels;
|
||||
}
|
||||
|
||||
let cmp_f32 = (self.data_type == F32).cmp(&(other.data_type == F32));
|
||||
let cmp_f32 = (self.sample_format == F32).cmp(&(other.sample_format == F32));
|
||||
if cmp_f32 != Equal {
|
||||
return cmp_f32;
|
||||
}
|
||||
|
||||
let cmp_i16 = (self.data_type == I16).cmp(&(other.data_type == I16));
|
||||
let cmp_i16 = (self.sample_format == I16).cmp(&(other.sample_format == I16));
|
||||
if cmp_i16 != Equal {
|
||||
return cmp_i16;
|
||||
}
|
||||
|
||||
let cmp_u16 = (self.data_type == U16).cmp(&(other.data_type == U16));
|
||||
let cmp_u16 = (self.sample_format == U16).cmp(&(other.sample_format == U16));
|
||||
if cmp_u16 != Equal {
|
||||
return cmp_u16;
|
||||
}
|
||||
|
@ -410,14 +445,20 @@ impl SupportedFormat {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Format> for SupportedFormat {
|
||||
impl From<SupportedStreamConfig> for StreamConfig {
|
||||
fn from(conf: SupportedStreamConfig) -> Self {
|
||||
conf.config()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SupportedStreamConfig> for SupportedStreamConfigRange {
|
||||
#[inline]
|
||||
fn from(format: Format) -> SupportedFormat {
|
||||
SupportedFormat {
|
||||
fn from(format: SupportedStreamConfig) -> SupportedStreamConfigRange {
|
||||
SupportedStreamConfigRange {
|
||||
channels: format.channels,
|
||||
min_sample_rate: format.sample_rate,
|
||||
max_sample_rate: format.sample_rate,
|
||||
data_type: format.data_type,
|
||||
sample_format: format.sample_format,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ pub use self::platform_impl::*;
|
|||
// }
|
||||
// ```
|
||||
//
|
||||
// And so on for Device, Devices, EventLoop, Host, StreamId, SupportedInputFormats,
|
||||
// SupportedOutputFormats and all their necessary trait implementations.
|
||||
// And so on for Device, Devices, EventLoop, Host, StreamId, SupportedInputConfigs,
|
||||
// SupportedOutputConfigs and all their necessary trait implementations.
|
||||
// ```
|
||||
macro_rules! impl_platform_host {
|
||||
($($HostVariant:ident $host_mod:ident $host_name:literal),*) => {
|
||||
|
@ -67,13 +67,13 @@ macro_rules! impl_platform_host {
|
|||
// TODO: Confirm this and add more specific detail and references.
|
||||
pub struct Stream(StreamInner, crate::platform::NotSendSyncAcrossAllPlatforms);
|
||||
|
||||
/// The **SupportedInputFormats** iterator associated with the platform's dynamically
|
||||
/// The **SupportedInputConfigs** iterator associated with the platform's dynamically
|
||||
/// dispatched **Host** type.
|
||||
pub struct SupportedInputFormats(SupportedInputFormatsInner);
|
||||
pub struct SupportedInputConfigs(SupportedInputConfigsInner);
|
||||
|
||||
/// The **SupportedOutputFormats** iterator associated with the platform's dynamically
|
||||
/// The **SupportedOutputConfigs** iterator associated with the platform's dynamically
|
||||
/// dispatched **Host** type.
|
||||
pub struct SupportedOutputFormats(SupportedOutputFormatsInner);
|
||||
pub struct SupportedOutputConfigs(SupportedOutputConfigsInner);
|
||||
|
||||
/// Unique identifier for available hosts on the platform.
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
|
@ -107,15 +107,15 @@ macro_rules! impl_platform_host {
|
|||
)*
|
||||
}
|
||||
|
||||
enum SupportedInputFormatsInner {
|
||||
enum SupportedInputConfigsInner {
|
||||
$(
|
||||
$HostVariant(crate::host::$host_mod::SupportedInputFormats),
|
||||
$HostVariant(crate::host::$host_mod::SupportedInputConfigs),
|
||||
)*
|
||||
}
|
||||
|
||||
enum SupportedOutputFormatsInner {
|
||||
enum SupportedOutputConfigsInner {
|
||||
$(
|
||||
$HostVariant(crate::host::$host_mod::SupportedOutputFormats),
|
||||
$HostVariant(crate::host::$host_mod::SupportedOutputConfigs),
|
||||
)*
|
||||
}
|
||||
|
||||
|
@ -162,13 +162,13 @@ macro_rules! impl_platform_host {
|
|||
}
|
||||
}
|
||||
|
||||
impl Iterator for SupportedInputFormats {
|
||||
type Item = crate::SupportedFormat;
|
||||
impl Iterator for SupportedInputConfigs {
|
||||
type Item = crate::SupportedStreamConfigRange;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.0 {
|
||||
$(
|
||||
SupportedInputFormatsInner::$HostVariant(ref mut s) => s.next(),
|
||||
SupportedInputConfigsInner::$HostVariant(ref mut s) => s.next(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
@ -176,19 +176,19 @@ macro_rules! impl_platform_host {
|
|||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self.0 {
|
||||
$(
|
||||
SupportedInputFormatsInner::$HostVariant(ref d) => d.size_hint(),
|
||||
SupportedInputConfigsInner::$HostVariant(ref d) => d.size_hint(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SupportedOutputFormats {
|
||||
type Item = crate::SupportedFormat;
|
||||
impl Iterator for SupportedOutputConfigs {
|
||||
type Item = crate::SupportedStreamConfigRange;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.0 {
|
||||
$(
|
||||
SupportedOutputFormatsInner::$HostVariant(ref mut s) => s.next(),
|
||||
SupportedOutputConfigsInner::$HostVariant(ref mut s) => s.next(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
@ -196,15 +196,15 @@ macro_rules! impl_platform_host {
|
|||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self.0 {
|
||||
$(
|
||||
SupportedOutputFormatsInner::$HostVariant(ref d) => d.size_hint(),
|
||||
SupportedOutputConfigsInner::$HostVariant(ref d) => d.size_hint(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::traits::DeviceTrait for Device {
|
||||
type SupportedInputFormats = SupportedInputFormats;
|
||||
type SupportedOutputFormats = SupportedOutputFormats;
|
||||
type SupportedInputConfigs = SupportedInputConfigs;
|
||||
type SupportedOutputConfigs = SupportedOutputConfigs;
|
||||
type Stream = Stream;
|
||||
|
||||
fn name(&self) -> Result<String, crate::DeviceNameError> {
|
||||
|
@ -215,49 +215,50 @@ macro_rules! impl_platform_host {
|
|||
}
|
||||
}
|
||||
|
||||
fn supported_input_formats(&self) -> Result<Self::SupportedInputFormats, crate::SupportedFormatsError> {
|
||||
fn supported_input_configs(&self) -> Result<Self::SupportedInputConfigs, crate::SupportedStreamConfigsError> {
|
||||
match self.0 {
|
||||
$(
|
||||
DeviceInner::$HostVariant(ref d) => {
|
||||
d.supported_input_formats()
|
||||
.map(SupportedInputFormatsInner::$HostVariant)
|
||||
.map(SupportedInputFormats)
|
||||
d.supported_input_configs()
|
||||
.map(SupportedInputConfigsInner::$HostVariant)
|
||||
.map(SupportedInputConfigs)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn supported_output_formats(&self) -> Result<Self::SupportedOutputFormats, crate::SupportedFormatsError> {
|
||||
fn supported_output_configs(&self) -> Result<Self::SupportedOutputConfigs, crate::SupportedStreamConfigsError> {
|
||||
match self.0 {
|
||||
$(
|
||||
DeviceInner::$HostVariant(ref d) => {
|
||||
d.supported_output_formats()
|
||||
.map(SupportedOutputFormatsInner::$HostVariant)
|
||||
.map(SupportedOutputFormats)
|
||||
d.supported_output_configs()
|
||||
.map(SupportedOutputConfigsInner::$HostVariant)
|
||||
.map(SupportedOutputConfigs)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn default_input_format(&self) -> Result<crate::Format, crate::DefaultFormatError> {
|
||||
fn default_input_config(&self) -> Result<crate::SupportedStreamConfig, crate::DefaultStreamConfigError> {
|
||||
match self.0 {
|
||||
$(
|
||||
DeviceInner::$HostVariant(ref d) => d.default_input_format(),
|
||||
DeviceInner::$HostVariant(ref d) => d.default_input_config(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn default_output_format(&self) -> Result<crate::Format, crate::DefaultFormatError> {
|
||||
fn default_output_config(&self) -> Result<crate::SupportedStreamConfig, crate::DefaultStreamConfigError> {
|
||||
match self.0 {
|
||||
$(
|
||||
DeviceInner::$HostVariant(ref d) => d.default_output_format(),
|
||||
DeviceInner::$HostVariant(ref d) => d.default_output_config(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &crate::Format,
|
||||
config: &crate::StreamConfig,
|
||||
sample_format: crate::SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, crate::BuildStreamError>
|
||||
|
@ -267,7 +268,13 @@ macro_rules! impl_platform_host {
|
|||
{
|
||||
match self.0 {
|
||||
$(
|
||||
DeviceInner::$HostVariant(ref d) => d.build_input_stream_raw(format, data_callback, error_callback)
|
||||
DeviceInner::$HostVariant(ref d) => d
|
||||
.build_input_stream_raw(
|
||||
config,
|
||||
sample_format,
|
||||
data_callback,
|
||||
error_callback,
|
||||
)
|
||||
.map(StreamInner::$HostVariant)
|
||||
.map(Stream::from),
|
||||
)*
|
||||
|
@ -276,7 +283,8 @@ macro_rules! impl_platform_host {
|
|||
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &crate::Format,
|
||||
config: &crate::StreamConfig,
|
||||
sample_format: crate::SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, crate::BuildStreamError>
|
||||
|
@ -286,7 +294,13 @@ macro_rules! impl_platform_host {
|
|||
{
|
||||
match self.0 {
|
||||
$(
|
||||
DeviceInner::$HostVariant(ref d) => d.build_output_stream_raw(format, data_callback, error_callback)
|
||||
DeviceInner::$HostVariant(ref d) => d
|
||||
.build_output_stream_raw(
|
||||
config,
|
||||
sample_format,
|
||||
data_callback,
|
||||
error_callback,
|
||||
)
|
||||
.map(StreamInner::$HostVariant)
|
||||
.map(Stream::from),
|
||||
)*
|
||||
|
@ -436,8 +450,8 @@ macro_rules! impl_platform_host {
|
|||
mod platform_impl {
|
||||
pub use crate::host::alsa::{
|
||||
Device as AlsaDevice, Devices as AlsaDevices, Host as AlsaHost, Stream as AlsaStream,
|
||||
SupportedInputFormats as AlsaSupportedInputFormats,
|
||||
SupportedOutputFormats as AlsaSupportedOutputFormats,
|
||||
SupportedInputConfigs as AlsaSupportedInputConfigs,
|
||||
SupportedOutputConfigs as AlsaSupportedOutputConfigs,
|
||||
};
|
||||
|
||||
impl_platform_host!(Alsa alsa "ALSA");
|
||||
|
@ -454,8 +468,8 @@ mod platform_impl {
|
|||
mod platform_impl {
|
||||
pub use crate::host::coreaudio::{
|
||||
Device as CoreAudioDevice, Devices as CoreAudioDevices, Host as CoreAudioHost,
|
||||
Stream as CoreAudioStream, SupportedInputFormats as CoreAudioSupportedInputFormats,
|
||||
SupportedOutputFormats as CoreAudioSupportedOutputFormats,
|
||||
Stream as CoreAudioStream, SupportedInputConfigs as CoreAudioSupportedInputConfigs,
|
||||
SupportedOutputConfigs as CoreAudioSupportedOutputConfigs,
|
||||
};
|
||||
|
||||
impl_platform_host!(CoreAudio coreaudio "CoreAudio");
|
||||
|
@ -472,8 +486,8 @@ mod platform_impl {
|
|||
mod platform_impl {
|
||||
pub use crate::host::emscripten::{
|
||||
Device as EmscriptenDevice, Devices as EmscriptenDevices, Host as EmscriptenHost,
|
||||
Stream as EmscriptenStream, SupportedInputFormats as EmscriptenSupportedInputFormats,
|
||||
SupportedOutputFormats as EmscriptenSupportedOutputFormats,
|
||||
Stream as EmscriptenStream, SupportedInputConfigs as EmscriptenSupportedInputConfigs,
|
||||
SupportedOutputConfigs as EmscriptenSupportedOutputConfigs,
|
||||
};
|
||||
|
||||
impl_platform_host!(Emscripten emscripten "Emscripten");
|
||||
|
@ -491,13 +505,13 @@ mod platform_impl {
|
|||
#[cfg(feature = "asio")]
|
||||
pub use crate::host::asio::{
|
||||
Device as AsioDevice, Devices as AsioDevices, Host as AsioHost, Stream as AsioStream,
|
||||
SupportedInputFormats as AsioSupportedInputFormats,
|
||||
SupportedOutputFormats as AsioSupportedOutputFormats,
|
||||
SupportedInputConfigs as AsioSupportedInputConfigs,
|
||||
SupportedOutputConfigs as AsioSupportedOutputConfigs,
|
||||
};
|
||||
pub use crate::host::wasapi::{
|
||||
Device as WasapiDevice, Devices as WasapiDevices, Host as WasapiHost,
|
||||
Stream as WasapiStream, SupportedInputFormats as WasapiSupportedInputFormats,
|
||||
SupportedOutputFormats as WasapiSupportedOutputFormats,
|
||||
Stream as WasapiStream, SupportedInputConfigs as WasapiSupportedInputConfigs,
|
||||
SupportedOutputConfigs as WasapiSupportedOutputConfigs,
|
||||
};
|
||||
|
||||
#[cfg(feature = "asio")]
|
||||
|
@ -526,8 +540,8 @@ mod platform_impl {
|
|||
mod platform_impl {
|
||||
pub use crate::host::null::{
|
||||
Device as NullDevice, Devices as NullDevices, EventLoop as NullEventLoop, Host as NullHost,
|
||||
StreamId as NullStreamId, SupportedInputFormats as NullSupportedInputFormats,
|
||||
SupportedOutputFormats as NullSupportedOutputFormats,
|
||||
StreamId as NullStreamId, SupportedInputConfigs as NullSupportedInputConfigs,
|
||||
SupportedOutputConfigs as NullSupportedOutputConfigs,
|
||||
};
|
||||
|
||||
impl_platform_host!(Null null "Null");
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
//! The suite of traits allowing CPAL to abstract over hosts, devices, event loops and stream IDs.
|
||||
|
||||
use {
|
||||
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||
InputDevices, OutputDevices, PauseStreamError, PlayStreamError, Sample, Shape, StreamError,
|
||||
SupportedFormat, SupportedFormatsError,
|
||||
BuildStreamError, Data, DefaultStreamConfigError, DeviceNameError, DevicesError, InputDevices,
|
||||
OutputDevices, PauseStreamError, PlayStreamError, Sample, SampleFormat, StreamConfig,
|
||||
StreamError, SupportedStreamConfig, SupportedStreamConfigRange, SupportedStreamConfigsError,
|
||||
};
|
||||
|
||||
/// A **Host** provides access to the available audio devices on the system.
|
||||
|
@ -56,7 +56,7 @@ pub trait HostTrait {
|
|||
fn input_devices(&self) -> Result<InputDevices<Self::Devices>, DevicesError> {
|
||||
fn supports_input<D: DeviceTrait>(device: &D) -> bool {
|
||||
device
|
||||
.supported_input_formats()
|
||||
.supported_input_configs()
|
||||
.map(|mut iter| iter.next().is_some())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ pub trait HostTrait {
|
|||
fn output_devices(&self) -> Result<OutputDevices<Self::Devices>, DevicesError> {
|
||||
fn supports_output<D: DeviceTrait>(device: &D) -> bool {
|
||||
device
|
||||
.supported_output_formats()
|
||||
.supported_output_configs()
|
||||
.map(|mut iter| iter.next().is_some())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
@ -84,9 +84,9 @@ pub trait HostTrait {
|
|||
/// methods that involve a device return a `Result` allowing the user to handle this case.
|
||||
pub trait DeviceTrait {
|
||||
/// The iterator type yielding supported input stream formats.
|
||||
type SupportedInputFormats: Iterator<Item = SupportedFormat>;
|
||||
type SupportedInputConfigs: Iterator<Item = SupportedStreamConfigRange>;
|
||||
/// The iterator type yielding supported output stream formats.
|
||||
type SupportedOutputFormats: Iterator<Item = SupportedFormat>;
|
||||
type SupportedOutputConfigs: Iterator<Item = SupportedStreamConfigRange>;
|
||||
/// The stream type created by `build_input_stream_raw` and `build_output_stream_raw`.
|
||||
type Stream: StreamTrait;
|
||||
|
||||
|
@ -96,26 +96,27 @@ pub trait DeviceTrait {
|
|||
/// An iterator yielding formats that are supported by the backend.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
||||
fn supported_input_formats(&self)
|
||||
-> Result<Self::SupportedInputFormats, SupportedFormatsError>;
|
||||
fn supported_input_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedInputConfigs, SupportedStreamConfigsError>;
|
||||
|
||||
/// An iterator yielding output stream formats that are supported by the device.
|
||||
///
|
||||
/// Can return an error if the device is no longer valid (eg. it has been disconnected).
|
||||
fn supported_output_formats(
|
||||
fn supported_output_configs(
|
||||
&self,
|
||||
) -> Result<Self::SupportedOutputFormats, SupportedFormatsError>;
|
||||
) -> Result<Self::SupportedOutputConfigs, SupportedStreamConfigsError>;
|
||||
|
||||
/// The default input stream format for the device.
|
||||
fn default_input_format(&self) -> Result<Format, DefaultFormatError>;
|
||||
fn default_input_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError>;
|
||||
|
||||
/// The default output stream format for the device.
|
||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError>;
|
||||
fn default_output_config(&self) -> Result<SupportedStreamConfig, DefaultStreamConfigError>;
|
||||
|
||||
/// Create an input stream.
|
||||
fn build_input_stream<T, D, E>(
|
||||
&self,
|
||||
shape: &Shape,
|
||||
config: &StreamConfig,
|
||||
mut data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -125,7 +126,8 @@ pub trait DeviceTrait {
|
|||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
self.build_input_stream_raw(
|
||||
&Format::with_shape(shape, T::FORMAT),
|
||||
config,
|
||||
T::FORMAT,
|
||||
move |data| {
|
||||
data_callback(
|
||||
data.as_slice()
|
||||
|
@ -139,7 +141,7 @@ pub trait DeviceTrait {
|
|||
/// Create an output stream.
|
||||
fn build_output_stream<T, D, E>(
|
||||
&self,
|
||||
shape: &Shape,
|
||||
config: &StreamConfig,
|
||||
mut data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -149,7 +151,8 @@ pub trait DeviceTrait {
|
|||
E: FnMut(StreamError) + Send + 'static,
|
||||
{
|
||||
self.build_output_stream_raw(
|
||||
&Format::with_shape(shape, T::FORMAT),
|
||||
config,
|
||||
T::FORMAT,
|
||||
move |data| {
|
||||
data_callback(
|
||||
data.as_slice_mut()
|
||||
|
@ -163,7 +166,8 @@ pub trait DeviceTrait {
|
|||
/// Create a dynamically typed input stream.
|
||||
fn build_input_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
@ -174,7 +178,8 @@ pub trait DeviceTrait {
|
|||
/// Create a dynamically typed output stream.
|
||||
fn build_output_stream_raw<D, E>(
|
||||
&self,
|
||||
format: &Format,
|
||||
config: &StreamConfig,
|
||||
sample_format: SampleFormat,
|
||||
data_callback: D,
|
||||
error_callback: E,
|
||||
) -> Result<Self::Stream, BuildStreamError>
|
||||
|
|
Loading…
Reference in New Issue