Remove `UnknownTypeBuffer` in favour of specifying sample type.

This is an implementation of the planned changes described in #119.

For a quick overview of how the API has changed, check out the updated
examples.

**TODO:**

- [x] Update API.
- [x] Update examples.
- [ ] Remove `data_type` field from `Format` (see [here](https://github.com/RustAudio/cpal/issues/119#issuecomment-573788380)).
- Update backends:
  - [x] null
  - [x] ALSA
  - [ ] ASIO
  - [ ] WASAPI
  - [ ] CoreAudio
  - [ ] Emscripten

Closes #119
Closes #260
This commit is contained in:
mitchmindtree 2020-01-13 15:27:41 +01:00
parent c4ef3ac14c
commit 05b62bb1c0
9 changed files with 543 additions and 420 deletions

View File

@ -1,56 +1,61 @@
extern crate anyhow; extern crate anyhow;
extern crate cpal; extern crate cpal;
use cpal::traits::{DeviceTrait, StreamTrait, HostTrait}; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
fn main() -> Result<(), anyhow::Error> { fn main() -> Result<(), anyhow::Error> {
let host = cpal::default_host(); let host = cpal::default_host();
let device = host.default_output_device().expect("failed to find a default output device"); let device = host
.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 sample_rate = format.sample_rate.0 as f32;
let channels = format.channels; let channels = format.channels as usize;
let mut sample_clock = 0f32;
// Produce a sinusoid of maximum amplitude. // Produce a sinusoid of maximum amplitude.
let mut sample_clock = 0f32;
let mut next_value = move || { let mut next_value = move || {
sample_clock = (sample_clock + 1.0) % sample_rate; sample_clock = (sample_clock + 1.0) % sample_rate;
(sample_clock * 440.0 * 2.0 * 3.141592 / sample_rate).sin() (sample_clock * 440.0 * 2.0 * 3.141592 / sample_rate).sin()
}; };
let stream = device.build_output_stream(&format, move |data| { let err_fn = |err| {
match data {
cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::U16(mut buffer) } => {
for sample in buffer.chunks_mut(channels as usize) {
let value = ((next_value() * 0.5 + 0.5) * std::u16::MAX as f32) as u16;
for out in sample.iter_mut() {
*out = value;
}
}
},
cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::I16(mut buffer) } => {
for sample in buffer.chunks_mut(channels as usize) {
let value = (next_value() * std::i16::MAX as f32) as i16;
for out in sample.iter_mut() {
*out = value;
}
}
},
cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer) } => {
for sample in buffer.chunks_mut(channels as usize) {
let value = next_value();
for out in sample.iter_mut() {
*out = value;
}
}
},
_ => (),
}
}, move |err| {
eprintln!("an error occurred on stream: {}", err); eprintln!("an error occurred on stream: {}", err);
})?; };
let stream = match format.data_type {
cpal::SampleFormat::F32 => device.build_output_stream(
&format,
move |mut data| write_data::<f32>(&mut *data, channels, &mut next_value),
err_fn,
),
cpal::SampleFormat::I16 => device.build_output_stream(
&format,
move |mut data| write_data::<i16>(&mut *data, channels, &mut next_value),
err_fn,
),
cpal::SampleFormat::U16 => device.build_output_stream(
&format,
move |mut data| write_data::<u16>(&mut *data, channels, &mut next_value),
err_fn,
),
}?;
stream.play()?; stream.play()?;
std::thread::sleep(std::time::Duration::from_millis(1000)); std::thread::sleep(std::time::Duration::from_millis(1000));
Ok(()) Ok(())
} }
fn write_data<T>(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> f32)
where
T: cpal::Sample,
{
for frame in output.chunks_mut(channels) {
let value: T = cpal::Sample::from::<f32>(&next_sample());
for sample in frame.iter_mut() {
*sample = value;
}
}
}

View File

@ -49,13 +49,11 @@ fn main() -> Result<(), anyhow::Error> {
// Build streams. // Build streams.
println!("Attempting to build both streams with `{:?}`.", format); println!("Attempting to build both streams with `{:?}`.", format);
let input_stream = input_device.build_input_stream(&format, move |data| { let input_stream = input_device.build_input_stream(
match data { &format,
cpal::StreamData::Input { move |data| {
buffer: cpal::UnknownTypeInputBuffer::F32(buffer),
} => {
let mut output_fell_behind = false; let mut output_fell_behind = false;
for &sample in buffer.iter() { for &sample in data.iter() {
if producer.push(sample).is_err() { if producer.push(sample).is_err() {
output_fell_behind = true; output_fell_behind = true;
} }
@ -64,18 +62,15 @@ fn main() -> Result<(), anyhow::Error> {
eprintln!("output stream fell behind: try increasing latency"); eprintln!("output stream fell behind: try increasing latency");
} }
}, },
_ => panic!("Expected input with f32 data"), |err| {
} eprintln!("an error occurred on stream: {}", err);
}, move |err| { },
eprintln!("an error occurred on input stream: {}", err); )?;
})?; let output_stream = output_device.build_output_stream(
let output_stream = output_device.build_output_stream(&format, move |data| { &format,
match data { move |mut data| {
cpal::StreamData::Output {
buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer),
} => {
let mut input_fell_behind = None; let mut input_fell_behind = None;
for sample in buffer.iter_mut() { for sample in data.iter_mut() {
*sample = match consumer.pop() { *sample = match consumer.pop() {
Ok(s) => s, Ok(s) => s,
Err(err) => { Err(err) => {
@ -88,11 +83,10 @@ fn main() -> Result<(), anyhow::Error> {
eprintln!("input stream fell behind: {:?}: try increasing latency", err); eprintln!("input stream fell behind: {:?}: try increasing latency", err);
} }
}, },
_ => panic!("Expected output with f32 data"), move |err| {
}
}, move |err| {
eprintln!("an error occurred on output stream: {}", err); eprintln!("an error occurred on output stream: {}", err);
})?; },
)?;
println!("Successfully built streams."); println!("Successfully built streams.");
// Play the streams. // Play the streams.

View File

@ -7,6 +7,9 @@ extern crate cpal;
extern crate hound; extern crate hound;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use std::sync::{Arc, Mutex};
use std::fs::File;
use std::io::BufWriter;
fn main() -> Result<(), anyhow::Error> { fn main() -> Result<(), anyhow::Error> {
// Use the default host for working with audio devices. // Use the default host for working with audio devices.
@ -25,55 +28,36 @@ fn main() -> Result<(), anyhow::Error> {
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_format(&format);
let writer = hound::WavWriter::create(PATH, spec)?; let writer = hound::WavWriter::create(PATH, spec)?;
let writer = std::sync::Arc::new(std::sync::Mutex::new(Some(writer))); let writer = Arc::new(Mutex::new(Some(writer)));
// A flag to indicate that recording is in progress. // A flag to indicate that recording is in progress.
println!("Begin recording..."); println!("Begin recording...");
// Run the input stream on a separate thread. // Run the input stream on a separate thread.
let writer_2 = writer.clone(); let writer_2 = writer.clone();
let stream = device.build_input_stream(&format, move |data| {
// Otherwise write to the wav writer. let err_fn = move |err| {
match data {
cpal::StreamData::Input {
buffer: cpal::UnknownTypeInputBuffer::U16(buffer),
} => {
if let Ok(mut guard) = writer_2.try_lock() {
if let Some(writer) = guard.as_mut() {
for sample in buffer.iter() {
let sample = cpal::Sample::to_i16(sample);
writer.write_sample(sample).ok();
}
}
}
},
cpal::StreamData::Input {
buffer: cpal::UnknownTypeInputBuffer::I16(buffer),
} => {
if let Ok(mut guard) = writer_2.try_lock() {
if let Some(writer) = guard.as_mut() {
for &sample in buffer.iter() {
writer.write_sample(sample).ok();
}
}
}
},
cpal::StreamData::Input {
buffer: cpal::UnknownTypeInputBuffer::F32(buffer),
} => {
if let Ok(mut guard) = writer_2.try_lock() {
if let Some(writer) = guard.as_mut() {
for &sample in buffer.iter() {
writer.write_sample(sample).ok();
}
}
}
},
_ => (),
}
}, move |err| {
eprintln!("an error occurred on stream: {}", err); eprintln!("an error occurred on stream: {}", err);
})?; };
let stream = match format.data_type {
cpal::SampleFormat::F32 => device.build_input_stream(
&format,
move |mut data| write_input_data::<f32, f32>(&*data, &writer_2),
err_fn,
),
cpal::SampleFormat::I16 => device.build_input_stream(
&format,
move |mut data| write_input_data::<i16, i16>(&*data, &writer_2),
err_fn,
),
cpal::SampleFormat::U16 => device.build_input_stream(
&format,
move |mut data| write_input_data::<u16, i16>(&*data, &writer_2),
err_fn,
),
}?;
stream.play()?; stream.play()?;
// Let recording go for roughly three seconds. // Let recording go for roughly three seconds.
@ -100,3 +84,20 @@ fn wav_spec_from_format(format: &cpal::Format) -> hound::WavSpec {
sample_format: sample_format(format.data_type), sample_format: sample_format(format.data_type),
} }
} }
type WavWriterHandle = Arc<Mutex<Option<hound::WavWriter<BufWriter<File>>>>>;
fn write_input_data<T, U>(input: &[T], writer: &WavWriterHandle)
where
T: cpal::Sample,
U: cpal::Sample + hound::Sample,
{
if let Ok(mut guard) = writer.try_lock() {
if let Some(writer) = guard.as_mut() {
for &sample in input.iter() {
let sample: U = cpal::Sample::from(&sample);
writer.write_sample(sample).ok();
}
}
}
}

View File

@ -1,29 +1,30 @@
extern crate alsa_sys as alsa; extern crate alsa_sys as alsa;
extern crate libc; extern crate libc;
use std::{cmp, ffi, io, mem, ptr}; use crate::{
BackendSpecificError,
BuildStreamError,
ChannelCount,
DefaultFormatError,
DeviceNameError,
DevicesError,
Format,
InputData,
OutputData,
PauseStreamError,
PlayStreamError,
Sample,
SampleFormat,
SampleRate,
StreamError,
SupportedFormat,
SupportedFormatsError,
};
use std::{cmp, ffi, io, ptr};
use std::sync::Arc; use std::sync::Arc;
use std::thread::{self, JoinHandle}; use std::thread::{self, JoinHandle};
use std::vec::IntoIter as VecIntoIter; use std::vec::IntoIter as VecIntoIter;
use BackendSpecificError;
use BuildStreamError;
use ChannelCount;
use DefaultFormatError;
use DeviceNameError;
use DevicesError;
use Format;
use PauseStreamError;
use PlayStreamError;
use SampleFormat;
use SampleRate;
use StreamData;
use StreamError;
use SupportedFormat;
use SupportedFormatsError;
use traits::{DeviceTrait, HostTrait, StreamTrait}; use traits::{DeviceTrait, HostTrait, StreamTrait};
use UnknownTypeInputBuffer;
use UnknownTypeOutputBuffer;
pub use self::enumerate::{default_input_device, default_output_device, Devices}; pub use self::enumerate::{default_input_device, default_output_device, Devices};
@ -89,12 +90,40 @@ impl DeviceTrait for Device {
Device::default_output_format(self) Device::default_output_format(self)
} }
fn build_input_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError> where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static { fn build_input_stream<T, D, E>(
Ok(Stream::new(Arc::new(self.build_stream_inner(format, alsa::SND_PCM_STREAM_CAPTURE)?), data_callback, error_callback)) &self,
format: &Format,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
where
T: Sample,
D: FnMut(InputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
// TODO: Consider removing `data_type` field from `Format` and removing this.
assert_eq!(format.data_type, T::FORMAT, "sample format mismatch");
let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_CAPTURE)?;
let stream = Stream::new_input(Arc::new(stream_inner), data_callback, error_callback);
Ok(stream)
} }
fn build_output_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError> where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static { fn build_output_stream<T, D, E>(
Ok(Stream::new(Arc::new(self.build_stream_inner(format, alsa::SND_PCM_STREAM_PLAYBACK)?), data_callback, error_callback)) &self,
format: &Format,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
where
T: Sample,
D: FnMut(OutputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
// TODO: Consider removing `data_type` field from `Format` and removing this.
assert_eq!(format.data_type, T::FORMAT, "sample format mismatch");
let stream_inner = self.build_stream_inner(format, alsa::SND_PCM_STREAM_PLAYBACK)?;
let stream = Stream::new_output(Arc::new(stream_inner), data_callback, error_callback);
Ok(stream)
} }
} }
@ -147,7 +176,11 @@ impl Drop for TriggerReceiver {
pub struct Device(String); pub struct Device(String);
impl Device { impl Device {
fn build_stream_inner(&self, format: &Format, stream_type: alsa::snd_pcm_stream_t) -> Result<StreamInner, BuildStreamError> { fn build_stream_inner(
&self,
format: &Format,
stream_type: alsa::snd_pcm_stream_t,
) -> 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");
let handle = unsafe { let handle = unsafe {
@ -510,6 +543,7 @@ unsafe impl Send for StreamInner {}
unsafe impl Sync for StreamInner {} unsafe impl Sync for StreamInner {}
#[derive(Debug, Eq, PartialEq)]
enum StreamType { Input, Output } enum StreamType { Input, Output }
pub struct Stream { pub struct Stream {
@ -524,16 +558,97 @@ pub struct Stream {
trigger: TriggerSender, trigger: TriggerSender,
} }
/// The inner body of the audio processing thread. Takes the polymorphic #[derive(Default)]
/// callback to avoid generating too much generic code. struct StreamWorkerContext {
fn stream_worker(rx: TriggerReceiver, descriptors: Vec<libc::pollfd>,
buffer: Vec<u8>,
}
fn input_stream_worker<T>(
rx: TriggerReceiver,
stream: &StreamInner, stream: &StreamInner,
data_callback: &mut (dyn FnMut(StreamData) + Send + 'static), data_callback: &mut (dyn FnMut(InputData<T>) + Send + 'static),
error_callback: &mut (dyn FnMut(StreamError) + Send + 'static)) { error_callback: &mut (dyn FnMut(StreamError) + Send + 'static),
let mut descriptors = Vec::new(); ) where
let mut buffer = Vec::new(); T: Sample,
{
let mut ctxt = StreamWorkerContext::default();
loop { loop {
match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) {
PollDescriptorsFlow::Continue => continue,
PollDescriptorsFlow::Return => return,
PollDescriptorsFlow::Ready { available_frames, stream_type } => {
assert_eq!(
stream_type,
StreamType::Input,
"expected input stream, but polling descriptors indicated output",
);
process_input::<T>(
stream,
&mut ctxt.buffer,
available_frames,
data_callback,
error_callback,
);
}
}
}
}
fn output_stream_worker<T>(
rx: TriggerReceiver,
stream: &StreamInner,
data_callback: &mut (dyn FnMut(OutputData<T>) + Send + 'static),
error_callback: &mut (dyn FnMut(StreamError) + Send + 'static),
) where
T: Sample,
{
let mut ctxt = StreamWorkerContext::default();
loop {
match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) {
PollDescriptorsFlow::Continue => continue,
PollDescriptorsFlow::Return => return,
PollDescriptorsFlow::Ready { available_frames, stream_type } => {
assert_eq!(
stream_type,
StreamType::Output,
"expected output stream, but polling descriptors indicated input",
);
process_output::<T>(
stream,
&mut ctxt.buffer,
available_frames,
data_callback,
error_callback,
);
}
}
}
}
enum PollDescriptorsFlow {
Continue,
Return,
Ready {
stream_type: StreamType,
available_frames: usize,
}
}
// This block is shared between both input and output stream worker functions.
fn poll_descriptors_and_prepare_buffer(
rx: &TriggerReceiver,
stream: &StreamInner,
ctxt: &mut StreamWorkerContext,
error_callback: &mut (dyn FnMut(StreamError) + Send + 'static),
) -> PollDescriptorsFlow {
let StreamWorkerContext {
ref mut descriptors,
ref mut buffer,
} = *ctxt;
descriptors.clear(); descriptors.clear();
// Add the self-pipe for signaling termination. // Add the self-pipe for signaling termination.
descriptors.push(libc::pollfd { descriptors.push(libc::pollfd {
fd: rx.0, fd: rx.0,
@ -563,28 +678,28 @@ fn stream_worker(rx: TriggerReceiver,
if res < 0 { if res < 0 {
let description = format!("`libc::poll()` failed: {}", io::Error::last_os_error()); let description = format!("`libc::poll()` failed: {}", io::Error::last_os_error());
error_callback(BackendSpecificError { description }.into()); error_callback(BackendSpecificError { description }.into());
continue; return PollDescriptorsFlow::Continue;
} else if res == 0 { } else if res == 0 {
let description = String::from("`libc::poll()` spuriously returned"); let description = String::from("`libc::poll()` spuriously returned");
error_callback(BackendSpecificError { description }.into()); error_callback(BackendSpecificError { description }.into());
continue; return PollDescriptorsFlow::Continue;
} }
if descriptors[0].revents != 0 { if descriptors[0].revents != 0 {
// The stream has been requested to be destroyed. // The stream has been requested to be destroyed.
rx.clear_pipe(); rx.clear_pipe();
return; return PollDescriptorsFlow::Return;
} }
let stream_type = match check_for_pollout_or_pollin(stream, descriptors[1..].as_mut_ptr()) { let stream_type = match check_for_pollout_or_pollin(stream, descriptors[1..].as_mut_ptr()) {
Ok(Some(ty)) => ty, Ok(Some(ty)) => ty,
Ok(None) => { Ok(None) => {
// Nothing to process, poll again // Nothing to process, poll again
continue; return PollDescriptorsFlow::Continue;
}, },
Err(err) => { Err(err) => {
error_callback(err.into()); error_callback(err.into());
continue; return PollDescriptorsFlow::Continue;
} }
}; };
// Get the number of available samples for reading/writing. // Get the number of available samples for reading/writing.
@ -593,13 +708,13 @@ fn stream_worker(rx: TriggerReceiver,
Err(err) => { Err(err) => {
let description = format!("Failed to query the number of available samples: {}", err); let description = format!("Failed to query the number of available samples: {}", err);
error_callback(BackendSpecificError { description }.into()); error_callback(BackendSpecificError { description }.into());
continue; return PollDescriptorsFlow::Continue;
} }
}; };
// Only go on if there is at least `stream.period_len` samples. // Only go on if there is at least `stream.period_len` samples.
if available_samples < stream.period_len { if available_samples < stream.period_len {
continue; return PollDescriptorsFlow::Continue;
} }
// Prepare the data buffer. // Prepare the data buffer.
@ -607,8 +722,22 @@ fn stream_worker(rx: TriggerReceiver,
buffer.resize(buffer_size, 0u8); buffer.resize(buffer_size, 0u8);
let available_frames = available_samples / stream.num_channels as usize; let available_frames = available_samples / stream.num_channels as usize;
match stream_type { PollDescriptorsFlow::Ready {
StreamType::Input => { stream_type,
available_frames,
}
}
// Read input data from ALSA and deliver it to the user.
fn process_input<T>(
stream: &StreamInner,
buffer: &mut [u8],
available_frames: usize,
data_callback: &mut (dyn FnMut(InputData<T>) + Send + 'static),
error_callback: &mut dyn FnMut(StreamError),
) where
T: Sample,
{
let result = unsafe { let result = unsafe {
alsa::snd_pcm_readi( alsa::snd_pcm_readi(
stream.channel, stream.channel,
@ -619,44 +748,30 @@ fn stream_worker(rx: TriggerReceiver,
if let Err(err) = check_errors(result as _) { if let Err(err) = check_errors(result as _) {
let description = format!("`snd_pcm_readi` failed: {}", err); let description = format!("`snd_pcm_readi` failed: {}", err);
error_callback(BackendSpecificError { description }.into()); error_callback(BackendSpecificError { description }.into());
continue; return;
} }
let buffer = unsafe { cast_input_buffer::<T>(buffer) };
let input_data = InputData { buffer };
data_callback(input_data);
}
let input_buffer = match stream.sample_format { // Request data from the user's function and write it via ALSA.
SampleFormat::I16 => UnknownTypeInputBuffer::I16(::InputBuffer { //
buffer: unsafe { cast_input_buffer(&mut buffer) }, // Returns `true`
}), fn process_output<T>(
SampleFormat::U16 => UnknownTypeInputBuffer::U16(::InputBuffer { stream: &StreamInner,
buffer: unsafe { cast_input_buffer(&mut buffer) }, buffer: &mut [u8],
}), available_frames: usize,
SampleFormat::F32 => UnknownTypeInputBuffer::F32(::InputBuffer { data_callback: &mut (dyn FnMut(OutputData<T>) + Send + 'static),
buffer: unsafe { cast_input_buffer(&mut buffer) }, error_callback: &mut dyn FnMut(StreamError),
}), ) where
}; T: Sample,
let stream_data = StreamData::Input { {
buffer: input_buffer,
};
data_callback(stream_data);
},
StreamType::Output => {
{ {
// We're now sure that we're ready to write data. // We're now sure that we're ready to write data.
let output_buffer = match stream.sample_format { let buffer = unsafe { cast_output_buffer::<T>(buffer) };
SampleFormat::I16 => UnknownTypeOutputBuffer::I16(::OutputBuffer { let output_data = OutputData { buffer };
buffer: unsafe { cast_output_buffer(&mut buffer) }, data_callback(output_data);
}),
SampleFormat::U16 => UnknownTypeOutputBuffer::U16(::OutputBuffer {
buffer: unsafe { cast_output_buffer(&mut buffer) },
}),
SampleFormat::F32 => UnknownTypeOutputBuffer::F32(::OutputBuffer {
buffer: unsafe { cast_output_buffer(&mut buffer) },
}),
};
let stream_data = StreamData::Output {
buffer: output_buffer,
};
data_callback(stream_data);
} }
loop { loop {
let result = unsafe { let result = unsafe {
@ -666,7 +781,6 @@ fn stream_worker(rx: TriggerReceiver,
available_frames as alsa::snd_pcm_uframes_t, available_frames as alsa::snd_pcm_uframes_t,
) )
}; };
if result == -libc::EPIPE as i64 { if result == -libc::EPIPE as i64 {
// buffer underrun // buffer underrun
// TODO: Notify the user of this. // TODO: Notify the user of this.
@ -688,19 +802,47 @@ fn stream_worker(rx: TriggerReceiver,
break; break;
} }
} }
},
}
}
} }
impl Stream { impl Stream {
fn new<D, E>(inner: Arc<StreamInner>, mut data_callback: D, mut error_callback: E) -> Stream fn new_input<T, D, E>(
where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static { inner: Arc<StreamInner>,
mut data_callback: D,
mut error_callback: E,
) -> Stream
where
T: Sample,
D: FnMut(InputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
let (tx, rx) = trigger(); let (tx, rx) = trigger();
// Clone the handle for passing into worker thread. // Clone the handle for passing into worker thread.
let stream = inner.clone(); let stream = inner.clone();
let thread = thread::spawn(move || { let thread = thread::spawn(move || {
stream_worker(rx, &*stream, &mut data_callback, &mut error_callback); input_stream_worker::<T>(rx, &*stream, &mut data_callback, &mut error_callback);
});
Stream {
thread: Some(thread),
inner,
trigger: tx,
}
}
fn new_output<T, D, E>(
inner: Arc<StreamInner>,
mut data_callback: D,
mut error_callback: E,
) -> Stream
where
T: Sample,
D: FnMut(OutputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
let (tx, rx) = trigger();
// Clone the handle for passing into worker thread.
let stream = inner.clone();
let thread = thread::spawn(move || {
output_stream_worker::<T>(rx, &*stream, &mut data_callback, &mut error_callback);
}); });
Stream { Stream {
thread: Some(thread), thread: Some(thread),

View File

@ -1,14 +1,17 @@
use BuildStreamError; use crate::{
use DefaultFormatError; BuildStreamError,
use DevicesError; DefaultFormatError,
use DeviceNameError; DevicesError,
use Format; DeviceNameError,
use PauseStreamError; Format,
use PlayStreamError; InputData,
use StreamData; OutputData,
use StreamError; PauseStreamError,
use SupportedFormatsError; PlayStreamError,
use SupportedFormat; StreamError,
SupportedFormatsError,
SupportedFormat,
};
use traits::{DeviceTrait, HostTrait, StreamTrait}; use traits::{DeviceTrait, HostTrait, StreamTrait};
#[derive(Default)] #[derive(Default)]
@ -68,14 +71,30 @@ impl DeviceTrait for Device {
unimplemented!() unimplemented!()
} }
fn build_input_stream<D, E>(&self, _format: &Format, _data_callback: D, _error_callback: E) -> Result<Self::Stream, BuildStreamError> fn build_input_stream<T, D, E>(
where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static { &self,
_format: &Format,
_data_callback: D,
_error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
where
D: FnMut(InputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
unimplemented!() unimplemented!()
} }
/// Create an output stream. /// Create an output stream.
fn build_output_stream<D, E>(&self, _format: &Format, _data_callback: D, _error_callback: E) -> Result<Self::Stream, BuildStreamError> fn build_output_stream<T, D, E>(
where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static{ &self,
_format: &Format,
_data_callback: D,
_error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
where
D: FnMut(OutputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static,
{
unimplemented!() unimplemented!()
} }
} }

View File

@ -143,6 +143,7 @@
//! # let format = device.default_output_format().unwrap(); //! # let format = device.default_output_format().unwrap();
//! # let stream = device.build_output_stream(&format, move |_data| {}, move |_err| {}).unwrap(); //! # let stream = device.build_output_stream(&format, move |_data| {}, move |_err| {}).unwrap();
//! stream.pause().unwrap(); //! stream.pause().unwrap();
//! ```
#![recursion_limit = "512"] #![recursion_limit = "512"]
@ -202,25 +203,14 @@ pub struct SupportedFormat {
pub data_type: SampleFormat, pub data_type: SampleFormat,
} }
/// Stream data passed to the `EventLoop::run` callback.
#[derive(Debug)]
pub enum StreamData<'a> {
Input {
buffer: UnknownTypeInputBuffer<'a>,
},
Output {
buffer: UnknownTypeOutputBuffer<'a>,
},
}
/// Represents a buffer containing audio data that may be read. /// Represents a buffer containing audio data that may be read.
/// ///
/// This struct implements the `Deref` trait targeting `[T]`. Therefore this buffer can be read the /// This struct implements the `Deref` trait targeting `[T]`. Therefore this buffer can be read the
/// same way as reading from a `Vec` or any other kind of Rust array. /// same way as reading from a `Vec` or any other kind of Rust array.
// TODO: explain audio stuff in general // TODO: explain audio stuff in general
// TODO: remove the wrapper and just use slices in next major version // TODO: Consider making this an `enum` with `Interleaved` and `NonInterleaved` variants.
#[derive(Debug)] #[derive(Debug)]
pub struct InputBuffer<'a, T: 'a> pub struct InputData<'a, T: 'a>
where where
T: Sample, T: Sample,
{ {
@ -233,42 +223,16 @@ where
/// This struct implements the `Deref` and `DerefMut` traits to `[T]`. Therefore writing to this /// This struct implements the `Deref` and `DerefMut` traits to `[T]`. Therefore writing to this
/// buffer is done in the same way as writing to a `Vec` or any other kind of Rust array. /// buffer is done in the same way as writing to a `Vec` or any other kind of Rust array.
// TODO: explain audio stuff in general // TODO: explain audio stuff in general
// TODO: remove the wrapper and just use slices // TODO: Consider making this an `enum` with `Interleaved` and `NonInterleaved` variants.
#[must_use] #[must_use]
#[derive(Debug)] #[derive(Debug)]
pub struct OutputBuffer<'a, T: 'a> pub struct OutputData<'a, T: 'a>
where where
T: Sample, T: Sample,
{ {
buffer: &'a mut [T], buffer: &'a mut [T],
} }
/// This is the struct that is provided to you by cpal when you want to read samples from a buffer.
///
/// Since the type of data is only known at runtime, you have to read the right buffer.
#[derive(Debug)]
pub enum UnknownTypeInputBuffer<'a> {
/// Samples whose format is `u16`.
U16(InputBuffer<'a, u16>),
/// Samples whose format is `i16`.
I16(InputBuffer<'a, i16>),
/// Samples whose format is `f32`.
F32(InputBuffer<'a, f32>),
}
/// This is the struct that is provided to you by cpal when you want to write samples to a buffer.
///
/// Since the type of data is only known at runtime, you have to fill the right buffer.
#[derive(Debug)]
pub enum UnknownTypeOutputBuffer<'a> {
/// Samples whose format is `u16`.
U16(OutputBuffer<'a, u16>),
/// Samples whose format is `i16`.
I16(OutputBuffer<'a, i16>),
/// Samples whose format is `f32`.
F32(OutputBuffer<'a, f32>),
}
impl SupportedFormat { impl SupportedFormat {
/// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate. /// Turns this `SupportedFormat` into a `Format` corresponding to the maximum samples rate.
#[inline] #[inline]
@ -352,8 +316,9 @@ impl SupportedFormat {
} }
} }
impl<'a, T> Deref for InputBuffer<'a, T> impl<'a, T> Deref for InputData<'a, T>
where T: Sample where
T: Sample,
{ {
type Target = [T]; type Target = [T];
@ -363,8 +328,9 @@ impl<'a, T> Deref for InputBuffer<'a, T>
} }
} }
impl<'a, T> Deref for OutputBuffer<'a, T> impl<'a, T> Deref for OutputData<'a, T>
where T: Sample where
T: Sample,
{ {
type Target = [T]; type Target = [T];
@ -374,8 +340,9 @@ impl<'a, T> Deref for OutputBuffer<'a, T>
} }
} }
impl<'a, T> DerefMut for OutputBuffer<'a, T> impl<'a, T> DerefMut for OutputData<'a, T>
where T: Sample where
T: Sample,
{ {
#[inline] #[inline]
fn deref_mut(&mut self) -> &mut [T] { fn deref_mut(&mut self) -> &mut [T] {
@ -383,30 +350,6 @@ impl<'a, T> DerefMut for OutputBuffer<'a, T>
} }
} }
impl<'a> UnknownTypeInputBuffer<'a> {
/// Returns the length of the buffer in number of samples.
#[inline]
pub fn len(&self) -> usize {
match self {
&UnknownTypeInputBuffer::U16(ref buf) => buf.len(),
&UnknownTypeInputBuffer::I16(ref buf) => buf.len(),
&UnknownTypeInputBuffer::F32(ref buf) => buf.len(),
}
}
}
impl<'a> UnknownTypeOutputBuffer<'a> {
/// Returns the length of the buffer in number of samples.
#[inline]
pub fn len(&self) -> usize {
match self {
&UnknownTypeOutputBuffer::U16(ref buf) => buf.len(),
&UnknownTypeOutputBuffer::I16(ref buf) => buf.len(),
&UnknownTypeOutputBuffer::F32(ref buf) => buf.len(),
}
}
}
impl From<Format> for SupportedFormat { impl From<Format> for SupportedFormat {
#[inline] #[inline]
fn from(format: Format) -> SupportedFormat { fn from(format: Format) -> SupportedFormat {

View File

@ -255,8 +255,17 @@ macro_rules! impl_platform_host {
} }
} }
fn build_input_stream<D, E>(&self, format: &crate::Format, data_callback: D, error_callback: E) -> Result<Self::Stream, crate::BuildStreamError> fn build_input_stream<T, D, E>(
where D: FnMut(crate::StreamData) + Send + 'static, E: FnMut(crate::StreamError) + Send + 'static { &self,
format: &crate::Format,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, crate::BuildStreamError>
where
T: crate::Sample,
D: FnMut(crate::InputData<T>) + Send + 'static,
E: FnMut(crate::StreamError) + Send + 'static,
{
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(format, data_callback, error_callback)
@ -266,8 +275,17 @@ macro_rules! impl_platform_host {
} }
} }
fn build_output_stream<D, E>(&self, format: &crate::Format, data_callback: D, error_callback: E) -> Result<Self::Stream, crate::BuildStreamError> fn build_output_stream<T, D, E>(
where D: FnMut(crate::StreamData) + Send + 'static, E: FnMut(crate::StreamError) + Send + 'static { &self,
format: &crate::Format,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, crate::BuildStreamError>
where
T: crate::Sample,
D: FnMut(crate::OutputData<T>) + Send + 'static,
E: FnMut(crate::StreamError) + Send + 'static,
{
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(format, data_callback, error_callback)

View File

@ -21,20 +21,12 @@ impl SampleFormat {
&SampleFormat::F32 => mem::size_of::<f32>(), &SampleFormat::F32 => mem::size_of::<f32>(),
} }
} }
/// Deprecated. Use `sample_size` instead.
#[inline]
#[deprecated]
pub fn get_sample_size(&self) -> usize {
self.sample_size()
}
} }
/// Trait for containers that contain PCM data. /// Trait for containers that contain PCM data.
pub unsafe trait Sample: Copy + Clone { pub unsafe trait Sample: Copy + Clone {
/// Returns the `SampleFormat` corresponding to this data type. /// The `SampleFormat` corresponding to this data type.
// TODO: rename to `format()`. Requires a breaking change. const FORMAT: SampleFormat;
fn get_format() -> SampleFormat;
/// Turns the sample into its equivalent as a floating-point. /// Turns the sample into its equivalent as a floating-point.
fn to_f32(&self) -> f32; fn to_f32(&self) -> f32;
@ -49,10 +41,7 @@ pub unsafe trait Sample: Copy + Clone {
} }
unsafe impl Sample for u16 { unsafe impl Sample for u16 {
#[inline] const FORMAT: SampleFormat = SampleFormat::U16;
fn get_format() -> SampleFormat {
SampleFormat::U16
}
#[inline] #[inline]
fn to_f32(&self) -> f32 { fn to_f32(&self) -> f32 {
@ -82,10 +71,7 @@ unsafe impl Sample for u16 {
} }
unsafe impl Sample for i16 { unsafe impl Sample for i16 {
#[inline] const FORMAT: SampleFormat = SampleFormat::I16;
fn get_format() -> SampleFormat {
SampleFormat::I16
}
#[inline] #[inline]
fn to_f32(&self) -> f32 { fn to_f32(&self) -> f32 {
@ -119,10 +105,7 @@ unsafe impl Sample for i16 {
} }
unsafe impl Sample for f32 { unsafe impl Sample for f32 {
#[inline] const FORMAT: SampleFormat = SampleFormat::F32;
fn get_format() -> SampleFormat {
SampleFormat::F32
}
#[inline] #[inline]
fn to_f32(&self) -> f32 { fn to_f32(&self) -> f32 {

View File

@ -6,11 +6,13 @@ use {
DeviceNameError, DeviceNameError,
DevicesError, DevicesError,
Format, Format,
InputData,
InputDevices, InputDevices,
OutputData,
OutputDevices, OutputDevices,
PauseStreamError, PauseStreamError,
PlayStreamError, PlayStreamError,
StreamData, Sample,
StreamError, StreamError,
SupportedFormat, SupportedFormat,
SupportedFormatsError, SupportedFormatsError,
@ -118,12 +120,28 @@ 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>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError> fn build_input_stream<T, D, E>(
where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static; &self,
format: &Format,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
where
T: Sample,
D: FnMut(InputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static;
/// Create an output stream. /// Create an output stream.
fn build_output_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError> fn build_output_stream<T, D, E>(
where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static; &self,
format: &Format,
data_callback: D,
error_callback: E,
) -> Result<Self::Stream, BuildStreamError>
where
T: Sample,
D: FnMut(OutputData<T>) + Send + 'static,
E: FnMut(StreamError) + Send + 'static;
} }
/// A stream created from `Device`, with methods to control playback. /// A stream created from `Device`, with methods to control playback.