Improved buffer management
- ALSA backend: reuse the buffers - Make `InputBuffer` and `OutputBuffer` types just a wrapper of slice * Buffer is now submitted at the end of callback
This commit is contained in:
parent
0354548426
commit
4d3fe57fe3
198
src/alsa/mod.rs
198
src/alsa/mod.rs
|
@ -15,7 +15,7 @@ use SupportedFormat;
|
||||||
use UnknownTypeInputBuffer;
|
use UnknownTypeInputBuffer;
|
||||||
use UnknownTypeOutputBuffer;
|
use UnknownTypeOutputBuffer;
|
||||||
|
|
||||||
use std::{cmp, ffi, iter, mem, ptr};
|
use std::{cmp, ffi, mem, ptr};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::vec::IntoIter as VecIntoIter;
|
use std::vec::IntoIter as VecIntoIter;
|
||||||
|
@ -370,6 +370,11 @@ struct StreamInner {
|
||||||
// A file descriptor opened with `eventfd`.
|
// A file descriptor opened with `eventfd`.
|
||||||
// It is used to wait for resume signal.
|
// It is used to wait for resume signal.
|
||||||
resume_trigger: Trigger,
|
resume_trigger: Trigger,
|
||||||
|
|
||||||
|
// Lazily allocated buffer that is reused inside the loop.
|
||||||
|
// Zero-allocate a new buffer (the fastest way to have zeroed memory) at the first time this is
|
||||||
|
// used.
|
||||||
|
buffer: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -543,67 +548,78 @@ impl EventLoop {
|
||||||
|
|
||||||
let available_frames = available_samples / stream_inner.num_channels as usize;
|
let available_frames = available_samples / stream_inner.num_channels as usize;
|
||||||
|
|
||||||
|
let buffer_size = stream_inner.sample_format.sample_size() * available_samples;
|
||||||
|
// Could be written with a match with improved borrow checking
|
||||||
|
if stream_inner.buffer.is_none() {
|
||||||
|
stream_inner.buffer = Some(vec![0u8; buffer_size]);
|
||||||
|
} else {
|
||||||
|
stream_inner.buffer.as_mut().unwrap().resize(buffer_size, 0u8);
|
||||||
|
}
|
||||||
|
let buffer = stream_inner.buffer.as_mut().unwrap();
|
||||||
|
|
||||||
match stream_type {
|
match stream_type {
|
||||||
StreamType::Input => {
|
StreamType::Input => {
|
||||||
// Simplify shared logic across the sample format branches.
|
let err = alsa::snd_pcm_readi(
|
||||||
macro_rules! read_buffer {
|
stream_inner.channel,
|
||||||
($T:ty, $Variant:ident) => {{
|
buffer.as_mut_ptr() as *mut _,
|
||||||
// The buffer to read into.
|
available_frames as alsa::snd_pcm_uframes_t,
|
||||||
let mut buffer: Vec<$T> = vec![Default::default(); available_samples];
|
);
|
||||||
let err = alsa::snd_pcm_readi(
|
check_errors(err as _).expect("snd_pcm_readi error");
|
||||||
stream_inner.channel,
|
|
||||||
buffer.as_mut_ptr() as *mut _,
|
|
||||||
available_frames as alsa::snd_pcm_uframes_t,
|
|
||||||
);
|
|
||||||
check_errors(err as _).expect("snd_pcm_readi error");
|
|
||||||
let input_buffer = InputBuffer {
|
|
||||||
buffer: &buffer,
|
|
||||||
};
|
|
||||||
let buffer = UnknownTypeInputBuffer::$Variant(::InputBuffer {
|
|
||||||
buffer: Some(input_buffer),
|
|
||||||
});
|
|
||||||
let stream_data = StreamData::Input { buffer: buffer };
|
|
||||||
callback(stream_id, stream_data);
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
match stream_inner.sample_format {
|
let input_buffer = match stream_inner.sample_format {
|
||||||
SampleFormat::I16 => read_buffer!(i16, I16),
|
SampleFormat::I16 => UnknownTypeInputBuffer::I16(::InputBuffer {
|
||||||
SampleFormat::U16 => read_buffer!(u16, U16),
|
buffer: cast_input_buffer(buffer),
|
||||||
SampleFormat::F32 => read_buffer!(f32, F32),
|
}),
|
||||||
}
|
SampleFormat::U16 => UnknownTypeInputBuffer::U16(::InputBuffer {
|
||||||
|
buffer: cast_input_buffer(buffer),
|
||||||
|
}),
|
||||||
|
SampleFormat::F32 => UnknownTypeInputBuffer::F32(::InputBuffer {
|
||||||
|
buffer: cast_input_buffer(buffer),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
let stream_data = StreamData::Input {
|
||||||
|
buffer: input_buffer,
|
||||||
|
};
|
||||||
|
callback(stream_id, stream_data);
|
||||||
},
|
},
|
||||||
StreamType::Output => {
|
StreamType::Output => {
|
||||||
// We're now sure that we're ready to write data.
|
{
|
||||||
let buffer = match stream_inner.sample_format {
|
// We're now sure that we're ready to write data.
|
||||||
SampleFormat::I16 => {
|
let output_buffer = match stream_inner.sample_format {
|
||||||
let buffer = OutputBuffer {
|
SampleFormat::I16 => UnknownTypeOutputBuffer::I16(::OutputBuffer {
|
||||||
stream_inner: stream_inner,
|
buffer: cast_output_buffer(buffer),
|
||||||
buffer: vec![Default::default(); available_samples],
|
}),
|
||||||
};
|
SampleFormat::U16 => UnknownTypeOutputBuffer::U16(::OutputBuffer {
|
||||||
|
buffer: cast_output_buffer(buffer),
|
||||||
|
}),
|
||||||
|
SampleFormat::F32 => UnknownTypeOutputBuffer::F32(::OutputBuffer {
|
||||||
|
buffer: cast_output_buffer(buffer),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
UnknownTypeOutputBuffer::I16(::OutputBuffer { target: Some(buffer) })
|
let stream_data = StreamData::Output {
|
||||||
},
|
buffer: output_buffer,
|
||||||
SampleFormat::U16 => {
|
};
|
||||||
let buffer = OutputBuffer {
|
callback(stream_id, stream_data);
|
||||||
stream_inner: stream_inner,
|
}
|
||||||
buffer: vec![Default::default(); available_samples],
|
loop {
|
||||||
};
|
let result = alsa::snd_pcm_writei(
|
||||||
|
stream_inner.channel,
|
||||||
|
buffer.as_ptr() as *const _,
|
||||||
|
available_frames as alsa::snd_pcm_uframes_t,
|
||||||
|
);
|
||||||
|
|
||||||
UnknownTypeOutputBuffer::U16(::OutputBuffer { target: Some(buffer) })
|
if result == -32 {
|
||||||
},
|
// buffer underrun
|
||||||
SampleFormat::F32 => {
|
alsa::snd_pcm_prepare(stream_inner.channel);
|
||||||
let buffer = OutputBuffer {
|
} else if result < 0 {
|
||||||
stream_inner: stream_inner,
|
check_errors(result as libc::c_int)
|
||||||
buffer: vec![Default::default(); available_samples]
|
.expect("could not write pcm");
|
||||||
};
|
} else {
|
||||||
|
assert_eq!(result as usize, available_frames);
|
||||||
UnknownTypeOutputBuffer::F32(::OutputBuffer { target: Some(buffer) })
|
break;
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
let stream_data = StreamData::Output { buffer: buffer };
|
|
||||||
callback(stream_id, stream_data);
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -661,6 +677,7 @@ impl EventLoop {
|
||||||
can_pause: can_pause,
|
can_pause: can_pause,
|
||||||
is_paused: false,
|
is_paused: false,
|
||||||
resume_trigger: Trigger::new(),
|
resume_trigger: Trigger::new(),
|
||||||
|
buffer: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
check_errors(alsa::snd_pcm_start(capture_handle))
|
check_errors(alsa::snd_pcm_start(capture_handle))
|
||||||
|
@ -721,6 +738,7 @@ impl EventLoop {
|
||||||
can_pause: can_pause,
|
can_pause: can_pause,
|
||||||
is_paused: false,
|
is_paused: false,
|
||||||
resume_trigger: Trigger::new(),
|
resume_trigger: Trigger::new(),
|
||||||
|
buffer: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.push_command(Command::NewStream(stream_inner));
|
self.push_command(Command::NewStream(stream_inner));
|
||||||
|
@ -834,15 +852,6 @@ unsafe fn set_sw_params_from_format(
|
||||||
(buffer_len, period_len)
|
(buffer_len, period_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InputBuffer<'a, T: 'a> {
|
|
||||||
buffer: &'a [T],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OutputBuffer<'a, T: 'a> {
|
|
||||||
stream_inner: &'a mut StreamInner,
|
|
||||||
buffer: Vec<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper around `hw_params`.
|
/// Wrapper around `hw_params`.
|
||||||
struct HwParams(*mut alsa::snd_pcm_hw_params_t);
|
struct HwParams(*mut alsa::snd_pcm_hw_params_t);
|
||||||
|
|
||||||
|
@ -874,53 +883,6 @@ impl Drop for StreamInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> InputBuffer<'a, T> {
|
|
||||||
#[inline]
|
|
||||||
pub fn buffer(&self) -> &[T] {
|
|
||||||
&self.buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
// Nothing to be done.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> OutputBuffer<'a, T> {
|
|
||||||
#[inline]
|
|
||||||
pub fn buffer(&mut self) -> &mut [T] {
|
|
||||||
&mut self.buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.buffer.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finish(self) {
|
|
||||||
let to_write = (self.buffer.len() / self.stream_inner.num_channels as usize) as
|
|
||||||
alsa::snd_pcm_uframes_t;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
loop {
|
|
||||||
let result = alsa::snd_pcm_writei(self.stream_inner.channel,
|
|
||||||
self.buffer.as_ptr() as *const _,
|
|
||||||
to_write);
|
|
||||||
|
|
||||||
if result == -32 {
|
|
||||||
// buffer underrun
|
|
||||||
alsa::snd_pcm_prepare(self.stream_inner.channel);
|
|
||||||
} else if result < 0 {
|
|
||||||
check_errors(result as libc::c_int).expect("could not write pcm");
|
|
||||||
} else {
|
|
||||||
assert_eq!(result as alsa::snd_pcm_uframes_t, to_write);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn check_errors(err: libc::c_int) -> Result<(), String> {
|
fn check_errors(err: libc::c_int) -> Result<(), String> {
|
||||||
use std::ffi;
|
use std::ffi;
|
||||||
|
@ -937,3 +899,17 @@ 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>())
|
||||||
|
}
|
|
@ -671,8 +671,7 @@ impl EventLoop {
|
||||||
Some(cb) => cb,
|
Some(cb) => cb,
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
let buffer = InputBuffer { buffer: data_slice };
|
let unknown_type_buffer = UnknownTypeInputBuffer::$SampleFormat(::InputBuffer { buffer: data_slice });
|
||||||
let unknown_type_buffer = UnknownTypeInputBuffer::$SampleFormat(::InputBuffer { buffer: Some(buffer) });
|
|
||||||
let stream_data = StreamData::Input { buffer: unknown_type_buffer };
|
let stream_data = StreamData::Input { buffer: unknown_type_buffer };
|
||||||
callback(StreamId(stream_id), stream_data);
|
callback(StreamId(stream_id), stream_data);
|
||||||
}};
|
}};
|
||||||
|
@ -748,8 +747,7 @@ impl EventLoop {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let buffer = OutputBuffer { buffer: data_slice };
|
let unknown_type_buffer = UnknownTypeOutputBuffer::$SampleFormat(::OutputBuffer { buffer: data_slice });
|
||||||
let unknown_type_buffer = UnknownTypeOutputBuffer::$SampleFormat(::OutputBuffer { target: Some(buffer) });
|
|
||||||
let stream_data = StreamData::Output { buffer: unknown_type_buffer };
|
let stream_data = StreamData::Output { buffer: unknown_type_buffer };
|
||||||
callback(StreamId(stream_id), stream_data);
|
callback(StreamId(stream_id), stream_data);
|
||||||
}};
|
}};
|
||||||
|
@ -798,42 +796,3 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InputBuffer<'a, T: 'a> {
|
|
||||||
buffer: &'a [T],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OutputBuffer<'a, T: 'a> {
|
|
||||||
buffer: &'a mut [T],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> InputBuffer<'a, T> {
|
|
||||||
#[inline]
|
|
||||||
pub fn buffer(&self) -> &[T] {
|
|
||||||
&self.buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
// Nothing to be done.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> OutputBuffer<'a, T>
|
|
||||||
where T: Sample
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
pub fn buffer(&mut self) -> &mut [T] {
|
|
||||||
&mut self.buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.buffer.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
// Do nothing. We wrote directly to the buffer.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ use CreationError;
|
||||||
use DefaultFormatError;
|
use DefaultFormatError;
|
||||||
use Format;
|
use Format;
|
||||||
use FormatsEnumerationError;
|
use FormatsEnumerationError;
|
||||||
use Sample;
|
|
||||||
use StreamData;
|
use StreamData;
|
||||||
use SupportedFormat;
|
use SupportedFormat;
|
||||||
use UnknownTypeOutputBuffer;
|
use UnknownTypeOutputBuffer;
|
||||||
|
@ -63,14 +62,47 @@ impl EventLoop {
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
let buffer = OutputBuffer {
|
let mut temporary_buffer = vec![0.0; 44100 * 2 / 3];
|
||||||
temporary_buffer: vec![0.0; 44100 * 2 / 3],
|
|
||||||
stream: &stream,
|
{
|
||||||
|
let buffer = UnknownTypeOutputBuffer::F32(::OutputBuffer { buffer: &mut temporary_buffer });
|
||||||
|
let data = StreamData::Output { buffer: buffer };
|
||||||
|
user_cb(StreamId(stream_id), data);
|
||||||
|
// TODO: directly use a TypedArray<f32> once this is supported by stdweb
|
||||||
|
}
|
||||||
|
|
||||||
|
let typed_array = {
|
||||||
|
let f32_slice = temporary_buffer.as_slice();
|
||||||
|
let u8_slice: &[u8] = unsafe {
|
||||||
|
from_raw_parts(f32_slice.as_ptr() as *const _,
|
||||||
|
f32_slice.len() * mem::size_of::<f32>())
|
||||||
|
};
|
||||||
|
let typed_array: TypedArray<u8> = u8_slice.into();
|
||||||
|
typed_array
|
||||||
};
|
};
|
||||||
|
|
||||||
let buffer = UnknownTypeOutputBuffer::F32(::OutputBuffer { target: Some(buffer) });
|
let num_channels = 2u32; // TODO: correct value
|
||||||
let data = StreamData::Output { buffer: buffer };
|
debug_assert_eq!(temporary_buffer.len() % num_channels as usize, 0);
|
||||||
user_cb(StreamId(stream_id), data);
|
|
||||||
|
js!(
|
||||||
|
var src_buffer = new Float32Array(@{typed_array}.buffer);
|
||||||
|
var context = @{stream};
|
||||||
|
var buf_len = @{temporary_buffer.len() as u32};
|
||||||
|
var num_channels = @{num_channels};
|
||||||
|
|
||||||
|
var buffer = context.createBuffer(num_channels, buf_len / num_channels, 44100);
|
||||||
|
for (var channel = 0; channel < num_channels; ++channel) {
|
||||||
|
var buffer_content = buffer.getChannelData(channel);
|
||||||
|
for (var i = 0; i < buf_len / num_channels; ++i) {
|
||||||
|
buffer_content[i] = src_buffer[i * num_channels + channel];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var node = context.createBufferSource();
|
||||||
|
node.buffer = buffer;
|
||||||
|
node.connect(context.destination);
|
||||||
|
node.start();
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_timeout(|| callback_fn::<F>(user_data_ptr), 330);
|
set_timeout(|| callback_fn::<F>(user_data_ptr), 330);
|
||||||
|
@ -235,77 +267,3 @@ impl Device {
|
||||||
|
|
||||||
pub type SupportedInputFormats = ::std::vec::IntoIter<SupportedFormat>;
|
pub type SupportedInputFormats = ::std::vec::IntoIter<SupportedFormat>;
|
||||||
pub type SupportedOutputFormats = ::std::vec::IntoIter<SupportedFormat>;
|
pub type SupportedOutputFormats = ::std::vec::IntoIter<SupportedFormat>;
|
||||||
|
|
||||||
pub struct InputBuffer<'a, T: 'a> {
|
|
||||||
marker: ::std::marker::PhantomData<&'a T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OutputBuffer<'a, T: 'a>
|
|
||||||
where T: Sample
|
|
||||||
{
|
|
||||||
temporary_buffer: Vec<T>,
|
|
||||||
stream: &'a Reference,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> InputBuffer<'a, T> {
|
|
||||||
#[inline]
|
|
||||||
pub fn buffer(&self) -> &[T] {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> OutputBuffer<'a, T>
|
|
||||||
where T: Sample
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
pub fn buffer(&mut self) -> &mut [T] {
|
|
||||||
&mut self.temporary_buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.temporary_buffer.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
// TODO: directly use a TypedArray<f32> once this is supported by stdweb
|
|
||||||
|
|
||||||
let typed_array = {
|
|
||||||
let t_slice: &[T] = self.temporary_buffer.as_slice();
|
|
||||||
let u8_slice: &[u8] = unsafe {
|
|
||||||
from_raw_parts(t_slice.as_ptr() as *const _,
|
|
||||||
t_slice.len() * mem::size_of::<T>())
|
|
||||||
};
|
|
||||||
let typed_array: TypedArray<u8> = u8_slice.into();
|
|
||||||
typed_array
|
|
||||||
};
|
|
||||||
|
|
||||||
let num_channels = 2u32; // TODO: correct value
|
|
||||||
debug_assert_eq!(self.temporary_buffer.len() % num_channels as usize, 0);
|
|
||||||
|
|
||||||
js!(
|
|
||||||
var src_buffer = new Float32Array(@{typed_array}.buffer);
|
|
||||||
var context = @{self.stream};
|
|
||||||
var buf_len = @{self.temporary_buffer.len() as u32};
|
|
||||||
var num_channels = @{num_channels};
|
|
||||||
|
|
||||||
var buffer = context.createBuffer(num_channels, buf_len / num_channels, 44100);
|
|
||||||
for (var channel = 0; channel < num_channels; ++channel) {
|
|
||||||
var buffer_content = buffer.getChannelData(channel);
|
|
||||||
for (var i = 0; i < buf_len / num_channels; ++i) {
|
|
||||||
buffer_content[i] = src_buffer[i * num_channels + channel];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var node = context.createBufferSource();
|
|
||||||
node.buffer = buffer;
|
|
||||||
node.connect(context.destination);
|
|
||||||
node.start();
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
44
src/lib.rs
44
src/lib.rs
|
@ -211,31 +211,27 @@ pub enum StreamData<'a> {
|
||||||
/// 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
|
||||||
pub struct InputBuffer<'a, T: 'a>
|
pub struct InputBuffer<'a, T: 'a>
|
||||||
where
|
where
|
||||||
T: Sample,
|
T: Sample,
|
||||||
{
|
{
|
||||||
// Always contains something, taken by `Drop`
|
buffer: &'a [T],
|
||||||
// TODO: change that
|
|
||||||
buffer: Option<cpal_impl::InputBuffer<'a, T>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a buffer that must be filled with audio data.
|
/// Represents a buffer that must be filled with audio data. The buffer in unfilled state may
|
||||||
///
|
/// contain garbage values.
|
||||||
/// You should destroy this object as soon as possible. Data is only sent to the audio device when
|
|
||||||
/// this object is destroyed.
|
|
||||||
///
|
///
|
||||||
/// 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
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct OutputBuffer<'a, T: 'a>
|
pub struct OutputBuffer<'a, T: 'a>
|
||||||
where
|
where
|
||||||
T: Sample,
|
T: Sample,
|
||||||
{
|
{
|
||||||
// Always contains something, taken by `Drop`
|
buffer: &'a mut [T],
|
||||||
// TODO: change that
|
|
||||||
target: Option<cpal_impl::OutputBuffer<'a, T>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the struct that is provided to you by cpal when you want to read samples from a buffer.
|
/// This is the struct that is provided to you by cpal when you want to read samples from a buffer.
|
||||||
|
@ -586,16 +582,7 @@ impl<'a, T> Deref for InputBuffer<'a, T>
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref(&self) -> &[T] {
|
fn deref(&self) -> &[T] {
|
||||||
self.buffer.as_ref().unwrap().buffer()
|
self.buffer
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Drop for InputBuffer<'a, T>
|
|
||||||
where T: Sample
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.buffer.take().unwrap().finish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,16 +602,7 @@ impl<'a, T> DerefMut for OutputBuffer<'a, T>
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref_mut(&mut self) -> &mut [T] {
|
fn deref_mut(&mut self) -> &mut [T] {
|
||||||
self.target.as_mut().unwrap().buffer()
|
self.buffer
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Drop for OutputBuffer<'a, T>
|
|
||||||
where T: Sample
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.target.take().unwrap().finish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,9 +623,9 @@ impl<'a> UnknownTypeOutputBuffer<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
&UnknownTypeOutputBuffer::U16(ref buf) => buf.target.as_ref().unwrap().len(),
|
&UnknownTypeOutputBuffer::U16(ref buf) => buf.len(),
|
||||||
&UnknownTypeOutputBuffer::I16(ref buf) => buf.target.as_ref().unwrap().len(),
|
&UnknownTypeOutputBuffer::I16(ref buf) => buf.len(),
|
||||||
&UnknownTypeOutputBuffer::F32(ref buf) => buf.target.as_ref().unwrap().len(),
|
&UnknownTypeOutputBuffer::F32(ref buf) => buf.len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,31 +132,4 @@ pub struct InputBuffer<'a, T: 'a> {
|
||||||
|
|
||||||
pub struct OutputBuffer<'a, T: 'a> {
|
pub struct OutputBuffer<'a, T: 'a> {
|
||||||
marker: PhantomData<&'a mut T>,
|
marker: PhantomData<&'a mut T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> InputBuffer<'a, T> {
|
|
||||||
#[inline]
|
|
||||||
pub fn buffer(&self) -> &[T] {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> OutputBuffer<'a, T> {
|
|
||||||
#[inline]
|
|
||||||
pub fn buffer(&mut self) -> &mut [T] {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ extern crate winapi;
|
||||||
use std::io::Error as IoError;
|
use std::io::Error as IoError;
|
||||||
|
|
||||||
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
|
pub use self::device::{Device, Devices, SupportedInputFormats, SupportedOutputFormats, default_input_device, default_output_device};
|
||||||
pub use self::stream::{InputBuffer, OutputBuffer, EventLoop, StreamId};
|
pub use self::stream::{EventLoop, StreamId};
|
||||||
use self::winapi::um::winnt::HRESULT;
|
use self::winapi::um::winnt::HRESULT;
|
||||||
|
|
||||||
mod com;
|
mod com;
|
||||||
|
|
|
@ -535,9 +535,8 @@ impl EventLoop {
|
||||||
($T:ty, $Variant:ident) => {{
|
($T:ty, $Variant:ident) => {{
|
||||||
let buffer_data = buffer as *mut _ as *const $T;
|
let buffer_data = buffer as *mut _ as *const $T;
|
||||||
let slice = slice::from_raw_parts(buffer_data, buffer_len);
|
let slice = slice::from_raw_parts(buffer_data, buffer_len);
|
||||||
let input_buffer = InputBuffer { buffer: slice };
|
|
||||||
let unknown_buffer = UnknownTypeInputBuffer::$Variant(::InputBuffer {
|
let unknown_buffer = UnknownTypeInputBuffer::$Variant(::InputBuffer {
|
||||||
buffer: Some(input_buffer),
|
buffer: slice,
|
||||||
});
|
});
|
||||||
let data = StreamData::Input { buffer: unknown_buffer };
|
let data = StreamData::Input { buffer: unknown_buffer };
|
||||||
callback(stream_id, data);
|
callback(stream_id, data);
|
||||||
|
@ -576,19 +575,24 @@ impl EventLoop {
|
||||||
macro_rules! render_callback {
|
macro_rules! render_callback {
|
||||||
($T:ty, $Variant:ident) => {{
|
($T:ty, $Variant:ident) => {{
|
||||||
let buffer_data = buffer as *mut $T;
|
let buffer_data = buffer as *mut $T;
|
||||||
let output_buffer = OutputBuffer {
|
let slice = slice::from_raw_parts_mut(buffer_data, buffer_len);
|
||||||
stream: stream,
|
|
||||||
buffer_data: buffer_data,
|
|
||||||
buffer_len: buffer_len,
|
|
||||||
frames: frames_available,
|
|
||||||
marker: PhantomData,
|
|
||||||
};
|
|
||||||
let unknown_buffer = UnknownTypeOutputBuffer::$Variant(::OutputBuffer {
|
let unknown_buffer = UnknownTypeOutputBuffer::$Variant(::OutputBuffer {
|
||||||
target: Some(output_buffer)
|
buffer: slice
|
||||||
});
|
});
|
||||||
let data = StreamData::Output { buffer: unknown_buffer };
|
let data = StreamData::Output { buffer: unknown_buffer };
|
||||||
callback(stream_id, data);
|
callback(stream_id, data);
|
||||||
}};
|
let hresult = match stream.client_flow {
|
||||||
|
AudioClientFlow::Render { render_client } => {
|
||||||
|
(*render_client).ReleaseBuffer(frames_available as u32, 0)
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
match check_result(hresult) {
|
||||||
|
// Ignoring the error that is produced if the device has been disconnected.
|
||||||
|
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => (),
|
||||||
|
e => e.unwrap(),
|
||||||
|
};
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
match stream.sample_format {
|
match stream.sample_format {
|
||||||
|
@ -661,64 +665,6 @@ impl Drop for StreamInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InputBuffer<'a, T: 'a> {
|
|
||||||
buffer: &'a [T],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct OutputBuffer<'a, T: 'a> {
|
|
||||||
stream: &'a mut StreamInner,
|
|
||||||
|
|
||||||
buffer_data: *mut T,
|
|
||||||
buffer_len: usize,
|
|
||||||
frames: UINT32,
|
|
||||||
|
|
||||||
marker: PhantomData<&'a mut [T]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<'a, T> Send for OutputBuffer<'a, T> {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> InputBuffer<'a, T> {
|
|
||||||
#[inline]
|
|
||||||
pub fn buffer(&self) -> &[T] {
|
|
||||||
&self.buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
// Nothing to be done.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> OutputBuffer<'a, T> {
|
|
||||||
#[inline]
|
|
||||||
pub fn buffer(&mut self) -> &mut [T] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self.buffer_data, self.buffer_len) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.buffer_len
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn finish(self) {
|
|
||||||
unsafe {
|
|
||||||
let hresult = match self.stream.client_flow {
|
|
||||||
AudioClientFlow::Render { render_client } => {
|
|
||||||
(*render_client).ReleaseBuffer(self.frames as u32, 0)
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
match check_result(hresult) {
|
|
||||||
// Ignoring the error that is produced if the device has been disconnected.
|
|
||||||
Err(ref e) if e.raw_os_error() == Some(AUDCLNT_E_DEVICE_INVALIDATED) => (),
|
|
||||||
e => e.unwrap(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turns a `Format` into a `WAVEFORMATEXTENSIBLE`.
|
// Turns a `Format` into a `WAVEFORMATEXTENSIBLE`.
|
||||||
//
|
//
|
||||||
// Returns `None` if the WAVEFORMATEXTENSIBLE does not support the given format.
|
// Returns `None` if the WAVEFORMATEXTENSIBLE does not support the given format.
|
||||||
|
|
Loading…
Reference in New Issue