pub struct StreamFormat {
pub sample_rate: f64,
pub sample_format: SampleFormat,
pub flags: LinearPcmFlags,
pub channels: u32,
}
Expand description
A representation of the AudioStreamBasicDescription specifically for use with the AudioUnit API.
By using a type specific to the audio unit API, we can remove a lot of unnecessary boilerplate that is normally associated with the AudioStreamBasicDescription.
Seeing as LinearPCM
data (the AudioFormat
used by the AudioUnit
API) implies a single
frame per packet, we can infer many of the fields in an ASBD from the sample type.
bytes_per_packet = size_of::<S>()
bytes_per_frame = size_of::<S>()
frames_per_packet
= 1
bits_per_channel = size_of::<S>()
/ channels_per_frame * 8
A packet is a collection of one or more contiguous frames. In linear PCM audio, a packet is always a single frame.
The canonical formats in Core Audio are as follows:
- iOS input and output: Linear PCM with 16-bit integer samples.
- iOS audio units and other audio processing: Noninterleaved linear PCM with 8.24-bit fixed-point samples
- Mac input and output: Linear PCM with 32-bit floating point samples.
- Mac audio units and other audio processing: Noninterleaved linear PCM with 32-bit floating point samples.
Fields§
§sample_rate: f64
The number of frames of audio data per second used to represent a signal.
sample_format: SampleFormat
The sample format used to represent the audio data.
In OS X, Core Audio expects audio data to be in native-endian, 32-bit floating-point, linear PCM format.
iOS uses integer and fixed-point audio data. The result is faster calculations and less battery drain when processing audio. iOS provides a Converter audio unit and inclues the interfaces from Audio Converter Services (TODO: look into exposing this).
flags: LinearPcmFlags
The format flags for the given StreamFormat.
channels: u32
The number of channels.
Implementations§
Source§impl StreamFormat
impl StreamFormat
Sourcepub fn from_asbd(
asbd: AudioStreamBasicDescription,
) -> Result<StreamFormat, Error>
pub fn from_asbd( asbd: AudioStreamBasicDescription, ) -> Result<StreamFormat, Error>
Convert an AudioStreamBasicDescription into a StreamFormat.
Note: audio_unit::StreamFormat
exclusively uses the LinearPCM
AudioFormat
. This is as
specified in the documentation:
Specify kAudioFormatLinearPCM for the mFormatID field. Audio units use uncompressed audio data, so this is the correct format identifier to use whenever you work with audio units.
Audio Unit Hosting Guide for iOS
Returns an Error
if the AudioFormat
inferred by the ASBD is not LinearPCM
.
Returns an Error
if the sample format of the asbd cannot be matched to a format supported by SampleFormat.
Sourcepub fn to_asbd(self) -> AudioStreamBasicDescription
pub fn to_asbd(self) -> AudioStreamBasicDescription
Convert a StreamFormat into an AudioStreamBasicDescription.
Note that this function assumes that only packed formats are used.
This only affects I24, since all other formats supported by StreamFormat
are always packed.
Examples found in repository?
22fn main() -> Result<(), coreaudio::Error> {
23 let mut input_audio_unit =
24 audio_unit_from_device_id(get_default_device_id(true).unwrap(), true)?;
25 let mut output_audio_unit =
26 audio_unit_from_device_id(get_default_device_id(false).unwrap(), false)?;
27
28 let format_flag = match SAMPLE_FORMAT {
29 SampleFormat::F32 => LinearPcmFlags::IS_FLOAT,
30 SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
31 LinearPcmFlags::IS_SIGNED_INTEGER
32 }
33 _ => {
34 unimplemented!("Other formats are not implemented for this example.");
35 }
36 };
37
38 // Using IS_NON_INTERLEAVED everywhere because data::Interleaved is commented out / not implemented
39 let in_stream_format = StreamFormat {
40 sample_rate: SAMPLE_RATE,
41 sample_format: SAMPLE_FORMAT,
42 flags: format_flag | LinearPcmFlags::IS_PACKED | LinearPcmFlags::IS_NON_INTERLEAVED,
43 // audio_unit.set_input_callback is hardcoded to 1 buffer, and when using non_interleaved
44 // we are forced to 1 channel
45 channels: 1,
46 };
47
48 let out_stream_format = StreamFormat {
49 sample_rate: SAMPLE_RATE,
50 sample_format: SAMPLE_FORMAT,
51 flags: format_flag | LinearPcmFlags::IS_PACKED | LinearPcmFlags::IS_NON_INTERLEAVED,
52 // you can change this to 1
53 channels: 2,
54 };
55
56 println!("input={:#?}", &in_stream_format);
57 println!("output={:#?}", &out_stream_format);
58 println!("input_asbd={:#?}", &in_stream_format.to_asbd());
59 println!("output_asbd={:#?}", &out_stream_format.to_asbd());
60
61 let id = kAudioUnitProperty_StreamFormat;
62 let asbd = in_stream_format.to_asbd();
63 input_audio_unit.set_property(id, Scope::Output, Element::Input, Some(&asbd))?;
64
65 let asbd = out_stream_format.to_asbd();
66 output_audio_unit.set_property(id, Scope::Input, Element::Output, Some(&asbd))?;
67
68 let buffer_left = Arc::new(Mutex::new(VecDeque::<S>::new()));
69 let producer_left = buffer_left.clone();
70 let consumer_left = buffer_left.clone();
71 let buffer_right = Arc::new(Mutex::new(VecDeque::<S>::new()));
72 let producer_right = buffer_right.clone();
73 let consumer_right = buffer_right.clone();
74
75 // seed roughly 1 second of data to create a delay in the feedback loop for easier testing
76 for buffer in vec![buffer_left, buffer_right] {
77 let mut buffer = buffer.lock().unwrap();
78 for _ in 0..(out_stream_format.sample_rate as i32) {
79 buffer.push_back(0 as S);
80 }
81 }
82
83 type Args = render_callback::Args<data::NonInterleaved<S>>;
84
85 input_audio_unit.set_input_callback(move |args| {
86 let Args {
87 num_frames,
88 mut data,
89 ..
90 } = args;
91 // Print the number of frames the callback provides.
92 // Included to aid understanding, don't use println and other things
93 // that may block for an unknown amount of time inside the callback
94 // of a real application.
95 println!("input cb {} frames", num_frames);
96 let buffer_left = producer_left.lock().unwrap();
97 let buffer_right = producer_right.lock().unwrap();
98 let mut buffers = vec![buffer_left, buffer_right];
99 for i in 0..num_frames {
100 for (ch, channel) in data.channels_mut().enumerate() {
101 let value: S = channel[i];
102 buffers[ch].push_back(value);
103 }
104 }
105 Ok(())
106 })?;
107 input_audio_unit.start()?;
108
109 output_audio_unit.set_render_callback(move |args: Args| {
110 let Args {
111 num_frames,
112 mut data,
113 ..
114 } = args;
115 // Print the number of frames the callback requests.
116 // Included to aid understanding, don't use println and other things
117 // that may block for an unknown amount of time inside the callback
118 // of a real application.
119 println!("output cb {} frames", num_frames);
120 let buffer_left = consumer_left.lock().unwrap();
121 let buffer_right = consumer_right.lock().unwrap();
122 let mut buffers = vec![buffer_left, buffer_right];
123 for i in 0..num_frames {
124 // Default other channels to copy value from first channel as a fallback
125 let zero: S = 0 as S;
126 let f: S = *buffers[0].front().unwrap_or(&zero);
127 for (ch, channel) in data.channels_mut().enumerate() {
128 let sample: S = buffers[ch].pop_front().unwrap_or(f);
129 channel[i] = sample;
130 }
131 }
132 Ok(())
133 })?;
134 output_audio_unit.start()?;
135
136 std::thread::sleep(std::time::Duration::from_millis(100000));
137
138 Ok(())
139}
More examples
24fn main() -> Result<(), coreaudio::Error> {
25 let input_device_id = get_default_device_id(true).unwrap();
26 let output_device_id = get_default_device_id(false).unwrap();
27 println!(
28 "Input device: {}",
29 get_device_name(input_device_id).unwrap()
30 );
31 println!(
32 "Output device: {}",
33 get_device_name(output_device_id).unwrap()
34 );
35 let mut input_audio_unit = audio_unit_from_device_id(input_device_id, true)?;
36 let mut output_audio_unit = audio_unit_from_device_id(output_device_id, false)?;
37
38 let format_flag = match SAMPLE_FORMAT {
39 SampleFormat::F32 => LinearPcmFlags::IS_FLOAT | LinearPcmFlags::IS_PACKED,
40 SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
41 LinearPcmFlags::IS_SIGNED_INTEGER | LinearPcmFlags::IS_PACKED
42 }
43 _ => {
44 unimplemented!("Please use one of the packed formats");
45 }
46 };
47
48 let in_stream_format = StreamFormat {
49 sample_rate: SAMPLE_RATE,
50 sample_format: SAMPLE_FORMAT,
51 flags: format_flag,
52 channels: 2,
53 };
54
55 let out_stream_format = StreamFormat {
56 sample_rate: SAMPLE_RATE,
57 sample_format: SAMPLE_FORMAT,
58 flags: format_flag,
59 channels: 2,
60 };
61
62 println!("input={:#?}", &in_stream_format);
63 println!("output={:#?}", &out_stream_format);
64 println!("input_asbd={:#?}", &in_stream_format.to_asbd());
65 println!("output_asbd={:#?}", &out_stream_format.to_asbd());
66
67 let id = kAudioUnitProperty_StreamFormat;
68 let asbd = in_stream_format.to_asbd();
69 input_audio_unit.set_property(id, Scope::Output, Element::Input, Some(&asbd))?;
70
71 let asbd = out_stream_format.to_asbd();
72 output_audio_unit.set_property(id, Scope::Input, Element::Output, Some(&asbd))?;
73
74 let buffer_left = Arc::new(Mutex::new(VecDeque::<S>::new()));
75 let producer_left = buffer_left.clone();
76 let consumer_left = buffer_left.clone();
77 let buffer_right = Arc::new(Mutex::new(VecDeque::<S>::new()));
78 let producer_right = buffer_right.clone();
79 let consumer_right = buffer_right.clone();
80
81 // Register a rate listener for playback
82 let mut listener_pb = RateListener::new(output_device_id, None);
83 listener_pb.register()?;
84
85 // Register a rate listener for capture
86 let mut listener_cap = RateListener::new(input_device_id, None);
87 listener_cap.register()?;
88
89 // seed roughly 1 second of data to create a delay in the feedback loop for easier testing
90 for buffer in vec![buffer_left, buffer_right] {
91 let mut buffer = buffer.lock().unwrap();
92 for _ in 0..(out_stream_format.sample_rate as i32) {
93 buffer.push_back(0 as S);
94 }
95 }
96
97 type Args = render_callback::Args<data::Interleaved<S>>;
98
99 input_audio_unit.set_input_callback(move |args| {
100 let Args {
101 num_frames, data, ..
102 } = args;
103 // Print the number of frames the callback requests.
104 // Included to aid understanding, don't use println and other things
105 // that may block for an unknown amount of time inside the callback
106 // of a real application.
107 println!("input cb {} frames", num_frames);
108 let buffer_left = producer_left.lock().unwrap();
109 let buffer_right = producer_right.lock().unwrap();
110 let mut buffers = vec![buffer_left, buffer_right];
111 for i in 0..num_frames {
112 for channel in 0..2 {
113 let value: S = data.buffer[2 * i + channel];
114 buffers[channel].push_back(value);
115 }
116 }
117 Ok(())
118 })?;
119 input_audio_unit.start()?;
120
121 output_audio_unit.set_render_callback(move |args: Args| {
122 let Args {
123 num_frames, data, ..
124 } = args;
125 // Print the number of frames the callback requests.
126 println!("output cb {} frames", num_frames);
127 let buffer_left = consumer_left.lock().unwrap();
128 let buffer_right = consumer_right.lock().unwrap();
129 let mut buffers = vec![buffer_left, buffer_right];
130 for i in 0..num_frames {
131 // Default other channels to copy value from first channel as a fallback
132 let zero: S = 0 as S;
133 let f: S = *buffers[0].front().unwrap_or(&zero);
134 for channel in 0..2 {
135 let sample: S = buffers[channel].pop_front().unwrap_or(f);
136 data.buffer[2 * i + channel] = sample;
137 }
138 }
139 Ok(())
140 })?;
141 output_audio_unit.start()?;
142 for _ in 0..1000 {
143 std::thread::sleep(std::time::Duration::from_millis(100));
144 if listener_cap.get_nbr_values() > 0 {
145 println!("capture rate change: {:?}", listener_cap.drain_values());
146 }
147 if listener_pb.get_nbr_values() > 0 {
148 println!("playback rate change: {:?}", listener_pb.drain_values());
149 }
150 }
151 Ok(())
152}
54fn main() -> Result<(), coreaudio::Error> {
55 let frequency_hz_l = 1000.;
56 let frequency_hz_r = 1200.;
57 let volume = 0.95;
58 let mut samples_l = SineWaveGenerator::new(frequency_hz_l, volume);
59 let mut samples_r = SineWaveGenerator::new(frequency_hz_r, volume);
60
61 // Construct an Output audio unit that delivers audio to the default output device.
62 let audio_unit_id = get_default_device_id(false).unwrap();
63 let mut audio_unit = audio_unit_from_device_id(audio_unit_id, false)?;
64
65 let pid = get_hogging_pid(audio_unit_id)?;
66 if pid != -1 {
67 println!("Device is owned by another process with pid {}!", pid);
68 } else {
69 println!("Device is free, trying to get exclusive access..");
70 let new_pid = toggle_hog_mode(audio_unit_id)?;
71 let process_id = process::id();
72 if new_pid == process_id as i32 {
73 println!("We have exclusive access.");
74 } else {
75 println!(
76 "Could not get exclusive access. Process pid: {}, new pid value: {}",
77 process_id, new_pid
78 );
79 }
80 }
81
82 let mut format_flag = match SAMPLE_FORMAT {
83 SampleFormat::F32 => LinearPcmFlags::IS_FLOAT | LinearPcmFlags::IS_PACKED,
84 SampleFormat::I32 | SampleFormat::I16 | SampleFormat::I8 => {
85 LinearPcmFlags::IS_SIGNED_INTEGER | LinearPcmFlags::IS_PACKED
86 }
87 _ => {
88 unimplemented!("Please use one of the packed formats");
89 }
90 };
91
92 if !INTERLEAVED {
93 format_flag = format_flag | LinearPcmFlags::IS_NON_INTERLEAVED;
94 }
95
96 let stream_format = StreamFormat {
97 sample_rate: SAMPLE_RATE,
98 sample_format: SAMPLE_FORMAT,
99 flags: format_flag,
100 // you can change this to 1
101 channels: 2,
102 };
103
104 println!("stream format={:#?}", &stream_format);
105 println!("asbd={:#?}", &stream_format.to_asbd());
106
107 // Lets print all supported formats, disabled for now since it often crashes.
108 println!("All supported formats");
109 let formats = get_supported_physical_stream_formats(audio_unit_id)?;
110 for fmt in formats {
111 println!("{:?}", &fmt);
112 }
113
114 // set the sample rate. This isn't actually needed since the sample rate
115 // will anyway be changed when setting the sample format later.
116 // Keeping it here as an example.
117 //println!("set device sample rate");
118 //set_device_sample_rate(audio_unit_id, SAMPLE_RATE)?;
119
120 println!("setting hardware (physical) format");
121 let hw_stream_format = StreamFormat {
122 sample_rate: SAMPLE_RATE,
123 sample_format: SampleFormat::I16,
124 flags: LinearPcmFlags::empty(),
125 channels: 2,
126 };
127
128 let hw_asbd = find_matching_physical_format(audio_unit_id, hw_stream_format)
129 .ok_or(coreaudio::Error::UnsupportedStreamFormat)?;
130
131 println!("asbd: {:?}", hw_asbd);
132
133 // Note that using a StreamFormat here is convenient, but it only supports a few sample formats.
134 // Setting the format to for example 24 bit integers requires using an ASBD.
135 set_device_physical_stream_format(audio_unit_id, hw_asbd)?;
136
137 println!("write audio unit StreamFormat property");
138 let id = kAudioUnitProperty_StreamFormat;
139 let asbd = stream_format.to_asbd();
140 audio_unit.set_property(id, Scope::Input, Element::Output, Some(&asbd))?;
141
142 // For this example, our sine wave expects `f32` data.
143 assert!(SampleFormat::F32 == stream_format.sample_format);
144
145 // Register rate and alive listeners
146 let mut rate_listener = RateListener::new(audio_unit_id, None);
147 rate_listener.register()?;
148 let mut alive_listener = AliveListener::new(audio_unit_id);
149 alive_listener.register()?;
150
151 if INTERLEAVED {
152 println!("Register interleaved callback");
153 type Args = render_callback::Args<data::Interleaved<f32>>;
154 audio_unit.set_render_callback(move |args| {
155 let Args {
156 num_frames, data, ..
157 } = args;
158 // Print the number of frames the callback requests.
159 // Included to aid understanding, don't use println and other things
160 // that may block for an unknown amount of time inside the callback
161 // of a real application.
162 println!("frames: {}", num_frames);
163 for i in 0..num_frames {
164 let sample_l = samples_l.next().unwrap();
165 let sample_r = samples_r.next().unwrap();
166 data.buffer[2 * i] = sample_l;
167 data.buffer[2 * i + 1] = sample_r;
168 }
169 Ok(())
170 })?;
171 } else {
172 println!("Register non-interleaved callback");
173 type Args = render_callback::Args<data::NonInterleaved<f32>>;
174 audio_unit.set_render_callback(move |args| {
175 let Args {
176 num_frames,
177 mut data,
178 ..
179 } = args;
180 for i in 0..num_frames {
181 let sample_l = samples_l.next().unwrap();
182 let sample_r = samples_r.next().unwrap();
183 let mut channels = data.channels_mut();
184 let left = channels.next().unwrap();
185 left[i] = sample_l;
186 let right = channels.next().unwrap();
187 right[i] = sample_r;
188 }
189 Ok(())
190 })?;
191 }
192 audio_unit.start()?;
193
194 for _ in 0..100 {
195 std::thread::sleep(std::time::Duration::from_millis(100));
196 // print all sample change events
197 println!("rate events: {:?}", rate_listener.copy_values());
198 println!("alive state: {}", alive_listener.is_alive());
199 }
200
201 // Release exclusive access, not really needed as the process exits anyway after this.
202 let owner_pid = get_hogging_pid(audio_unit_id)?;
203 let process_id = process::id();
204 if owner_pid == process_id as i32 {
205 println!("Releasing exclusive access");
206 let new_pid = toggle_hog_mode(audio_unit_id)?;
207 if new_pid == -1 {
208 println!("Exclusive access released.");
209 } else {
210 println!(
211 "Could not release exclusive access. Process pid: {}, new pid value: {}",
212 process_id, new_pid
213 );
214 }
215 }
216 Ok(())
217}
Trait Implementations§
Source§impl Clone for StreamFormat
impl Clone for StreamFormat
Source§fn clone(&self) -> StreamFormat
fn clone(&self) -> StreamFormat
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read more