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