2016-07-05 20:54:03 +00:00
|
|
|
extern crate coreaudio;
|
2018-01-26 12:49:47 +00:00
|
|
|
extern crate core_foundation_sys;
|
2015-02-28 18:50:29 +00:00
|
|
|
|
2018-02-04 12:02:16 +00:00
|
|
|
use ChannelCount;
|
2015-09-24 03:02:28 +00:00
|
|
|
use CreationError;
|
|
|
|
use Format;
|
|
|
|
use FormatsEnumerationError;
|
2016-08-12 07:49:13 +00:00
|
|
|
use Sample;
|
2015-09-24 03:02:28 +00:00
|
|
|
use SampleFormat;
|
2018-02-04 12:02:16 +00:00
|
|
|
use SampleRate;
|
2017-10-20 19:18:40 +00:00
|
|
|
use SupportedFormat;
|
2016-08-12 07:49:13 +00:00
|
|
|
use UnknownTypeBuffer;
|
|
|
|
|
2018-01-26 12:49:47 +00:00
|
|
|
use std::ffi::CStr;
|
2017-10-18 18:24:05 +00:00
|
|
|
use std::mem;
|
2018-01-26 12:49:47 +00:00
|
|
|
use std::os::raw::c_char;
|
|
|
|
use std::ptr::null;
|
2016-08-12 07:49:13 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
2017-04-07 10:06:00 +00:00
|
|
|
use std::thread;
|
|
|
|
use std::time::Duration;
|
2017-11-03 09:51:02 +00:00
|
|
|
use std::slice;
|
2016-08-12 07:49:13 +00:00
|
|
|
|
2017-11-03 09:51:02 +00:00
|
|
|
use self::coreaudio::audio_unit::{AudioUnit, Scope, Element};
|
2016-08-12 07:49:13 +00:00
|
|
|
use self::coreaudio::audio_unit::render_callback::{self, data};
|
2018-01-26 12:49:47 +00:00
|
|
|
use self::coreaudio::sys::{
|
2017-11-03 09:51:02 +00:00
|
|
|
AudioBuffer,
|
2018-01-26 12:49:47 +00:00
|
|
|
AudioBufferList,
|
|
|
|
AudioDeviceID,
|
|
|
|
AudioObjectGetPropertyData,
|
|
|
|
AudioObjectGetPropertyDataSize,
|
|
|
|
AudioObjectPropertyAddress,
|
|
|
|
AudioStreamBasicDescription,
|
|
|
|
AudioValueRange,
|
|
|
|
kAudioDevicePropertyAvailableNominalSampleRates,
|
|
|
|
kAudioDevicePropertyDeviceNameCFString,
|
|
|
|
kAudioDevicePropertyScopeOutput,
|
|
|
|
kAudioDevicePropertyStreamConfiguration,
|
2017-11-03 09:51:02 +00:00
|
|
|
kAudioFormatFlagIsFloat,
|
|
|
|
kAudioFormatFlagIsPacked,
|
2018-01-26 12:49:47 +00:00
|
|
|
kAudioFormatLinearPCM,
|
|
|
|
kAudioHardwareNoError,
|
|
|
|
kAudioObjectPropertyElementMaster,
|
|
|
|
kAudioObjectPropertyScopeOutput,
|
|
|
|
kAudioOutputUnitProperty_CurrentDevice,
|
|
|
|
kAudioUnitProperty_StreamFormat,
|
|
|
|
kCFStringEncodingUTF8,
|
|
|
|
};
|
|
|
|
use self::core_foundation_sys::string::{
|
|
|
|
CFStringRef,
|
|
|
|
CFStringGetCStringPtr,
|
2017-11-03 09:51:02 +00:00
|
|
|
};
|
2015-02-28 18:50:29 +00:00
|
|
|
|
2015-09-24 03:02:28 +00:00
|
|
|
mod enumerate;
|
|
|
|
|
2017-10-12 09:54:09 +00:00
|
|
|
pub use self::enumerate::{EndpointsIterator, SupportedFormatsIterator, default_endpoint};
|
2015-09-24 03:02:28 +00:00
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Eq)]
|
2018-01-26 12:49:47 +00:00
|
|
|
pub struct Endpoint {
|
|
|
|
audio_device_id: AudioDeviceID,
|
|
|
|
}
|
2015-09-24 03:02:28 +00:00
|
|
|
|
|
|
|
impl Endpoint {
|
2017-10-23 14:41:38 +00:00
|
|
|
pub fn supported_formats(&self) -> Result<SupportedFormatsIterator, FormatsEnumerationError> {
|
2018-01-26 12:49:47 +00:00
|
|
|
let mut property_address = AudioObjectPropertyAddress {
|
|
|
|
mSelector: kAudioDevicePropertyStreamConfiguration,
|
|
|
|
mScope: kAudioObjectPropertyScopeOutput,
|
|
|
|
mElement: kAudioObjectPropertyElementMaster,
|
|
|
|
};
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
// Retrieve the devices audio buffer list.
|
|
|
|
let data_size = 0u32;
|
|
|
|
let status = AudioObjectGetPropertyDataSize(
|
|
|
|
self.audio_device_id,
|
|
|
|
&property_address as *const _,
|
|
|
|
0,
|
|
|
|
null(),
|
|
|
|
&data_size as *const _ as *mut _,
|
|
|
|
);
|
|
|
|
if status != kAudioHardwareNoError as i32 {
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
let mut audio_buffer_list: Vec<u8> = vec![];
|
|
|
|
audio_buffer_list.reserve_exact(data_size as usize);
|
|
|
|
let status = AudioObjectGetPropertyData(
|
|
|
|
self.audio_device_id,
|
|
|
|
&property_address as *const _,
|
|
|
|
0,
|
|
|
|
null(),
|
|
|
|
&data_size as *const _ as *mut _,
|
|
|
|
audio_buffer_list.as_mut_ptr() as *mut _,
|
|
|
|
);
|
|
|
|
if status != kAudioHardwareNoError as i32 {
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
let audio_buffer_list = audio_buffer_list.as_mut_ptr() as *mut AudioBufferList;
|
|
|
|
|
|
|
|
// If there's no buffers, skip.
|
|
|
|
if (*audio_buffer_list).mNumberBuffers == 0 {
|
|
|
|
return Ok(vec![].into_iter());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Count the number of channels as the sum of all channels in all output buffers.
|
|
|
|
let n_buffers = (*audio_buffer_list).mNumberBuffers as usize;
|
|
|
|
let first: *const AudioBuffer = (*audio_buffer_list).mBuffers.as_ptr();
|
|
|
|
let buffers: &'static [AudioBuffer] = slice::from_raw_parts(first, n_buffers);
|
|
|
|
let mut n_channels = 0;
|
|
|
|
for buffer in buffers {
|
|
|
|
n_channels += buffer.mNumberChannels as usize;
|
|
|
|
}
|
|
|
|
|
|
|
|
// AFAIK the sample format should always be f32 on macos and i16 on iOS? Feel free to
|
|
|
|
// fix this if more pcm formats are supported.
|
|
|
|
let sample_format = if cfg!(target_os = "ios") {
|
|
|
|
SampleFormat::I16
|
|
|
|
} else {
|
|
|
|
SampleFormat::F32
|
|
|
|
};
|
|
|
|
|
|
|
|
// Get available sample rate ranges.
|
|
|
|
property_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
|
|
|
|
let data_size = 0u32;
|
|
|
|
let status = AudioObjectGetPropertyDataSize(
|
|
|
|
self.audio_device_id,
|
|
|
|
&property_address as *const _,
|
|
|
|
0,
|
|
|
|
null(),
|
|
|
|
&data_size as *const _ as *mut _,
|
|
|
|
);
|
|
|
|
if status != kAudioHardwareNoError as i32 {
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
let n_ranges = data_size as usize / mem::size_of::<AudioValueRange>();
|
|
|
|
let mut ranges: Vec<u8> = vec![];
|
|
|
|
ranges.reserve_exact(data_size as usize);
|
|
|
|
let status = AudioObjectGetPropertyData(
|
|
|
|
self.audio_device_id,
|
|
|
|
&property_address as *const _,
|
|
|
|
0,
|
|
|
|
null(),
|
|
|
|
&data_size as *const _ as *mut _,
|
|
|
|
ranges.as_mut_ptr() as *mut _,
|
|
|
|
);
|
|
|
|
if status != kAudioHardwareNoError as i32 {
|
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
let ranges: *mut AudioValueRange = ranges.as_mut_ptr() as *mut _;
|
|
|
|
let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges);
|
|
|
|
|
|
|
|
// Collect the supported formats for the device.
|
|
|
|
let mut fmts = vec![];
|
|
|
|
for range in ranges {
|
|
|
|
let fmt = SupportedFormat {
|
2018-02-04 12:02:16 +00:00
|
|
|
channels: n_channels as ChannelCount,
|
|
|
|
min_sample_rate: SampleRate(range.mMinimum as _),
|
|
|
|
max_sample_rate: SampleRate(range.mMaximum as _),
|
2018-01-26 12:49:47 +00:00
|
|
|
data_type: sample_format,
|
|
|
|
};
|
|
|
|
fmts.push(fmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(fmts.into_iter())
|
|
|
|
}
|
2015-09-24 03:02:28 +00:00
|
|
|
}
|
|
|
|
|
2017-10-12 09:54:09 +00:00
|
|
|
pub fn name(&self) -> String {
|
2018-01-26 12:49:47 +00:00
|
|
|
let property_address = AudioObjectPropertyAddress {
|
|
|
|
mSelector: kAudioDevicePropertyDeviceNameCFString,
|
|
|
|
mScope: kAudioDevicePropertyScopeOutput,
|
|
|
|
mElement: kAudioObjectPropertyElementMaster,
|
|
|
|
};
|
|
|
|
let device_name: CFStringRef = null();
|
|
|
|
let data_size = mem::size_of::<CFStringRef>();
|
|
|
|
let c_str = unsafe {
|
|
|
|
let status = AudioObjectGetPropertyData(
|
|
|
|
self.audio_device_id,
|
|
|
|
&property_address as *const _,
|
|
|
|
0,
|
|
|
|
null(),
|
|
|
|
&data_size as *const _ as *mut _,
|
|
|
|
&device_name as *const _ as *mut _,
|
|
|
|
);
|
|
|
|
if status != kAudioHardwareNoError as i32 {
|
|
|
|
return format!("<OSStatus: {:?}>", status);
|
|
|
|
}
|
|
|
|
let c_string: *const c_char = CFStringGetCStringPtr(device_name, kCFStringEncodingUTF8);
|
|
|
|
if c_string == null() {
|
|
|
|
return "<null>".into();
|
|
|
|
}
|
|
|
|
CStr::from_ptr(c_string as *mut _)
|
|
|
|
};
|
|
|
|
c_str.to_string_lossy().into_owned()
|
2015-09-24 03:02:28 +00:00
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// The ID of a voice is its index within the `voices` array of the events loop.
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct VoiceId(usize);
|
|
|
|
|
|
|
|
pub struct EventLoop {
|
|
|
|
// This `Arc` is shared with all the callbacks of coreaudio.
|
|
|
|
active_callbacks: Arc<ActiveCallbacks>,
|
|
|
|
voices: Mutex<Vec<Option<VoiceInner>>>,
|
2016-08-12 07:49:13 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
struct ActiveCallbacks {
|
|
|
|
// Whenever the `run()` method is called with a callback, this callback is put in this list.
|
2017-12-06 11:13:36 +00:00
|
|
|
callbacks: Mutex<Vec<&'static mut (FnMut(VoiceId, UnknownTypeBuffer) + Send)>>,
|
2015-02-28 18:50:29 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
struct VoiceInner {
|
|
|
|
playing: bool,
|
|
|
|
audio_unit: AudioUnit,
|
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
|
2017-11-03 09:50:02 +00:00
|
|
|
// TODO need stronger error identification
|
|
|
|
impl From<coreaudio::Error> for CreationError {
|
|
|
|
fn from(err: coreaudio::Error) -> CreationError {
|
|
|
|
match err {
|
|
|
|
coreaudio::Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat |
|
|
|
|
coreaudio::Error::NoKnownSubtype |
|
|
|
|
coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported) |
|
|
|
|
coreaudio::Error::AudioCodec(_) |
|
|
|
|
coreaudio::Error::AudioFormat(_) => CreationError::FormatNotSupported,
|
|
|
|
_ => CreationError::DeviceNotAvailable,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
impl EventLoop {
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn new() -> EventLoop {
|
|
|
|
EventLoop {
|
2017-10-23 14:41:38 +00:00
|
|
|
active_callbacks: Arc::new(ActiveCallbacks { callbacks: Mutex::new(Vec::new()) }),
|
2017-10-18 18:24:05 +00:00
|
|
|
voices: Mutex::new(Vec::new()),
|
|
|
|
}
|
2015-09-24 03:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn run<F>(&self, mut callback: F) -> !
|
2017-12-06 11:13:36 +00:00
|
|
|
where F: FnMut(VoiceId, UnknownTypeBuffer) + Send
|
2017-10-18 18:24:05 +00:00
|
|
|
{
|
2017-12-06 11:13:36 +00:00
|
|
|
let callback: &mut (FnMut(VoiceId, UnknownTypeBuffer) + Send) = &mut callback;
|
2017-10-23 14:41:38 +00:00
|
|
|
self.active_callbacks
|
|
|
|
.callbacks
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.push(unsafe { mem::transmute(callback) });
|
2016-08-12 07:49:13 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
loop {
|
|
|
|
// So the loop does not get optimised out in --release
|
|
|
|
thread::sleep(Duration::new(1u64, 0u32));
|
2015-09-24 03:02:28 +00:00
|
|
|
}
|
2016-08-12 07:49:13 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// Note: if we ever change this API so that `run` can return, then it is critical that
|
|
|
|
// we remove the callback from `active_callbacks`.
|
2016-08-12 07:49:13 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
#[inline]
|
2018-01-26 12:49:47 +00:00
|
|
|
pub fn build_voice(&self, endpoint: &Endpoint, format: &Format)
|
2017-10-23 14:41:38 +00:00
|
|
|
-> Result<VoiceId, CreationError> {
|
2017-10-18 18:24:05 +00:00
|
|
|
let mut audio_unit = {
|
|
|
|
let au_type = if cfg!(target_os = "ios") {
|
2017-10-23 14:41:38 +00:00
|
|
|
// The DefaultOutput unit isn't available in iOS unfortunately.
|
|
|
|
// RemoteIO is a sensible replacement.
|
|
|
|
// See https://goo.gl/CWwRTx
|
2017-10-18 18:24:05 +00:00
|
|
|
coreaudio::audio_unit::IOType::RemoteIO
|
|
|
|
} else {
|
|
|
|
coreaudio::audio_unit::IOType::DefaultOutput
|
|
|
|
};
|
|
|
|
|
2017-11-03 09:50:02 +00:00
|
|
|
AudioUnit::new(au_type)?
|
2017-10-10 16:24:53 +00:00
|
|
|
};
|
2017-10-18 18:24:05 +00:00
|
|
|
|
2018-01-26 12:49:47 +00:00
|
|
|
// TODO: Set the audio output unit device as the given endpoint device.
|
|
|
|
audio_unit.set_property(
|
|
|
|
kAudioOutputUnitProperty_CurrentDevice,
|
|
|
|
Scope::Global,
|
|
|
|
Element::Output,
|
|
|
|
Some(&endpoint.audio_device_id),
|
|
|
|
)?;
|
2017-11-03 09:51:02 +00:00
|
|
|
|
|
|
|
// Set the stream in interleaved mode.
|
2018-02-04 09:38:06 +00:00
|
|
|
let n_channels = format.channels as usize;
|
2018-02-04 12:02:16 +00:00
|
|
|
let sample_rate = format.sample_rate.0;
|
2018-01-23 05:58:37 +00:00
|
|
|
let bytes_per_channel = format.data_type.sample_size();
|
|
|
|
let bits_per_channel = bytes_per_channel * 8;
|
|
|
|
let bytes_per_frame = n_channels * bytes_per_channel;
|
|
|
|
let frames_per_packet = 1;
|
|
|
|
let bytes_per_packet = frames_per_packet * bytes_per_frame;
|
|
|
|
let sample_format = format.data_type;
|
|
|
|
let format_flags = match sample_format {
|
|
|
|
SampleFormat::F32 => (kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked) as u32,
|
|
|
|
_ => kAudioFormatFlagIsPacked as u32,
|
|
|
|
};
|
2017-11-03 09:51:02 +00:00
|
|
|
let asbd = AudioStreamBasicDescription {
|
2018-01-24 10:34:18 +00:00
|
|
|
mBitsPerChannel: bits_per_channel as _,
|
|
|
|
mBytesPerFrame: bytes_per_frame as _,
|
|
|
|
mChannelsPerFrame: n_channels as _,
|
|
|
|
mBytesPerPacket: bytes_per_packet as _,
|
|
|
|
mFramesPerPacket: frames_per_packet as _,
|
2018-01-23 05:58:37 +00:00
|
|
|
mFormatFlags: format_flags,
|
2017-11-03 09:51:02 +00:00
|
|
|
mFormatID: kAudioFormatLinearPCM,
|
2018-01-23 05:58:37 +00:00
|
|
|
mSampleRate: sample_rate as _,
|
2017-11-03 09:51:02 +00:00
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
audio_unit.set_property(
|
|
|
|
kAudioUnitProperty_StreamFormat,
|
|
|
|
Scope::Input,
|
|
|
|
Element::Output,
|
|
|
|
Some(&asbd)
|
2017-12-05 07:47:08 +00:00
|
|
|
)?;
|
2017-11-03 09:51:02 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// Determine the future ID of the voice.
|
|
|
|
let mut voices_lock = self.voices.lock().unwrap();
|
2017-10-23 14:41:38 +00:00
|
|
|
let voice_id = voices_lock
|
|
|
|
.iter()
|
|
|
|
.position(|n| n.is_none())
|
|
|
|
.unwrap_or(voices_lock.len());
|
2015-09-24 21:24:12 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// Register the callback that is being called by coreaudio whenever it needs data to be
|
|
|
|
// fed to the audio buffer.
|
|
|
|
let active_callbacks = self.active_callbacks.clone();
|
2017-11-03 09:51:02 +00:00
|
|
|
audio_unit.set_render_callback(move |args: render_callback::Args<data::Raw>| unsafe {
|
2017-10-18 18:24:05 +00:00
|
|
|
// If `run()` is currently running, then a callback will be available from this list.
|
|
|
|
// Otherwise, we just fill the buffer with zeroes and return.
|
2017-11-03 09:51:02 +00:00
|
|
|
|
|
|
|
let AudioBuffer {
|
|
|
|
mNumberChannels: _num_channels,
|
|
|
|
mDataByteSize: data_byte_size,
|
|
|
|
mData: data
|
|
|
|
} = (*args.data.data).mBuffers[0];
|
2018-01-23 05:58:37 +00:00
|
|
|
|
2017-11-03 09:51:02 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
let mut callbacks = active_callbacks.callbacks.lock().unwrap();
|
2017-11-03 09:51:02 +00:00
|
|
|
|
2018-01-23 05:58:37 +00:00
|
|
|
// A small macro to simplify handling the callback for different sample types.
|
|
|
|
macro_rules! try_callback {
|
|
|
|
($SampleFormat:ident, $SampleType:ty, $equilibrium:expr) => {{
|
2018-01-24 10:34:18 +00:00
|
|
|
let data_len = (data_byte_size as usize / bytes_per_channel) as usize;
|
2018-01-23 05:58:37 +00:00
|
|
|
let data_slice = slice::from_raw_parts_mut(data as *mut $SampleType, data_len);
|
|
|
|
let callback = match callbacks.get_mut(0) {
|
|
|
|
Some(cb) => cb,
|
|
|
|
None => {
|
|
|
|
for sample in data_slice.iter_mut() {
|
|
|
|
*sample = $equilibrium;
|
|
|
|
}
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let buffer = Buffer { buffer: data_slice };
|
|
|
|
let unknown_type_buffer = UnknownTypeBuffer::$SampleFormat(::Buffer { target: Some(buffer) });
|
|
|
|
callback(VoiceId(voice_id), unknown_type_buffer);
|
|
|
|
}};
|
|
|
|
}
|
2017-10-18 18:24:05 +00:00
|
|
|
|
2018-01-23 05:58:37 +00:00
|
|
|
match sample_format {
|
|
|
|
SampleFormat::F32 => try_callback!(F32, f32, 0.0),
|
|
|
|
SampleFormat::I16 => try_callback!(I16, i16, 0),
|
2018-01-24 10:34:18 +00:00
|
|
|
SampleFormat::U16 => try_callback!(U16, u16, ::std::u16::MAX / 2),
|
2018-01-23 05:58:37 +00:00
|
|
|
}
|
2016-08-12 07:49:13 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
Ok(())
|
2017-11-03 09:50:02 +00:00
|
|
|
})?;
|
2015-09-24 03:02:28 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
// TODO: start playing now? is that consistent with the other backends?
|
2017-11-03 09:50:02 +00:00
|
|
|
audio_unit.start()?;
|
2017-10-18 18:24:05 +00:00
|
|
|
|
|
|
|
// Add the voice to the list of voices within `self`.
|
|
|
|
{
|
|
|
|
let inner = VoiceInner {
|
|
|
|
playing: true,
|
|
|
|
audio_unit: audio_unit,
|
|
|
|
};
|
|
|
|
|
|
|
|
if voice_id == voices_lock.len() {
|
|
|
|
voices_lock.push(Some(inner));
|
|
|
|
} else {
|
|
|
|
voices_lock[voice_id] = Some(inner);
|
|
|
|
}
|
2015-09-24 03:02:28 +00:00
|
|
|
}
|
2016-01-12 16:06:14 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
Ok(VoiceId(voice_id))
|
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn destroy_voice(&self, voice_id: VoiceId) {
|
|
|
|
let mut voices = self.voices.lock().unwrap();
|
|
|
|
voices[voice_id.0] = None;
|
|
|
|
}
|
2017-04-19 10:44:42 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn play(&self, voice: VoiceId) {
|
|
|
|
let mut voices = self.voices.lock().unwrap();
|
|
|
|
let voice = voices[voice.0].as_mut().unwrap();
|
2016-08-12 07:49:13 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
if !voice.playing {
|
|
|
|
voice.audio_unit.start().unwrap();
|
|
|
|
voice.playing = true;
|
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
}
|
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn pause(&self, voice: VoiceId) {
|
|
|
|
let mut voices = self.voices.lock().unwrap();
|
|
|
|
let voice = voices[voice.0].as_mut().unwrap();
|
|
|
|
|
|
|
|
if voice.playing {
|
|
|
|
voice.audio_unit.stop().unwrap();
|
|
|
|
voice.playing = false;
|
2016-10-18 06:20:40 +00:00
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
}
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Buffer<'a, T: 'a> {
|
2017-11-03 09:51:02 +00:00
|
|
|
buffer: &'a mut [T],
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
2015-02-28 18:50:29 +00:00
|
|
|
|
2017-10-18 18:24:05 +00:00
|
|
|
impl<'a, T> Buffer<'a, T>
|
|
|
|
where T: Sample
|
|
|
|
{
|
2015-09-11 08:55:29 +00:00
|
|
|
#[inline]
|
2017-10-18 18:24:05 +00:00
|
|
|
pub fn buffer(&mut self) -> &mut [T] {
|
2017-11-03 09:51:02 +00:00
|
|
|
&mut self.buffer
|
2017-10-18 18:24:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.buffer.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn finish(self) {
|
2017-11-03 09:51:02 +00:00
|
|
|
// Do nothing. We wrote directly to the buffer.
|
2015-02-28 18:50:29 +00:00
|
|
|
}
|
|
|
|
}
|