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:
Tatsuyuki Ishi 2019-04-30 15:43:47 +09:00
parent 0354548426
commit 4d3fe57fe3
7 changed files with 156 additions and 366 deletions

View File

@ -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>())
}

View File

@ -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.
}
}

View File

@ -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();
);
}
}

View File

@ -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(),
} }
} }
} }

View File

@ -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) {
}
}

View File

@ -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;

View File

@ -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.