[coreaudio] Remove intermediate buffer (#182)
* [coreaudio] Remove intermediate buffer Fixes #181 * [coreaudio] Create voice id after setting format
This commit is contained in:
parent
77cd690b00
commit
e9856c07ed
|
@ -3,6 +3,7 @@
|
||||||
- Changed the emscripten backend to consume less CPU.
|
- Changed the emscripten backend to consume less CPU.
|
||||||
- Added improvements to the crate documentation.
|
- Added improvements to the crate documentation.
|
||||||
- Implement `pause` and `play` for ALSA backend.
|
- Implement `pause` and `play` for ALSA backend.
|
||||||
|
- Reduced the number of allocations in the CoreAudio backend.
|
||||||
|
|
||||||
# Version 0.5.1 (2017-10-21)
|
# Version 0.5.1 (2017-10-21)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use super::Endpoint;
|
use super::Endpoint;
|
||||||
|
|
||||||
use Format;
|
|
||||||
use SupportedFormat;
|
use SupportedFormat;
|
||||||
|
|
||||||
use std::vec::IntoIter as VecIntoIter;
|
use std::vec::IntoIter as VecIntoIter;
|
||||||
|
|
|
@ -14,9 +14,18 @@ use std::mem;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
use self::coreaudio::audio_unit::AudioUnit;
|
use self::coreaudio::audio_unit::{AudioUnit, Scope, Element};
|
||||||
use self::coreaudio::audio_unit::render_callback::{self, data};
|
use self::coreaudio::audio_unit::render_callback::{self, data};
|
||||||
|
use self::coreaudio::bindings::audio_unit::{
|
||||||
|
AudioStreamBasicDescription,
|
||||||
|
AudioBuffer,
|
||||||
|
kAudioFormatLinearPCM,
|
||||||
|
kAudioFormatFlagIsFloat,
|
||||||
|
kAudioFormatFlagIsPacked,
|
||||||
|
kAudioUnitProperty_StreamFormat
|
||||||
|
};
|
||||||
|
|
||||||
mod enumerate;
|
mod enumerate;
|
||||||
|
|
||||||
|
@ -108,7 +117,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build_voice(&self, endpoint: &Endpoint, format: &Format)
|
pub fn build_voice(&self, _endpoint: &Endpoint, _format: &Format)
|
||||||
-> Result<VoiceId, CreationError> {
|
-> Result<VoiceId, CreationError> {
|
||||||
let mut audio_unit = {
|
let mut audio_unit = {
|
||||||
let au_type = if cfg!(target_os = "ios") {
|
let au_type = if cfg!(target_os = "ios") {
|
||||||
|
@ -123,6 +132,27 @@ impl EventLoop {
|
||||||
AudioUnit::new(au_type)?
|
AudioUnit::new(au_type)?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: iOS uses integer and fixed-point data
|
||||||
|
|
||||||
|
// Set the stream in interleaved mode.
|
||||||
|
let asbd = AudioStreamBasicDescription {
|
||||||
|
mBitsPerChannel: 32,
|
||||||
|
mBytesPerFrame: 8,
|
||||||
|
mChannelsPerFrame: 2,
|
||||||
|
mBytesPerPacket: 8,
|
||||||
|
mFramesPerPacket: 1,
|
||||||
|
mFormatFlags: (kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked) as u32,
|
||||||
|
mFormatID: kAudioFormatLinearPCM,
|
||||||
|
mSampleRate: 44100.0,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
audio_unit.set_property(
|
||||||
|
kAudioUnitProperty_StreamFormat,
|
||||||
|
Scope::Input,
|
||||||
|
Element::Output,
|
||||||
|
Some(&asbd)
|
||||||
|
).map_err(convert_error)?;
|
||||||
|
|
||||||
// Determine the future ID of the voice.
|
// Determine the future ID of the voice.
|
||||||
let mut voices_lock = self.voices.lock().unwrap();
|
let mut voices_lock = self.voices.lock().unwrap();
|
||||||
let voice_id = voices_lock
|
let voice_id = voices_lock
|
||||||
|
@ -130,31 +160,34 @@ impl EventLoop {
|
||||||
.position(|n| n.is_none())
|
.position(|n| n.is_none())
|
||||||
.unwrap_or(voices_lock.len());
|
.unwrap_or(voices_lock.len());
|
||||||
|
|
||||||
// TODO: iOS uses integer and fixed-point data
|
|
||||||
|
|
||||||
// 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 active_callbacks = self.active_callbacks.clone();
|
let active_callbacks = self.active_callbacks.clone();
|
||||||
audio_unit.set_render_callback(move |mut args: render_callback::Args<data::NonInterleaved<f32>>| {
|
audio_unit.set_render_callback(move |args: render_callback::Args<data::Raw>| unsafe {
|
||||||
// If `run()` is currently running, then a callback will be available from this list.
|
// If `run()` is currently running, then a callback will be available from this list.
|
||||||
// Otherwise, we just fill the buffer with zeroes and return.
|
// Otherwise, we just fill the buffer with zeroes and return.
|
||||||
|
|
||||||
|
let AudioBuffer {
|
||||||
|
mNumberChannels: _num_channels,
|
||||||
|
mDataByteSize: data_byte_size,
|
||||||
|
mData: data
|
||||||
|
} = (*args.data.data).mBuffers[0];
|
||||||
|
let data_slice = slice::from_raw_parts_mut(data as *mut f32, (data_byte_size / 4) as usize);
|
||||||
|
|
||||||
let mut callbacks = active_callbacks.callbacks.lock().unwrap();
|
let mut callbacks = active_callbacks.callbacks.lock().unwrap();
|
||||||
let callback = if let Some(cb) = callbacks.get_mut(0) {
|
let callback = if let Some(cb) = callbacks.get_mut(0) {
|
||||||
cb
|
cb
|
||||||
} else {
|
} else {
|
||||||
for channel in args.data.channels_mut() {
|
for sample in data_slice.iter_mut() {
|
||||||
for elem in channel.iter_mut() {
|
*sample = 0.0;
|
||||||
*elem = 0.0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let buffer = {
|
let buffer = {
|
||||||
let buffer_len = args.num_frames * args.data.channels().count();
|
|
||||||
Buffer {
|
Buffer {
|
||||||
args: &mut args,
|
buffer: data_slice
|
||||||
buffer: vec![0.0; buffer_len],
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -210,8 +243,7 @@ impl EventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Buffer<'a, T: 'a> {
|
pub struct Buffer<'a, T: 'a> {
|
||||||
args: &'a mut render_callback::Args<data::NonInterleaved<T>>,
|
buffer: &'a mut [T],
|
||||||
buffer: Vec<T>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Buffer<'a, T>
|
impl<'a, T> Buffer<'a, T>
|
||||||
|
@ -219,7 +251,7 @@ impl<'a, T> Buffer<'a, T>
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn buffer(&mut self) -> &mut [T] {
|
pub fn buffer(&mut self) -> &mut [T] {
|
||||||
&mut self.buffer[..]
|
&mut self.buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -229,13 +261,6 @@ impl<'a, T> Buffer<'a, T>
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn finish(self) {
|
pub fn finish(self) {
|
||||||
// TODO: At the moment this assumes the Vec<T> is a Vec<f32>.
|
// Do nothing. We wrote directly to the buffer.
|
||||||
// Need to add T: Sample and use Sample::to_vec_f32.
|
|
||||||
let num_channels = self.args.data.channels().count();
|
|
||||||
for (i, frame) in self.buffer.chunks(num_channels).enumerate() {
|
|
||||||
for (channel, sample) in self.args.data.channels_mut().zip(frame.iter()) {
|
|
||||||
channel[i] = *sample;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,7 @@
|
||||||
|
|
||||||
#![recursion_limit = "512"]
|
#![recursion_limit = "512"]
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue