Skip to content

Commit b3e605f

Browse files
Merge pull request #82 from HEnquist/master
New features, modernize, format
2 parents d83c59e + 8dbe730 commit b3e605f

16 files changed

+1755
-623
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
[package]
22
name = "coreaudio-rs"
3-
version = "0.10.0"
3+
version = "0.11.0"
44
authors = ["mitchmindtree <mitchell.nordine@gmail.com>", "yupferris <jake@fusetools.com>"]
55
description = "A friendly rust interface for Apple's CoreAudio API."
66
keywords = ["core", "audio", "unit", "osx", "ios"]
77
readme = "README.md"
88
license = "MIT/Apache-2.0"
9+
edition = '2018'
910
repository = "https://github.com/RustAudio/coreaudio-rs.git"
1011
homepage = "https://github.com/RustAudio/coreaudio-rs"
1112

@@ -23,6 +24,7 @@ core_midi = ["coreaudio-sys/core_midi"]
2324
[dependencies]
2425
bitflags = "1.0"
2526
coreaudio-sys = { version = "0.2", default-features = false }
27+
core-foundation-sys = "0.6.2"
2628

2729
[package.metadata.docs.rs]
2830
all-features = true

examples/feedback.rs

Lines changed: 24 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,34 @@
33
extern crate coreaudio;
44

55
use std::collections::VecDeque;
6-
use std::mem;
7-
use std::ptr::null;
86
use std::sync::{Arc, Mutex};
97

108
use coreaudio::audio_unit::audio_format::LinearPcmFlags;
119
use coreaudio::audio_unit::render_callback::{self, data};
12-
use coreaudio::audio_unit::{AudioUnit, Element, SampleFormat, Scope, StreamFormat};
10+
use coreaudio::audio_unit::{Element, SampleFormat, Scope, StreamFormat};
11+
use coreaudio::audio_unit::macos_helpers::{audio_unit_from_device_id, get_default_device_id};
1312
use coreaudio::sys::*;
1413

1514
const SAMPLE_RATE: f64 = 44100.0;
1615

17-
type S = f32; const SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
16+
type S = f32;
17+
const SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
1818
// type S = i32; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I32;
1919
// type S = i16; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I16;
2020
// type S = i8; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I8;
2121

2222
fn main() -> Result<(), coreaudio::Error> {
23-
let mut input_audio_unit = audio_unit_from_device(default_input_device().unwrap(), true)?;
24-
let mut output_audio_unit = audio_unit_from_device(default_output_device().unwrap(), false)?;
23+
let mut input_audio_unit = audio_unit_from_device_id(get_default_device_id(true).unwrap(), true)?;
24+
let mut output_audio_unit = audio_unit_from_device_id(get_default_device_id(false).unwrap(), false)?;
2525

2626
let format_flag = match SAMPLE_FORMAT {
2727
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT,
28-
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => LinearPcmFlags::IS_SIGNED_INTEGER,
28+
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
29+
LinearPcmFlags::IS_SIGNED_INTEGER
30+
}
31+
_ => {
32+
unimplemented!("Other formats are not implemented for this example.");
33+
}
2934
};
3035

3136
// Using IS_NON_INTERLEAVED everywhere because data::Interleaved is commented out / not implemented
@@ -35,15 +40,15 @@ fn main() -> Result<(), coreaudio::Error> {
3540
flags: format_flag | LinearPcmFlags::IS_PACKED | LinearPcmFlags::IS_NON_INTERLEAVED,
3641
// audio_unit.set_input_callback is hardcoded to 1 buffer, and when using non_interleaved
3742
// we are forced to 1 channel
38-
channels_per_frame: 1,
43+
channels: 1,
3944
};
4045

4146
let out_stream_format = StreamFormat {
4247
sample_rate: SAMPLE_RATE,
4348
sample_format: SAMPLE_FORMAT,
4449
flags: format_flag | LinearPcmFlags::IS_PACKED | LinearPcmFlags::IS_NON_INTERLEAVED,
4550
// you can change this to 1
46-
channels_per_frame: 2,
51+
channels: 2,
4752
};
4853

4954
println!("input={:#?}", &in_stream_format);
@@ -81,6 +86,11 @@ fn main() -> Result<(), coreaudio::Error> {
8186
mut data,
8287
..
8388
} = args;
89+
// Print the number of frames the callback provides.
90+
// Included to aid understanding, don't use println and other things
91+
// that may block for an unknown amount of time inside the callback
92+
// of a real application.
93+
println!("input cb {} frames", num_frames);
8494
let buffer_left = producer_left.lock().unwrap();
8595
let buffer_right = producer_right.lock().unwrap();
8696
let mut buffers = vec![buffer_left, buffer_right];
@@ -100,7 +110,11 @@ fn main() -> Result<(), coreaudio::Error> {
100110
mut data,
101111
..
102112
} = args;
103-
113+
// Print the number of frames the callback requests.
114+
// Included to aid understanding, don't use println and other things
115+
// that may block for an unknown amount of time inside the callback
116+
// of a real application.
117+
println!("output cb {} frames", num_frames);
104118
let buffer_left = consumer_left.lock().unwrap();
105119
let buffer_right = consumer_right.lock().unwrap();
106120
let mut buffers = vec![buffer_left, buffer_right];
@@ -121,94 +135,3 @@ fn main() -> Result<(), coreaudio::Error> {
121135

122136
Ok(())
123137
}
124-
125-
/// Copied from cpal
126-
pub fn default_input_device() -> Option<AudioDeviceID> {
127-
let property_address = AudioObjectPropertyAddress {
128-
mSelector: kAudioHardwarePropertyDefaultInputDevice,
129-
mScope: kAudioObjectPropertyScopeGlobal,
130-
mElement: kAudioObjectPropertyElementMaster,
131-
};
132-
133-
let audio_device_id: AudioDeviceID = 0;
134-
let data_size = mem::size_of::<AudioDeviceID>();
135-
let status = unsafe {
136-
AudioObjectGetPropertyData(
137-
kAudioObjectSystemObject,
138-
&property_address as *const _,
139-
0,
140-
null(),
141-
&data_size as *const _ as *mut _,
142-
&audio_device_id as *const _ as *mut _,
143-
)
144-
};
145-
if status != kAudioHardwareNoError as i32 {
146-
return None;
147-
}
148-
149-
Some(audio_device_id)
150-
}
151-
152-
/// Copied from cpal
153-
pub fn default_output_device() -> Option<AudioDeviceID> {
154-
let property_address = AudioObjectPropertyAddress {
155-
mSelector: kAudioHardwarePropertyDefaultOutputDevice,
156-
mScope: kAudioObjectPropertyScopeGlobal,
157-
mElement: kAudioObjectPropertyElementMaster,
158-
};
159-
160-
let audio_device_id: AudioDeviceID = 0;
161-
let data_size = mem::size_of::<AudioDeviceID>();
162-
let status = unsafe {
163-
AudioObjectGetPropertyData(
164-
kAudioObjectSystemObject,
165-
&property_address as *const _,
166-
0,
167-
null(),
168-
&data_size as *const _ as *mut _,
169-
&audio_device_id as *const _ as *mut _,
170-
)
171-
};
172-
if status != kAudioHardwareNoError as i32 {
173-
return None;
174-
}
175-
176-
Some(audio_device_id)
177-
}
178-
179-
/// Copied from cpal
180-
fn audio_unit_from_device(
181-
device_id: AudioDeviceID,
182-
input: bool,
183-
) -> Result<AudioUnit, coreaudio::Error> {
184-
let mut audio_unit = AudioUnit::new(coreaudio::audio_unit::IOType::HalOutput)?;
185-
186-
if input {
187-
// Enable input processing.
188-
let enable_input = 1u32;
189-
audio_unit.set_property(
190-
kAudioOutputUnitProperty_EnableIO,
191-
Scope::Input,
192-
Element::Input,
193-
Some(&enable_input),
194-
)?;
195-
196-
// Disable output processing.
197-
let disable_output = 0u32;
198-
audio_unit.set_property(
199-
kAudioOutputUnitProperty_EnableIO,
200-
Scope::Output,
201-
Element::Output,
202-
Some(&disable_output),
203-
)?;
204-
}
205-
206-
audio_unit.set_property(
207-
kAudioOutputUnitProperty_CurrentDevice,
208-
Scope::Global,
209-
Element::Output,
210-
Some(&device_id),
211-
)?;
212-
213-
Ok(audio_unit)
214-
}

examples/feedback_interleaved.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
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::sync::{Arc, Mutex};
7+
8+
use coreaudio::audio_unit::audio_format::LinearPcmFlags;
9+
use coreaudio::audio_unit::render_callback::{self, data};
10+
use coreaudio::audio_unit::macos_helpers::{audio_unit_from_device_id, get_default_device_id, get_device_name, RateListener};
11+
use coreaudio::audio_unit::{Element, SampleFormat, Scope, StreamFormat};
12+
use coreaudio::sys::*;
13+
14+
const SAMPLE_RATE: f64 = 44100.0;
15+
16+
type S = f32;
17+
const SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
18+
// type S = i32; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I32;
19+
// type S = i16; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I16;
20+
// type S = i8; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I8;
21+
22+
fn main() -> Result<(), coreaudio::Error> {
23+
let input_device_id = get_default_device_id(true).unwrap();
24+
let output_device_id = get_default_device_id(false).unwrap();
25+
println!(
26+
"Input device: {}",
27+
get_device_name(input_device_id).unwrap()
28+
);
29+
println!(
30+
"Output device: {}",
31+
get_device_name(output_device_id).unwrap()
32+
);
33+
let mut input_audio_unit = audio_unit_from_device_id(input_device_id, true)?;
34+
let mut output_audio_unit = audio_unit_from_device_id(output_device_id, false)?;
35+
36+
let format_flag = match SAMPLE_FORMAT {
37+
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT | LinearPcmFlags::IS_PACKED,
38+
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
39+
LinearPcmFlags::IS_SIGNED_INTEGER | LinearPcmFlags::IS_PACKED
40+
}
41+
_ => {
42+
unimplemented!("Please use one of the packed formats");
43+
}
44+
};
45+
46+
let in_stream_format = StreamFormat {
47+
sample_rate: SAMPLE_RATE,
48+
sample_format: SAMPLE_FORMAT,
49+
flags: format_flag,
50+
channels: 2,
51+
};
52+
53+
let out_stream_format = StreamFormat {
54+
sample_rate: SAMPLE_RATE,
55+
sample_format: SAMPLE_FORMAT,
56+
flags: format_flag,
57+
channels: 2,
58+
};
59+
60+
println!("input={:#?}", &in_stream_format);
61+
println!("output={:#?}", &out_stream_format);
62+
println!("input_asbd={:#?}", &in_stream_format.to_asbd());
63+
println!("output_asbd={:#?}", &out_stream_format.to_asbd());
64+
65+
let id = kAudioUnitProperty_StreamFormat;
66+
let asbd = in_stream_format.to_asbd();
67+
input_audio_unit.set_property(id, Scope::Output, Element::Input, Some(&asbd))?;
68+
69+
let asbd = out_stream_format.to_asbd();
70+
output_audio_unit.set_property(id, Scope::Input, Element::Output, Some(&asbd))?;
71+
72+
let buffer_left = Arc::new(Mutex::new(VecDeque::<S>::new()));
73+
let producer_left = buffer_left.clone();
74+
let consumer_left = buffer_left.clone();
75+
let buffer_right = Arc::new(Mutex::new(VecDeque::<S>::new()));
76+
let producer_right = buffer_right.clone();
77+
let consumer_right = buffer_right.clone();
78+
79+
// Register a rate listener for playback
80+
let mut listener_pb = RateListener::new(output_device_id, None);
81+
listener_pb.register()?;
82+
83+
// Register a rate listener for capture
84+
let mut listener_cap = RateListener::new(input_device_id, None);
85+
listener_cap.register()?;
86+
87+
// seed roughly 1 second of data to create a delay in the feedback loop for easier testing
88+
for buffer in vec![buffer_left, buffer_right] {
89+
let mut buffer = buffer.lock().unwrap();
90+
for _ in 0..(out_stream_format.sample_rate as i32) {
91+
buffer.push_back(0 as S);
92+
}
93+
}
94+
95+
type Args = render_callback::Args<data::Interleaved<S>>;
96+
97+
input_audio_unit.set_input_callback(move |args| {
98+
let Args {
99+
num_frames, data, ..
100+
} = args;
101+
// Print the number of frames the callback requests.
102+
// Included to aid understanding, don't use println and other things
103+
// that may block for an unknown amount of time inside the callback
104+
// of a real application.
105+
println!("input cb {} frames", num_frames);
106+
let buffer_left = producer_left.lock().unwrap();
107+
let buffer_right = producer_right.lock().unwrap();
108+
let mut buffers = vec![buffer_left, buffer_right];
109+
for i in 0..num_frames {
110+
for channel in 0..2 {
111+
let value: S = data.buffer[2 * i + channel];
112+
buffers[channel].push_back(value);
113+
}
114+
}
115+
Ok(())
116+
})?;
117+
input_audio_unit.start()?;
118+
119+
output_audio_unit.set_render_callback(move |args: Args| {
120+
let Args {
121+
num_frames, data, ..
122+
} = args;
123+
// Print the number of frames the callback requests.
124+
println!("output cb {} frames", num_frames);
125+
let buffer_left = consumer_left.lock().unwrap();
126+
let buffer_right = consumer_right.lock().unwrap();
127+
let mut buffers = vec![buffer_left, buffer_right];
128+
for i in 0..num_frames {
129+
// Default other channels to copy value from first channel as a fallback
130+
let zero: S = 0 as S;
131+
let f: S = *buffers[0].front().unwrap_or(&zero);
132+
for channel in 0..2 {
133+
let sample: S = buffers[channel].pop_front().unwrap_or(f);
134+
data.buffer[2 * i + channel] = sample;
135+
}
136+
}
137+
Ok(())
138+
})?;
139+
output_audio_unit.start()?;
140+
for _ in 0..1000 {
141+
std::thread::sleep(std::time::Duration::from_millis(100));
142+
if listener_cap.get_nbr_values() > 0 {
143+
println!("capture rate change: {:?}", listener_cap.drain_values());
144+
}
145+
if listener_pb.get_nbr_values() > 0 {
146+
println!("playback rate change: {:?}", listener_pb.drain_values());
147+
}
148+
}
149+
Ok(())
150+
}

0 commit comments

Comments
 (0)