OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "audio_synchronized_mac.h" | |
scherkus (not reviewing)
2012/09/12 14:05:29
should be full path
Chris Rogers
2012/09/13 01:03:05
Done.
| |
6 | |
7 #include <CoreServices/CoreServices.h> | |
8 | |
9 #if CHROME | |
10 #include "base/basictypes.h" | |
11 #include "base/logging.h" | |
12 #include "base/mac/mac_logging.h" | |
13 #include "media/audio/audio_util.h" | |
14 #include "media/audio/mac/audio_manager_mac.h" | |
15 #endif | |
16 | |
17 namespace media { | |
18 | |
19 const int kHardwareBufferSize = 128; | |
no longer working on chromium
2012/09/12 09:11:56
put them into an anonymous space or static
scherkus (not reviewing)
2012/09/12 14:05:29
static, please! :)
Chris Rogers
2012/09/13 01:03:05
I think that "const int" is static automatically u
| |
20 const int kFIFOSize = 16384; | |
scherkus (not reviewing)
2012/09/12 14:05:29
s/FIFO/Fifo here + below
Chris Rogers
2012/09/13 01:03:05
Done.
| |
21 | |
22 // TODO(crogers): handle the non-stereo case. | |
23 const int kFIFOChannels = 2; | |
24 | |
25 // This value is empirically determined for minimum latency | |
26 // while still guarding against FIFO under-runs. | |
27 // TODO(crogers): refine this, taking into account different input/output | |
28 // sample-rate combinations. | |
29 const int kTargetDelayFrames = 256; | |
30 | |
31 static void MakeBufferSilent(AudioBufferList* io_data) { | |
32 for (UInt32 i = 0; i < io_data->mNumberBuffers; ++i) | |
33 memset(io_data->mBuffers[i].mData, 0, io_data->mBuffers[i].mDataByteSize); | |
34 } | |
35 | |
36 AudioSynchronizedStream::AudioSynchronizedStream( | |
37 AudioManagerMac* manager, | |
38 const AudioParameters&, | |
39 AudioDeviceID input_id, | |
40 AudioDeviceID output_id) | |
41 : manager_(manager), | |
42 input_id_(input_id), | |
43 output_id_(output_id), | |
44 input_buffer_(NULL), | |
45 fifo_(kFIFOChannels, kFIFOSize), | |
46 is_fifo_initialized_(false), | |
no longer working on chromium
2012/09/12 09:11:56
Is this a special user case for fifo? why not ask
Chris Rogers
2012/09/13 01:03:05
I can still clean this part up a little - not yet
| |
47 fifo_rate_compensation_(1.0), | |
48 output_sample_rate_(0), | |
49 input_unit_(0), | |
50 varispeed_unit_(0), | |
51 output_unit_(0), | |
52 first_input_time_(-1), | |
53 first_output_time_(-1), | |
54 in_to_out_sample_offset_(0), | |
55 is_running_(false), | |
56 hardware_frame_size_(kHardwareBufferSize) { | |
57 // TODO(crogers): actually do something with |params| | |
58 // We at least need to verify the sample-rate matches | |
59 // the hardware sample-rate of the output device, and we | |
60 // should take into account the |channels|. | |
61 // For now we're limited to stereo output. | |
62 } | |
63 | |
64 AudioSynchronizedStream::~AudioSynchronizedStream() { | |
65 DCHECK_EQ(input_unit_, 0); | |
66 DCHECK_EQ(output_unit_, 0); | |
67 DCHECK_EQ(varispeed_unit_, 0); | |
68 } | |
69 | |
70 bool AudioSynchronizedStream::Open() { | |
71 // Create the input, output, and varispeed AudioUnits. | |
72 OSStatus result = CreateAudioUnits(); | |
73 if (result != noErr) { | |
74 LOG(ERROR) << "Cannot create AudioUnits."; | |
75 return false; | |
76 } | |
77 | |
78 result = SetupInput(input_id_); | |
79 if (result != noErr) { | |
80 LOG(ERROR) << "Error configuring input AudioUnit."; | |
81 return false; | |
82 } | |
83 | |
84 result = SetupOutput(output_id_); | |
85 if (result != noErr) { | |
86 LOG(ERROR) << "Error configuring output AudioUnit."; | |
87 return false; | |
88 } | |
89 | |
90 result = SetupCallbacks(); | |
91 if (result != noErr) { | |
92 LOG(ERROR) << "Error setting up callbacks on AudioUnits."; | |
93 return false; | |
94 } | |
95 | |
96 result = SetupStreamFormats(); | |
97 if (result != noErr) { | |
98 LOG(ERROR) << "Error configuring stream formats on AudioUnits."; | |
99 return false; | |
100 } | |
101 | |
102 // Final initialization of the AudioUnits. | |
103 | |
no longer working on chromium
2012/09/12 09:11:56
remove this empty line.
Chris Rogers
2012/09/13 01:03:05
Done.
| |
104 result = AudioUnitInitialize(input_unit_); | |
105 if (result != noErr) { | |
106 LOG(ERROR) << "Error initializing input AudioUnit."; | |
107 return false; | |
108 } | |
109 | |
110 result = AudioUnitInitialize(output_unit_); | |
111 if (result != noErr) { | |
112 LOG(ERROR) << "Error initializing output AudioUnit."; | |
113 return false; | |
114 } | |
115 | |
116 result = AudioUnitInitialize(varispeed_unit_); | |
117 if (result != noErr) { | |
118 LOG(ERROR) << "Error initializing varispeed AudioUnit."; | |
119 return false; | |
120 } | |
121 | |
122 ComputeThruOffset(); | |
123 | |
124 return true; | |
125 } | |
126 | |
127 void AudioSynchronizedStream::Close() { | |
128 DCHECK(!is_running_); | |
129 | |
130 if (input_buffer_) { | |
131 for (UInt32 i = 0; i < input_buffer_->mNumberBuffers; ++i) | |
132 free(input_buffer_->mBuffers[i].mData); | |
133 free(input_buffer_); | |
134 input_buffer_ = 0; | |
135 } | |
136 | |
137 AudioUnitUninitialize(input_unit_); | |
no longer working on chromium
2012/09/12 09:11:56
Is it all right to call AudioUnitUninitialize if i
Chris Rogers
2012/09/13 01:03:05
I've added extra checking here. It *is* ok to cal
no longer working on chromium
2012/09/17 08:26:01
good, thanks.
| |
138 AudioUnitUninitialize(output_unit_); | |
139 AudioUnitUninitialize(varispeed_unit_); | |
140 input_unit_ = 0; | |
scherkus (not reviewing)
2012/09/12 14:05:29
s/0/NULL/?
Chris Rogers
2012/09/13 01:03:05
Done.
| |
141 output_unit_ = 0; | |
142 varispeed_unit_ = 0; | |
143 | |
144 // Inform the audio manager that we have been closed. This can cause our | |
145 // destruction. | |
146 manager_->ReleaseOutputStream(this); | |
147 } | |
148 | |
149 void AudioSynchronizedStream::Start(AudioSourceCallback* callback) { | |
150 bool good = input_unit_ && output_unit_ && varispeed_unit_; | |
151 | |
152 DCHECK(good); | |
scherkus (not reviewing)
2012/09/12 14:05:29
a more informative DCHECK would be to DCHECK on ea
Chris Rogers
2012/09/13 01:03:05
Done.
| |
153 DCHECK(callback); | |
154 | |
155 if (!good) | |
no longer working on chromium
2012/09/12 09:11:56
early return if (!good || is_running_)
Chris Rogers
2012/09/13 01:03:05
Done.
| |
156 return; | |
157 | |
158 source_ = callback; | |
159 | |
160 OSStatus result = noErr; | |
161 | |
162 if (!is_running_) { | |
163 result = AudioOutputUnitStart(input_unit_); | |
164 OSSTATUS_DCHECK(result == noErr, result); | |
165 | |
166 if (result == noErr) { | |
167 result = AudioOutputUnitStart(output_unit_); | |
168 OSSTATUS_DCHECK(result == noErr, result); | |
169 } | |
170 | |
171 first_input_time_ = -1; | |
172 first_output_time_ = -1; | |
173 } | |
174 | |
175 if (result == noErr) | |
176 is_running_ = true; | |
no longer working on chromium
2012/09/12 09:11:56
I think we should set is_running_ to true here reg
Chris Rogers
2012/09/13 01:03:05
Done.
| |
177 } | |
178 | |
179 void AudioSynchronizedStream::Stop() { | |
180 OSStatus result = noErr; | |
181 if (is_running_) { | |
182 result = AudioOutputUnitStop(input_unit_); | |
183 OSSTATUS_DCHECK(result == noErr, result); | |
184 | |
185 if (result == noErr) { | |
186 result = AudioOutputUnitStop(output_unit_); | |
187 OSSTATUS_DCHECK(result == noErr, result); | |
188 } | |
189 | |
190 first_input_time_ = -1; | |
no longer working on chromium
2012/09/12 09:11:56
we are setting the values to -1 in both Start() an
Chris Rogers
2012/09/13 01:03:05
Done.
| |
191 first_output_time_ = -1; | |
192 } | |
193 | |
194 if (result == noErr) | |
195 is_running_ = false; | |
196 } | |
197 | |
198 bool AudioSynchronizedStream::IsRunning() { | |
199 return is_running_; | |
200 } | |
201 | |
202 OSStatus AudioSynchronizedStream::SetOutputDeviceAsCurrent( | |
203 AudioDeviceID output_id) { | |
204 OSStatus result = noErr; | |
205 | |
206 // Get the default output device if device is unknown. | |
207 if (output_id == kAudioDeviceUnknown) { | |
208 AudioObjectPropertyAddress pa; | |
209 pa.mSelector = kAudioHardwarePropertyDefaultOutputDevice; | |
210 pa.mScope = kAudioObjectPropertyScopeGlobal; | |
211 pa.mElement = kAudioObjectPropertyElementMaster; | |
212 UInt32 size = sizeof(AudioDeviceID); | |
scherkus (not reviewing)
2012/09/12 14:05:29
if possible sizeof(variable) instead of sizeof(typ
Chris Rogers
2012/09/13 01:03:05
Done.
| |
213 | |
214 result = AudioObjectGetPropertyData( | |
215 kAudioObjectSystemObject, | |
216 &pa, | |
217 0, | |
218 0, | |
219 &size, | |
220 &output_id); | |
221 | |
222 OSSTATUS_DCHECK(result == noErr, result); | |
223 if (result != noErr) | |
224 return result; | |
225 } | |
226 | |
227 // Set the render frame size. | |
228 UInt32 frame_size = hardware_frame_size_; | |
229 AudioObjectPropertyAddress pa; | |
230 pa.mSelector = kAudioDevicePropertyBufferFrameSize; | |
231 pa.mScope = kAudioDevicePropertyScopeInput; | |
232 pa.mElement = kAudioObjectPropertyElementMaster; | |
233 result = AudioObjectSetPropertyData( | |
234 output_id, | |
235 &pa, | |
236 0, | |
237 0, | |
238 sizeof(frame_size), | |
239 &frame_size); | |
240 | |
241 OSSTATUS_DCHECK(result == noErr, result); | |
242 if (result != noErr) | |
243 return result; | |
244 | |
245 output_info_.Initialize(output_id, false); | |
246 | |
247 // Set the Current Device to the Default Output Unit. | |
248 result = AudioUnitSetProperty( | |
249 output_unit_, | |
250 kAudioOutputUnitProperty_CurrentDevice, | |
251 kAudioUnitScope_Global, | |
252 0, | |
253 &output_info_.id_, | |
254 sizeof(output_info_.id_)); | |
255 | |
256 OSSTATUS_DCHECK(result == noErr, result); | |
257 return result; | |
258 } | |
259 | |
260 OSStatus AudioSynchronizedStream::SetInputDeviceAsCurrent( | |
261 AudioDeviceID input_id) { | |
262 OSStatus result = noErr; | |
263 | |
264 // Get the default input device if device is unknown. | |
265 if (input_id == kAudioDeviceUnknown) { | |
266 AudioObjectPropertyAddress pa; | |
267 pa.mSelector = kAudioHardwarePropertyDefaultInputDevice; | |
268 pa.mScope = kAudioObjectPropertyScopeGlobal; | |
269 pa.mElement = kAudioObjectPropertyElementMaster; | |
270 UInt32 size = sizeof(AudioDeviceID); | |
scherkus (not reviewing)
2012/09/12 14:05:29
if possible sizeof(variable) instead of sizeof(typ
Chris Rogers
2012/09/13 01:03:05
Done.
| |
271 | |
272 result = AudioObjectGetPropertyData( | |
273 kAudioObjectSystemObject, | |
274 &pa, | |
275 0, | |
276 0, | |
277 &size, | |
278 &input_id); | |
279 | |
280 OSSTATUS_DCHECK(result == noErr, result); | |
281 if (result != noErr) | |
282 return result; | |
283 } | |
284 | |
285 // Set the render frame size. | |
286 UInt32 frame_size = hardware_frame_size_; | |
287 AudioObjectPropertyAddress pa; | |
288 pa.mSelector = kAudioDevicePropertyBufferFrameSize; | |
289 pa.mScope = kAudioDevicePropertyScopeInput; | |
290 pa.mElement = kAudioObjectPropertyElementMaster; | |
291 result = AudioObjectSetPropertyData( | |
292 input_id, | |
293 &pa, | |
294 0, | |
295 0, | |
296 sizeof(frame_size), | |
297 &frame_size); | |
298 | |
299 OSSTATUS_DCHECK(result == noErr, result); | |
300 if (result != noErr) | |
301 return result; | |
302 | |
303 input_info_.Initialize(input_id, true); | |
304 | |
305 // Set the Current Device to the AUHAL. | |
306 // This should be done only after I/O has been enabled on the AUHAL. | |
307 result = AudioUnitSetProperty( | |
308 input_unit_, | |
309 kAudioOutputUnitProperty_CurrentDevice, | |
310 kAudioUnitScope_Global, | |
311 0, | |
312 &input_info_.id_, | |
313 sizeof(input_info_.id_)); | |
314 | |
315 OSSTATUS_DCHECK(result == noErr, result); | |
316 return result; | |
317 } | |
318 | |
319 OSStatus AudioSynchronizedStream::CreateAudioUnits() { | |
320 // Q: Why do we need a varispeed unit? | |
321 // A: If the input device and the output device are running at | |
322 // different sample rates and/or on different clocks, we will need | |
323 // to compensate to avoid a pitch change and | |
324 // to avoid buffer under and over runs. | |
325 ComponentDescription varispeed_desc; | |
326 varispeed_desc.componentType = kAudioUnitType_FormatConverter; | |
327 varispeed_desc.componentSubType = kAudioUnitSubType_Varispeed; | |
328 varispeed_desc.componentManufacturer = kAudioUnitManufacturer_Apple; | |
329 varispeed_desc.componentFlags = 0; | |
330 varispeed_desc.componentFlagsMask = 0; | |
331 | |
332 Component varispeed_comp = FindNextComponent(NULL, &varispeed_desc); | |
333 if (varispeed_comp == NULL) | |
334 return -1; | |
335 OpenAComponent(varispeed_comp, &varispeed_unit_); | |
336 | |
337 // Open input AudioUnit. | |
338 ComponentDescription input_desc; | |
339 input_desc.componentType = kAudioUnitType_Output; | |
340 input_desc.componentSubType = kAudioUnitSubType_HALOutput; | |
341 input_desc.componentManufacturer = kAudioUnitManufacturer_Apple; | |
342 input_desc.componentFlags = 0; | |
343 input_desc.componentFlagsMask = 0; | |
344 | |
345 Component input_comp = FindNextComponent(NULL, &input_desc); | |
346 if (input_comp == NULL) | |
347 return -1; | |
348 | |
349 OpenAComponent(input_comp, &input_unit_); | |
no longer working on chromium
2012/09/12 09:11:56
why don't we care about the return value here?
Chris Rogers
2012/09/13 01:03:05
We do ;) added checks and early return for these
Chris Rogers
2012/09/13 01:03:05
Done.
| |
350 | |
351 // Open output AudioUnit. | |
352 ComponentDescription output_desc; | |
353 output_desc.componentType = kAudioUnitType_Output; | |
354 output_desc.componentSubType = kAudioUnitSubType_DefaultOutput; | |
355 output_desc.componentManufacturer = kAudioUnitManufacturer_Apple; | |
356 output_desc.componentFlags = 0; | |
357 output_desc.componentFlagsMask = 0; | |
358 | |
359 Component output_comp = FindNextComponent(NULL, &output_desc); | |
360 if (output_comp == NULL) | |
361 return -1; | |
362 OpenAComponent(output_comp, &output_unit_); | |
no longer working on chromium
2012/09/12 09:11:56
ditto
Chris Rogers
2012/09/13 01:03:05
Done.
| |
363 | |
364 return noErr; | |
365 } | |
366 | |
367 OSStatus AudioSynchronizedStream::SetupInput(AudioDeviceID input_id) { | |
368 // The AUHAL used for input needs to be initialized | |
369 // before anything is done to it. | |
370 OSStatus result = AudioUnitInitialize(input_unit_); | |
371 OSSTATUS_DCHECK(result == noErr, result); | |
372 if (result != noErr) | |
373 return result; | |
374 | |
375 // We must enable the Audio Unit (AUHAL) for input and disable output | |
376 // BEFORE setting the AUHAL's current device. | |
377 result = EnableIO(); | |
378 OSSTATUS_DCHECK(result == noErr, result); | |
379 if (result != noErr) | |
380 return result; | |
381 | |
382 result = SetInputDeviceAsCurrent(input_id); | |
383 OSSTATUS_DCHECK(result == noErr, result); | |
384 | |
385 return result; | |
386 } | |
387 | |
388 OSStatus AudioSynchronizedStream::EnableIO() { | |
no longer working on chromium
2012/09/12 09:11:56
can we change the name to something else like Setu
Chris Rogers
2012/09/13 01:03:05
Leaving name for now, since the property name we'r
| |
389 // Enable input on the AUHAL. | |
390 UInt32 enable_io = 1; | |
391 OSStatus result = AudioUnitSetProperty( | |
392 input_unit_, | |
393 kAudioOutputUnitProperty_EnableIO, | |
394 kAudioUnitScope_Input, | |
395 1, // input element | |
396 &enable_io, | |
397 sizeof(enable_io)); | |
398 | |
399 OSSTATUS_DCHECK(result == noErr, result); | |
400 if (result != noErr) | |
401 return result; | |
402 | |
403 // Disable Output on the AUHAL. | |
404 enable_io = 0; | |
405 result = AudioUnitSetProperty( | |
406 input_unit_, | |
407 kAudioOutputUnitProperty_EnableIO, | |
408 kAudioUnitScope_Output, | |
409 0, // output element | |
410 &enable_io, | |
411 sizeof(enable_io)); | |
412 | |
413 OSSTATUS_DCHECK(result == noErr, result); | |
414 return result; | |
415 } | |
416 | |
417 OSStatus AudioSynchronizedStream::SetupOutput(AudioDeviceID output_id) { | |
418 OSStatus result = noErr; | |
419 | |
420 result = SetOutputDeviceAsCurrent(output_id); | |
421 OSSTATUS_DCHECK(result == noErr, result); | |
422 if (result != noErr) | |
423 return result; | |
424 | |
425 // Tell the output unit not to reset timestamps. | |
426 // Otherwise sample rate changes will cause sync loss. | |
427 UInt32 start_at_zero = 0; | |
428 result = AudioUnitSetProperty( | |
429 output_unit_, | |
430 kAudioOutputUnitProperty_StartTimestampsAtZero, | |
431 kAudioUnitScope_Global, | |
432 0, | |
scherkus (not reviewing)
2012/09/12 14:05:29
these zeros and ones aren't very telling -- is the
Chris Rogers
2012/09/13 01:03:05
For now I haven't done anything about this. I cou
| |
433 &start_at_zero, | |
434 sizeof(start_at_zero)); | |
435 | |
436 OSSTATUS_DCHECK(result == noErr, result); | |
437 | |
438 return result; | |
439 } | |
440 | |
441 OSStatus AudioSynchronizedStream::SetupCallbacks() { | |
442 // Set the input callback. | |
443 AURenderCallbackStruct callback; | |
444 callback.inputProc = InputProc; | |
445 callback.inputProcRefCon = this; | |
446 OSStatus result = AudioUnitSetProperty( | |
447 input_unit_, | |
448 kAudioOutputUnitProperty_SetInputCallback, | |
449 kAudioUnitScope_Global, | |
450 0, | |
no longer working on chromium
2012/09/12 09:11:56
shouldn't the AudioUnitElement to be 1 here?
Chris Rogers
2012/09/13 01:03:05
No, since it's "global" scope there isn't any elem
| |
451 &callback, | |
452 sizeof(callback)); | |
453 | |
454 OSSTATUS_DCHECK(result == noErr, result); | |
455 if (result != noErr) | |
456 return result; | |
457 | |
458 // Set the output callback. | |
459 callback.inputProc = OutputProc; | |
460 callback.inputProcRefCon = this; | |
461 result = AudioUnitSetProperty( | |
462 output_unit_, | |
463 kAudioUnitProperty_SetRenderCallback, | |
464 kAudioUnitScope_Input, | |
465 0, | |
466 &callback, | |
467 sizeof(callback)); | |
468 | |
469 OSSTATUS_DCHECK(result == noErr, result); | |
470 if (result != noErr) | |
471 return result; | |
472 | |
473 // Set the varispeed callback. | |
474 callback.inputProc = VarispeedProc; | |
475 callback.inputProcRefCon = this; | |
476 result = AudioUnitSetProperty( | |
477 varispeed_unit_, | |
478 kAudioUnitProperty_SetRenderCallback, | |
479 kAudioUnitScope_Input, | |
480 0, | |
481 &callback, | |
482 sizeof(callback)); | |
483 | |
484 OSSTATUS_DCHECK(result == noErr, result); | |
485 | |
486 return result; | |
487 } | |
488 | |
489 OSStatus AudioSynchronizedStream::SetupStreamFormats() { | |
490 UInt32 buffer_size_frames, buffer_size_bytes; | |
491 | |
492 AudioStreamBasicDescription asbd, asbd_dev1_in, asbd_dev2_out; | |
493 AudioObjectPropertyAddress pa; | |
494 | |
495 // Get the size of the IO buffer(s) | |
no longer working on chromium
2012/09/12 09:11:56
add a period at the end of the comments. Please fi
Chris Rogers
2012/09/13 01:03:05
Done.
| |
496 UInt32 property_size = sizeof(buffer_size_frames); | |
497 OSStatus result = AudioUnitGetProperty( | |
498 input_unit_, | |
499 kAudioDevicePropertyBufferFrameSize, | |
500 kAudioUnitScope_Global, | |
501 0, | |
502 &buffer_size_frames, | |
503 &property_size); | |
504 | |
505 OSSTATUS_DCHECK(result == noErr, result); | |
506 if (result != noErr) | |
507 return result; | |
508 | |
509 buffer_size_bytes = buffer_size_frames * sizeof(Float32); | |
scherkus (not reviewing)
2012/09/12 14:05:29
if possible sizeof(variable) instead of sizeof(typ
Chris Rogers
2012/09/13 01:03:05
I've moved |buffer_size_bytes| down closer to usag
| |
510 | |
511 // Get the Stream Format (Output client side) | |
512 property_size = sizeof(asbd_dev1_in); | |
513 result = AudioUnitGetProperty( | |
514 input_unit_, | |
515 kAudioUnitProperty_StreamFormat, | |
516 kAudioUnitScope_Input, | |
517 1, | |
518 &asbd_dev1_in, | |
519 &property_size); | |
520 | |
521 OSSTATUS_DCHECK(result == noErr, result); | |
522 if (result != noErr) | |
523 return result; | |
524 | |
525 // Get the Stream Format (client side) | |
526 property_size = sizeof(asbd); | |
527 result = AudioUnitGetProperty( | |
528 input_unit_, | |
529 kAudioUnitProperty_StreamFormat, | |
530 kAudioUnitScope_Output, | |
531 1, | |
no longer working on chromium
2012/09/12 09:11:56
Could you please explain which side of format it i
Chris Rogers
2012/09/13 01:03:05
Sorry, I still need to address this (better commen
| |
532 &asbd, | |
533 &property_size); | |
534 | |
535 OSSTATUS_DCHECK(result == noErr, result); | |
536 if (result != noErr) | |
537 return result; | |
538 | |
539 // Get the Stream Format (Output client side) | |
540 property_size = sizeof(asbd_dev2_out); | |
541 result = AudioUnitGetProperty( | |
542 output_unit_, | |
543 kAudioUnitProperty_StreamFormat, | |
544 kAudioUnitScope_Output, | |
545 0, | |
546 &asbd_dev2_out, | |
547 &property_size); | |
548 | |
549 OSSTATUS_DCHECK(result == noErr, result); | |
550 if (result != noErr) | |
551 return result; | |
552 | |
553 ////////////////////////////////////// | |
no longer working on chromium
2012/09/12 09:11:56
move ///////////////
Chris Rogers
2012/09/13 01:03:05
Done.
| |
554 // Set the format of all the AUs to the input/output devices channel count | |
555 // For a simple case, you want to set this to | |
556 // the lower of count of the channels in the input device vs output device | |
557 ////////////////////////////////////// | |
558 asbd.mChannelsPerFrame = | |
559 ((asbd_dev1_in.mChannelsPerFrame < asbd_dev2_out.mChannelsPerFrame) ? | |
560 asbd_dev1_in.mChannelsPerFrame : asbd_dev2_out.mChannelsPerFrame); | |
no longer working on chromium
2012/09/12 09:11:56
use std::min()
Chris Rogers
2012/09/13 01:03:05
Done.
| |
561 | |
562 // We must get the sample rate of the input device | |
563 // and set it to the stream format of AUHAL | |
scherkus (not reviewing)
2012/09/12 14:05:29
nit: part of this comment can go on previous line
Chris Rogers
2012/09/13 01:03:05
Done.
| |
564 property_size = sizeof(Float64); | |
scherkus (not reviewing)
2012/09/12 14:05:29
if possible sizeof(variable) instead of sizeof(typ
Chris Rogers
2012/09/13 01:03:05
Done.
| |
565 Float64 rate = 0; | |
566 | |
567 pa.mSelector = kAudioDevicePropertyNominalSampleRate; | |
568 pa.mScope = kAudioObjectPropertyScopeWildcard; | |
569 pa.mElement = kAudioObjectPropertyElementMaster; | |
570 result = AudioObjectGetPropertyData( | |
571 input_info_.id_, | |
572 &pa, | |
573 0, | |
574 0, | |
575 &property_size, | |
576 &rate); | |
577 | |
578 OSSTATUS_DCHECK(result == noErr, result); | |
579 if (result != noErr) | |
580 return result; | |
581 | |
582 asbd.mSampleRate = rate; | |
583 property_size = sizeof(asbd); | |
584 | |
585 // Set the new formats to the AUs... | |
586 result = AudioUnitSetProperty( | |
587 input_unit_, | |
588 kAudioUnitProperty_StreamFormat, | |
589 kAudioUnitScope_Output, | |
no longer working on chromium
2012/09/12 09:11:56
I don't understand this part, are we setting the f
Chris Rogers
2012/09/13 01:03:05
I still need to do a better job explaining this...
| |
590 1, | |
591 &asbd, | |
592 property_size); | |
593 | |
594 OSSTATUS_DCHECK(result == noErr, result); | |
595 if (result != noErr) | |
596 return result; | |
597 | |
598 result = AudioUnitSetProperty( | |
599 varispeed_unit_, | |
600 kAudioUnitProperty_StreamFormat, | |
601 kAudioUnitScope_Input, | |
602 0, | |
603 &asbd, | |
604 property_size); | |
605 | |
606 OSSTATUS_DCHECK(result == noErr, result); | |
607 if (result != noErr) | |
608 return result; | |
609 | |
610 // Set the correct sample rate for the output device, | |
611 // but keep the channel count the same. | |
612 property_size = sizeof(Float64); | |
scherkus (not reviewing)
2012/09/12 14:05:29
if possible sizeof(variable) instead of sizeof(typ
Chris Rogers
2012/09/13 01:03:05
Done.
| |
613 | |
614 pa.mSelector = kAudioDevicePropertyNominalSampleRate; | |
615 pa.mScope = kAudioObjectPropertyScopeWildcard; | |
616 pa.mElement = kAudioObjectPropertyElementMaster; | |
617 result = AudioObjectGetPropertyData( | |
618 output_info_.id_, | |
619 &pa, | |
620 0, | |
621 0, | |
622 &property_size, | |
623 &rate); | |
624 | |
625 OSSTATUS_DCHECK(result == noErr, result); | |
626 if (result != noErr) | |
627 return result; | |
628 | |
629 output_sample_rate_ = rate; | |
630 | |
631 asbd.mSampleRate = rate; | |
632 property_size = sizeof(asbd); | |
633 | |
634 // Set the new audio stream formats for the rest of the AUs... | |
635 result = AudioUnitSetProperty( | |
636 varispeed_unit_, | |
637 kAudioUnitProperty_StreamFormat, | |
638 kAudioUnitScope_Output, | |
639 0, | |
640 &asbd, | |
641 property_size); | |
642 | |
643 OSSTATUS_DCHECK(result == noErr, result); | |
644 if (result != noErr) | |
645 return result; | |
646 | |
647 result = AudioUnitSetProperty( | |
648 output_unit_, | |
649 kAudioUnitProperty_StreamFormat, | |
650 kAudioUnitScope_Input, | |
651 0, | |
652 &asbd, | |
653 property_size); | |
654 | |
655 OSSTATUS_DCHECK(result == noErr, result); | |
656 if (result != noErr) | |
657 return result; | |
658 | |
659 // Calculate number of buffers from channels. | |
660 UInt32 malloc_size = offsetof(AudioBufferList, mBuffers[0]) + | |
661 (sizeof(AudioBuffer) * asbd.mChannelsPerFrame); | |
scherkus (not reviewing)
2012/09/12 14:05:29
if possible sizeof(variable) instead of sizeof(typ
Chris Rogers
2012/09/13 01:03:05
Here I'm not sure that's easily possible
| |
662 | |
663 // malloc buffer lists | |
664 input_buffer_ = static_cast<AudioBufferList*>(malloc(malloc_size)); | |
665 input_buffer_->mNumberBuffers = asbd.mChannelsPerFrame; | |
666 | |
667 // pre-malloc buffers for AudioBufferLists | |
668 for (UInt32 i = 0; i < input_buffer_->mNumberBuffers; ++i) { | |
669 input_buffer_->mBuffers[i].mNumberChannels = 1; | |
670 input_buffer_->mBuffers[i].mDataByteSize = buffer_size_bytes; | |
671 input_buffer_->mBuffers[i].mData = malloc(buffer_size_bytes); | |
672 } | |
673 | |
674 return result; | |
675 } | |
676 | |
677 void AudioSynchronizedStream::ComputeThruOffset() { | |
678 // The initial latency will at least be the safety offsets | |
679 // of the devices + the buffer sizes. | |
680 in_to_out_sample_offset_ = SInt32(input_info_.buffer_size_frames_ + | |
681 output_info_.buffer_size_frames_); | |
682 } | |
683 | |
684 static int count = 0; | |
no longer working on chromium
2012/09/12 09:11:56
How this is used in production code?
Chris Rogers
2012/09/13 01:03:05
Removed!
| |
685 | |
686 OSStatus AudioSynchronizedStream::InputProc( | |
687 void* user_data, | |
688 AudioUnitRenderActionFlags* io_action_flags, | |
689 const AudioTimeStamp* time_stamp, | |
690 UInt32 bus_number, | |
691 UInt32 number_of_frames, | |
692 AudioBufferList* io_data) { | |
693 AudioSynchronizedStream* This = | |
scherkus (not reviewing)
2012/09/12 14:05:29
hrmmm... can you follow the pattern you've done in
Chris Rogers
2012/09/13 01:03:05
Yes, much better this way :)
Done
| |
694 static_cast<AudioSynchronizedStream*>(user_data); | |
695 if (This->first_input_time_ < 0.) | |
scherkus (not reviewing)
2012/09/12 14:05:29
0.0? 0.0f? or just 0?
Chris Rogers
2012/09/13 01:03:05
Done.
| |
696 This->first_input_time_ = time_stamp->mSampleTime; | |
697 | |
698 // Get the new audio input data. | |
699 OSStatus result = AudioUnitRender( | |
700 This->input_unit_, | |
701 io_action_flags, | |
702 time_stamp, | |
703 bus_number, | |
704 number_of_frames, | |
705 This->input_buffer_); | |
706 | |
707 OSSTATUS_DCHECK(result == noErr, result); | |
708 if (result != noErr) | |
709 return result; | |
710 | |
711 // Buffer input into FIFO. | |
712 if (This->is_fifo_initialized_) { | |
713 AudioBus bus(This->input_buffer_, number_of_frames); | |
714 // printf("pushing %d : n = %d\n", | |
no longer working on chromium
2012/09/12 09:11:56
remove
| |
715 // (int)number_of_frames, (int)This->fifo_.framesInFifo()); | |
716 ++count; | |
717 This->fifo_.Push(&bus); | |
718 } | |
719 | |
720 return result; | |
721 } | |
722 | |
723 OSStatus AudioSynchronizedStream::VarispeedProc( | |
724 void* user_data, | |
725 AudioUnitRenderActionFlags* io_action_flags, | |
726 const AudioTimeStamp* time_stamp, | |
727 UInt32 bus_number, | |
728 UInt32 number_of_frames, | |
729 AudioBufferList* io_data) { | |
730 AudioSynchronizedStream* This = | |
no longer working on chromium
2012/09/12 09:11:56
use stream as the name?
scherkus (not reviewing)
2012/09/12 14:05:29
ditto for This
Chris Rogers
2012/09/13 01:03:05
Done.
| |
731 static_cast<AudioSynchronizedStream*>(user_data); | |
732 | |
733 // Create a wrapper bus on the AudioBufferList. | |
734 AudioBus bus(io_data, number_of_frames); | |
735 | |
736 if (This->fifo_.frames() < number_of_frames) { | |
737 // We don't DCHECK here, since this is a possible run-time condition | |
738 // if the machine is bogged down. | |
739 bus.Zero(); | |
740 return noErr; | |
741 } | |
742 | |
743 // Read from the FIFO to feed the varispeed. | |
744 This->fifo_.Consume(&bus, 0, number_of_frames); | |
745 | |
746 // Calculate a varispeed rate scalar factor to compensate for drift between | |
747 // input and output. We use the actual number of frames still in the FIFO | |
748 // compared with the ideal value of kTargetDelayFrames. | |
749 size_t n = This->fifo_.frames(); | |
750 int delta = n - kTargetDelayFrames; | |
751 double sample_rate = This->output_sample_rate_; | |
752 double x = (sample_rate + delta) / sample_rate; | |
753 | |
754 // printf("n = %d : x = %f\n", (int)n, x); | |
scherkus (not reviewing)
2012/09/12 14:05:29
??
Chris Rogers
2012/09/13 01:03:05
Done.
| |
755 | |
756 This->fifo_rate_compensation_ = x; | |
757 | |
758 return noErr; | |
759 } | |
760 | |
761 OSStatus AudioSynchronizedStream::OutputProc( | |
762 void* user_data, | |
763 AudioUnitRenderActionFlags* io_action_flags, | |
764 const AudioTimeStamp* time_stamp, | |
765 UInt32 bus_number, | |
766 UInt32 number_of_frames, | |
767 AudioBufferList* io_data) { | |
768 AudioSynchronizedStream* This = | |
scherkus (not reviewing)
2012/09/12 14:05:29
ditto for This
Chris Rogers
2012/09/13 01:03:05
Done.
| |
769 static_cast<AudioSynchronizedStream*>(user_data); | |
770 | |
771 if (This->first_input_time_ < 0.) { | |
scherkus (not reviewing)
2012/09/12 14:05:29
0.0? 0.0f? or just 0?
Chris Rogers
2012/09/13 01:03:05
Done.
| |
772 // Input callback hasn't run yet -> silence. | |
773 MakeBufferSilent(io_data); | |
774 return noErr; | |
775 } | |
776 | |
777 --count; | |
778 printf("%d: number_of_frames = %d\n", (int)count, (int)number_of_frames); | |
779 | |
780 AudioTimeStamp input_ts; | |
781 OSStatus result = AudioDeviceGetCurrentTime( | |
782 This->input_info_.id_, &input_ts); | |
783 | |
784 if (result != noErr) { | |
785 MakeBufferSilent(io_data); | |
786 return noErr; | |
787 } | |
788 | |
789 AudioTimeStamp output_ts; | |
790 result = AudioDeviceGetCurrentTime(This->output_info_.id_, &output_ts); | |
791 | |
792 OSSTATUS_DCHECK(result == noErr, result); | |
793 if (result != noErr) | |
794 return result; | |
795 | |
796 // Use the varispeed playback rate to offset small discrepancies | |
797 // in hardware clocks, and also any differences in sample-rate | |
798 // between input and output devices. | |
799 | |
800 // Adjust for rate scalars of the input and output devices. | |
801 Float64 rate = input_ts.mRateScalar / output_ts.mRateScalar; | |
802 | |
803 // Adjust for FIFO drift. | |
804 rate *= This->fifo_rate_compensation_; | |
805 | |
806 result = AudioUnitSetParameter( | |
807 This->varispeed_unit_, | |
808 kVarispeedParam_PlaybackRate, | |
809 kAudioUnitScope_Global, | |
810 0, | |
811 rate, | |
812 0); | |
813 | |
814 OSSTATUS_DCHECK(result == noErr, result); | |
815 if (result != noErr) | |
816 return result; | |
817 | |
818 // Get the delta between the devices and add it to the offset. | |
819 if (This->first_output_time_ < 0.) { | |
scherkus (not reviewing)
2012/09/12 14:05:29
0.0? 0.0f? or just 0?
Chris Rogers
2012/09/13 01:03:05
Done.
| |
820 This->first_output_time_ = time_stamp->mSampleTime; | |
821 This->ComputeThruOffset(); | |
822 | |
823 // Buffer initial silence corresponding to I/O delay. | |
824 unsigned n = static_cast<unsigned>(This->in_to_out_sample_offset_); | |
825 AudioBus silence(kFIFOChannels, n); | |
826 This->fifo_.Push(&silence); | |
827 This->is_fifo_initialized_ = true; | |
828 | |
829 MakeBufferSilent(io_data); | |
830 return noErr; | |
831 } | |
832 | |
833 // Render to the output using the varispeed. | |
834 result = AudioUnitRender( | |
835 This->varispeed_unit_, | |
836 io_action_flags, | |
837 time_stamp, | |
838 0, | |
839 number_of_frames, | |
840 io_data); | |
841 | |
842 OSSTATUS_DCHECK(result == noErr, result); | |
843 | |
844 return noErr; | |
845 } | |
846 | |
847 void AudioSynchronizedStream::AudioDeviceInfo::Initialize( | |
848 AudioDeviceID id, bool is_input) { | |
849 id_ = id; | |
850 is_input_ = is_input; | |
851 if (id_ == kAudioDeviceUnknown) | |
852 return; | |
853 | |
854 UInt32 property_size = sizeof(UInt32); | |
scherkus (not reviewing)
2012/09/12 14:05:29
if possible sizeof(variable) instead of sizeof(typ
Chris Rogers
2012/09/13 01:03:05
Done.
| |
855 | |
856 AudioObjectPropertyAddress pa; | |
857 pa.mSelector = kAudioDevicePropertyBufferFrameSize; | |
858 pa.mScope = kAudioObjectPropertyScopeWildcard; | |
859 pa.mElement = kAudioObjectPropertyElementMaster; | |
860 OSStatus result = AudioObjectGetPropertyData( | |
861 id_, | |
862 &pa, | |
863 0, | |
864 0, | |
865 &property_size, | |
866 &buffer_size_frames_); | |
867 | |
868 OSSTATUS_DCHECK(result == noErr, result); | |
869 } | |
870 | |
871 } // namespace media | |
OLD | NEW |