Skip to content

Commit 46e156d

Browse files
authored
Merge pull request #1 from HEnquist/i24
Add support for 24-bit formats
2 parents 9ebdd60 + b1bfb95 commit 46e156d

File tree

7 files changed

+166
-81
lines changed

7 files changed

+166
-81
lines changed

examples/feedback.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ fn main() -> Result<(), coreaudio::Error> {
2929
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
3030
LinearPcmFlags::IS_SIGNED_INTEGER
3131
}
32+
_ => {
33+
unimplemented!("Other formats are not implemented for this example.");
34+
}
3235
};
3336

3437
// Using IS_NON_INTERLEAVED everywhere because data::Interleaved is commented out / not implemented

examples/feedback_interleaved.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,26 @@ fn main() -> Result<(), coreaudio::Error> {
3434
let mut output_audio_unit = audio_unit_from_device_id(output_device_id, false)?;
3535

3636
let format_flag = match SAMPLE_FORMAT {
37-
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT,
37+
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT | LinearPcmFlags::IS_PACKED,
3838
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
39-
LinearPcmFlags::IS_SIGNED_INTEGER
39+
LinearPcmFlags::IS_SIGNED_INTEGER | LinearPcmFlags::IS_PACKED
40+
}
41+
_ => {
42+
unimplemented!("Please use one of the packed formats");
4043
}
4144
};
4245

4346
let in_stream_format = StreamFormat {
4447
sample_rate: SAMPLE_RATE,
4548
sample_format: SAMPLE_FORMAT,
46-
flags: format_flag | LinearPcmFlags::IS_PACKED,
49+
flags: format_flag,
4750
channels: 2,
4851
};
4952

5053
let out_stream_format = StreamFormat {
5154
sample_rate: SAMPLE_RATE,
5255
sample_format: SAMPLE_FORMAT,
53-
flags: format_flag | LinearPcmFlags::IS_PACKED,
56+
flags: format_flag,
5457
channels: 2,
5558
};
5659

examples/sine_advanced.rs

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ extern crate coreaudio;
55
use coreaudio::audio_unit::audio_format::LinearPcmFlags;
66
use coreaudio::audio_unit::render_callback::{self, data};
77
use coreaudio::audio_unit::{
8-
audio_unit_from_device_id, get_default_device_id, set_device_sample_format,
8+
audio_unit_from_device_id, find_matching_physical_format, get_default_device_id,
9+
get_supported_physical_stream_formats, set_device_physical_stream_format,
910
set_device_sample_rate, AliveListener, RateListener,
1011
};
1112
use coreaudio::audio_unit::{Element, SampleFormat, Scope, StreamFormat};
@@ -17,7 +18,7 @@ const SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
1718
// type S = i16; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I16;
1819
// type S = i8; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I8;
1920

20-
const SAMPLE_RATE: f64 = 48000.0;
21+
const SAMPLE_RATE: f64 = 44100.0;
2122

2223
const INTERLEAVED: bool = true;
2324

@@ -60,9 +61,12 @@ fn main() -> Result<(), coreaudio::Error> {
6061
let mut audio_unit = audio_unit_from_device_id(audio_unit_id, false)?;
6162

6263
let mut format_flag = match SAMPLE_FORMAT {
63-
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT,
64+
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT | LinearPcmFlags::IS_PACKED,
6465
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
65-
LinearPcmFlags::IS_SIGNED_INTEGER
66+
LinearPcmFlags::IS_SIGNED_INTEGER | LinearPcmFlags::IS_PACKED
67+
}
68+
_ => {
69+
unimplemented!("Please use one of the packed formats");
6670
}
6771
};
6872

@@ -73,31 +77,43 @@ fn main() -> Result<(), coreaudio::Error> {
7377
let stream_format = StreamFormat {
7478
sample_rate: SAMPLE_RATE,
7579
sample_format: SAMPLE_FORMAT,
76-
flags: format_flag | LinearPcmFlags::IS_PACKED,
80+
flags: format_flag,
7781
// you can change this to 1
7882
channels: 2,
7983
};
8084

8185
println!("stream format={:#?}", &stream_format);
8286
println!("asbd={:#?}", &stream_format.to_asbd());
8387

88+
// Lets print all supported formats, disabled for now since it often crashes.
89+
println!("All supported formats");
90+
let formats = get_supported_physical_stream_formats(audio_unit_id)?;
91+
for fmt in formats {
92+
println!("{:?}", &fmt);
93+
}
94+
8495
// set the sample rate. This isn't actually needed since the sample rate
8596
// will anyway be changed when setting the sample format later.
86-
println!("set device sample rate");
87-
set_device_sample_rate(audio_unit_id, SAMPLE_RATE)?;
97+
// Keeping it here as an example.
98+
//println!("set device sample rate");
99+
//set_device_sample_rate(audio_unit_id, SAMPLE_RATE)?;
88100

89-
println!("setting hardware format to i16");
90-
let hw_format_flag = LinearPcmFlags::IS_PACKED | LinearPcmFlags::IS_SIGNED_INTEGER;
101+
println!("setting hardware (physical) format");
91102
let hw_stream_format = StreamFormat {
92103
sample_rate: SAMPLE_RATE,
93-
sample_format: SampleFormat::I16,
94-
flags: hw_format_flag,
104+
sample_format: SampleFormat::I24,
105+
flags: LinearPcmFlags::empty(),
95106
channels: 2,
96107
};
97108

109+
let hw_asbd = find_matching_physical_format(audio_unit_id, hw_stream_format)
110+
.ok_or(coreaudio::Error::UnsupportedStreamFormat)?;
111+
112+
println!("asbd: {:?}", hw_asbd);
113+
98114
// Note that using a StreamFormat here is convenient, but it only supports a few sample formats.
99115
// Setting the format to for example 24 bit integers requires using an ASBD.
100-
set_device_sample_format(audio_unit_id, hw_stream_format.to_asbd())?;
116+
set_device_physical_stream_format(audio_unit_id, hw_asbd)?;
101117

102118
println!("write audio unit StreamFormat property");
103119
let id = kAudioUnitProperty_StreamFormat;

src/audio_unit/macos_helpers.rs

Lines changed: 82 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
88
use std::sync::mpsc::{channel, Sender};
99
use std::sync::Mutex;
1010
use std::time::Duration;
11-
use std::{mem, slice, thread};
11+
use std::{mem, thread};
1212

1313
use core_foundation_sys::string::{CFStringGetCString, CFStringGetCStringPtr, CFStringRef};
1414
use sys;
@@ -24,9 +24,12 @@ use sys::{
2424
kCFStringEncodingUTF8, AudioDeviceID, AudioObjectAddPropertyListener,
2525
AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID,
2626
AudioObjectPropertyAddress, AudioObjectRemovePropertyListener, AudioObjectSetPropertyData,
27-
AudioStreamBasicDescription, AudioValueRange, OSStatus,
27+
AudioStreamBasicDescription, AudioStreamRangedDescription, AudioValueRange, OSStatus,
2828
};
2929

30+
use crate::audio_unit::audio_format::{AudioFormat, LinearPcmFlags};
31+
use crate::audio_unit::sample_format::SampleFormat;
32+
use crate::audio_unit::stream_format::StreamFormat;
3033
use crate::audio_unit::{AudioUnit, Element, IOType, Scope};
3134

3235
/// Helper function to get the device id of the default input or output device
@@ -144,6 +147,7 @@ pub fn get_audio_device_ids() -> Result<Vec<AudioDeviceID>, Error> {
144147
let device_count = data_size / mem::size_of::<AudioDeviceID>() as u32;
145148
let mut audio_devices = vec![];
146149
audio_devices.reserve_exact(device_count as usize);
150+
unsafe { audio_devices.set_len(device_count as usize) };
147151

148152
let status = unsafe {
149153
AudioObjectGetPropertyData(
@@ -156,9 +160,6 @@ pub fn get_audio_device_ids() -> Result<Vec<AudioDeviceID>, Error> {
156160
)
157161
};
158162
try_status_or_return!(status);
159-
160-
unsafe { audio_devices.set_len(device_count as usize) };
161-
162163
Ok(audio_devices)
163164
}
164165

@@ -259,8 +260,9 @@ pub fn set_device_sample_rate(device_id: AudioDeviceID, new_rate: f64) -> Result
259260
);
260261
Error::from_os_status(status)?;
261262
let n_ranges = data_size as usize / mem::size_of::<AudioValueRange>();
262-
let mut ranges: Vec<u8> = vec![];
263-
ranges.reserve_exact(data_size as usize);
263+
let mut ranges: Vec<AudioValueRange> = vec![];
264+
ranges.reserve_exact(n_ranges as usize);
265+
ranges.set_len(n_ranges);
264266
let status = AudioObjectGetPropertyData(
265267
device_id,
266268
&property_address as *const _,
@@ -270,8 +272,6 @@ pub fn set_device_sample_rate(device_id: AudioDeviceID, new_rate: f64) -> Result
270272
ranges.as_mut_ptr() as *mut _,
271273
);
272274
Error::from_os_status(status)?;
273-
let ranges: *mut AudioValueRange = ranges.as_mut_ptr() as *mut _;
274-
let ranges: &'static [AudioValueRange] = slice::from_raw_parts(ranges, n_ranges);
275275

276276
// Now that we have the available ranges, pick the one matching the desired rate.
277277
let new_rate_integer = new_rate as u32;
@@ -322,15 +322,68 @@ pub fn set_device_sample_rate(device_id: AudioDeviceID, new_rate: f64) -> Result
322322
}
323323
}
324324

325-
/// Change the sample rate and format of a device.
325+
/// Find the closest match of the physical formats to the provided `StreamFormat`.
326+
/// It will pick the first format it finds that supports the provided sample format, rate and number of channels.
327+
/// The provided format flags in the `StreamFormat` are ignored.
328+
pub fn find_matching_physical_format(
329+
device_id: AudioDeviceID,
330+
stream_format: StreamFormat,
331+
) -> Option<AudioStreamBasicDescription> {
332+
if let Ok(all_formats) = get_supported_physical_stream_formats(device_id) {
333+
let requested_samplerate = stream_format.sample_rate as usize;
334+
let requested_bits = stream_format.sample_format.size_in_bits();
335+
let requested_float = stream_format.sample_format == SampleFormat::F32;
336+
let requested_channels = stream_format.channels;
337+
for fmt in all_formats {
338+
let min_rate = fmt.mSampleRateRange.mMinimum as usize;
339+
let max_rate = fmt.mSampleRateRange.mMaximum as usize;
340+
let rate = fmt.mFormat.mSampleRate as usize;
341+
let channels = fmt.mFormat.mChannelsPerFrame;
342+
if let Some(AudioFormat::LinearPCM(flags)) = AudioFormat::from_format_and_flag(
343+
fmt.mFormat.mFormatID,
344+
Some(fmt.mFormat.mFormatFlags),
345+
) {
346+
let is_float = flags.contains(LinearPcmFlags::IS_FLOAT);
347+
let is_int = flags.contains(LinearPcmFlags::IS_SIGNED_INTEGER);
348+
if is_int && is_float {
349+
// Probably never occurs, check just in case
350+
continue;
351+
}
352+
if requested_float && !is_float {
353+
// Wrong number type
354+
continue;
355+
}
356+
if !requested_float && !is_int {
357+
// Wrong number type
358+
continue;
359+
}
360+
if requested_bits != fmt.mFormat.mBitsPerChannel {
361+
// Wrong number of bits
362+
continue;
363+
}
364+
if requested_channels > channels {
365+
// Too few channels
366+
continue;
367+
}
368+
if rate == requested_samplerate
369+
|| (requested_samplerate >= min_rate && requested_samplerate <= max_rate)
370+
{
371+
return Some(fmt.mFormat);
372+
}
373+
}
374+
}
375+
}
376+
None
377+
}
378+
379+
/// Change the physical stream format (sample rate and format) of a device.
326380
/// Only implemented for macOS, not iOS.
327-
pub fn set_device_sample_format(
381+
pub fn set_device_physical_stream_format(
328382
device_id: AudioDeviceID,
329383
new_asbd: AudioStreamBasicDescription,
330384
) -> Result<(), Error> {
331-
// Check whether or not we need to change the device sample format and rate.
332385
unsafe {
333-
// Get the current sample rate.
386+
// Get the current format.
334387
let property_address = AudioObjectPropertyAddress {
335388
mSelector: kAudioStreamPropertyPhysicalFormat,
336389
mScope: kAudioObjectPropertyScopeGlobal,
@@ -349,7 +402,6 @@ pub fn set_device_sample_format(
349402
Error::from_os_status(status)?;
350403
let asbd = maybe_asbd.assume_init();
351404

352-
// If the requested sample rate and/or format is different to the device sample rate, update the device.
353405
if !asbds_are_equal(&asbd, &new_asbd) {
354406
let property_address = AudioObjectPropertyAddress {
355407
mSelector: kAudioStreamPropertyPhysicalFormat,
@@ -372,8 +424,7 @@ pub fn set_device_sample_format(
372424
Error::from_os_status(status)?;
373425

374426
// Wait for the reported format to change.
375-
//
376-
// This should not take longer than a few ms, but we timeout after 1 sec just in case.
427+
// This can take up to half a second, but we timeout after 2 sec just in case.
377428
let timer = ::std::time::Instant::now();
378429
loop {
379430
let status = AudioObjectGetPropertyData(
@@ -389,8 +440,8 @@ pub fn set_device_sample_format(
389440
break;
390441
}
391442
thread::sleep(Duration::from_millis(5));
392-
if timer.elapsed() > Duration::from_secs(1) {
393-
return Err(Error::UnsupportedSampleRate);
443+
if timer.elapsed() > Duration::from_secs(2) {
444+
return Err(Error::UnsupportedStreamFormat);
394445
}
395446
}
396447
}
@@ -413,34 +464,33 @@ fn asbds_are_equal(
413464
&& left.mBitsPerChannel == right.mBitsPerChannel
414465
}
415466

416-
/// Get a vector with all supported formats as AudioBasicStreamDescriptions.
467+
/// Get a vector with all supported physical formats as AudioBasicRangedDescriptions.
417468
/// Only implemented for macOS, not iOS.
418-
pub fn get_supported_stream_formats(
469+
pub fn get_supported_physical_stream_formats(
419470
device_id: AudioDeviceID,
420-
) -> Result<Vec<AudioStreamBasicDescription>, Error> {
471+
) -> Result<Vec<AudioStreamRangedDescription>, Error> {
421472
// Get available formats.
422473
let mut property_address = AudioObjectPropertyAddress {
423474
mSelector: kAudioStreamPropertyPhysicalFormat,
424475
mScope: kAudioObjectPropertyScopeGlobal,
425-
//mScope: kAudioDevicePropertyScopeInput,
426-
//mScope: kAudioObjectPropertyScopeOutput,
427-
//mScope: kAudioDevicePropertyScopeOutput,
428476
mElement: kAudioObjectPropertyElementMaster,
429477
};
430-
let formats = unsafe {
478+
let allformats = unsafe {
431479
property_address.mSelector = kAudioStreamPropertyAvailablePhysicalFormats;
432-
let data_size = 0u32;
480+
let mut data_size = 0u32;
433481
let status = AudioObjectGetPropertyDataSize(
434482
device_id,
435483
&property_address as *const _,
436484
0,
437485
null(),
438-
&data_size as *const _ as *mut _,
486+
&mut data_size as *mut _,
439487
);
440488
Error::from_os_status(status)?;
441-
let n_formats = data_size as usize / mem::size_of::<AudioStreamBasicDescription>();
442-
let mut formats: Vec<u8> = vec![];
443-
formats.reserve_exact(data_size as usize);
489+
let n_formats = data_size as usize / mem::size_of::<AudioStreamRangedDescription>();
490+
let mut formats: Vec<AudioStreamRangedDescription> = vec![];
491+
formats.reserve_exact(n_formats as usize);
492+
formats.set_len(n_formats);
493+
444494
let status = AudioObjectGetPropertyData(
445495
device_id,
446496
&property_address as *const _,
@@ -450,19 +500,9 @@ pub fn get_supported_stream_formats(
450500
formats.as_mut_ptr() as *mut _,
451501
);
452502
Error::from_os_status(status)?;
453-
let formats: *mut AudioStreamBasicDescription = formats.as_mut_ptr() as *mut _;
454-
Vec::from_raw_parts(formats, n_formats, n_formats)
503+
formats
455504
};
456-
/*
457-
println!("---- All supported formats ----");
458-
for asbd in formats.iter() {
459-
if let Ok(sf) = StreamFormat::from_asbd(*asbd) {
460-
println!("{:#?}", asbd);
461-
println!("{:#?}", sf);
462-
}
463-
}
464-
*/
465-
Ok(formats)
505+
Ok(allformats)
466506
}
467507

468508
/// Changing the sample rate is an asynchonous process.

0 commit comments

Comments
 (0)