Port CoreAudio backend

This commit is contained in:
Tatsuyuki Ishi 2019-09-05 17:34:07 +09:00 committed by mitchmindtree
parent c09a58ac02
commit 70dcf2390a
2 changed files with 47 additions and 221 deletions

View File

@ -14,20 +14,18 @@ use SupportedFormatsError;
use SampleFormat; use SampleFormat;
use SampleRate; use SampleRate;
use StreamData; use StreamData;
use StreamDataResult; use StreamError;
use SupportedFormat; use SupportedFormat;
use UnknownTypeInputBuffer; use UnknownTypeInputBuffer;
use UnknownTypeOutputBuffer; use UnknownTypeOutputBuffer;
use traits::{DeviceTrait, EventLoopTrait, HostTrait, StreamIdTrait}; use traits::{DeviceTrait, HostTrait, StreamTrait};
use std::ffi::CStr; use std::ffi::CStr;
use std::fmt; use std::fmt;
use std::mem; use std::mem;
use std::cell::RefCell;
use std::os::raw::c_char; use std::os::raw::c_char;
use std::ptr::null; use std::ptr::null;
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::Duration;
use std::slice; use std::slice;
use self::coreaudio::audio_unit::{AudioUnit, Scope, Element}; use self::coreaudio::audio_unit::{AudioUnit, Scope, Element};
@ -87,7 +85,6 @@ impl Host {
impl HostTrait for Host { impl HostTrait for Host {
type Devices = Devices; type Devices = Devices;
type Device = Device; type Device = Device;
type EventLoop = EventLoop;
fn is_available() -> bool { fn is_available() -> bool {
// Assume coreaudio is always available on macOS and iOS. // Assume coreaudio is always available on macOS and iOS.
@ -105,15 +102,12 @@ impl HostTrait for Host {
fn default_output_device(&self) -> Option<Self::Device> { fn default_output_device(&self) -> Option<Self::Device> {
default_output_device() default_output_device()
} }
fn event_loop(&self) -> Self::EventLoop {
EventLoop::new()
}
} }
impl DeviceTrait for Device { impl DeviceTrait for Device {
type SupportedInputFormats = SupportedInputFormats; type SupportedInputFormats = SupportedInputFormats;
type SupportedOutputFormats = SupportedOutputFormats; type SupportedOutputFormats = SupportedOutputFormats;
type Stream = Stream;
fn name(&self) -> Result<String, DeviceNameError> { fn name(&self) -> Result<String, DeviceNameError> {
Device::name(self) Device::name(self)
@ -134,50 +128,16 @@ impl DeviceTrait for Device {
fn default_output_format(&self) -> Result<Format, DefaultFormatError> { fn default_output_format(&self) -> Result<Format, DefaultFormatError> {
Device::default_output_format(self) Device::default_output_format(self)
} }
}
impl EventLoopTrait for EventLoop { fn build_input_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError> where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static {
type Device = Device; Device::build_input_stream(self, format, data_callback, error_callback)
type StreamId = StreamId;
fn build_input_stream(
&self,
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError> {
EventLoop::build_input_stream(self, device, format)
} }
fn build_output_stream( fn build_output_stream<D, E>(&self, format: &Format, data_callback: D, error_callback: E) -> Result<Self::Stream, BuildStreamError> where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static {
&self, Device::build_output_stream(self, format, data_callback, error_callback)
device: &Self::Device,
format: &Format,
) -> Result<Self::StreamId, BuildStreamError> {
EventLoop::build_output_stream(self, device, format)
}
fn play_stream(&self, stream: Self::StreamId) -> Result<(), PlayStreamError> {
EventLoop::play_stream(self, stream)
}
fn pause_stream(&self, stream: Self::StreamId) -> Result<(), PauseStreamError> {
EventLoop::pause_stream(self, stream)
}
fn destroy_stream(&self, stream: Self::StreamId) {
EventLoop::destroy_stream(self, stream)
}
fn run<F>(&self, callback: F) -> !
where
F: FnMut(Self::StreamId, StreamDataResult) + Send,
{
EventLoop::run(self, callback)
} }
} }
impl StreamIdTrait for StreamId {}
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
pub struct Device { pub struct Device {
audio_device_id: AudioDeviceID, audio_device_id: AudioDeviceID,
@ -420,31 +380,6 @@ impl fmt::Debug for Device {
} }
} }
// The ID of a stream is its index within the `streams` array of the events loop.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StreamId(usize);
pub struct EventLoop {
// This `Arc` is shared with all the callbacks of coreaudio.
//
// TODO: Eventually, CPAL's API should be changed to allow for submitting a unique callback per
// stream to avoid streams blocking one another.
user_callback: Arc<Mutex<UserCallback>>,
streams: Mutex<Vec<Option<StreamInner>>>,
loop_cond: Arc<(Mutex<bool>, Condvar)>,
}
enum UserCallback {
// When `run` is called with a callback, that callback will be stored here.
//
// It is essential for the safety of the program that this callback is removed before `run`
// returns (not possible with the current CPAL API).
Active(&'static mut (dyn FnMut(StreamId, StreamDataResult) + Send)),
// A queue of events that have occurred but that have not yet been emitted to the user as we
// don't yet have a callback to do so.
Inactive,
}
struct StreamInner { struct StreamInner {
playing: bool, playing: bool,
audio_unit: AudioUnit, audio_unit: AudioUnit,
@ -540,75 +475,8 @@ fn audio_unit_from_device(device: &Device, input: bool) -> Result<AudioUnit, cor
Ok(audio_unit) Ok(audio_unit)
} }
impl EventLoop { impl Device {
#[inline] fn build_input_stream<D, E>(&self, format: &Format, mut data_callback: D, _error_callback: E) -> Result<Stream, BuildStreamError> where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static {
fn new() -> EventLoop {
EventLoop {
user_callback: Arc::new(Mutex::new(UserCallback::Inactive)),
streams: Mutex::new(Vec::new()),
loop_cond: Arc::new((Mutex::new(false), Condvar::new())),
}
}
#[inline]
fn run<F>(&self, mut callback: F) -> !
where F: FnMut(StreamId, StreamDataResult) + Send
{
{
let mut guard = self.user_callback.lock().unwrap();
if let UserCallback::Active(_) = *guard {
panic!("`EventLoop::run` was called when the event loop was already running");
}
let callback: &mut (dyn FnMut(StreamId, StreamDataResult) + Send) = &mut callback;
*guard = UserCallback::Active(unsafe { mem::transmute(callback) });
}
// Wait on a condvar to notify, which should never happen.
let &(ref lock, ref cvar) = &*self.loop_cond;
let mut running = lock.lock().unwrap();
*running = true;
while *running {
running = cvar.wait(running).unwrap();
}
unreachable!("current `EventLoop` API requires that `run` may not return");
// It is critical that we remove the callback before returning (currently not possible).
// *self.user_callback.lock().unwrap() = UserCallback::Inactive;
}
fn next_stream_id(&self) -> usize {
let streams_lock = self.streams.lock().unwrap();
let stream_id = streams_lock
.iter()
.position(|n| n.is_none())
.unwrap_or(streams_lock.len());
stream_id
}
// Add the stream to the list of streams within `self`.
fn add_stream(&self, stream_id: usize, au: AudioUnit, device_id: AudioDeviceID) {
let inner = StreamInner {
playing: true,
audio_unit: au,
device_id: device_id,
};
let mut streams_lock = self.streams.lock().unwrap();
if stream_id == streams_lock.len() {
streams_lock.push(Some(inner));
} else {
streams_lock[stream_id] = Some(inner);
}
}
#[inline]
fn build_input_stream(
&self,
device: &Device,
format: &Format,
) -> Result<StreamId, BuildStreamError>
{
// The scope and element for working with a device's input stream. // The scope and element for working with a device's input stream.
let scope = Scope::Output; let scope = Scope::Output;
let element = Element::Input; let element = Element::Input;
@ -624,7 +492,7 @@ impl EventLoop {
let sample_rate: f64 = 0.0; let sample_rate: f64 = 0.0;
let data_size = mem::size_of::<f64>() as u32; let data_size = mem::size_of::<f64>() as u32;
let status = AudioObjectGetPropertyData( let status = AudioObjectGetPropertyData(
device.audio_device_id, self.audio_device_id,
&property_address as *const _, &property_address as *const _,
0, 0,
null(), null(),
@ -635,26 +503,11 @@ impl EventLoop {
// If the requested sample rate is different to the device sample rate, update the device. // If the requested sample rate is different to the device sample rate, update the device.
if sample_rate as u32 != format.sample_rate.0 { if sample_rate as u32 != format.sample_rate.0 {
// In order to avoid breaking existing input streams we return an error if there is
// already an active input stream for this device with the actual sample rate.
for stream in &*self.streams.lock().unwrap() {
if let Some(stream) = stream.as_ref() {
if stream.device_id == device.audio_device_id {
let description = "cannot change device sample rate for stream as an \
existing stream is already running at the current sample rate"
.into();
let err = BackendSpecificError { description };
return Err(err.into());
}
}
}
// Get available sample rate ranges. // Get available sample rate ranges.
property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
let data_size = 0u32; let data_size = 0u32;
let status = AudioObjectGetPropertyDataSize( let status = AudioObjectGetPropertyDataSize(
device.audio_device_id, self.audio_device_id,
&property_address as *const _, &property_address as *const _,
0, 0,
null(), null(),
@ -665,7 +518,7 @@ impl EventLoop {
let mut ranges: Vec<u8> = vec![]; let mut ranges: Vec<u8> = vec![];
ranges.reserve_exact(data_size as usize); ranges.reserve_exact(data_size as usize);
let status = AudioObjectGetPropertyData( let status = AudioObjectGetPropertyData(
device.audio_device_id, self.audio_device_id,
&property_address as *const _, &property_address as *const _,
0, 0,
null(), null(),
@ -719,7 +572,7 @@ impl EventLoop {
// Add our sample rate change listener callback. // Add our sample rate change listener callback.
let reported_rate: f64 = 0.0; let reported_rate: f64 = 0.0;
let status = AudioObjectAddPropertyListener( let status = AudioObjectAddPropertyListener(
device.audio_device_id, self.audio_device_id,
&property_address as *const _, &property_address as *const _,
Some(rate_listener), Some(rate_listener),
&reported_rate as *const _ as *mut _, &reported_rate as *const _ as *mut _,
@ -729,7 +582,7 @@ impl EventLoop {
// Finally, set the sample rate. // Finally, set the sample rate.
let sample_rate = sample_rate as f64; let sample_rate = sample_rate as f64;
let status = AudioObjectSetPropertyData( let status = AudioObjectSetPropertyData(
device.audio_device_id, self.audio_device_id,
&property_address as *const _, &property_address as *const _,
0, 0,
null(), null(),
@ -753,7 +606,7 @@ impl EventLoop {
// Remove the `rate_listener` callback. // Remove the `rate_listener` callback.
let status = AudioObjectRemovePropertyListener( let status = AudioObjectRemovePropertyListener(
device.audio_device_id, self.audio_device_id,
&property_address as *const _, &property_address as *const _,
Some(rate_listener), Some(rate_listener),
&reported_rate as *const _ as *mut _, &reported_rate as *const _ as *mut _,
@ -762,18 +615,14 @@ impl EventLoop {
} }
} }
let mut audio_unit = audio_unit_from_device(device, true)?; let mut audio_unit = audio_unit_from_device(self, true)?;
// Set the stream in interleaved mode. // Set the stream in interleaved mode.
let asbd = asbd_from_format(format); let asbd = asbd_from_format(format);
audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?; audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?;
// Determine the future ID of the stream.
let stream_id = self.next_stream_id();
// 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 user_callback = self.user_callback.clone();
let sample_format = format.data_type; let sample_format = format.data_type;
let bytes_per_channel = format.data_type.sample_size(); let bytes_per_channel = format.data_type.sample_size();
type Args = render_callback::Args<data::Raw>; type Args = render_callback::Args<data::Raw>;
@ -789,20 +638,14 @@ impl EventLoop {
mData: data mData: data
} = buffers[0]; } = buffers[0];
let mut user_callback = user_callback.lock().unwrap();
// A small macro to simplify handling the callback for different sample types. // A small macro to simplify handling the callback for different sample types.
macro_rules! try_callback { macro_rules! try_callback {
($SampleFormat:ident, $SampleType:ty) => {{ ($SampleFormat:ident, $SampleType:ty) => {{
let data_len = (data_byte_size as usize / bytes_per_channel) as usize; let data_len = (data_byte_size as usize / bytes_per_channel) as usize;
let data_slice = slice::from_raw_parts(data as *const $SampleType, data_len); let data_slice = slice::from_raw_parts(data as *const $SampleType, data_len);
let callback = match *user_callback {
UserCallback::Active(ref mut cb) => cb,
UserCallback::Inactive => return Ok(()),
};
let unknown_type_buffer = UnknownTypeInputBuffer::$SampleFormat(::InputBuffer { buffer: data_slice }); let unknown_type_buffer = UnknownTypeInputBuffer::$SampleFormat(::InputBuffer { buffer: data_slice });
let stream_data = StreamData::Input { buffer: unknown_type_buffer }; let stream_data = StreamData::Input { buffer: unknown_type_buffer };
callback(StreamId(stream_id), Ok(stream_data)); data_callback(stream_data);
}}; }};
} }
@ -815,23 +658,17 @@ impl EventLoop {
Ok(()) Ok(())
})?; })?;
// TODO: start playing now? is that consistent with the other backends?
audio_unit.start()?; audio_unit.start()?;
// Add the stream to the list of streams within `self`. Ok(Stream::new(StreamInner {
self.add_stream(stream_id, audio_unit, device.audio_device_id); playing: true,
audio_unit,
Ok(StreamId(stream_id)) device_id: self.audio_device_id,
}))
} }
#[inline] fn build_output_stream<D, E>(&self, format: &Format, mut data_callback: D, _error_callback: E) -> Result<Stream, BuildStreamError> where D: FnMut(StreamData) + Send + 'static, E: FnMut(StreamError) + Send + 'static {
fn build_output_stream( let mut audio_unit = audio_unit_from_device(self, false)?;
&self,
device: &Device,
format: &Format,
) -> Result<StreamId, BuildStreamError>
{
let mut audio_unit = audio_unit_from_device(device, false)?;
// The scope and element for working with a device's output stream. // The scope and element for working with a device's output stream.
let scope = Scope::Input; let scope = Scope::Input;
@ -841,12 +678,8 @@ impl EventLoop {
let asbd = asbd_from_format(format); let asbd = asbd_from_format(format);
audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?; audio_unit.set_property(kAudioUnitProperty_StreamFormat, scope, element, Some(&asbd))?;
// Determine the future ID of the stream.
let stream_id = self.next_stream_id();
// 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 user_callback = self.user_callback.clone();
let sample_format = format.data_type; let sample_format = format.data_type;
let bytes_per_channel = format.data_type.sample_size(); let bytes_per_channel = format.data_type.sample_size();
type Args = render_callback::Args<data::Raw>; type Args = render_callback::Args<data::Raw>;
@ -860,25 +693,14 @@ impl EventLoop {
mData: data mData: data
} = (*args.data.data).mBuffers[0]; } = (*args.data.data).mBuffers[0];
let mut user_callback = user_callback.lock().unwrap();
// A small macro to simplify handling the callback for different sample types. // A small macro to simplify handling the callback for different sample types.
macro_rules! try_callback { macro_rules! try_callback {
($SampleFormat:ident, $SampleType:ty, $equilibrium:expr) => {{ ($SampleFormat:ident, $SampleType:ty, $equilibrium:expr) => {{
let data_len = (data_byte_size as usize / bytes_per_channel) as usize; let data_len = (data_byte_size as usize / bytes_per_channel) as usize;
let data_slice = slice::from_raw_parts_mut(data as *mut $SampleType, data_len); let data_slice = slice::from_raw_parts_mut(data as *mut $SampleType, data_len);
let callback = match *user_callback {
UserCallback::Active(ref mut cb) => cb,
UserCallback::Inactive => {
for sample in data_slice.iter_mut() {
*sample = $equilibrium;
}
return Ok(());
}
};
let unknown_type_buffer = UnknownTypeOutputBuffer::$SampleFormat(::OutputBuffer { buffer: data_slice }); let unknown_type_buffer = UnknownTypeOutputBuffer::$SampleFormat(::OutputBuffer { buffer: data_slice });
let stream_data = StreamData::Output { buffer: unknown_type_buffer }; let stream_data = StreamData::Output { buffer: unknown_type_buffer };
callback(StreamId(stream_id), Ok(stream_data)); data_callback(stream_data);
}}; }};
} }
@ -891,25 +713,31 @@ impl EventLoop {
Ok(()) Ok(())
})?; })?;
// TODO: start playing now? is that consistent with the other backends?
audio_unit.start()?; audio_unit.start()?;
// Add the stream to the list of streams within `self`. Ok(Stream::new(StreamInner {
self.add_stream(stream_id, audio_unit, device.audio_device_id); playing: true,
audio_unit,
Ok(StreamId(stream_id)) device_id: self.audio_device_id,
}))
} }
}
fn destroy_stream(&self, stream_id: StreamId) { pub struct Stream {
{ inner: RefCell<StreamInner>,
let mut streams = self.streams.lock().unwrap(); }
streams[stream_id.0] = None;
impl Stream {
fn new(inner: StreamInner) -> Self {
Self {
inner: RefCell::new(inner),
} }
} }
}
fn play_stream(&self, stream_id: StreamId) -> Result<(), PlayStreamError> { impl StreamTrait for Stream {
let mut streams = self.streams.lock().unwrap(); fn play(&self) -> Result<(), PlayStreamError> {
let stream = streams[stream_id.0].as_mut().unwrap(); let mut stream = self.inner.borrow_mut();
if !stream.playing { if !stream.playing {
if let Err(e) = stream.audio_unit.start() { if let Err(e) = stream.audio_unit.start() {
@ -922,9 +750,8 @@ impl EventLoop {
Ok(()) Ok(())
} }
fn pause_stream(&self, stream_id: StreamId) -> Result<(), PauseStreamError> { fn pause(&self) -> Result<(), PauseStreamError> {
let mut streams = self.streams.lock().unwrap(); let mut stream = self.inner.borrow_mut();
let stream = streams[stream_id.0].as_mut().unwrap();
if stream.playing { if stream.playing {
if let Err(e) = stream.audio_unit.stop() { if let Err(e) = stream.audio_unit.stop() {

View File

@ -414,9 +414,8 @@ mod platform_impl {
pub use crate::host::coreaudio::{ pub use crate::host::coreaudio::{
Device as CoreAudioDevice, Device as CoreAudioDevice,
Devices as CoreAudioDevices, Devices as CoreAudioDevices,
EventLoop as CoreAudioEventLoop,
Host as CoreAudioHost, Host as CoreAudioHost,
StreamId as CoreAudioStreamId, Stream as CoreAudioStream,
SupportedInputFormats as CoreAudioSupportedInputFormats, SupportedInputFormats as CoreAudioSupportedInputFormats,
SupportedOutputFormats as CoreAudioSupportedOutputFormats, SupportedOutputFormats as CoreAudioSupportedOutputFormats,
}; };