Skip to content

Commit f6c5c01

Browse files
committed
Add support for 24-bit formats
1 parent 9ebdd60 commit f6c5c01

File tree

6 files changed

+103
-53
lines changed

6 files changed

+103
-53
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: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ fn main() -> Result<(), coreaudio::Error> {
3535

3636
let format_flag = match SAMPLE_FORMAT {
3737
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT,
38-
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
39-
LinearPcmFlags::IS_SIGNED_INTEGER
38+
SampleFormat::I32 | SampleFormat::I24_3 | SampleFormat::I16 | SampleFormat::I8 => {
39+
LinearPcmFlags::IS_SIGNED_INTEGER | LinearPcmFlags::IS_PACKED
4040
}
41+
SampleFormat::I24_4 => LinearPcmFlags::IS_SIGNED_INTEGER,
4142
};
4243

4344
let in_stream_format = StreamFormat {

examples/sine_advanced.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +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,
9-
set_device_sample_rate, AliveListener, RateListener,
8+
audio_unit_from_device_id, get_default_device_id, get_supported_physical_stream_formats,
9+
set_device_physical_stream_format, set_device_sample_rate, AliveListener, RateListener,
1010
};
1111
use coreaudio::audio_unit::{Element, SampleFormat, Scope, StreamFormat};
1212
use coreaudio::sys::kAudioUnitProperty_StreamFormat;
@@ -17,7 +17,7 @@ const SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;
1717
// type S = i16; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I16;
1818
// type S = i8; const SAMPLE_FORMAT: SampleFormat = SampleFormat::I8;
1919

20-
const SAMPLE_RATE: f64 = 48000.0;
20+
const SAMPLE_RATE: f64 = 44100.0;
2121

2222
const INTERLEAVED: bool = true;
2323

@@ -61,9 +61,10 @@ fn main() -> Result<(), coreaudio::Error> {
6161

6262
let mut format_flag = match SAMPLE_FORMAT {
6363
SampleFormat::F32 => LinearPcmFlags::IS_FLOAT,
64-
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
65-
LinearPcmFlags::IS_SIGNED_INTEGER
64+
SampleFormat::I32 | SampleFormat::I24_3 | SampleFormat::I16 | SampleFormat::I8 => {
65+
LinearPcmFlags::IS_SIGNED_INTEGER | LinearPcmFlags::IS_PACKED
6666
}
67+
SampleFormat::I24_4 => LinearPcmFlags::IS_SIGNED_INTEGER,
6768
};
6869

6970
if !INTERLEAVED {
@@ -81,23 +82,38 @@ fn main() -> Result<(), coreaudio::Error> {
8182
println!("stream format={:#?}", &stream_format);
8283
println!("asbd={:#?}", &stream_format.to_asbd());
8384

85+
// 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+
//}
91+
8492
// set the sample rate. This isn't actually needed since the sample rate
8593
// 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)?;
94+
//println!("set device sample rate");
95+
//set_device_sample_rate(audio_unit_id, SAMPLE_RATE)?;
96+
97+
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;
88104

89-
println!("setting hardware format to i16");
90-
let hw_format_flag = LinearPcmFlags::IS_PACKED | LinearPcmFlags::IS_SIGNED_INTEGER;
91105
let hw_stream_format = StreamFormat {
92106
sample_rate: SAMPLE_RATE,
93-
sample_format: SampleFormat::I16,
107+
sample_format: SampleFormat::I24_4,
94108
flags: hw_format_flag,
95109
channels: 2,
96110
};
97111

112+
println!("asbd: {:?}", hw_stream_format.to_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_stream_format.to_asbd())?;
101117

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

src/audio_unit/macos_helpers.rs

Lines changed: 20 additions & 20 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;
@@ -144,6 +144,7 @@ pub fn get_audio_device_ids() -> Result<Vec<AudioDeviceID>, Error> {
144144
let device_count = data_size / mem::size_of::<AudioDeviceID>() as u32;
145145
let mut audio_devices = vec![];
146146
audio_devices.reserve_exact(device_count as usize);
147+
unsafe { audio_devices.set_len(device_count as usize) };
147148

148149
let status = unsafe {
149150
AudioObjectGetPropertyData(
@@ -156,9 +157,6 @@ pub fn get_audio_device_ids() -> Result<Vec<AudioDeviceID>, Error> {
156157
)
157158
};
158159
try_status_or_return!(status);
159-
160-
unsafe { audio_devices.set_len(device_count as usize) };
161-
162160
Ok(audio_devices)
163161
}
164162

@@ -259,8 +257,9 @@ pub fn set_device_sample_rate(device_id: AudioDeviceID, new_rate: f64) -> Result
259257
);
260258
Error::from_os_status(status)?;
261259
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);
260+
let mut ranges: Vec<AudioValueRange> = vec![];
261+
ranges.reserve_exact(n_ranges as usize);
262+
ranges.set_len(n_ranges);
264263
let status = AudioObjectGetPropertyData(
265264
device_id,
266265
&property_address as *const _,
@@ -270,8 +269,6 @@ pub fn set_device_sample_rate(device_id: AudioDeviceID, new_rate: f64) -> Result
270269
ranges.as_mut_ptr() as *mut _,
271270
);
272271
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);
275272

276273
// Now that we have the available ranges, pick the one matching the desired rate.
277274
let new_rate_integer = new_rate as u32;
@@ -322,9 +319,9 @@ pub fn set_device_sample_rate(device_id: AudioDeviceID, new_rate: f64) -> Result
322319
}
323320
}
324321

325-
/// Change the sample rate and format of a device.
322+
/// Change the physical stream format (sample rate and format) of a device.
326323
/// Only implemented for macOS, not iOS.
327-
pub fn set_device_sample_format(
324+
pub fn set_device_physical_stream_format(
328325
device_id: AudioDeviceID,
329326
new_asbd: AudioStreamBasicDescription,
330327
) -> Result<(), Error> {
@@ -413,9 +410,11 @@ fn asbds_are_equal(
413410
&& left.mBitsPerChannel == right.mBitsPerChannel
414411
}
415412

416-
/// Get a vector with all supported formats as AudioBasicStreamDescriptions.
413+
/// Get a vector with all supported physical formats as AudioBasicStreamDescriptions.
417414
/// Only implemented for macOS, not iOS.
418-
pub fn get_supported_stream_formats(
415+
/// TODO: figure out why this sometimes crashes with:
416+
/// malloc: Incorrect checksum for freed object 0x7fca6bc3c538: probably modified after being freed.
417+
pub fn get_supported_physical_stream_formats(
419418
device_id: AudioDeviceID,
420419
) -> Result<Vec<AudioStreamBasicDescription>, Error> {
421420
// Get available formats.
@@ -427,20 +426,22 @@ pub fn get_supported_stream_formats(
427426
//mScope: kAudioDevicePropertyScopeOutput,
428427
mElement: kAudioObjectPropertyElementMaster,
429428
};
430-
let formats = unsafe {
429+
let allformats = unsafe {
431430
property_address.mSelector = kAudioStreamPropertyAvailablePhysicalFormats;
432-
let data_size = 0u32;
431+
let mut data_size = 0u32;
433432
let status = AudioObjectGetPropertyDataSize(
434433
device_id,
435434
&property_address as *const _,
436435
0,
437436
null(),
438-
&data_size as *const _ as *mut _,
437+
&mut data_size as *mut _,
439438
);
440439
Error::from_os_status(status)?;
441440
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);
441+
println!("n_formats {}", n_formats);
442+
let mut formats: Vec<AudioStreamBasicDescription> = vec![];
443+
formats.reserve_exact(n_formats as usize);
444+
formats.set_len(n_formats);
444445
let status = AudioObjectGetPropertyData(
445446
device_id,
446447
&property_address as *const _,
@@ -450,8 +451,7 @@ pub fn get_supported_stream_formats(
450451
formats.as_mut_ptr() as *mut _,
451452
);
452453
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)
454+
formats
455455
};
456456
/*
457457
println!("---- All supported formats ----");
@@ -462,7 +462,7 @@ pub fn get_supported_stream_formats(
462462
}
463463
}
464464
*/
465-
Ok(formats)
465+
Ok(allformats)
466466
}
467467

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

src/audio_unit/sample_format.rs

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,53 @@ use super::audio_format::{self, LinearPcmFlags};
33
/// Dynamic representation of audio data sample format.
44
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
55
pub enum SampleFormat {
6+
/// 32-bit float.
67
F32,
8+
/// 32-bit signed integer.
79
I32,
10+
/// 24-bit signed integer, packed in 3 bytes.
11+
I24_3,
12+
/// 24-bit signed integer, stored in 4 bytes where one is padding.
13+
I24_4,
14+
// 16-bit signed integer.
815
I16,
16+
// 8-bit signed integer.
917
I8,
1018
}
1119

1220
impl SampleFormat {
21+
/// Check if the format flags are appropriate for the given format.
1322
pub fn does_match_flags(&self, flags: audio_format::LinearPcmFlags) -> bool {
1423
let is_float = flags.contains(LinearPcmFlags::IS_FLOAT);
1524
let is_signed_integer = flags.contains(LinearPcmFlags::IS_SIGNED_INTEGER);
25+
let is_packed = flags.contains(LinearPcmFlags::IS_PACKED);
1626
match *self {
17-
SampleFormat::F32 => is_float && !is_signed_integer,
18-
SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
19-
is_signed_integer && !is_float
27+
SampleFormat::F32 => is_float && !is_signed_integer && is_packed,
28+
SampleFormat::I32 | SampleFormat::I24_3 | SampleFormat::I16 | SampleFormat::I8 => {
29+
is_signed_integer && !is_float && is_packed
2030
}
31+
SampleFormat::I24_4 => is_signed_integer && !is_float && !is_packed,
2132
}
2233
}
2334

35+
/// Convert format flags and bits_per_sample to a SampleFormat.
2436
pub fn from_flags_and_bits_per_sample(
2537
flags: audio_format::LinearPcmFlags,
2638
bits_per_sample: u32,
2739
) -> Option<Self> {
28-
// All the currently supported formats are packed.
29-
if !flags.contains(LinearPcmFlags::IS_PACKED) {
30-
return None;
31-
}
40+
let packed = flags.contains(LinearPcmFlags::IS_PACKED);
3241
let sample_format = if flags.contains(LinearPcmFlags::IS_FLOAT) {
33-
match bits_per_sample {
34-
32 => SampleFormat::F32,
42+
match (bits_per_sample, packed) {
43+
(32, true) => SampleFormat::F32,
3544
_ => return None,
3645
}
3746
} else if flags.contains(LinearPcmFlags::IS_SIGNED_INTEGER) {
38-
match bits_per_sample {
39-
8 => SampleFormat::I8,
40-
16 => SampleFormat::I16,
41-
32 => SampleFormat::I32,
47+
match (bits_per_sample, packed) {
48+
(8, true) => SampleFormat::I8,
49+
(16, true) => SampleFormat::I16,
50+
(24, true) => SampleFormat::I24_3,
51+
(24, false) => SampleFormat::I24_4,
52+
(32, true) => SampleFormat::I32,
4253
_ => return None,
4354
}
4455
} else {
@@ -48,15 +59,30 @@ impl SampleFormat {
4859
Some(sample_format)
4960
}
5061

62+
/// Return the size of one sample in bytes.
5163
pub fn size_in_bytes(&self) -> usize {
5264
use std::mem::size_of;
5365
match *self {
5466
SampleFormat::F32 => size_of::<f32>(),
5567
SampleFormat::I32 => size_of::<i32>(),
68+
SampleFormat::I24_3 => 3 * size_of::<u8>(),
69+
SampleFormat::I24_4 => 4 * size_of::<u8>(),
5670
SampleFormat::I16 => size_of::<i16>(),
5771
SampleFormat::I8 => size_of::<i8>(),
5872
}
5973
}
74+
75+
/// Return the number of valid bits for one sample.
76+
pub fn size_in_bits(&self) -> u32 {
77+
match *self {
78+
SampleFormat::F32 => 32,
79+
SampleFormat::I32 => 32,
80+
SampleFormat::I24_3 => 24,
81+
SampleFormat::I24_4 => 24,
82+
SampleFormat::I16 => 16,
83+
SampleFormat::I8 => 8,
84+
}
85+
}
6086
}
6187

6288
/// Audio data sample types.
@@ -66,6 +92,7 @@ pub trait Sample {
6692
}
6793

6894
/// Simplified implementation of the `Sample` trait for sample types.
95+
/// This is only implemented for the types that map directly to a numeric type.
6996
macro_rules! impl_sample {
7097
($($T:ident $format:ident),* $(,)*) => {
7198
$(

src/audio_unit/stream_format.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,16 @@ pub struct StreamFormat {
4040
pub sample_rate: f64,
4141
/// The sample format used to represent the audio data.
4242
///
43-
/// In OS X, Core Audio xpects audio data to be in native-endian, 32-bit floating-point,
43+
/// In OS X, Core Audio expects audio data to be in native-endian, 32-bit floating-point,
4444
/// linear PCM format.
4545
///
4646
/// iOS uses integer and fixed-point audio data. The result is faster calculations and less
4747
/// battery drain when processing audio. iOS provides a Converter audio unit and inclues the
4848
/// interfaces from Audio Converter Services (TODO: look into exposing this).
4949
pub sample_format: SampleFormat,
50+
/// The format flags for the given StreamFormat.
5051
pub flags: super::audio_format::LinearPcmFlags,
52+
/// The number of channels.
5153
pub channels: u32,
5254
}
5355

@@ -64,7 +66,7 @@ impl StreamFormat {
6466
///
6567
/// Returns an `Error` if the `AudioFormat` inferred by the ASBD is not `LinearPCM`.
6668
///
67-
/// Returns an `Error` if the sample format type kkkkkkkkkkkkkkkkkkkkkk
69+
/// Returns an `Error` if the sample format of the asbd cannot be matched to a format supported by SampleFormat.
6870
#[allow(non_snake_case)]
6971
pub fn from_asbd(asbd: sys::AudioStreamBasicDescription) -> Result<StreamFormat, Error> {
7072
const NOT_SUPPORTED: Error = Error::AudioUnit(error::audio_unit::Error::FormatNotSupported);
@@ -123,11 +125,12 @@ impl StreamFormat {
123125
const FRAMES_PER_PACKET: u32 = 1;
124126
let bytes_per_packet = bytes_per_frame * FRAMES_PER_PACKET;
125127
//let bits_per_channel = bytes_per_frame / channels * 8;
126-
let bits_per_channel = if non_interleaved {
127-
bytes_per_frame * 8
128-
} else {
129-
bytes_per_frame / channels * 8
130-
};
128+
//let bits_per_channel = if non_interleaved {
129+
// bytes_per_frame * 8
130+
//} else {
131+
// bytes_per_frame / channels * 8
132+
//};
133+
let bits_per_channel = sample_format.size_in_bits();
131134
//let bits_per_channel = bytes_per_frame * 8;
132135

133136
sys::AudioStreamBasicDescription {

0 commit comments

Comments
 (0)