Skip to content

Commit 7de3603

Browse files
committed
Find matching physical format
1 parent d45bd15 commit 7de3603

File tree

3 files changed

+61
-23
lines changed

3 files changed

+61
-23
lines changed

examples/sine_advanced.rs

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use coreaudio::audio_unit::render_callback::{self, data};
77
use coreaudio::audio_unit::{
88
audio_unit_from_device_id, get_default_device_id, get_supported_physical_stream_formats,
99
set_device_physical_stream_format, set_device_sample_rate, AliveListener, RateListener,
10+
find_matching_physical_format,
1011
};
1112
use coreaudio::audio_unit::{Element, SampleFormat, Scope, StreamFormat};
1213
use coreaudio::sys::kAudioUnitProperty_StreamFormat;
@@ -83,37 +84,32 @@ fn main() -> Result<(), coreaudio::Error> {
8384
println!("asbd={:#?}", &stream_format.to_asbd());
8485

8586
// Lets print all supported formats, disabled for now since it often crashes.
86-
//println!("All supported formats");
87-
//let formats = get_supported_physical_stream_formats(audio_unit_id)?;
88-
//for fmt in formats {
89-
// println!("{:?}", &fmt);
90-
//}
87+
println!("All supported formats");
88+
let formats = get_supported_physical_stream_formats(audio_unit_id)?;
89+
for fmt in formats {
90+
println!("{:?}", &fmt);
91+
}
9192

9293
// set the sample rate. This isn't actually needed since the sample rate
9394
// will anyway be changed when setting the sample format later.
9495
//println!("set device sample rate");
9596
//set_device_sample_rate(audio_unit_id, SAMPLE_RATE)?;
9697

9798
println!("setting hardware (physical) format");
98-
99-
// use this for packed formats (most of them)
100-
//let hw_format_flag = LinearPcmFlags::IS_PACKED | LinearPcmFlags::IS_SIGNED_INTEGER;
101-
102-
// use for non-packed formats (only I24_4)
103-
let hw_format_flag = LinearPcmFlags::IS_SIGNED_INTEGER;
104-
10599
let hw_stream_format = StreamFormat {
106100
sample_rate: SAMPLE_RATE,
107-
sample_format: SampleFormat::I24_4,
108-
flags: hw_format_flag,
101+
sample_format: SampleFormat::I24_3,
102+
flags: LinearPcmFlags::empty(),
109103
channels: 2,
110104
};
111105

112-
println!("asbd: {:?}", hw_stream_format.to_asbd());
106+
let hw_asbd = find_matching_physical_format(audio_unit_id, hw_stream_format).unwrap();
107+
108+
println!("asbd: {:?}", hw_asbd);
113109

114110
// Note that using a StreamFormat here is convenient, but it only supports a few sample formats.
115111
// Setting the format to for example 24 bit integers requires using an ASBD.
116-
set_device_physical_stream_format(audio_unit_id, hw_stream_format.to_asbd())?;
112+
set_device_physical_stream_format(audio_unit_id, hw_asbd)?;
117113

118114
println!("write audio unit StreamFormat property");
119115
let id = kAudioUnitProperty_StreamFormat;

src/audio_unit/macos_helpers.rs

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@ use sys::{
2424
kCFStringEncodingUTF8, AudioDeviceID, AudioObjectAddPropertyListener,
2525
AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID,
2626
AudioObjectPropertyAddress, AudioObjectRemovePropertyListener, AudioObjectSetPropertyData,
27-
AudioStreamBasicDescription, AudioValueRange, OSStatus,
27+
AudioStreamBasicDescription, AudioValueRange, OSStatus, AudioStreamRangedDescription,
2828
};
2929

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

3235
/// Helper function to get the device id of the default input or output device
3336
/// Only implemented for macOS, not iOS.
@@ -319,6 +322,41 @@ pub fn set_device_sample_rate(device_id: AudioDeviceID, new_rate: f64) -> Result
319322
}
320323
}
321324

325+
/// Find the closest match of the physical formats to the provided StreamFormat.
326+
/// Note that only the sample format and rate will be matched, the flags will be ignored.
327+
pub fn find_matching_physical_format(
328+
device_id: AudioDeviceID,
329+
stream_format: StreamFormat,
330+
) -> Option<AudioStreamBasicDescription> {
331+
if let Ok(all_formats) = get_supported_physical_stream_formats(device_id) {
332+
let wanted_samplerate = stream_format.sample_rate as usize;
333+
let wanted_bits = stream_format.sample_format.size_in_bits();
334+
let wanted_float = stream_format.sample_format == SampleFormat::F32;
335+
for fmt in all_formats {
336+
let minrate = fmt.mSampleRateRange.mMinimum as usize;
337+
let maxrate = fmt.mSampleRateRange.mMaximum as usize;
338+
let rate = fmt.mFormat.mSampleRate as usize;
339+
if let Some(AudioFormat::LinearPCM(flags)) = AudioFormat::from_format_and_flag(fmt.mFormat.mFormatID, Some(fmt.mFormat.mFormatFlags)) {
340+
let floats = flags.contains(LinearPcmFlags::IS_FLOAT);
341+
let ints = flags.contains(LinearPcmFlags::IS_SIGNED_INTEGER);
342+
if wanted_float != floats || wanted_float == ints {
343+
// Wrong number type
344+
continue;
345+
}
346+
if wanted_bits != fmt.mFormat.mBitsPerChannel {
347+
// Wrong number of bits
348+
continue;
349+
}
350+
if rate == wanted_samplerate || (wanted_samplerate >= minrate && wanted_samplerate <= maxrate) {
351+
return Some(fmt.mFormat);
352+
}
353+
354+
}
355+
}
356+
}
357+
return None
358+
}
359+
322360
/// Change the physical stream format (sample rate and format) of a device.
323361
/// Only implemented for macOS, not iOS.
324362
pub fn set_device_physical_stream_format(
@@ -345,6 +383,8 @@ pub fn set_device_physical_stream_format(
345383
);
346384
Error::from_os_status(status)?;
347385
let asbd = maybe_asbd.assume_init();
386+
//println!("Current: {:?}", asbd);
387+
//println!("New: {:?}", new_asbd);
348388

349389
// If the requested sample rate and/or format is different to the device sample rate, update the device.
350390
if !asbds_are_equal(&asbd, &new_asbd) {
@@ -386,8 +426,8 @@ pub fn set_device_physical_stream_format(
386426
break;
387427
}
388428
thread::sleep(Duration::from_millis(5));
389-
if timer.elapsed() > Duration::from_secs(1) {
390-
return Err(Error::UnsupportedSampleRate);
429+
if timer.elapsed() > Duration::from_secs(2) {
430+
return Err(Error::UnsupportedStreamFormat);
391431
}
392432
}
393433
}
@@ -416,7 +456,7 @@ fn asbds_are_equal(
416456
/// malloc: Incorrect checksum for freed object 0x7fca6bc3c538: probably modified after being freed.
417457
pub fn get_supported_physical_stream_formats(
418458
device_id: AudioDeviceID,
419-
) -> Result<Vec<AudioStreamBasicDescription>, Error> {
459+
) -> Result<Vec<AudioStreamRangedDescription>, Error> {
420460
// Get available formats.
421461
let mut property_address = AudioObjectPropertyAddress {
422462
mSelector: kAudioStreamPropertyPhysicalFormat,
@@ -437,11 +477,11 @@ pub fn get_supported_physical_stream_formats(
437477
&mut data_size as *mut _,
438478
);
439479
Error::from_os_status(status)?;
440-
let n_formats = data_size as usize / mem::size_of::<AudioStreamBasicDescription>();
441-
println!("n_formats {}", n_formats);
442-
let mut formats: Vec<AudioStreamBasicDescription> = vec![];
480+
let n_formats = data_size as usize / mem::size_of::<AudioStreamRangedDescription>();
481+
let mut formats: Vec<AudioStreamRangedDescription> = vec![];
443482
formats.reserve_exact(n_formats as usize);
444483
formats.set_len(n_formats);
484+
445485
let status = AudioObjectGetPropertyData(
446486
device_id,
447487
&property_address as *const _,

src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ pub enum Error {
246246
NoKnownSubtype,
247247
NonInterleavedInputOnlySupportsMono,
248248
UnsupportedSampleRate,
249+
UnsupportedStreamFormat,
249250
Audio(AudioError),
250251
AudioCodec(AudioCodecError),
251252
AudioFormat(AudioFormatError),
@@ -312,6 +313,7 @@ impl ::std::fmt::Display for Error {
312313
Error::NoKnownSubtype => write!(f, "The type has no known subtypes"),
313314
Error::NonInterleavedInputOnlySupportsMono => write!(f, "In non-interleaved mode input only supports one channel"),
314315
Error::UnsupportedSampleRate => write!(f, "The requested sample rate is not available"),
316+
Error::UnsupportedStreamFormat => write!(f, "The requested stream format is not available"),
315317
Error::Audio(ref err) => write!(f, "{}", err),
316318
Error::AudioCodec(ref err) => write!(f, "{}", err),
317319
Error::AudioFormat(ref err) => write!(f, "{}", err),

0 commit comments

Comments
 (0)