Merge pull request #3 from mitchmindtree/remove-unknown-buffer-type-alt
An alternative approach to removing `UnknownBufferType`
This commit is contained in:
commit
d7d82ac863
|
@ -19,27 +19,15 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
(sample_clock * 440.0 * 2.0 * 3.141592 / sample_rate).sin()
|
(sample_clock * 440.0 * 2.0 * 3.141592 / sample_rate).sin()
|
||||||
};
|
};
|
||||||
|
|
||||||
let err_fn = |err| {
|
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
|
||||||
eprintln!("an error occurred on stream: {}", err);
|
|
||||||
|
let data_fn = move |data: &mut cpal::Data| match data.sample_format() {
|
||||||
|
cpal::SampleFormat::F32 => write_data::<f32>(data, channels, &mut next_value),
|
||||||
|
cpal::SampleFormat::I16 => write_data::<i16>(data, channels, &mut next_value),
|
||||||
|
cpal::SampleFormat::U16 => write_data::<u16>(data, channels, &mut next_value),
|
||||||
};
|
};
|
||||||
|
|
||||||
let stream = match format.data_type {
|
let stream = device.build_output_stream(&format, data_fn, err_fn)?;
|
||||||
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()?;
|
||||||
|
|
||||||
|
@ -48,10 +36,11 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_data<T>(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> f32)
|
fn write_data<T>(output: &mut cpal::Data, 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() {
|
||||||
|
|
|
@ -47,13 +47,10 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
producer.push(0.0).unwrap();
|
producer.push(0.0).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build streams.
|
let input_data_fn = move |data: &cpal::Data| {
|
||||||
println!("Attempting to build both streams with `{:?}`.", format);
|
|
||||||
let input_stream = input_device.build_input_stream(
|
|
||||||
&format,
|
|
||||||
move |data| {
|
|
||||||
let mut output_fell_behind = false;
|
let mut output_fell_behind = false;
|
||||||
for &sample in data.iter() {
|
let data = data.as_slice::<f32>().expect("unexpected sample type");
|
||||||
|
for &sample in data {
|
||||||
if producer.push(sample).is_err() {
|
if producer.push(sample).is_err() {
|
||||||
output_fell_behind = true;
|
output_fell_behind = true;
|
||||||
}
|
}
|
||||||
|
@ -61,16 +58,12 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
if output_fell_behind {
|
if output_fell_behind {
|
||||||
eprintln!("output stream fell behind: try increasing latency");
|
eprintln!("output stream fell behind: try increasing latency");
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|err| {
|
|
||||||
eprintln!("an error occurred on stream: {}", err);
|
let output_data_fn = move |data: &mut cpal::Data| {
|
||||||
},
|
|
||||||
)?;
|
|
||||||
let output_stream = output_device.build_output_stream(
|
|
||||||
&format,
|
|
||||||
move |mut data| {
|
|
||||||
let mut input_fell_behind = None;
|
let mut input_fell_behind = None;
|
||||||
for sample in data.iter_mut() {
|
let data = data.as_slice_mut::<f32>().expect("unexpected sample type");
|
||||||
|
for sample in data {
|
||||||
*sample = match consumer.pop() {
|
*sample = match consumer.pop() {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -82,11 +75,12 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
if let Some(err) = input_fell_behind {
|
if let Some(err) = input_fell_behind {
|
||||||
eprintln!("input stream fell behind: {:?}: try increasing latency", err);
|
eprintln!("input stream fell behind: {:?}: try increasing latency", err);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
move |err| {
|
|
||||||
eprintln!("an error occurred on output stream: {}", err);
|
// Build streams.
|
||||||
},
|
println!("Attempting to build both streams with `{:?}`.", format);
|
||||||
)?;
|
let input_stream = input_device.build_input_stream(&format, input_data_fn, err_fn)?;
|
||||||
|
let output_stream = output_device.build_output_stream(&format, output_data_fn, err_fn)?;
|
||||||
println!("Successfully built streams.");
|
println!("Successfully built streams.");
|
||||||
|
|
||||||
// Play the streams.
|
// Play the streams.
|
||||||
|
@ -105,3 +99,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||||
println!("Done!");
|
println!("Done!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn err_fn(err: cpal::StreamError) {
|
||||||
|
eprintln!("an error occurred on stream: {}", err);
|
||||||
|
}
|
||||||
|
|
|
@ -40,23 +40,15 @@ 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 data_fn = move |data: &cpal::Data| {
|
||||||
cpal::SampleFormat::F32 => device.build_input_stream(
|
match data.sample_format() {
|
||||||
&format,
|
cpal::SampleFormat::F32 => write_input_data::<f32, f32>(data, &writer_2),
|
||||||
move |data| write_input_data::<f32, f32>(&*data, &writer_2),
|
cpal::SampleFormat::I16 => write_input_data::<i16, i16>(data, &writer_2),
|
||||||
err_fn,
|
cpal::SampleFormat::U16 => write_input_data::<u16, i16>(data, &writer_2),
|
||||||
),
|
}
|
||||||
cpal::SampleFormat::I16 => device.build_input_stream(
|
};
|
||||||
&format,
|
|
||||||
move |data| write_input_data::<i16, i16>(&*data, &writer_2),
|
let stream = device.build_input_stream(&format, data_fn, err_fn)?;
|
||||||
err_fn,
|
|
||||||
),
|
|
||||||
cpal::SampleFormat::U16 => device.build_input_stream(
|
|
||||||
&format,
|
|
||||||
move |data| write_input_data::<u16, i16>(&*data, &writer_2),
|
|
||||||
err_fn,
|
|
||||||
),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
stream.play()?;
|
stream.play()?;
|
||||||
|
|
||||||
|
@ -87,11 +79,12 @@ 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: &[T], writer: &WavWriterHandle)
|
fn write_input_data<T, U>(input: &cpal::Data, 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() {
|
||||||
|
|
|
@ -5,15 +5,13 @@ use crate::{
|
||||||
BackendSpecificError,
|
BackendSpecificError,
|
||||||
BuildStreamError,
|
BuildStreamError,
|
||||||
ChannelCount,
|
ChannelCount,
|
||||||
|
Data,
|
||||||
DefaultFormatError,
|
DefaultFormatError,
|
||||||
DeviceNameError,
|
DeviceNameError,
|
||||||
DevicesError,
|
DevicesError,
|
||||||
Format,
|
Format,
|
||||||
InputData,
|
|
||||||
OutputData,
|
|
||||||
PauseStreamError,
|
PauseStreamError,
|
||||||
PlayStreamError,
|
PlayStreamError,
|
||||||
Sample,
|
|
||||||
SampleFormat,
|
SampleFormat,
|
||||||
SampleRate,
|
SampleRate,
|
||||||
StreamError,
|
StreamError,
|
||||||
|
@ -90,37 +88,31 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<T, D, E>(
|
fn build_input_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
D: FnMut(InputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + 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_inner = self.build_stream_inner(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<T, D, E>(
|
fn build_output_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
D: FnMut(OutputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + 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_inner = self.build_stream_inner(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)
|
||||||
|
@ -564,14 +556,12 @@ struct StreamWorkerContext {
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_stream_worker<T>(
|
fn input_stream_worker(
|
||||||
rx: TriggerReceiver,
|
rx: TriggerReceiver,
|
||||||
stream: &StreamInner,
|
stream: &StreamInner,
|
||||||
data_callback: &mut (dyn FnMut(InputData<T>) + Send + 'static),
|
data_callback: &mut (dyn FnMut(&Data) + Send + 'static),
|
||||||
error_callback: &mut (dyn FnMut(StreamError) + Send + 'static),
|
error_callback: &mut (dyn FnMut(StreamError) + Send + 'static),
|
||||||
) where
|
) {
|
||||||
T: Sample,
|
|
||||||
{
|
|
||||||
let mut ctxt = StreamWorkerContext::default();
|
let mut ctxt = StreamWorkerContext::default();
|
||||||
loop {
|
loop {
|
||||||
match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) {
|
match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) {
|
||||||
|
@ -583,7 +573,7 @@ fn input_stream_worker<T>(
|
||||||
StreamType::Input,
|
StreamType::Input,
|
||||||
"expected input stream, but polling descriptors indicated output",
|
"expected input stream, but polling descriptors indicated output",
|
||||||
);
|
);
|
||||||
process_input::<T>(
|
process_input(
|
||||||
stream,
|
stream,
|
||||||
&mut ctxt.buffer,
|
&mut ctxt.buffer,
|
||||||
available_frames,
|
available_frames,
|
||||||
|
@ -595,14 +585,12 @@ fn input_stream_worker<T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn output_stream_worker<T>(
|
fn output_stream_worker(
|
||||||
rx: TriggerReceiver,
|
rx: TriggerReceiver,
|
||||||
stream: &StreamInner,
|
stream: &StreamInner,
|
||||||
data_callback: &mut (dyn FnMut(OutputData<T>) + Send + 'static),
|
data_callback: &mut (dyn FnMut(&mut Data) + Send + 'static),
|
||||||
error_callback: &mut (dyn FnMut(StreamError) + Send + 'static),
|
error_callback: &mut (dyn FnMut(StreamError) + Send + 'static),
|
||||||
) where
|
) {
|
||||||
T: Sample,
|
|
||||||
{
|
|
||||||
let mut ctxt = StreamWorkerContext::default();
|
let mut ctxt = StreamWorkerContext::default();
|
||||||
loop {
|
loop {
|
||||||
match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) {
|
match poll_descriptors_and_prepare_buffer(&rx, stream, &mut ctxt, error_callback) {
|
||||||
|
@ -614,7 +602,7 @@ fn output_stream_worker<T>(
|
||||||
StreamType::Output,
|
StreamType::Output,
|
||||||
"expected output stream, but polling descriptors indicated input",
|
"expected output stream, but polling descriptors indicated input",
|
||||||
);
|
);
|
||||||
process_output::<T>(
|
process_output(
|
||||||
stream,
|
stream,
|
||||||
&mut ctxt.buffer,
|
&mut ctxt.buffer,
|
||||||
available_frames,
|
available_frames,
|
||||||
|
@ -729,15 +717,13 @@ fn poll_descriptors_and_prepare_buffer(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read input data from ALSA and deliver it to the user.
|
// Read input data from ALSA and deliver it to the user.
|
||||||
fn process_input<T>(
|
fn process_input(
|
||||||
stream: &StreamInner,
|
stream: &StreamInner,
|
||||||
buffer: &mut [u8],
|
buffer: &mut [u8],
|
||||||
available_frames: usize,
|
available_frames: usize,
|
||||||
data_callback: &mut (dyn FnMut(InputData<T>) + Send + 'static),
|
data_callback: &mut (dyn FnMut(&Data) + Send + 'static),
|
||||||
error_callback: &mut dyn FnMut(StreamError),
|
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,
|
||||||
|
@ -750,28 +736,34 @@ fn process_input<T>(
|
||||||
error_callback(BackendSpecificError { description }.into());
|
error_callback(BackendSpecificError { description }.into());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let buffer = unsafe { cast_input_buffer::<T>(buffer) };
|
let sample_format = stream.sample_format;
|
||||||
let input_data = InputData { buffer };
|
let data = buffer.as_mut_ptr() as *mut ();
|
||||||
data_callback(input_data);
|
let len = buffer.len() / sample_format.sample_size();
|
||||||
|
let data = unsafe {
|
||||||
|
Data::from_parts(data, len, sample_format)
|
||||||
|
};
|
||||||
|
data_callback(&data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request data from the user's function and write it via ALSA.
|
// Request data from the user's function and write it via ALSA.
|
||||||
//
|
//
|
||||||
// Returns `true`
|
// Returns `true`
|
||||||
fn process_output<T>(
|
fn process_output(
|
||||||
stream: &StreamInner,
|
stream: &StreamInner,
|
||||||
buffer: &mut [u8],
|
buffer: &mut [u8],
|
||||||
available_frames: usize,
|
available_frames: usize,
|
||||||
data_callback: &mut (dyn FnMut(OutputData<T>) + Send + 'static),
|
data_callback: &mut (dyn FnMut(&mut Data) + Send + 'static),
|
||||||
error_callback: &mut dyn FnMut(StreamError),
|
error_callback: &mut dyn FnMut(StreamError),
|
||||||
) where
|
) {
|
||||||
T: Sample,
|
|
||||||
{
|
|
||||||
{
|
{
|
||||||
// We're now sure that we're ready to write data.
|
// We're now sure that we're ready to write data.
|
||||||
let buffer = unsafe { cast_output_buffer::<T>(buffer) };
|
let sample_format = stream.sample_format;
|
||||||
let output_data = OutputData { buffer };
|
let data = buffer.as_mut_ptr() as *mut ();
|
||||||
data_callback(output_data);
|
let len = buffer.len() / sample_format.sample_size();
|
||||||
|
let mut data = unsafe {
|
||||||
|
Data::from_parts(data, len, sample_format)
|
||||||
|
};
|
||||||
|
data_callback(&mut data);
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
let result = unsafe {
|
let result = unsafe {
|
||||||
|
@ -805,21 +797,20 @@ fn process_output<T>(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream {
|
impl Stream {
|
||||||
fn new_input<T, D, E>(
|
fn new_input<D, E>(
|
||||||
inner: Arc<StreamInner>,
|
inner: Arc<StreamInner>,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
mut error_callback: E,
|
mut error_callback: E,
|
||||||
) -> Stream
|
) -> Stream
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
D: FnMut(InputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + 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 || {
|
||||||
input_stream_worker::<T>(rx, &*stream, &mut data_callback, &mut error_callback);
|
input_stream_worker(rx, &*stream, &mut data_callback, &mut error_callback);
|
||||||
});
|
});
|
||||||
Stream {
|
Stream {
|
||||||
thread: Some(thread),
|
thread: Some(thread),
|
||||||
|
@ -828,21 +819,20 @@ impl Stream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_output<T, D, E>(
|
fn new_output<D, E>(
|
||||||
inner: Arc<StreamInner>,
|
inner: Arc<StreamInner>,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
mut error_callback: E,
|
mut error_callback: E,
|
||||||
) -> Stream
|
) -> Stream
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
D: FnMut(OutputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + 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 || {
|
||||||
output_stream_worker::<T>(rx, &*stream, &mut data_callback, &mut error_callback);
|
output_stream_worker(rx, &*stream, &mut data_callback, &mut error_callback);
|
||||||
});
|
});
|
||||||
Stream {
|
Stream {
|
||||||
thread: Some(thread),
|
thread: Some(thread),
|
||||||
|
@ -1080,17 +1070,3 @@ fn check_errors(err: libc::c_int) -> Result<(), String> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cast a byte slice into a (immutable) slice of desired type.
|
|
||||||
/// Safety: it's up to the caller to ensure that the input slice has valid bit representations.
|
|
||||||
unsafe fn cast_input_buffer<T>(v: &[u8]) -> &[T] {
|
|
||||||
debug_assert!(v.len() % std::mem::size_of::<T>() == 0);
|
|
||||||
std::slice::from_raw_parts(v.as_ptr() as *const T, v.len() / std::mem::size_of::<T>())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cast a byte slice into a mutable slice of desired type.
|
|
||||||
/// Safety: it's up to the caller to ensure that the input slice has valid bit representations.
|
|
||||||
unsafe fn cast_output_buffer<T>(v: &mut [u8]) -> &mut [T] {
|
|
||||||
debug_assert!(v.len() % std::mem::size_of::<T>() == 0);
|
|
||||||
std::slice::from_raw_parts_mut(v.as_mut_ptr() as *mut T, v.len() / std::mem::size_of::<T>())
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,15 +3,13 @@ extern crate parking_lot;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
BuildStreamError,
|
BuildStreamError,
|
||||||
|
Data,
|
||||||
DefaultFormatError,
|
DefaultFormatError,
|
||||||
DeviceNameError,
|
DeviceNameError,
|
||||||
DevicesError,
|
DevicesError,
|
||||||
Format,
|
Format,
|
||||||
InputData,
|
|
||||||
OutputData,
|
|
||||||
PauseStreamError,
|
PauseStreamError,
|
||||||
PlayStreamError,
|
PlayStreamError,
|
||||||
Sample,
|
|
||||||
StreamError,
|
StreamError,
|
||||||
SupportedFormatsError,
|
SupportedFormatsError,
|
||||||
};
|
};
|
||||||
|
@ -91,29 +89,27 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<T, D, E>(
|
fn build_input_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
D: FnMut(InputData<T>) + 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(self, format, data_callback, error_callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<T, D, E>(
|
fn build_output_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
D: FnMut(OutputData<T>) + 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(self, format, data_callback, error_callback)
|
||||||
|
|
|
@ -9,9 +9,8 @@ use std::sync::Arc;
|
||||||
use super::parking_lot::Mutex;
|
use super::parking_lot::Mutex;
|
||||||
use BackendSpecificError;
|
use BackendSpecificError;
|
||||||
use BuildStreamError;
|
use BuildStreamError;
|
||||||
|
use Data;
|
||||||
use Format;
|
use Format;
|
||||||
use InputData;
|
|
||||||
use OutputData;
|
|
||||||
use PauseStreamError;
|
use PauseStreamError;
|
||||||
use PlayStreamError;
|
use PlayStreamError;
|
||||||
use Sample;
|
use Sample;
|
||||||
|
@ -58,18 +57,16 @@ impl Stream {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
pub fn build_input_stream<T, D, E>(
|
pub fn build_input_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
_error_callback: E,
|
_error_callback: E,
|
||||||
) -> Result<Stream, BuildStreamError>
|
) -> Result<Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
D: FnMut(InputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
assert_eq!(format.data_type, T::FORMAT, "sample type does not match `format.data_type`");
|
|
||||||
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.
|
||||||
|
@ -118,7 +115,7 @@ impl Device {
|
||||||
where
|
where
|
||||||
A: AsioSample,
|
A: AsioSample,
|
||||||
B: Sample,
|
B: Sample,
|
||||||
D: FnMut(InputData<B>) + Send + 'static,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
F: Fn(A) -> A,
|
F: Fn(A) -> A,
|
||||||
{
|
{
|
||||||
// 1. Write the ASIO channels to the CPAL buffer.
|
// 1. Write the ASIO channels to the CPAL buffer.
|
||||||
|
@ -132,13 +129,15 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Deliver the interleaved buffer to the callback.
|
// 2. Deliver the interleaved buffer to the callback.
|
||||||
let data = InputData { buffer: interleaved };
|
let data = interleaved.as_mut_ptr() as *mut ();
|
||||||
callback(data);
|
let len = interleaved.len();
|
||||||
|
let data = Data::from_parts(data, len, B::FORMAT);
|
||||||
|
callback(&data);
|
||||||
}
|
}
|
||||||
|
|
||||||
match (&stream_type, data_type) {
|
match (&stream_type, data_type) {
|
||||||
(&sys::AsioSampleType::ASIOSTInt16LSB, SampleFormat::I16) => {
|
(&sys::AsioSampleType::ASIOSTInt16LSB, SampleFormat::I16) => {
|
||||||
process_input_callback::<i16, T, _, _>(
|
process_input_callback::<i16, i16, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
|
@ -147,7 +146,7 @@ impl Device {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(&sys::AsioSampleType::ASIOSTInt16MSB, SampleFormat::I16) => {
|
(&sys::AsioSampleType::ASIOSTInt16MSB, SampleFormat::I16) => {
|
||||||
process_input_callback::<i16, T, _, _>(
|
process_input_callback::<i16, i16, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
|
@ -160,7 +159,7 @@ impl Device {
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32) |
|
(&sys::AsioSampleType::ASIOSTFloat32LSB, SampleFormat::F32) |
|
||||||
(&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => {
|
(&sys::AsioSampleType::ASIOSTFloat32MSB, SampleFormat::F32) => {
|
||||||
process_input_callback::<f32, T, _, _>(
|
process_input_callback::<f32, f32, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
|
@ -173,7 +172,7 @@ impl Device {
|
||||||
// `process_output_callback` function above by removing the unnecessary sample
|
// `process_output_callback` function above by removing the unnecessary sample
|
||||||
// conversion function.
|
// conversion function.
|
||||||
(&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I16) => {
|
(&sys::AsioSampleType::ASIOSTInt32LSB, SampleFormat::I16) => {
|
||||||
process_input_callback::<i32, T, _, _>(
|
process_input_callback::<i32, i16, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
|
@ -182,7 +181,7 @@ impl Device {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I16) => {
|
(&sys::AsioSampleType::ASIOSTInt32MSB, SampleFormat::I16) => {
|
||||||
process_input_callback::<i32, T, _, _>(
|
process_input_callback::<i32, i16, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
|
@ -194,7 +193,7 @@ impl Device {
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F32) |
|
(&sys::AsioSampleType::ASIOSTFloat64LSB, SampleFormat::F32) |
|
||||||
(&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F32) => {
|
(&sys::AsioSampleType::ASIOSTFloat64MSB, SampleFormat::F32) => {
|
||||||
process_input_callback::<f64, T, _, _>(
|
process_input_callback::<f64, f32, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
asio_stream,
|
asio_stream,
|
||||||
|
@ -224,18 +223,16 @@ impl Device {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_output_stream<T, D, E>(
|
pub fn build_output_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
_error_callback: E,
|
_error_callback: E,
|
||||||
) -> Result<Stream, BuildStreamError>
|
) -> Result<Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
D: FnMut(OutputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
assert_eq!(format.data_type, T::FORMAT, "sample type does not match `format.data_type`");
|
|
||||||
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.
|
||||||
|
@ -308,12 +305,15 @@ impl Device {
|
||||||
where
|
where
|
||||||
A: Sample,
|
A: Sample,
|
||||||
B: AsioSample,
|
B: AsioSample,
|
||||||
D: FnMut(OutputData<A>) + Send + 'static,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
F: Fn(B) -> B,
|
F: Fn(B) -> B,
|
||||||
{
|
{
|
||||||
// 1. Render interleaved buffer from callback.
|
// 1. Render interleaved buffer from callback.
|
||||||
let interleaved: &mut [A] = cast_slice_mut(interleaved);
|
let interleaved: &mut [A] = cast_slice_mut(interleaved);
|
||||||
callback(OutputData { buffer: interleaved });
|
let data = interleaved.as_mut_ptr() as *mut ();
|
||||||
|
let len = interleaved.len();
|
||||||
|
let mut data = Data::from_parts(data, len, A::FORMAT);
|
||||||
|
callback(&mut data);
|
||||||
|
|
||||||
// 2. Silence ASIO channels if necessary.
|
// 2. Silence ASIO channels if necessary.
|
||||||
let n_channels = interleaved.len() / asio_stream.buffer_size as usize;
|
let n_channels = interleaved.len() / asio_stream.buffer_size as usize;
|
||||||
|
@ -337,7 +337,7 @@ impl Device {
|
||||||
|
|
||||||
match (data_type, &stream_type) {
|
match (data_type, &stream_type) {
|
||||||
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16LSB) => {
|
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16LSB) => {
|
||||||
process_output_callback::<T, i16, _, _>(
|
process_output_callback::<i16, i16, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
silence,
|
silence,
|
||||||
|
@ -347,7 +347,7 @@ impl Device {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16MSB) => {
|
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt16MSB) => {
|
||||||
process_output_callback::<T, i16, _, _>(
|
process_output_callback::<i16, i16, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
silence,
|
silence,
|
||||||
|
@ -361,7 +361,7 @@ impl Device {
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB) |
|
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32LSB) |
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => {
|
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat32MSB) => {
|
||||||
process_output_callback::<T, f32, _, _>(
|
process_output_callback::<f32, f32, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
silence,
|
silence,
|
||||||
|
@ -375,7 +375,7 @@ impl Device {
|
||||||
// `process_output_callback` function above by removing the unnecessary sample
|
// `process_output_callback` function above by removing the unnecessary sample
|
||||||
// conversion function.
|
// conversion function.
|
||||||
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32LSB) => {
|
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32LSB) => {
|
||||||
process_output_callback::<T, i32, _, _>(
|
process_output_callback::<i16, i32, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
silence,
|
silence,
|
||||||
|
@ -385,7 +385,7 @@ impl Device {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32MSB) => {
|
(SampleFormat::I16, &sys::AsioSampleType::ASIOSTInt32MSB) => {
|
||||||
process_output_callback::<T, i32, _, _>(
|
process_output_callback::<i16, i32, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
silence,
|
silence,
|
||||||
|
@ -398,7 +398,7 @@ impl Device {
|
||||||
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
// trait for the `to_le` and `to_be` methods, but this does not support floats.
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64LSB) |
|
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64LSB) |
|
||||||
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64MSB) => {
|
(SampleFormat::F32, &sys::AsioSampleType::ASIOSTFloat64MSB) => {
|
||||||
process_output_callback::<T, f64, _, _>(
|
process_output_callback::<f32, f64, _, _>(
|
||||||
&mut data_callback,
|
&mut data_callback,
|
||||||
&mut interleaved,
|
&mut interleaved,
|
||||||
silence,
|
silence,
|
||||||
|
|
|
@ -5,15 +5,13 @@ use crate::{
|
||||||
ChannelCount,
|
ChannelCount,
|
||||||
BackendSpecificError,
|
BackendSpecificError,
|
||||||
BuildStreamError,
|
BuildStreamError,
|
||||||
|
Data,
|
||||||
DefaultFormatError,
|
DefaultFormatError,
|
||||||
DeviceNameError,
|
DeviceNameError,
|
||||||
DevicesError,
|
DevicesError,
|
||||||
Format,
|
Format,
|
||||||
InputData,
|
|
||||||
OutputData,
|
|
||||||
PauseStreamError,
|
PauseStreamError,
|
||||||
PlayStreamError,
|
PlayStreamError,
|
||||||
Sample,
|
|
||||||
SampleFormat,
|
SampleFormat,
|
||||||
SampleRate,
|
SampleRate,
|
||||||
StreamError,
|
StreamError,
|
||||||
|
@ -131,33 +129,29 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<T, D, E>(
|
fn build_input_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
D: FnMut(InputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
assert_eq!(T::FORMAT, format.data_type);
|
|
||||||
Device::build_input_stream(self, format, data_callback, error_callback)
|
Device::build_input_stream(self, format, data_callback, error_callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<T, D, E>(
|
fn build_output_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
D: FnMut(OutputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
assert_eq!(T::FORMAT, format.data_type);
|
|
||||||
Device::build_output_stream(self, format, data_callback, error_callback)
|
Device::build_output_stream(self, format, data_callback, error_callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -500,15 +494,14 @@ fn audio_unit_from_device(device: &Device, input: bool) -> Result<AudioUnit, cor
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
fn build_input_stream<T, D, E>(
|
fn build_input_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
_error_callback: E,
|
_error_callback: E,
|
||||||
) -> Result<Stream, BuildStreamError>
|
) -> Result<Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
D: FnMut(InputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
// The scope and element for working with a device's input stream.
|
// The scope and element for working with a device's input stream.
|
||||||
|
@ -657,7 +650,8 @@ impl Device {
|
||||||
|
|
||||||
// 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 bytes_per_channel = std::mem::size_of::<T>();
|
let sample_format = format.data_type;
|
||||||
|
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 {
|
||||||
let ptr = (*args.data.data).mBuffers.as_ptr() as *const AudioBuffer;
|
let ptr = (*args.data.data).mBuffers.as_ptr() as *const AudioBuffer;
|
||||||
|
@ -671,10 +665,10 @@ impl Device {
|
||||||
mData: data
|
mData: data
|
||||||
} = buffers[0];
|
} = buffers[0];
|
||||||
|
|
||||||
let data_len = (data_byte_size as usize / bytes_per_channel) as usize;
|
let data = data as *mut ();
|
||||||
let data_slice = slice::from_raw_parts(data as *const T, data_len);
|
let len = (data_byte_size as usize / bytes_per_channel) as usize;
|
||||||
let input_data = InputData { buffer: data_slice };
|
let data = Data::from_parts(data, len, sample_format);
|
||||||
data_callback(input_data);
|
data_callback(&data);
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -687,15 +681,14 @@ impl Device {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<T, D, E>(
|
fn build_output_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
_error_callback: E,
|
_error_callback: E,
|
||||||
) -> Result<Stream, BuildStreamError>
|
) -> Result<Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
D: FnMut(OutputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
let mut audio_unit = audio_unit_from_device(self, false)?;
|
let mut audio_unit = audio_unit_from_device(self, false)?;
|
||||||
|
@ -710,7 +703,8 @@ impl Device {
|
||||||
|
|
||||||
// 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 bytes_per_channel = std::mem::size_of::<T>();
|
let sample_format = format.data_type;
|
||||||
|
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 {
|
||||||
// If `run()` is currently running, then a callback will be available from this list.
|
// If `run()` is currently running, then a callback will be available from this list.
|
||||||
|
@ -722,10 +716,10 @@ impl Device {
|
||||||
mData: data
|
mData: data
|
||||||
} = (*args.data.data).mBuffers[0];
|
} = (*args.data.data).mBuffers[0];
|
||||||
|
|
||||||
let data_len = (data_byte_size as usize / bytes_per_channel) as usize;
|
let data = data as *mut ();
|
||||||
let data_slice = slice::from_raw_parts_mut(data as *mut T, data_len);
|
let len = (data_byte_size as usize / bytes_per_channel) as usize;
|
||||||
let output_data = OutputData { buffer: data_slice };
|
let mut data = Data::from_parts(data, len, sample_format);
|
||||||
data_callback(output_data);
|
data_callback(&mut data);
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,13 @@ use stdweb::web::set_timeout;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
BuildStreamError,
|
BuildStreamError,
|
||||||
|
Data,
|
||||||
DefaultFormatError,
|
DefaultFormatError,
|
||||||
DeviceNameError,
|
DeviceNameError,
|
||||||
DevicesError,
|
DevicesError,
|
||||||
Format,
|
Format,
|
||||||
InputData,
|
|
||||||
OutputData,
|
|
||||||
PauseStreamError,
|
PauseStreamError,
|
||||||
PlayStreamError,
|
PlayStreamError,
|
||||||
Sample,
|
|
||||||
SampleFormat,
|
SampleFormat,
|
||||||
StreamError,
|
StreamError,
|
||||||
SupportedFormat,
|
SupportedFormat,
|
||||||
|
@ -160,32 +158,34 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<T, D, E>(
|
fn build_input_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
_format: &Format,
|
_format: &Format,
|
||||||
_data_callback: D,
|
_data_callback: D,
|
||||||
_error_callback: E,
|
_error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
D: FnMut(InputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<T, D, E>(
|
fn build_output_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
_format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
D: FnMut(OutputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
assert_eq!(T::FORMAT, SampleFormat::F32, "emscripten backend only supports `f32` data");
|
assert_eq!(
|
||||||
|
format.data_type,
|
||||||
|
SampleFormat::F32,
|
||||||
|
"emscripten backend currently only supports `f32` data",
|
||||||
|
);
|
||||||
|
|
||||||
// Create the stream.
|
// Create the stream.
|
||||||
let audio_ctxt_ref = js!(return new AudioContext()).into_reference().unwrap();
|
let audio_ctxt_ref = js!(return new AudioContext()).into_reference().unwrap();
|
||||||
|
@ -201,7 +201,7 @@ impl DeviceTrait for Device {
|
||||||
//
|
//
|
||||||
// See also: The call to `set_timeout` at the end of the `audio_callback_fn` which creates
|
// See also: The call to `set_timeout` at the end of the `audio_callback_fn` which creates
|
||||||
// the loop.
|
// the loop.
|
||||||
set_timeout(|| audio_callback_fn::<T, D, E>(user_data_ptr as *mut c_void), 10);
|
set_timeout(|| audio_callback_fn::<D, E>(user_data_ptr as *mut c_void), 10);
|
||||||
|
|
||||||
Ok(stream)
|
Ok(stream)
|
||||||
}
|
}
|
||||||
|
@ -223,10 +223,9 @@ impl StreamTrait for Stream {
|
||||||
|
|
||||||
// The first argument of the callback function (a `void*`) is a casted pointer to `self`
|
// The first argument of the callback function (a `void*`) is a casted pointer to `self`
|
||||||
// and to the `callback` parameter that was passed to `run`.
|
// and to the `callback` parameter that was passed to `run`.
|
||||||
fn audio_callback_fn<T, D, E>(user_data_ptr: *mut c_void)
|
fn audio_callback_fn<D, E>(user_data_ptr: *mut c_void)
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
D: FnMut(OutputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -236,12 +235,14 @@ where
|
||||||
let audio_ctxt = &stream.audio_ctxt_ref;
|
let audio_ctxt = &stream.audio_ctxt_ref;
|
||||||
|
|
||||||
// TODO: We should be re-using a buffer.
|
// TODO: We should be re-using a buffer.
|
||||||
let mut temporary_buffer: Vec<_> = (0..44100 * 2 / 3).map(|_| T::from(&0.0)).collect();
|
let mut temporary_buffer = vec![0.0; 44100 * 2 / 3];
|
||||||
|
|
||||||
{
|
{
|
||||||
let buffer = &mut temporary_buffer;
|
let len = temporary_buffer.len();
|
||||||
let data = OutputData { buffer };
|
let data = temporary_buffer.as_mut_ptr() as *mut ();
|
||||||
data_cb(data);
|
let sample_format = SampleFormat::F32;
|
||||||
|
let mut data = Data::from_parts(data, len, sample_format);
|
||||||
|
data_cb(&mut data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: directly use a TypedArray<f32> once this is supported by stdweb
|
// TODO: directly use a TypedArray<f32> once this is supported by stdweb
|
||||||
|
@ -281,7 +282,7 @@ where
|
||||||
// TODO: handle latency better ; right now we just use setInterval with the amount of sound
|
// TODO: handle latency better ; right now we just use setInterval with the amount of sound
|
||||||
// data that is in each buffer ; this is obviously bad, and also the schedule is too tight
|
// data that is in each buffer ; this is obviously bad, and also the schedule is too tight
|
||||||
// and there may be underflows
|
// and there may be underflows
|
||||||
set_timeout(|| audio_callback_fn::<T, D, E>(user_data_ptr), 330);
|
set_timeout(|| audio_callback_fn::<D, E>(user_data_ptr), 330);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
BuildStreamError,
|
BuildStreamError,
|
||||||
|
Data,
|
||||||
DefaultFormatError,
|
DefaultFormatError,
|
||||||
DevicesError,
|
DevicesError,
|
||||||
DeviceNameError,
|
DeviceNameError,
|
||||||
Format,
|
Format,
|
||||||
InputData,
|
|
||||||
OutputData,
|
|
||||||
PauseStreamError,
|
PauseStreamError,
|
||||||
PlayStreamError,
|
PlayStreamError,
|
||||||
StreamError,
|
StreamError,
|
||||||
|
@ -71,28 +70,28 @@ impl DeviceTrait for Device {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<T, D, E>(
|
fn build_input_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
_format: &Format,
|
_format: &Format,
|
||||||
_data_callback: D,
|
_data_callback: D,
|
||||||
_error_callback: E,
|
_error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
D: FnMut(InputData<T>) + Send + 'static,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an output stream.
|
/// Create an output stream.
|
||||||
fn build_output_stream<T, D, E>(
|
fn build_output_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
_format: &Format,
|
_format: &Format,
|
||||||
_data_callback: D,
|
_data_callback: D,
|
||||||
_error_callback: E,
|
_error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
D: FnMut(OutputData<T>) + Send + 'static,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
BackendSpecificError,
|
BackendSpecificError,
|
||||||
|
Data,
|
||||||
DefaultFormatError,
|
DefaultFormatError,
|
||||||
DeviceNameError,
|
DeviceNameError,
|
||||||
DevicesError,
|
DevicesError,
|
||||||
Format,
|
Format,
|
||||||
InputData,
|
|
||||||
OutputData,
|
|
||||||
Sample,
|
|
||||||
SampleFormat,
|
SampleFormat,
|
||||||
SampleRate,
|
SampleRate,
|
||||||
SupportedFormat,
|
SupportedFormat,
|
||||||
|
@ -106,30 +104,28 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<T, D, E>(
|
fn build_input_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
D: FnMut(InputData<T>) + 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_inner(format)?;
|
||||||
Ok(Stream::new_input(stream_inner, data_callback, error_callback))
|
Ok(Stream::new_input(stream_inner, data_callback, error_callback))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<T, D, E>(
|
fn build_output_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
D: FnMut(OutputData<T>) + 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_inner(format)?;
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
BackendSpecificError,
|
BackendSpecificError,
|
||||||
InputData,
|
Data,
|
||||||
OutputData,
|
|
||||||
PauseStreamError,
|
PauseStreamError,
|
||||||
PlayStreamError,
|
PlayStreamError,
|
||||||
Sample,
|
|
||||||
SampleFormat,
|
SampleFormat,
|
||||||
StreamError,
|
StreamError,
|
||||||
};
|
};
|
||||||
use crate::traits::StreamTrait;
|
use crate::traits::StreamTrait;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
use std::thread::{self, JoinHandle};
|
use std::thread::{self, JoinHandle};
|
||||||
use super::check_result;
|
use super::check_result;
|
||||||
|
@ -85,14 +82,13 @@ pub struct StreamInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream {
|
impl Stream {
|
||||||
pub(crate) fn new_input<T, D, E>(
|
pub(crate) fn new_input<D, E>(
|
||||||
stream_inner: StreamInner,
|
stream_inner: StreamInner,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
mut error_callback: E,
|
mut error_callback: E,
|
||||||
) -> Stream
|
) -> Stream
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
D: FnMut(InputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
let pending_scheduled_event =
|
let pending_scheduled_event =
|
||||||
|
@ -115,14 +111,13 @@ impl Stream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_output<T, D, E>(
|
pub(crate) fn new_output<D, E>(
|
||||||
stream_inner: StreamInner,
|
stream_inner: StreamInner,
|
||||||
mut data_callback: D,
|
mut data_callback: D,
|
||||||
mut error_callback: E,
|
mut error_callback: E,
|
||||||
) -> Stream
|
) -> Stream
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
D: FnMut(OutputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
let pending_scheduled_event =
|
let pending_scheduled_event =
|
||||||
|
@ -285,13 +280,11 @@ fn stream_error_from_hresult(hresult: winnt::HRESULT) -> Result<(), StreamError>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_input<T>(
|
fn run_input(
|
||||||
mut run_ctxt: RunContext,
|
mut run_ctxt: RunContext,
|
||||||
data_callback: &mut dyn FnMut(InputData<T>),
|
data_callback: &mut dyn FnMut(&Data),
|
||||||
error_callback: &mut dyn FnMut(StreamError),
|
error_callback: &mut dyn FnMut(StreamError),
|
||||||
) where
|
) {
|
||||||
T: Sample,
|
|
||||||
{
|
|
||||||
loop {
|
loop {
|
||||||
match process_commands_and_await_signal(&mut run_ctxt, error_callback) {
|
match process_commands_and_await_signal(&mut run_ctxt, error_callback) {
|
||||||
Some(ControlFlow::Break) => break,
|
Some(ControlFlow::Break) => break,
|
||||||
|
@ -309,13 +302,11 @@ fn run_input<T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_output<T>(
|
fn run_output(
|
||||||
mut run_ctxt: RunContext,
|
mut run_ctxt: RunContext,
|
||||||
data_callback: &mut dyn FnMut(OutputData<T>),
|
data_callback: &mut dyn FnMut(&mut Data),
|
||||||
error_callback: &mut dyn FnMut(StreamError),
|
error_callback: &mut dyn FnMut(StreamError),
|
||||||
) where
|
) {
|
||||||
T: Sample,
|
|
||||||
{
|
|
||||||
loop {
|
loop {
|
||||||
match process_commands_and_await_signal(&mut run_ctxt, error_callback) {
|
match process_commands_and_await_signal(&mut run_ctxt, error_callback) {
|
||||||
Some(ControlFlow::Break) => break,
|
Some(ControlFlow::Break) => break,
|
||||||
|
@ -371,15 +362,12 @@ fn process_commands_and_await_signal(
|
||||||
}
|
}
|
||||||
|
|
||||||
// The loop for processing pending input data.
|
// The loop for processing pending input data.
|
||||||
fn process_input<T>(
|
fn process_input(
|
||||||
stream: &StreamInner,
|
stream: &StreamInner,
|
||||||
capture_client: *mut audioclient::IAudioCaptureClient,
|
capture_client: *mut audioclient::IAudioCaptureClient,
|
||||||
data_callback: &mut dyn FnMut(InputData<T>),
|
data_callback: &mut dyn FnMut(&Data),
|
||||||
error_callback: &mut dyn FnMut(StreamError),
|
error_callback: &mut dyn FnMut(StreamError),
|
||||||
) -> ControlFlow
|
) -> ControlFlow {
|
||||||
where
|
|
||||||
T: Sample,
|
|
||||||
{
|
|
||||||
let mut frames_available = 0;
|
let mut frames_available = 0;
|
||||||
unsafe {
|
unsafe {
|
||||||
// Get the available data in the shared buffer.
|
// Get the available data in the shared buffer.
|
||||||
|
@ -412,15 +400,13 @@ where
|
||||||
|
|
||||||
debug_assert!(!buffer.is_null());
|
debug_assert!(!buffer.is_null());
|
||||||
|
|
||||||
let buffer_len = frames_available as usize
|
let data = buffer as *mut ();
|
||||||
|
let len = frames_available as usize
|
||||||
* stream.bytes_per_frame as usize
|
* stream.bytes_per_frame as usize
|
||||||
/ mem::size_of::<T>();
|
/ stream.sample_format.sample_size();
|
||||||
|
let data = Data::from_parts(data, len, stream.sample_format);
|
||||||
|
data_callback(&data);
|
||||||
|
|
||||||
// Simplify the capture callback sample format branches.
|
|
||||||
let buffer_data = buffer as *mut _ as *const T;
|
|
||||||
let slice = slice::from_raw_parts(buffer_data, buffer_len);
|
|
||||||
let input_data = InputData { buffer: slice };
|
|
||||||
data_callback(input_data);
|
|
||||||
// Release the buffer.
|
// Release the buffer.
|
||||||
let hresult = (*capture_client).ReleaseBuffer(frames_available);
|
let hresult = (*capture_client).ReleaseBuffer(frames_available);
|
||||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||||
|
@ -432,15 +418,12 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// The loop for writing output data.
|
// The loop for writing output data.
|
||||||
fn process_output<T>(
|
fn process_output(
|
||||||
stream: &StreamInner,
|
stream: &StreamInner,
|
||||||
render_client: *mut audioclient::IAudioRenderClient,
|
render_client: *mut audioclient::IAudioRenderClient,
|
||||||
data_callback: &mut dyn FnMut(OutputData<T>),
|
data_callback: &mut dyn FnMut(&mut Data),
|
||||||
error_callback: &mut dyn FnMut(StreamError),
|
error_callback: &mut dyn FnMut(StreamError),
|
||||||
) -> ControlFlow
|
) -> ControlFlow {
|
||||||
where
|
|
||||||
T: Sample,
|
|
||||||
{
|
|
||||||
// The number of frames available for writing.
|
// The number of frames available for writing.
|
||||||
let frames_available = match get_available_frames(&stream) {
|
let frames_available = match get_available_frames(&stream) {
|
||||||
Ok(0) => return ControlFlow::Continue, // TODO: Can this happen?
|
Ok(0) => return ControlFlow::Continue, // TODO: Can this happen?
|
||||||
|
@ -462,13 +445,14 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(!buffer.is_null());
|
debug_assert!(!buffer.is_null());
|
||||||
let buffer_len =
|
|
||||||
frames_available as usize * stream.bytes_per_frame as usize / mem::size_of::<T>();
|
|
||||||
|
|
||||||
let buffer_data = buffer as *mut T;
|
let data = buffer as *mut ();
|
||||||
let slice = slice::from_raw_parts_mut(buffer_data, buffer_len);
|
let len = frames_available as usize
|
||||||
let output_data = OutputData { buffer: slice };
|
* stream.bytes_per_frame as usize
|
||||||
data_callback(output_data);
|
/ stream.sample_format.sample_size();
|
||||||
|
let mut data = Data::from_parts(data, len, stream.sample_format);
|
||||||
|
data_callback(&mut data);
|
||||||
|
|
||||||
let hresult = (*render_client).ReleaseBuffer(frames_available as u32, 0);
|
let hresult = (*render_client).ReleaseBuffer(frames_available as u32, 0);
|
||||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||||
error_callback(err);
|
error_callback(err);
|
||||||
|
|
184
src/lib.rs
184
src/lib.rs
|
@ -55,14 +55,14 @@
|
||||||
//! Now that we have everything for the stream, we are ready to create it from our selected device:
|
//! Now that we have everything for the stream, we are ready to create it from our selected device:
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use cpal::OutputData;
|
//! use cpal::Data;
|
||||||
//! 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 format = device.default_output_format().unwrap();
|
||||||
//! let stream = device.build_output_stream(
|
//! let stream = device.build_output_stream(
|
||||||
//! &format,
|
//! &format,
|
||||||
//! move |data: OutputData<f32>| {
|
//! move |data: &mut Data| {
|
||||||
//! // 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| {
|
||||||
|
@ -72,9 +72,8 @@
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! While the stream is running, the selected audio device will periodically call the data callback
|
//! While the stream is running, the selected audio device will periodically call the data callback
|
||||||
//! that was passed to the function. The callback is passed an instance of either `InputData<T>` or
|
//! that was passed to the function. The callback is passed an instance of either `&Data` or
|
||||||
//! `OutputData<T>` depending on whether the stream is an input stream or output stream
|
//! `&mut Data` depending on whether the stream is an input stream or output stream respectively.
|
||||||
//! respectively. Type `T` represents the desired sample format type. Supported format types
|
|
||||||
//!
|
//!
|
||||||
//! > **Note**: Creating and running a stream will *not* block the thread. On modern platforms, the
|
//! > **Note**: Creating and running a stream will *not* block the thread. On modern platforms, the
|
||||||
//! > given callback is called by a dedicated, high-priority thread responsible for delivering
|
//! > given callback is called by a dedicated, high-priority thread responsible for delivering
|
||||||
|
@ -85,22 +84,24 @@
|
||||||
//! > please share your issue and use-case with the CPAL team on the github issue tracker for
|
//! > please share your issue and use-case with the CPAL team on the github issue tracker for
|
||||||
//! > consideration.*
|
//! > consideration.*
|
||||||
//!
|
//!
|
||||||
//! In this example, we simply fill the given output buffer with zeroes.
|
//! In this example, we simply fill the given output buffer with silence.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use cpal::{OutputData, Sample, SampleFormat};
|
//! use cpal::{Data, Sample, SampleFormat};
|
||||||
//! 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 format = device.default_output_format().unwrap();
|
||||||
//! let err_fn = move |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 stream = match format.data_type {
|
//! let data_fn = move |data: &mut Data| match data.sample_format() {
|
||||||
//! SampleFormat::F32 => device.build_output_stream(&format, write_silence::<f32>, err_fn),
|
//! SampleFormat::F32 => write_silence::<f32>(data),
|
||||||
//! SampleFormat::I16 => device.build_output_stream(&format, write_silence::<i16>, err_fn),
|
//! SampleFormat::I16 => write_silence::<i16>(data),
|
||||||
//! SampleFormat::U16 => device.build_output_stream(&format, write_silence::<u16>, err_fn),
|
//! SampleFormat::U16 => write_silence::<u16>(data),
|
||||||
//! };
|
//! };
|
||||||
|
//! let stream = device.build_output_stream(&format, data_fn, err_fn).unwrap();
|
||||||
//!
|
//!
|
||||||
//! fn write_silence<T: Sample>(mut data: OutputData<T>) {
|
//! fn write_silence<T: Sample>(data: &mut Data) {
|
||||||
|
//! 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);
|
||||||
//! }
|
//! }
|
||||||
|
@ -115,7 +116,7 @@
|
||||||
//! # 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 format = device.default_output_format().unwrap();
|
||||||
//! # let data_fn = move |_data: cpal::OutputData<f32>| {};
|
//! # 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(&format, data_fn, err_fn).unwrap();
|
||||||
//! stream.play().unwrap();
|
//! stream.play().unwrap();
|
||||||
|
@ -129,7 +130,7 @@
|
||||||
//! # 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 format = device.default_output_format().unwrap();
|
||||||
//! # let data_fn = move |_data: cpal::OutputData<f32>| {};
|
//! # 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(&format, data_fn, err_fn).unwrap();
|
||||||
//! stream.pause().unwrap();
|
//! stream.pause().unwrap();
|
||||||
|
@ -152,7 +153,6 @@ pub use platform::{
|
||||||
HostId, Stream, SupportedInputFormats, SupportedOutputFormats,
|
HostId, Stream, SupportedInputFormats, SupportedOutputFormats,
|
||||||
};
|
};
|
||||||
pub use samples_formats::{Sample, SampleFormat};
|
pub use samples_formats::{Sample, SampleFormat};
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod host;
|
mod host;
|
||||||
|
@ -193,34 +193,106 @@ pub struct SupportedFormat {
|
||||||
pub data_type: SampleFormat,
|
pub data_type: SampleFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a buffer containing audio data that may be read.
|
/// Represents a buffer of audio data, delivered via a user's stream data callback function.
|
||||||
///
|
///
|
||||||
/// This struct implements the `Deref` trait targeting `[T]`. Therefore this buffer can be read the
|
/// Input stream callbacks receive `&Data`, while output stream callbacks expect `&mut Data`.
|
||||||
/// same way as reading from a `Vec` or any other kind of Rust array.
|
|
||||||
// TODO: explain audio stuff in general
|
|
||||||
// TODO: Consider making this an `enum` with `Interleaved` and `NonInterleaved` variants.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InputData<'a, T: 'a>
|
pub struct Data {
|
||||||
where
|
data: *mut (),
|
||||||
T: Sample,
|
len: usize,
|
||||||
{
|
sample_format: SampleFormat,
|
||||||
buffer: &'a [T],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a buffer that must be filled with audio data. The buffer in unfilled state may
|
impl Data {
|
||||||
/// contain garbage values.
|
// Internal constructor for host implementations to use.
|
||||||
///
|
//
|
||||||
/// This struct implements the `Deref` and `DerefMut` traits to `[T]`. Therefore writing to this
|
// The following requirements must be met in order for the safety of `Data`'s public API.
|
||||||
/// 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
|
// - The `data` pointer must point to the first sample in the slice containing all samples.
|
||||||
// TODO: Consider making this an `enum` with `Interleaved` and `NonInterleaved` variants.
|
// - The `len` must describe the length of the buffer as a number of samples in the expected
|
||||||
#[must_use]
|
// format specified via the `sample_format` argument.
|
||||||
#[derive(Debug)]
|
// - The `sample_format` must correctly represent the underlying sample data delivered/expected
|
||||||
pub struct OutputData<'a, T: 'a>
|
// by the stream.
|
||||||
where
|
pub(crate) unsafe fn from_parts(
|
||||||
|
data: *mut (),
|
||||||
|
len: usize,
|
||||||
|
sample_format: SampleFormat,
|
||||||
|
) -> Self {
|
||||||
|
Data { data, len, sample_format }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The sample format of the internal audio data.
|
||||||
|
pub fn sample_format(&self) -> SampleFormat {
|
||||||
|
self.sample_format
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The full length of the buffer in samples.
|
||||||
|
///
|
||||||
|
/// The returned length is the same length as the slice of type `T` that would be returned via
|
||||||
|
/// `as_slice` given a sample type that matches the inner sample format.
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The raw slice of memory representing the underlying audio data as a slice of bytes.
|
||||||
|
///
|
||||||
|
/// It is up to the user to interpret the slice of memory based on `Data::sample_format`.
|
||||||
|
pub fn bytes(&self) -> &[u8] {
|
||||||
|
let len = self.len * self.sample_format.sample_size();
|
||||||
|
// The safety of this block relies on correct construction of the `Data` instance. See
|
||||||
|
// the unsafe `from_parts` constructor for these requirements.
|
||||||
|
unsafe {
|
||||||
|
std::slice::from_raw_parts(self.data as *const u8, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The raw slice of memory representing the underlying audio data as a slice of bytes.
|
||||||
|
///
|
||||||
|
/// It is up to the user to interpret the slice of memory based on `Data::sample_format`.
|
||||||
|
pub fn bytes_mut(&mut self) -> &mut [u8] {
|
||||||
|
let len = self.len * self.sample_format.sample_size();
|
||||||
|
// The safety of this block relies on correct construction of the `Data` instance. See
|
||||||
|
// the unsafe `from_parts` constructor for these requirements.
|
||||||
|
unsafe {
|
||||||
|
std::slice::from_raw_parts_mut(self.data as *mut u8, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the data as a slice of sample type `T`.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the sample type does not match the expected sample format.
|
||||||
|
pub fn as_slice<T>(&self) -> Option<&[T]>
|
||||||
|
where
|
||||||
T: Sample,
|
T: Sample,
|
||||||
{
|
{
|
||||||
buffer: &'a mut [T],
|
if T::FORMAT == self.sample_format {
|
||||||
|
// The safety of this block relies on correct construction of the `Data` instance. See
|
||||||
|
// the unsafe `from_parts` constructor for these requirements.
|
||||||
|
unsafe {
|
||||||
|
Some(std::slice::from_raw_parts(self.data as *const T, self.len))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the data as a slice of sample type `T`.
|
||||||
|
///
|
||||||
|
/// Returns `None` if the sample type does not match the expected sample format.
|
||||||
|
pub fn as_slice_mut<T>(&mut self) -> Option<&mut [T]>
|
||||||
|
where
|
||||||
|
T: Sample,
|
||||||
|
{
|
||||||
|
if T::FORMAT == self.sample_format {
|
||||||
|
// The safety of this block relies on correct construction of the `Data` instance. See
|
||||||
|
// the unsafe `from_parts` constructor for these requirements.
|
||||||
|
unsafe {
|
||||||
|
Some(std::slice::from_raw_parts_mut(self.data as *mut T, self.len))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SupportedFormat {
|
impl SupportedFormat {
|
||||||
|
@ -306,40 +378,6 @@ impl SupportedFormat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Deref for InputData<'a, T>
|
|
||||||
where
|
|
||||||
T: Sample,
|
|
||||||
{
|
|
||||||
type Target = [T];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn deref(&self) -> &[T] {
|
|
||||||
self.buffer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Deref for OutputData<'a, T>
|
|
||||||
where
|
|
||||||
T: Sample,
|
|
||||||
{
|
|
||||||
type Target = [T];
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn deref(&self) -> &[T] {
|
|
||||||
self.buffer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> DerefMut for OutputData<'a, T>
|
|
||||||
where
|
|
||||||
T: Sample,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn deref_mut(&mut self) -> &mut [T] {
|
|
||||||
self.buffer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Format> for SupportedFormat {
|
impl From<Format> for SupportedFormat {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(format: Format) -> SupportedFormat {
|
fn from(format: Format) -> SupportedFormat {
|
||||||
|
|
|
@ -255,15 +255,14 @@ macro_rules! impl_platform_host {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<T, D, E>(
|
fn build_input_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &crate::Format,
|
format: &crate::Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, crate::BuildStreamError>
|
) -> Result<Self::Stream, crate::BuildStreamError>
|
||||||
where
|
where
|
||||||
T: crate::Sample,
|
D: FnMut(&crate::Data) + Send + 'static,
|
||||||
D: FnMut(crate::InputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(crate::StreamError) + Send + 'static,
|
E: FnMut(crate::StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
match self.0 {
|
match self.0 {
|
||||||
|
@ -275,15 +274,14 @@ macro_rules! impl_platform_host {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<T, D, E>(
|
fn build_output_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &crate::Format,
|
format: &crate::Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, crate::BuildStreamError>
|
) -> Result<Self::Stream, crate::BuildStreamError>
|
||||||
where
|
where
|
||||||
T: crate::Sample,
|
D: FnMut(&mut crate::Data) + Send + 'static,
|
||||||
D: FnMut(crate::OutputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(crate::StreamError) + Send + 'static,
|
E: FnMut(crate::StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
match self.0 {
|
match self.0 {
|
||||||
|
|
|
@ -2,17 +2,15 @@
|
||||||
|
|
||||||
use {
|
use {
|
||||||
BuildStreamError,
|
BuildStreamError,
|
||||||
|
Data,
|
||||||
DefaultFormatError,
|
DefaultFormatError,
|
||||||
DeviceNameError,
|
DeviceNameError,
|
||||||
DevicesError,
|
DevicesError,
|
||||||
Format,
|
Format,
|
||||||
InputData,
|
|
||||||
InputDevices,
|
InputDevices,
|
||||||
OutputData,
|
|
||||||
OutputDevices,
|
OutputDevices,
|
||||||
PauseStreamError,
|
PauseStreamError,
|
||||||
PlayStreamError,
|
PlayStreamError,
|
||||||
Sample,
|
|
||||||
StreamError,
|
StreamError,
|
||||||
SupportedFormat,
|
SupportedFormat,
|
||||||
SupportedFormatsError,
|
SupportedFormatsError,
|
||||||
|
@ -120,27 +118,25 @@ 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<T, D, E>(
|
fn build_input_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&Data) + Send + 'static,
|
||||||
D: FnMut(InputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static;
|
E: FnMut(StreamError) + Send + 'static;
|
||||||
|
|
||||||
/// Create an output stream.
|
/// Create an output stream.
|
||||||
fn build_output_stream<T, D, E>(
|
fn build_output_stream<D, E>(
|
||||||
&self,
|
&self,
|
||||||
format: &Format,
|
format: &Format,
|
||||||
data_callback: D,
|
data_callback: D,
|
||||||
error_callback: E,
|
error_callback: E,
|
||||||
) -> Result<Self::Stream, BuildStreamError>
|
) -> Result<Self::Stream, BuildStreamError>
|
||||||
where
|
where
|
||||||
T: Sample,
|
D: FnMut(&mut Data) + Send + 'static,
|
||||||
D: FnMut(OutputData<T>) + Send + 'static,
|
|
||||||
E: FnMut(StreamError) + Send + 'static;
|
E: FnMut(StreamError) + Send + 'static;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue