Merge pull request #366 from Ralith/polymorphic-streams
Polymorphic streams
This commit is contained in:
commit
ae910e3fe5
|
@ -9,8 +9,22 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
.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 format = device.default_output_format()?;
|
||||||
let sample_rate = format.sample_rate.0 as f32;
|
|
||||||
let channels = format.channels as usize;
|
match format.data_type {
|
||||||
|
cpal::SampleFormat::F32 => run::<f32>(&device, &format.shape())?,
|
||||||
|
cpal::SampleFormat::I16 => run::<i16>(&device, &format.shape())?,
|
||||||
|
cpal::SampleFormat::U16 => run::<u16>(&device, &format.shape())?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run<T>(device: &cpal::Device, shape: &cpal::Shape) -> Result<(), anyhow::Error>
|
||||||
|
where
|
||||||
|
T: cpal::Sample,
|
||||||
|
{
|
||||||
|
let sample_rate = shape.sample_rate.0 as f32;
|
||||||
|
let channels = shape.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;
|
||||||
|
@ -21,14 +35,11 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
|
|
||||||
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
|
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
|
||||||
|
|
||||||
let data_fn = move |data: &mut cpal::Data| match data.sample_format() {
|
let stream = device.build_output_stream(
|
||||||
cpal::SampleFormat::F32 => write_data::<f32>(data, channels, &mut next_value),
|
shape,
|
||||||
cpal::SampleFormat::I16 => write_data::<i16>(data, channels, &mut next_value),
|
move |data: &mut [T]| write_data(data, channels, &mut next_value),
|
||||||
cpal::SampleFormat::U16 => write_data::<u16>(data, channels, &mut next_value),
|
err_fn,
|
||||||
};
|
)?;
|
||||||
|
|
||||||
let stream = device.build_output_stream(&format, data_fn, err_fn)?;
|
|
||||||
|
|
||||||
stream.play()?;
|
stream.play()?;
|
||||||
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(1000));
|
std::thread::sleep(std::time::Duration::from_millis(1000));
|
||||||
|
@ -36,11 +47,10 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_data<T>(output: &mut cpal::Data, channels: usize, next_sample: &mut dyn FnMut() -> f32)
|
fn write_data<T>(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> f32)
|
||||||
where
|
where
|
||||||
T: cpal::Sample,
|
T: cpal::Sample,
|
||||||
{
|
{
|
||||||
let output = output.as_slice_mut::<T>().expect("unexpected sample type");
|
|
||||||
for frame in output.chunks_mut(channels) {
|
for frame in output.chunks_mut(channels) {
|
||||||
let value: T = cpal::Sample::from::<f32>(&next_sample());
|
let value: T = cpal::Sample::from::<f32>(&next_sample());
|
||||||
for sample in frame.iter_mut() {
|
for sample in frame.iter_mut() {
|
||||||
|
|
|
@ -29,12 +29,11 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
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 format between streams to keep it simple
|
||||||
let mut format = input_device.default_input_format()?;
|
let shape = input_device.default_input_format()?.shape();
|
||||||
format.data_type = cpal::SampleFormat::F32;
|
|
||||||
|
|
||||||
// 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) * format.sample_rate.0 as f32;
|
let latency_frames = (LATENCY_MS / 1_000.0) * shape.sample_rate.0 as f32;
|
||||||
let latency_samples = latency_frames as usize * format.channels as usize;
|
let latency_samples = latency_frames as usize * shape.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);
|
||||||
|
@ -47,9 +46,8 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
producer.push(0.0).unwrap();
|
producer.push(0.0).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let input_data_fn = move |data: &cpal::Data| {
|
let input_data_fn = move |data: &[f32]| {
|
||||||
let mut output_fell_behind = false;
|
let mut output_fell_behind = false;
|
||||||
let data = data.as_slice::<f32>().expect("unexpected sample type");
|
|
||||||
for &sample in data {
|
for &sample in data {
|
||||||
if producer.push(sample).is_err() {
|
if producer.push(sample).is_err() {
|
||||||
output_fell_behind = true;
|
output_fell_behind = true;
|
||||||
|
@ -60,9 +58,8 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let output_data_fn = move |data: &mut cpal::Data| {
|
let output_data_fn = move |data: &mut [f32]| {
|
||||||
let mut input_fell_behind = None;
|
let mut input_fell_behind = None;
|
||||||
let data = data.as_slice_mut::<f32>().expect("unexpected sample type");
|
|
||||||
for sample in data {
|
for sample in data {
|
||||||
*sample = match consumer.pop() {
|
*sample = match consumer.pop() {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
|
@ -81,9 +78,12 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build streams.
|
// Build streams.
|
||||||
println!("Attempting to build both streams with `{:?}`.", format);
|
println!(
|
||||||
let input_stream = input_device.build_input_stream(&format, input_data_fn, err_fn)?;
|
"Attempting to build both streams with f32 samples and `{:?}`.",
|
||||||
let output_stream = output_device.build_output_stream(&format, output_data_fn, err_fn)?;
|
shape
|
||||||
|
);
|
||||||
|
let input_stream = input_device.build_input_stream(&shape, input_data_fn, err_fn)?;
|
||||||
|
let output_stream = output_device.build_output_stream(&shape, output_data_fn, err_fn)?;
|
||||||
println!("Successfully built streams.");
|
println!("Successfully built streams.");
|
||||||
|
|
||||||
// Play the streams.
|
// Play the streams.
|
||||||
|
|
|
@ -40,14 +40,24 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
eprintln!("an error occurred on stream: {}", err);
|
eprintln!("an error occurred on stream: {}", err);
|
||||||
};
|
};
|
||||||
|
|
||||||
let data_fn = move |data: &cpal::Data| match data.sample_format() {
|
let stream = match format.data_type {
|
||||||
cpal::SampleFormat::F32 => write_input_data::<f32, f32>(data, &writer_2),
|
cpal::SampleFormat::F32 => device.build_input_stream(
|
||||||
cpal::SampleFormat::I16 => write_input_data::<i16, i16>(data, &writer_2),
|
&format.shape(),
|
||||||
cpal::SampleFormat::U16 => write_input_data::<u16, i16>(data, &writer_2),
|
move |data| write_input_data::<f32, f32>(data, &writer_2),
|
||||||
|
err_fn,
|
||||||
|
)?,
|
||||||
|
cpal::SampleFormat::I16 => device.build_input_stream(
|
||||||
|
&format.shape(),
|
||||||
|
move |data| write_input_data::<i16, i16>(data, &writer_2),
|
||||||
|
err_fn,
|
||||||
|
)?,
|
||||||
|
cpal::SampleFormat::U16 => device.build_input_stream(
|
||||||
|
&format.shape(),
|
||||||
|
move |data| write_input_data::<u16, i16>(data, &writer_2),
|
||||||
|
err_fn,
|
||||||
|
)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let stream = device.build_input_stream(&format, data_fn, err_fn)?;
|
|
||||||
|
|
||||||
stream.play()?;
|
stream.play()?;
|
||||||
|
|
||||||
// Let recording go for roughly three seconds.
|
// Let recording go for roughly three seconds.
|
||||||
|
@ -77,12 +87,11 @@ fn wav_spec_from_format(format: &cpal::Format) -> hound::WavSpec {
|
||||||
|
|
||||||
type WavWriterHandle = Arc<Mutex<Option<hound::WavWriter<BufWriter<File>>>>>;
|
type WavWriterHandle = Arc<Mutex<Option<hound::WavWriter<BufWriter<File>>>>>;
|
||||||
|
|
||||||
fn write_input_data<T, U>(input: &cpal::Data, writer: &WavWriterHandle)
|
fn write_input_data<T, U>(input: &[T], writer: &WavWriterHandle)
|
||||||
where
|
where
|
||||||
T: cpal::Sample,
|
T: cpal::Sample,
|
||||||
U: cpal::Sample + hound::Sample,
|
U: cpal::Sample + hound::Sample,
|
||||||
{
|
{
|
||||||
let input = input.as_slice::<T>().expect("unexpected sample format");
|
|
||||||
if let Ok(mut guard) = writer.try_lock() {
|
if let Ok(mut guard) = writer.try_lock() {
|
||||||
if let Some(writer) = guard.as_mut() {
|
if let Some(writer) = guard.as_mut() {
|
||||||
for &sample in input.iter() {
|
for &sample in input.iter() {
|
||||||
|
|
|
@ -80,7 +80,7 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<D, E>(
|
fn build_input_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
@ -95,7 +95,7 @@ impl DeviceTrait for Device {
|
||||||
Ok(stream)
|
Ok(stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<D, E>(
|
fn build_output_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
|
|
@ -81,7 +81,7 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<D, E>(
|
fn build_input_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
@ -91,10 +91,10 @@ 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(self, format, data_callback, error_callback)
|
Device::build_input_stream_raw(self, format, data_callback, error_callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<D, E>(
|
fn build_output_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
@ -104,7 +104,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(self, format, data_callback, error_callback)
|
Device::build_output_stream_raw(self, format, data_callback, error_callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ impl Stream {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
pub fn build_input_stream<D, E>(
|
pub fn build_input_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
|
@ -202,7 +202,7 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsupported_format_pair => unreachable!(
|
unsupported_format_pair => unreachable!(
|
||||||
"`build_input_stream` should have returned with unsupported \
|
"`build_input_stream_raw` should have returned with unsupported \
|
||||||
format {:?}",
|
format {:?}",
|
||||||
unsupported_format_pair
|
unsupported_format_pair
|
||||||
),
|
),
|
||||||
|
@ -223,7 +223,7 @@ impl Device {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_output_stream<D, E>(
|
pub fn build_output_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
|
@ -410,7 +410,7 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsupported_format_pair => unreachable!(
|
unsupported_format_pair => unreachable!(
|
||||||
"`build_output_stream` should have returned with unsupported \
|
"`build_output_stream_raw` should have returned with unsupported \
|
||||||
format {:?}",
|
format {:?}",
|
||||||
unsupported_format_pair
|
unsupported_format_pair
|
||||||
),
|
),
|
||||||
|
|
|
@ -102,7 +102,7 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<D, E>(
|
fn build_input_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
@ -112,10 +112,10 @@ 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(self, format, data_callback, error_callback)
|
Device::build_input_stream_raw(self, format, data_callback, error_callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<D, E>(
|
fn build_output_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
@ -125,7 +125,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(self, format, data_callback, error_callback)
|
Device::build_output_stream_raw(self, format, data_callback, error_callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,7 +467,7 @@ fn audio_unit_from_device(device: &Device, input: bool) -> Result<AudioUnit, cor
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
fn build_input_stream<D, E>(
|
fn build_input_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
|
@ -655,7 +655,7 @@ impl Device {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<D, E>(
|
fn build_output_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
|
|
|
@ -148,7 +148,7 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<D, E>(
|
fn build_input_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
_format: &Format,
|
_format: &Format,
|
||||||
_data_callback: D,
|
_data_callback: D,
|
||||||
|
@ -161,7 +161,7 @@ impl DeviceTrait for Device {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<D, E>(
|
fn build_output_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
|
|
@ -61,7 +61,7 @@ impl DeviceTrait for Device {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<D, E>(
|
fn build_input_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
_format: &Format,
|
_format: &Format,
|
||||||
_data_callback: D,
|
_data_callback: D,
|
||||||
|
@ -75,7 +75,7 @@ impl DeviceTrait for Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an output stream.
|
/// Create an output stream.
|
||||||
fn build_output_stream<D, E>(
|
fn build_output_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
_format: &Format,
|
_format: &Format,
|
||||||
_data_callback: D,
|
_data_callback: D,
|
||||||
|
|
|
@ -95,7 +95,7 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<D, E>(
|
fn build_input_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
@ -105,7 +105,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_inner(format)?;
|
let stream_inner = self.build_input_stream_raw_inner(format)?;
|
||||||
Ok(Stream::new_input(
|
Ok(Stream::new_input(
|
||||||
stream_inner,
|
stream_inner,
|
||||||
data_callback,
|
data_callback,
|
||||||
|
@ -113,7 +113,7 @@ impl DeviceTrait for Device {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<D, E>(
|
fn build_output_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
@ -123,7 +123,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_inner(format)?;
|
let stream_inner = self.build_output_stream_raw_inner(format)?;
|
||||||
Ok(Stream::new_output(
|
Ok(Stream::new_output(
|
||||||
stream_inner,
|
stream_inner,
|
||||||
data_callback,
|
data_callback,
|
||||||
|
@ -610,7 +610,7 @@ impl Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_input_stream_inner(
|
pub(crate) fn build_input_stream_raw_inner(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
) -> Result<StreamInner, BuildStreamError> {
|
) -> Result<StreamInner, BuildStreamError> {
|
||||||
|
@ -754,7 +754,7 @@ impl Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_output_stream_inner(
|
pub(crate) fn build_output_stream_raw_inner(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
) -> Result<StreamInner, BuildStreamError> {
|
) -> Result<StreamInner, BuildStreamError> {
|
||||||
|
|
62
src/lib.rs
62
src/lib.rs
|
@ -59,10 +59,10 @@
|
||||||
//! 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 shape = device.default_output_format().unwrap().shape();
|
||||||
//! let stream = device.build_output_stream(
|
//! let stream = device.build_output_stream(
|
||||||
//! &format,
|
//! &shape,
|
||||||
//! move |data: &mut Data| {
|
//! 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.
|
||||||
//! },
|
//! },
|
||||||
//! move |err| {
|
//! move |err| {
|
||||||
|
@ -93,15 +93,14 @@
|
||||||
//! # let device = host.default_output_device().unwrap();
|
//! # let device = host.default_output_device().unwrap();
|
||||||
//! # let format = device.default_output_format().unwrap();
|
//! # let format = device.default_output_format().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 data_fn = move |data: &mut Data| match data.sample_format() {
|
//! let shape = format.shape();
|
||||||
//! SampleFormat::F32 => write_silence::<f32>(data),
|
//! let stream = match format.data_type {
|
||||||
//! SampleFormat::I16 => write_silence::<i16>(data),
|
//! SampleFormat::F32 => device.build_output_stream(&shape, write_silence::<f32>, err_fn),
|
||||||
//! SampleFormat::U16 => write_silence::<u16>(data),
|
//! SampleFormat::I16 => device.build_output_stream(&shape, write_silence::<i16>, err_fn),
|
||||||
//! };
|
//! SampleFormat::U16 => device.build_output_stream(&shape, write_silence::<u16>, err_fn),
|
||||||
//! let stream = device.build_output_stream(&format, data_fn, err_fn).unwrap();
|
//! }.unwrap();
|
||||||
//!
|
//!
|
||||||
//! fn write_silence<T: Sample>(data: &mut Data) {
|
//! fn write_silence<T: Sample>(data: &mut [T]) {
|
||||||
//! let data = data.as_slice_mut::<T>().unwrap();
|
|
||||||
//! for sample in data.iter_mut() {
|
//! for sample in data.iter_mut() {
|
||||||
//! *sample = Sample::from(&0.0);
|
//! *sample = Sample::from(&0.0);
|
||||||
//! }
|
//! }
|
||||||
|
@ -118,7 +117,7 @@
|
||||||
//! # let format = device.default_output_format().unwrap();
|
//! # let format = device.default_output_format().unwrap();
|
||||||
//! # 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(&format, data_fn, err_fn).unwrap();
|
//! # let stream = device.build_output_stream_raw(&format, data_fn, err_fn).unwrap();
|
||||||
//! stream.play().unwrap();
|
//! stream.play().unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
@ -132,7 +131,7 @@
|
||||||
//! # let format = device.default_output_format().unwrap();
|
//! # let format = device.default_output_format().unwrap();
|
||||||
//! # 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(&format, data_fn, err_fn).unwrap();
|
//! # let stream = device.build_output_stream_raw(&format, data_fn, err_fn).unwrap();
|
||||||
//! stream.pause().unwrap();
|
//! stream.pause().unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
@ -181,6 +180,38 @@ pub struct Format {
|
||||||
pub data_type: SampleFormat,
|
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 sample_rate: SampleRate,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Format> for Shape {
|
||||||
|
fn from(x: Format) -> Self {
|
||||||
|
Self {
|
||||||
|
channels: x.channels,
|
||||||
|
sample_rate: x.sample_rate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Describes a range of supported stream formats.
|
/// Describes a range of supported stream formats.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct SupportedFormat {
|
pub struct SupportedFormat {
|
||||||
|
@ -193,9 +224,10 @@ pub struct SupportedFormat {
|
||||||
pub data_type: SampleFormat,
|
pub data_type: SampleFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a buffer of audio data, delivered via a user's stream data callback function.
|
/// A buffer of dynamically typed audio data, passed to raw stream callbacks.
|
||||||
///
|
///
|
||||||
/// Input stream callbacks receive `&Data`, while output stream callbacks expect `&mut Data`.
|
/// Raw input stream callbacks receive `&Data`, while raw output stream callbacks expect `&mut
|
||||||
|
/// Data`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
data: *mut (),
|
data: *mut (),
|
||||||
|
|
|
@ -255,7 +255,7 @@ macro_rules! impl_platform_host {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<D, E>(
|
fn build_input_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &crate::Format,
|
format: &crate::Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
@ -267,14 +267,14 @@ macro_rules! impl_platform_host {
|
||||||
{
|
{
|
||||||
match self.0 {
|
match self.0 {
|
||||||
$(
|
$(
|
||||||
DeviceInner::$HostVariant(ref d) => d.build_input_stream(format, data_callback, error_callback)
|
DeviceInner::$HostVariant(ref d) => d.build_input_stream_raw(format, data_callback, error_callback)
|
||||||
.map(StreamInner::$HostVariant)
|
.map(StreamInner::$HostVariant)
|
||||||
.map(Stream::from),
|
.map(Stream::from),
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<D, E>(
|
fn build_output_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &crate::Format,
|
format: &crate::Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
@ -286,7 +286,7 @@ macro_rules! impl_platform_host {
|
||||||
{
|
{
|
||||||
match self.0 {
|
match self.0 {
|
||||||
$(
|
$(
|
||||||
DeviceInner::$HostVariant(ref d) => d.build_output_stream(format, data_callback, error_callback)
|
DeviceInner::$HostVariant(ref d) => d.build_output_stream_raw(format, data_callback, error_callback)
|
||||||
.map(StreamInner::$HostVariant)
|
.map(StreamInner::$HostVariant)
|
||||||
.map(Stream::from),
|
.map(Stream::from),
|
||||||
)*
|
)*
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
use {
|
use {
|
||||||
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
BuildStreamError, Data, DefaultFormatError, DeviceNameError, DevicesError, Format,
|
||||||
InputDevices, OutputDevices, PauseStreamError, PlayStreamError, StreamError, SupportedFormat,
|
InputDevices, OutputDevices, PauseStreamError, PlayStreamError, Sample, Shape, StreamError,
|
||||||
SupportedFormatsError,
|
SupportedFormat, SupportedFormatsError,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A **Host** provides access to the available audio devices on the system.
|
/// A **Host** provides access to the available audio devices on the system.
|
||||||
|
@ -87,7 +87,7 @@ pub trait DeviceTrait {
|
||||||
type SupportedInputFormats: Iterator<Item = SupportedFormat>;
|
type SupportedInputFormats: Iterator<Item = SupportedFormat>;
|
||||||
/// The iterator type yielding supported output stream formats.
|
/// The iterator type yielding supported output stream formats.
|
||||||
type SupportedOutputFormats: Iterator<Item = SupportedFormat>;
|
type SupportedOutputFormats: Iterator<Item = SupportedFormat>;
|
||||||
/// The stream type created by `build_input_stream` and `build_output_stream`.
|
/// The stream type created by `build_input_stream_raw` and `build_output_stream_raw`.
|
||||||
type Stream: StreamTrait;
|
type Stream: StreamTrait;
|
||||||
|
|
||||||
/// The human-readable name of the device.
|
/// The human-readable name of the device.
|
||||||
|
@ -113,7 +113,55 @@ pub trait DeviceTrait {
|
||||||
fn default_output_format(&self) -> Result<Format, DefaultFormatError>;
|
fn default_output_format(&self) -> Result<Format, DefaultFormatError>;
|
||||||
|
|
||||||
/// Create an input stream.
|
/// Create an input stream.
|
||||||
fn build_input_stream<D, E>(
|
fn build_input_stream<T, D, E>(
|
||||||
|
&self,
|
||||||
|
shape: &Shape,
|
||||||
|
mut data_callback: D,
|
||||||
|
error_callback: E,
|
||||||
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
|
where
|
||||||
|
T: Sample,
|
||||||
|
D: FnMut(&[T]) + Send + 'static,
|
||||||
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
|
{
|
||||||
|
self.build_input_stream_raw(
|
||||||
|
&Format::with_shape(shape, T::FORMAT),
|
||||||
|
move |data| {
|
||||||
|
data_callback(
|
||||||
|
data.as_slice()
|
||||||
|
.expect("host supplied incorrect sample type"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
error_callback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an output stream.
|
||||||
|
fn build_output_stream<T, D, E>(
|
||||||
|
&self,
|
||||||
|
shape: &Shape,
|
||||||
|
mut data_callback: D,
|
||||||
|
error_callback: E,
|
||||||
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
|
where
|
||||||
|
T: Sample,
|
||||||
|
D: FnMut(&mut [T]) + Send + 'static,
|
||||||
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
|
{
|
||||||
|
self.build_output_stream_raw(
|
||||||
|
&Format::with_shape(shape, T::FORMAT),
|
||||||
|
move |data| {
|
||||||
|
data_callback(
|
||||||
|
data.as_slice_mut()
|
||||||
|
.expect("host supplied incorrect sample type"),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
error_callback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a dynamically typed input stream.
|
||||||
|
fn build_input_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
@ -123,8 +171,8 @@ pub trait DeviceTrait {
|
||||||
D: FnMut(&Data) + Send + 'static,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
E: FnMut(StreamError) + Send + 'static;
|
E: FnMut(StreamError) + Send + 'static;
|
||||||
|
|
||||||
/// Create an output stream.
|
/// Create a dynamically typed output stream.
|
||||||
fn build_output_stream<D, E>(
|
fn build_output_stream_raw<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
|
|
Loading…
Reference in New Issue