Skip to content

Commit 5a92876

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

File tree

4 files changed

+277
-5
lines changed

4 files changed

+277
-5
lines changed

.travis.yml

+4
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

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

src/audio_unit/mod.rs

+48
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

+20-5
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)