Update WASAPI backend for removal of `UnknownTypeBuffer`
This commit is contained in:
parent
5a619877f9
commit
b5bfb8d422
|
@ -1,3 +1,18 @@
|
||||||
|
use crate::{
|
||||||
|
BackendSpecificError,
|
||||||
|
DefaultFormatError,
|
||||||
|
DeviceNameError,
|
||||||
|
DevicesError,
|
||||||
|
Format,
|
||||||
|
InputData,
|
||||||
|
OutputData,
|
||||||
|
Sample,
|
||||||
|
SampleFormat,
|
||||||
|
SampleRate,
|
||||||
|
SupportedFormat,
|
||||||
|
SupportedFormatsError,
|
||||||
|
COMMON_SAMPLE_RATES,
|
||||||
|
};
|
||||||
use std;
|
use std;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -9,17 +24,6 @@ use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::sync::{Arc, Mutex, MutexGuard, atomic::Ordering};
|
use std::sync::{Arc, Mutex, MutexGuard, atomic::Ordering};
|
||||||
|
|
||||||
use BackendSpecificError;
|
|
||||||
use DefaultFormatError;
|
|
||||||
use DeviceNameError;
|
|
||||||
use DevicesError;
|
|
||||||
use Format;
|
|
||||||
use SampleFormat;
|
|
||||||
use SampleRate;
|
|
||||||
use SupportedFormat;
|
|
||||||
use SupportedFormatsError;
|
|
||||||
use COMMON_SAMPLE_RATES;
|
|
||||||
|
|
||||||
use super::check_result;
|
use super::check_result;
|
||||||
use super::check_result_backend_specific;
|
use super::check_result_backend_specific;
|
||||||
use super::com;
|
use super::com;
|
||||||
|
@ -54,7 +58,7 @@ use super::{
|
||||||
stream::{AudioClientFlow, Stream, StreamInner},
|
stream::{AudioClientFlow, Stream, StreamInner},
|
||||||
winapi::um::synchapi,
|
winapi::um::synchapi,
|
||||||
};
|
};
|
||||||
use crate::{traits::DeviceTrait, BuildStreamError, StreamData, StreamError};
|
use crate::{traits::DeviceTrait, BuildStreamError, StreamError};
|
||||||
|
|
||||||
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>;
|
||||||
|
@ -102,38 +106,34 @@ impl DeviceTrait for Device {
|
||||||
Device::default_output_format(self)
|
Device::default_output_format(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_input_stream<D, E>(
|
fn build_input_stream<T, 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(StreamData) + Send + 'static,
|
T: Sample,
|
||||||
|
D: FnMut(InputData<T>) + Send + 'static,
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
Ok(Stream::new(
|
let stream_inner = self.build_input_stream_inner(format)?;
|
||||||
self.build_input_stream_inner(format)?,
|
Ok(Stream::new_input(stream_inner, data_callback, error_callback))
|
||||||
data_callback,
|
|
||||||
error_callback,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_stream<D, E>(
|
fn build_output_stream<T, 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(StreamData) + Send + 'static,
|
T: Sample,
|
||||||
|
D: FnMut(OutputData<T>) + Send + 'static,
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
Ok(Stream::new(
|
let stream_inner = self.build_output_stream_inner(format)?;
|
||||||
self.build_output_stream_inner(format)?,
|
Ok(Stream::new_output(stream_inner, data_callback, error_callback))
|
||||||
data_callback,
|
|
||||||
error_callback,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
use crate::{
|
||||||
|
BackendSpecificError,
|
||||||
|
InputData,
|
||||||
|
OutputData,
|
||||||
|
PauseStreamError,
|
||||||
|
PlayStreamError,
|
||||||
|
Sample,
|
||||||
|
SampleFormat,
|
||||||
|
StreamError,
|
||||||
|
};
|
||||||
|
use crate::traits::StreamTrait;
|
||||||
|
use std::mem;
|
||||||
|
use std::ptr;
|
||||||
|
use std::slice;
|
||||||
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
use std::thread::{self, JoinHandle};
|
||||||
use super::check_result;
|
use super::check_result;
|
||||||
use super::winapi::shared::basetsd::UINT32;
|
use super::winapi::shared::basetsd::UINT32;
|
||||||
use super::winapi::shared::minwindef::{BYTE, FALSE, WORD};
|
use super::winapi::shared::minwindef::{BYTE, FALSE, WORD};
|
||||||
|
@ -7,23 +23,6 @@ use super::winapi::um::synchapi;
|
||||||
use super::winapi::um::winbase;
|
use super::winapi::um::winbase;
|
||||||
use super::winapi::um::winnt;
|
use super::winapi::um::winnt;
|
||||||
|
|
||||||
use std::mem;
|
|
||||||
use std::ptr;
|
|
||||||
use std::slice;
|
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
|
||||||
|
|
||||||
use crate::traits::StreamTrait;
|
|
||||||
use std::thread::{self, JoinHandle};
|
|
||||||
|
|
||||||
use BackendSpecificError;
|
|
||||||
use PauseStreamError;
|
|
||||||
use PlayStreamError;
|
|
||||||
use SampleFormat;
|
|
||||||
use StreamData;
|
|
||||||
use StreamError;
|
|
||||||
use UnknownTypeInputBuffer;
|
|
||||||
use UnknownTypeOutputBuffer;
|
|
||||||
|
|
||||||
pub struct Stream {
|
pub struct Stream {
|
||||||
/// The high-priority audio processing thread calling callbacks.
|
/// The high-priority audio processing thread calling callbacks.
|
||||||
/// Option used for moving out in destructor.
|
/// Option used for moving out in destructor.
|
||||||
|
@ -86,13 +85,14 @@ pub struct StreamInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream {
|
impl Stream {
|
||||||
pub(crate) fn new<D, E>(
|
pub(crate) fn new_input<T, 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
|
||||||
D: FnMut(StreamData) + Send + 'static,
|
T: Sample,
|
||||||
|
D: FnMut(InputData<T>) + Send + 'static,
|
||||||
E: FnMut(StreamError) + Send + 'static,
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
{
|
{
|
||||||
let pending_scheduled_event =
|
let pending_scheduled_event =
|
||||||
|
@ -106,7 +106,37 @@ impl Stream {
|
||||||
};
|
};
|
||||||
|
|
||||||
let thread =
|
let thread =
|
||||||
thread::spawn(move || run_inner(run_context, &mut data_callback, &mut error_callback));
|
thread::spawn(move || run_input(run_context, &mut data_callback, &mut error_callback));
|
||||||
|
|
||||||
|
Stream {
|
||||||
|
thread: Some(thread),
|
||||||
|
commands: tx,
|
||||||
|
pending_scheduled_event,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_output<T, D, E>(
|
||||||
|
stream_inner: StreamInner,
|
||||||
|
mut data_callback: D,
|
||||||
|
mut error_callback: E,
|
||||||
|
) -> Stream
|
||||||
|
where
|
||||||
|
T: Sample,
|
||||||
|
D: FnMut(OutputData<T>) + Send + 'static,
|
||||||
|
E: FnMut(StreamError) + Send + 'static,
|
||||||
|
{
|
||||||
|
let pending_scheduled_event =
|
||||||
|
unsafe { synchapi::CreateEventA(ptr::null_mut(), 0, 0, ptr::null()) };
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
|
let run_context = RunContext {
|
||||||
|
handles: vec![pending_scheduled_event, stream_inner.event],
|
||||||
|
stream: stream_inner,
|
||||||
|
commands: rx,
|
||||||
|
};
|
||||||
|
|
||||||
|
let thread =
|
||||||
|
thread::spawn(move || run_output(run_context, &mut data_callback, &mut error_callback));
|
||||||
|
|
||||||
Stream {
|
Stream {
|
||||||
thread: Some(thread),
|
thread: Some(thread),
|
||||||
|
@ -255,20 +285,70 @@ fn stream_error_from_hresult(hresult: winnt::HRESULT) -> Result<(), StreamError>
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_inner(
|
fn run_input<T>(
|
||||||
mut run_context: RunContext,
|
mut run_ctxt: RunContext,
|
||||||
data_callback: &mut dyn FnMut(StreamData),
|
data_callback: &mut dyn FnMut(InputData<T>),
|
||||||
error_callback: &mut dyn FnMut(StreamError),
|
error_callback: &mut dyn FnMut(StreamError),
|
||||||
) {
|
) where
|
||||||
unsafe {
|
T: Sample,
|
||||||
'stream_loop: loop {
|
{
|
||||||
|
loop {
|
||||||
|
match process_commands_and_await_signal(&mut run_ctxt, error_callback) {
|
||||||
|
Some(ControlFlow::Break) => break,
|
||||||
|
Some(ControlFlow::Continue) => continue,
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
let capture_client = match run_ctxt.stream.client_flow {
|
||||||
|
AudioClientFlow::Capture { capture_client } => capture_client,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
match process_input(&mut run_ctxt.stream, capture_client, data_callback, error_callback) {
|
||||||
|
ControlFlow::Break => break,
|
||||||
|
ControlFlow::Continue => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_output<T>(
|
||||||
|
mut run_ctxt: RunContext,
|
||||||
|
data_callback: &mut dyn FnMut(OutputData<T>),
|
||||||
|
error_callback: &mut dyn FnMut(StreamError),
|
||||||
|
) where
|
||||||
|
T: Sample,
|
||||||
|
{
|
||||||
|
loop {
|
||||||
|
match process_commands_and_await_signal(&mut run_ctxt, error_callback) {
|
||||||
|
Some(ControlFlow::Break) => break,
|
||||||
|
Some(ControlFlow::Continue) => continue,
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
let render_client = match run_ctxt.stream.client_flow {
|
||||||
|
AudioClientFlow::Render { render_client } => render_client,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
match process_output(&mut run_ctxt.stream, render_client, data_callback, error_callback) {
|
||||||
|
ControlFlow::Break => break,
|
||||||
|
ControlFlow::Continue => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ControlFlow {
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_commands_and_await_signal(
|
||||||
|
run_context: &mut RunContext,
|
||||||
|
error_callback: &mut dyn FnMut(StreamError),
|
||||||
|
) -> Option<ControlFlow> {
|
||||||
// Process queued commands.
|
// Process queued commands.
|
||||||
match process_commands(&mut run_context) {
|
match process_commands(run_context) {
|
||||||
Ok(true) => (),
|
Ok(true) => (),
|
||||||
Ok(false) => break,
|
Ok(false) => return Some(ControlFlow::Break),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error_callback(err);
|
error_callback(err);
|
||||||
break 'stream_loop;
|
return Some(ControlFlow::Break);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -277,23 +357,31 @@ fn run_inner(
|
||||||
Ok(idx) => idx,
|
Ok(idx) => idx,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error_callback(err.into());
|
error_callback(err.into());
|
||||||
break 'stream_loop;
|
return Some(ControlFlow::Break);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// If `handle_idx` is 0, then it's `pending_scheduled_event` that was signalled in
|
// If `handle_idx` is 0, then it's `pending_scheduled_event` that was signalled in
|
||||||
// order for us to pick up the pending commands. Otherwise, a stream needs data.
|
// order for us to pick up the pending commands. Otherwise, a stream needs data.
|
||||||
if handle_idx == 0 {
|
if handle_idx == 0 {
|
||||||
continue;
|
return Some(ControlFlow::Continue);
|
||||||
}
|
}
|
||||||
|
|
||||||
let stream = &mut run_context.stream;
|
None
|
||||||
let sample_size = stream.sample_format.sample_size();
|
}
|
||||||
|
|
||||||
// Obtaining a pointer to the buffer.
|
// The loop for processing pending input data.
|
||||||
match stream.client_flow {
|
fn process_input<T>(
|
||||||
AudioClientFlow::Capture { capture_client } => {
|
stream: &StreamInner,
|
||||||
|
capture_client: *mut audioclient::IAudioCaptureClient,
|
||||||
|
data_callback: &mut dyn FnMut(InputData<T>),
|
||||||
|
error_callback: &mut dyn FnMut(StreamError),
|
||||||
|
) -> ControlFlow
|
||||||
|
where
|
||||||
|
T: Sample,
|
||||||
|
{
|
||||||
let mut frames_available = 0;
|
let mut frames_available = 0;
|
||||||
|
unsafe {
|
||||||
// Get the available data in the shared buffer.
|
// Get the available data in the shared buffer.
|
||||||
let mut buffer: *mut BYTE = mem::uninitialized();
|
let mut buffer: *mut BYTE = mem::uninitialized();
|
||||||
let mut flags = mem::uninitialized();
|
let mut flags = mem::uninitialized();
|
||||||
|
@ -301,10 +389,10 @@ fn run_inner(
|
||||||
let hresult = (*capture_client).GetNextPacketSize(&mut frames_available);
|
let hresult = (*capture_client).GetNextPacketSize(&mut frames_available);
|
||||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||||
error_callback(err);
|
error_callback(err);
|
||||||
break 'stream_loop;
|
return ControlFlow::Break;
|
||||||
}
|
}
|
||||||
if frames_available == 0 {
|
if frames_available == 0 {
|
||||||
break;
|
return ControlFlow::Continue;
|
||||||
}
|
}
|
||||||
let hresult = (*capture_client).GetBuffer(
|
let hresult = (*capture_client).GetBuffer(
|
||||||
&mut buffer,
|
&mut buffer,
|
||||||
|
@ -319,96 +407,74 @@ fn run_inner(
|
||||||
continue;
|
continue;
|
||||||
} else if let Err(err) = stream_error_from_hresult(hresult) {
|
} else if let Err(err) = stream_error_from_hresult(hresult) {
|
||||||
error_callback(err);
|
error_callback(err);
|
||||||
break 'stream_loop;
|
return ControlFlow::Break;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(!buffer.is_null());
|
debug_assert!(!buffer.is_null());
|
||||||
|
|
||||||
let buffer_len = frames_available as usize
|
let buffer_len = frames_available as usize
|
||||||
* stream.bytes_per_frame as usize
|
* stream.bytes_per_frame as usize
|
||||||
/ sample_size;
|
/ mem::size_of::<T>();
|
||||||
|
|
||||||
// Simplify the capture callback sample format branches.
|
// Simplify the capture callback sample format branches.
|
||||||
macro_rules! capture_callback {
|
let buffer_data = buffer as *mut _ as *const T;
|
||||||
($T:ty, $Variant:ident) => {{
|
|
||||||
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 unknown_buffer =
|
let input_data = InputData { buffer: slice };
|
||||||
UnknownTypeInputBuffer::$Variant(::InputBuffer {
|
data_callback(input_data);
|
||||||
buffer: slice,
|
|
||||||
});
|
|
||||||
let data = StreamData::Input {
|
|
||||||
buffer: unknown_buffer,
|
|
||||||
};
|
|
||||||
data_callback(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) {
|
||||||
error_callback(err);
|
error_callback(err);
|
||||||
break 'stream_loop;
|
return ControlFlow::Break;
|
||||||
}
|
}
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match stream.sample_format {
|
// The loop for writing output data.
|
||||||
SampleFormat::F32 => capture_callback!(f32, F32),
|
fn process_output<T>(
|
||||||
SampleFormat::I16 => capture_callback!(i16, I16),
|
stream: &StreamInner,
|
||||||
SampleFormat::U16 => capture_callback!(u16, U16),
|
render_client: *mut audioclient::IAudioRenderClient,
|
||||||
}
|
data_callback: &mut dyn FnMut(OutputData<T>),
|
||||||
}
|
error_callback: &mut dyn FnMut(StreamError),
|
||||||
}
|
) -> ControlFlow
|
||||||
|
where
|
||||||
AudioClientFlow::Render { render_client } => {
|
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) => continue, // TODO: Can this happen?
|
Ok(0) => return ControlFlow::Continue, // TODO: Can this happen?
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error_callback(err);
|
error_callback(err);
|
||||||
break 'stream_loop;
|
return ControlFlow::Break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
let mut buffer: *mut BYTE = mem::uninitialized();
|
let mut buffer: *mut BYTE = mem::uninitialized();
|
||||||
let hresult =
|
let hresult =
|
||||||
(*render_client).GetBuffer(frames_available, &mut buffer as *mut *mut _);
|
(*render_client).GetBuffer(frames_available, &mut buffer as *mut *mut _);
|
||||||
|
|
||||||
if let Err(err) = stream_error_from_hresult(hresult) {
|
if let Err(err) = stream_error_from_hresult(hresult) {
|
||||||
error_callback(err);
|
error_callback(err);
|
||||||
break 'stream_loop;
|
return ControlFlow::Break;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(!buffer.is_null());
|
debug_assert!(!buffer.is_null());
|
||||||
let buffer_len =
|
let buffer_len =
|
||||||
frames_available as usize * stream.bytes_per_frame as usize / sample_size;
|
frames_available as usize * stream.bytes_per_frame as usize / mem::size_of::<T>();
|
||||||
|
|
||||||
// Simplify the render callback sample format branches.
|
let buffer_data = buffer as *mut T;
|
||||||
macro_rules! render_callback {
|
|
||||||
($T:ty, $Variant:ident) => {{
|
|
||||||
let buffer_data = buffer as *mut $T;
|
|
||||||
let slice = slice::from_raw_parts_mut(buffer_data, buffer_len);
|
let slice = slice::from_raw_parts_mut(buffer_data, buffer_len);
|
||||||
let unknown_buffer =
|
let output_data = OutputData { buffer: slice };
|
||||||
UnknownTypeOutputBuffer::$Variant(::OutputBuffer { buffer: slice });
|
data_callback(output_data);
|
||||||
let data = StreamData::Output {
|
let hresult = (*render_client).ReleaseBuffer(frames_available as u32, 0);
|
||||||
buffer: unknown_buffer,
|
|
||||||
};
|
|
||||||
data_callback(data);
|
|
||||||
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);
|
||||||
break 'stream_loop;
|
return ControlFlow::Break;
|
||||||
}
|
}
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match stream.sample_format {
|
ControlFlow::Continue
|
||||||
SampleFormat::F32 => render_callback!(f32, F32),
|
|
||||||
SampleFormat::I16 => render_callback!(i16, I16),
|
|
||||||
SampleFormat::U16 => render_callback!(u16, U16),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue