Skip to content

Commit 1d8a55d

Browse files
committed
Fix iOS compilation error
1 parent 0cbf885 commit 1d8a55d

File tree

4 files changed

+280
-5
lines changed

4 files changed

+280
-5
lines changed

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ script:
2020
- cargo build --verbose
2121
- cargo test --verbose
2222
- cargo doc --verbose
23+
- cargo build --verbose --target aarch64-apple-ios
24+
- cargo test --verbose --target aarch64-apple-ios
25+
- cargo build --verbose --target x86_64-apple-ios
26+
- cargo test --verbose --target x86_64-apple-ios
2327
after_success: |
2428
[ $TRAVIS_BRANCH = master ] &&
2529
[ $TRAVIS_PULL_REQUEST = false ] &&

examples/feedback.rs

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
//! A basic input + output stream example, copying the mic input stream to the default output stream
2+
3+
extern crate coreaudio;
4+
5+
use std::collections::VecDeque;
6+
use std::mem;
7+
use std::ptr::null;
8+
use std::sync::{Arc, Mutex};
9+
10+
use coreaudio::audio_unit::audio_format::LinearPcmFlags;
11+
use coreaudio::audio_unit::render_callback::{self, data};
12+
use coreaudio::audio_unit::{AudioUnit, Element, SampleFormat, Scope, StreamFormat};
13+
use coreaudio::sys::*;
14+
15+
type S = f32;
16+
const SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
17+
const BASE_FLAGS: LinearPcmFlags = LinearPcmFlags::IS_FLOAT;
18+
19+
// type S = i16;
20+
// const SAMPLE_FORMAT: SampleFormat = SampleFormat::I16;
21+
// const BASE_FLAGS: LinearPcmFlags = LinearPcmFlags::IS_SIGNED_INTEGER;
22+
23+
fn main() -> Result<(), coreaudio::Error> {
24+
let mut input_audio_unit = audio_unit_from_device(default_input_device().unwrap(), true)?;
25+
let mut output_audio_unit = audio_unit_from_device(default_output_device().unwrap(), false)?;
26+
27+
// TODO
28+
// - input 1/2 channels float/signed-integer, output 1/2 channels float / signed integer
29+
30+
let in_stream_format = StreamFormat {
31+
sample_rate: 44100.0,
32+
sample_format: SAMPLE_FORMAT,
33+
flags: BASE_FLAGS | LinearPcmFlags::IS_PACKED | LinearPcmFlags::IS_NON_INTERLEAVED,
34+
channels_per_frame: 1,
35+
};
36+
37+
let out_stream_format = StreamFormat {
38+
sample_rate: 44100.0,
39+
sample_format: SAMPLE_FORMAT,
40+
flags: BASE_FLAGS | LinearPcmFlags::IS_PACKED | LinearPcmFlags::IS_NON_INTERLEAVED,
41+
channels_per_frame: 2,
42+
};
43+
44+
println!("input={:#?}", &in_stream_format);
45+
println!("output={:#?}", &out_stream_format);
46+
println!("input_asbd={:#?}", &in_stream_format.to_asbd());
47+
println!("output_asbd={:#?}", &out_stream_format.to_asbd());
48+
49+
let id = kAudioUnitProperty_StreamFormat;
50+
let asbd = in_stream_format.to_asbd();
51+
input_audio_unit.set_property(id, Scope::Output, Element::Input, Some(&asbd))?;
52+
53+
let asbd = out_stream_format.to_asbd();
54+
output_audio_unit.set_property(id, Scope::Input, Element::Output, Some(&asbd))?;
55+
56+
let buffer_left = Arc::new(Mutex::new(VecDeque::<S>::new()));
57+
let producer_left = buffer_left.clone();
58+
let consumer_left = buffer_left.clone();
59+
let buffer_right = Arc::new(Mutex::new(VecDeque::<S>::new()));
60+
let producer_right = buffer_right.clone();
61+
let consumer_right = buffer_right.clone();
62+
63+
// seed roughly 1 second of data to create a delay in the feedback loop for easier testing
64+
for buffer in vec![buffer_left, buffer_right] {
65+
let mut buffer = buffer.lock().unwrap();
66+
for _ in 0..(out_stream_format.sample_rate as i32) {
67+
buffer.push_back(0 as S);
68+
}
69+
}
70+
71+
type Args = render_callback::Args<data::NonInterleaved<S>>;
72+
input_audio_unit.set_input_callback(move |args| {
73+
let Args {
74+
num_frames,
75+
mut data,
76+
..
77+
} = args;
78+
let buffer_left = producer_left.lock().unwrap();
79+
let buffer_right = producer_right.lock().unwrap();
80+
let mut buffers = vec![buffer_left, buffer_right];
81+
for i in 0..num_frames {
82+
for (ch, channel) in data.channels_mut().enumerate() {
83+
let value: S = channel[i];
84+
buffers[ch].push_back(value);
85+
}
86+
}
87+
Ok(())
88+
})?;
89+
input_audio_unit.start()?;
90+
91+
output_audio_unit.set_render_callback(move |args: Args| {
92+
let Args {
93+
num_frames,
94+
mut data,
95+
..
96+
} = args;
97+
98+
let buffer_left = consumer_left.lock().unwrap();
99+
let buffer_right = consumer_right.lock().unwrap();
100+
let mut buffers = vec![buffer_left, buffer_right];
101+
for i in 0..num_frames {
102+
// Default other channels to copy value from first channel as a fallback
103+
let zero: S = 0 as S;
104+
let f: S = *buffers[0].front().unwrap_or(&zero);
105+
for (ch, channel) in data.channels_mut().enumerate() {
106+
let sample: S = buffers[ch].pop_front().unwrap_or(f);
107+
channel[i] = sample;
108+
}
109+
}
110+
Ok(())
111+
})?;
112+
output_audio_unit.start()?;
113+
114+
std::thread::sleep(std::time::Duration::from_millis(100000));
115+
116+
Ok(())
117+
}
118+
119+
/// Copied from cpal
120+
pub fn default_input_device() -> Option<AudioDeviceID> {
121+
let property_address = AudioObjectPropertyAddress {
122+
mSelector: kAudioHardwarePropertyDefaultInputDevice,
123+
mScope: kAudioObjectPropertyScopeGlobal,
124+
mElement: kAudioObjectPropertyElementMaster,
125+
};
126+
127+
let audio_device_id: AudioDeviceID = 0;
128+
let data_size = mem::size_of::<AudioDeviceID>();
129+
let status = unsafe {
130+
AudioObjectGetPropertyData(
131+
kAudioObjectSystemObject,
132+
&property_address as *const _,
133+
0,
134+
null(),
135+
&data_size as *const _ as *mut _,
136+
&audio_device_id as *const _ as *mut _,
137+
)
138+
};
139+
if status != kAudioHardwareNoError as i32 {
140+
return None;
141+
}
142+
143+
Some(audio_device_id)
144+
}
145+
146+
/// Copied from cpal
147+
pub fn default_output_device() -> Option<AudioDeviceID> {
148+
let property_address = AudioObjectPropertyAddress {
149+
mSelector: kAudioHardwarePropertyDefaultOutputDevice,
150+
mScope: kAudioObjectPropertyScopeGlobal,
151+
mElement: kAudioObjectPropertyElementMaster,
152+
};
153+
154+
let audio_device_id: AudioDeviceID = 0;
155+
let data_size = mem::size_of::<AudioDeviceID>();
156+
let status = unsafe {
157+
AudioObjectGetPropertyData(
158+
kAudioObjectSystemObject,
159+
&property_address as *const _,
160+
0,
161+
null(),
162+
&data_size as *const _ as *mut _,
163+
&audio_device_id as *const _ as *mut _,
164+
)
165+
};
166+
if status != kAudioHardwareNoError as i32 {
167+
return None;
168+
}
169+
170+
Some(audio_device_id)
171+
}
172+
173+
/// Copied from cpal
174+
fn audio_unit_from_device(
175+
device_id: AudioDeviceID,
176+
input: bool,
177+
) -> Result<AudioUnit, coreaudio::Error> {
178+
let mut audio_unit = AudioUnit::new(coreaudio::audio_unit::IOType::HalOutput)?;
179+
180+
if input {
181+
// Enable input processing.
182+
let enable_input = 1u32;
183+
audio_unit.set_property(
184+
kAudioOutputUnitProperty_EnableIO,
185+
Scope::Input,
186+
Element::Input,
187+
Some(&enable_input),
188+
)?;
189+
190+
// Disable output processing.
191+
let disable_output = 0u32;
192+
audio_unit.set_property(
193+
kAudioOutputUnitProperty_EnableIO,
194+
Scope::Output,
195+
Element::Output,
196+
Some(&disable_output),
197+
)?;
198+
}
199+
200+
audio_unit.set_property(
201+
kAudioOutputUnitProperty_CurrentDevice,
202+
Scope::Global,
203+
Element::Output,
204+
Some(&device_id),
205+
)?;
206+
207+
Ok(audio_unit)
208+
}

src/audio_unit/mod.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,29 @@ impl AudioUnit {
171171
}
172172
}
173173

174+
/// On successful initialization, the audio formats for input and output are valid
175+
/// and the audio unit is ready to render. During initialization, an audio unit
176+
/// allocates memory according to the maximum number of audio frames it can produce
177+
/// in response to a single render call.
178+
///
179+
/// Usually, the state of an audio unit (such as its I/O formats and memory allocations)
180+
/// cannot be changed while an audio unit is initialized.
181+
pub fn initialize(&mut self) -> Result<(), Error> {
182+
unsafe { try_os_status!(sys::AudioUnitInitialize(self.instance)); }
183+
Ok(())
184+
}
185+
186+
/// Before you change an initialize audio unit’s processing characteristics,
187+
/// such as its input or output audio data format or its sample rate, you must
188+
/// first uninitialize it. Calling this function deallocates the audio unit’s resources.
189+
///
190+
/// After calling this function, you can reconfigure the audio unit and then call
191+
/// AudioUnitInitialize to reinitialize it.
192+
pub fn uninitialize(&mut self) -> Result<(), Error> {
193+
unsafe { try_os_status!(sys::AudioUnitUninitialize(self.instance)); }
194+
Ok(())
195+
}
196+
174197
/// Sets the value for some property of the **AudioUnit**.
175198
///
176199
/// To clear an audio unit property value, set the data paramater with `None::<()>`.
@@ -375,3 +398,28 @@ pub fn get_property<T>(
375398
Ok(data)
376399
}
377400
}
401+
402+
/// Gets the value of a specified audio session property.
403+
///
404+
/// **Available** in iOS 2.0 and later.
405+
///
406+
/// Parameters
407+
/// ----------
408+
///
409+
/// - **id**: The identifier of the property.
410+
#[cfg(target_os = "ios")]
411+
pub fn audio_session_get_property<T>(
412+
id: u32,
413+
) -> Result<T, Error>
414+
{
415+
let mut size = ::std::mem::size_of::<T>() as u32;
416+
unsafe {
417+
let mut data: T = ::std::mem::uninitialized();
418+
let data_ptr = &mut data as *mut _ as *mut c_void;
419+
let size_ptr = &mut size as *mut _;
420+
try_os_status!(
421+
sys::AudioSessionGetProperty(id, size_ptr, data_ptr)
422+
);
423+
Ok(data)
424+
}
425+
}

src/audio_unit/render_callback.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ use sys;
88
pub use self::action_flags::ActionFlags;
99
pub use self::data::Data;
1010

11+
#[cfg(target_os = "ios")]
12+
use audio_unit::audio_session_get_property;
13+
1114

1215
/// When `set_render_callback` is called, a closure of this type will be used to wrap the given
1316
/// render callback function.
@@ -398,7 +401,7 @@ impl AudioUnit {
398401
// First, we'll retrieve the stream format so that we can ensure that the given callback
399402
// format matches the audio unit's format.
400403
let id = sys::kAudioUnitProperty_StreamFormat;
401-
let asbd = try!(self.get_property(id, Scope::Output, Element::Output));
404+
let asbd = try!(self.get_property(id, Scope::Input, Element::Output));
402405
let stream_format = super::StreamFormat::from_asbd(asbd)?;
403406

404407
// If the stream format does not match, return an error indicating this.
@@ -471,7 +474,7 @@ impl AudioUnit {
471474
// First, we'll retrieve the stream format so that we can ensure that the given callback
472475
// format matches the audio unit's format.
473476
let id = sys::kAudioUnitProperty_StreamFormat;
474-
let asbd = self.get_property(id, Scope::Input, Element::Input)?;
477+
let asbd = self.get_property(id, Scope::Output, Element::Input)?;
475478
let stream_format = super::StreamFormat::from_asbd(asbd)?;
476479

477480
// If the stream format does not match, return an error indicating this.
@@ -482,8 +485,20 @@ impl AudioUnit {
482485
// Pre-allocate a buffer list for input stream.
483486
//
484487
// First, get the current buffer size for pre-allocating the `AudioBuffer`s.
485-
let id = sys::kAudioDevicePropertyBufferFrameSize;
486-
let mut buffer_frame_size: u32 = self.get_property(id, Scope::Global, Element::Output)?;
488+
#[cfg(target_os = "macos")]
489+
let mut buffer_frame_size: u32 = {
490+
let id = sys::kAudioDevicePropertyBufferFrameSize;
491+
let buffer_frame_size: u32 = self.get_property(id, Scope::Global, Element::Output)?;
492+
buffer_frame_size
493+
};
494+
#[cfg(target_os = "ios")]
495+
let mut buffer_frame_size: u32 = {
496+
let id = sys::kAudioSessionProperty_CurrentHardwareIOBufferDuration;
497+
let seconds: f32 = super::audio_session_get_property(id)?;
498+
let id = sys::kAudioSessionProperty_CurrentHardwareSampleRate;
499+
let sample_rate: f64 = super::audio_session_get_property(id)?;
500+
(sample_rate * seconds as f64).round() as u32
501+
};
487502
let mut data: Vec<u8> = vec![];
488503
let sample_bytes = stream_format.sample_format.size_in_bytes();
489504
let n_channels = stream_format.channels_per_frame;
@@ -525,7 +540,7 @@ impl AudioUnit {
525540
unsafe {
526541
// Retrieve the up-to-date stream format.
527542
let id = sys::kAudioUnitProperty_StreamFormat;
528-
let asbd = match super::get_property(audio_unit, id, Scope::Input, Element::Output) {
543+
let asbd = match super::get_property(audio_unit, id, Scope::Output, Element::Input) {
529544
Err(err) => return err.to_os_status(),
530545
Ok(asbd) => asbd,
531546
};

0 commit comments

Comments
 (0)