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 "media/audio/mac/audio_synchronized_mac.h" | |
6 | |
7 #include <CoreServices/CoreServices.h> | |
8 #include <algorithm> | |
9 | |
10 #include "base/basictypes.h" | |
11 #include "base/debug/trace_event.h" | |
12 #include "base/logging.h" | |
13 #include "base/mac/mac_logging.h" | |
14 #include "media/audio/mac/audio_manager_mac.h" | |
15 #include "media/base/channel_mixer.h" | |
16 | |
17 namespace media { | |
18 | |
19 static const int kHardwareBufferSize = 128; | |
20 static const int kFifoSize = 16384; | |
21 | |
22 // TODO(crogers): handle the non-stereo case. | |
23 static const int kChannels = 2; | |
24 | |
25 // This value was determined empirically for minimum latency while still | |
26 // guarding against FIFO under-runs. | |
27 static const int kBaseTargetFifoFrames = 256 + 64; | |
28 | |
29 // If the input and output sample-rate don't match, then we need to maintain | |
30 // an additional safety margin due to the callback timing jitter and the | |
31 // varispeed buffering. This value was empirically tuned. | |
32 static const int kAdditionalTargetFifoFrames = 128; | |
33 | |
34 static void ZeroBufferList(AudioBufferList* buffer_list) { | |
35 for (size_t i = 0; i < buffer_list->mNumberBuffers; ++i) | |
36 memset(buffer_list->mBuffers[i].mData, | |
37 0, | |
38 buffer_list->mBuffers[i].mDataByteSize); | |
39 } | |
40 | |
41 static void WrapBufferList(AudioBufferList* buffer_list, | |
42 AudioBus* bus, | |
43 int frames) { | |
44 DCHECK(buffer_list); | |
45 DCHECK(bus); | |
46 int channels = bus->channels(); | |
47 int buffer_list_channels = buffer_list->mNumberBuffers; | |
48 | |
49 // Copy pointers from AudioBufferList. | |
50 int source_idx = 0; | |
51 for (int i = 0; i < channels; ++i) { | |
52 bus->SetChannelData( | |
53 i, static_cast<float*>(buffer_list->mBuffers[source_idx].mData)); | |
54 | |
55 // It's ok to pass in a |buffer_list| with fewer channels, in which | |
56 // case we just duplicate the last channel. | |
57 if (source_idx < buffer_list_channels - 1) | |
58 ++source_idx; | |
59 } | |
60 | |
61 // Finally set the actual length. | |
62 bus->set_frames(frames); | |
63 } | |
64 | |
65 AudioSynchronizedStream::AudioSynchronizedStream( | |
66 AudioManagerMac* manager, | |
67 const AudioParameters& params, | |
68 AudioDeviceID input_id, | |
69 AudioDeviceID output_id) | |
70 : manager_(manager), | |
71 params_(params), | |
72 input_sample_rate_(0), | |
73 output_sample_rate_(0), | |
74 input_id_(input_id), | |
75 output_id_(output_id), | |
76 input_buffer_list_(NULL), | |
77 fifo_(kChannels, kFifoSize), | |
78 target_fifo_frames_(kBaseTargetFifoFrames), | |
79 average_delta_(0.0), | |
80 fifo_rate_compensation_(1.0), | |
81 input_unit_(0), | |
82 varispeed_unit_(0), | |
83 output_unit_(0), | |
84 first_input_time_(-1), | |
85 is_running_(false), | |
86 hardware_buffer_size_(kHardwareBufferSize), | |
87 channels_(kChannels) { | |
88 VLOG(1) << "AudioSynchronizedStream::AudioSynchronizedStream()"; | |
89 } | |
90 | |
91 AudioSynchronizedStream::~AudioSynchronizedStream() { | |
92 DCHECK(!input_unit_); | |
93 DCHECK(!output_unit_); | |
94 DCHECK(!varispeed_unit_); | |
95 } | |
96 | |
97 bool AudioSynchronizedStream::Open() { | |
98 if (params_.channels() != kChannels) { | |
99 LOG(ERROR) << "Only stereo output is currently supported."; | |
100 return false; | |
101 } | |
102 | |
103 // Create the input, output, and varispeed AudioUnits. | |
104 OSStatus result = CreateAudioUnits(); | |
105 if (result != noErr) { | |
106 LOG(ERROR) << "Cannot create AudioUnits."; | |
107 return false; | |
108 } | |
109 | |
110 result = SetupInput(input_id_); | |
111 if (result != noErr) { | |
112 LOG(ERROR) << "Error configuring input AudioUnit."; | |
113 return false; | |
114 } | |
115 | |
116 result = SetupOutput(output_id_); | |
117 if (result != noErr) { | |
118 LOG(ERROR) << "Error configuring output AudioUnit."; | |
119 return false; | |
120 } | |
121 | |
122 result = SetupCallbacks(); | |
123 if (result != noErr) { | |
124 LOG(ERROR) << "Error setting up callbacks on AudioUnits."; | |
125 return false; | |
126 } | |
127 | |
128 result = SetupStreamFormats(); | |
129 if (result != noErr) { | |
130 LOG(ERROR) << "Error configuring stream formats on AudioUnits."; | |
131 return false; | |
132 } | |
133 | |
134 AllocateInputData(); | |
135 | |
136 // Final initialization of the AudioUnits. | |
137 result = AudioUnitInitialize(input_unit_); | |
138 if (result != noErr) { | |
139 LOG(ERROR) << "Error initializing input AudioUnit."; | |
140 return false; | |
141 } | |
142 | |
143 result = AudioUnitInitialize(output_unit_); | |
144 if (result != noErr) { | |
145 LOG(ERROR) << "Error initializing output AudioUnit."; | |
146 return false; | |
147 } | |
148 | |
149 result = AudioUnitInitialize(varispeed_unit_); | |
150 if (result != noErr) { | |
151 LOG(ERROR) << "Error initializing varispeed AudioUnit."; | |
152 return false; | |
153 } | |
154 | |
155 if (input_sample_rate_ != output_sample_rate_) { | |
156 // Add extra safety margin. | |
157 target_fifo_frames_ += kAdditionalTargetFifoFrames; | |
158 } | |
159 | |
160 // Buffer initial silence corresponding to target I/O buffering. | |
161 fifo_.Clear(); | |
162 scoped_ptr<AudioBus> silence = | |
163 AudioBus::Create(channels_, target_fifo_frames_); | |
164 silence->Zero(); | |
165 fifo_.Push(silence.get()); | |
166 | |
167 return true; | |
168 } | |
169 | |
170 void AudioSynchronizedStream::Close() { | |
171 DCHECK(!is_running_); | |
172 | |
173 if (input_buffer_list_) { | |
174 free(input_buffer_list_); | |
175 input_buffer_list_ = 0; | |
176 input_bus_.reset(NULL); | |
177 wrapper_bus_.reset(NULL); | |
178 } | |
179 | |
180 if (input_unit_) { | |
181 AudioUnitUninitialize(input_unit_); | |
182 CloseComponent(input_unit_); | |
183 } | |
184 | |
185 if (output_unit_) { | |
186 AudioUnitUninitialize(output_unit_); | |
187 CloseComponent(output_unit_); | |
188 } | |
189 | |
190 if (varispeed_unit_) { | |
191 AudioUnitUninitialize(varispeed_unit_); | |
192 CloseComponent(varispeed_unit_); | |
193 } | |
194 | |
195 input_unit_ = NULL; | |
196 output_unit_ = NULL; | |
197 varispeed_unit_ = NULL; | |
198 | |
199 // Inform the audio manager that we have been closed. This can cause our | |
200 // destruction. | |
201 manager_->ReleaseOutputStream(this); | |
202 } | |
203 | |
204 void AudioSynchronizedStream::Start(AudioSourceCallback* callback) { | |
205 DCHECK(callback); | |
206 DCHECK(input_unit_); | |
207 DCHECK(output_unit_); | |
208 DCHECK(varispeed_unit_); | |
209 | |
210 if (is_running_ || !input_unit_ || !output_unit_ || !varispeed_unit_) | |
211 return; | |
212 | |
213 source_ = callback; | |
214 | |
215 // Reset state variables each time we Start(). | |
216 fifo_rate_compensation_ = 1.0; | |
217 average_delta_ = 0.0; | |
218 | |
219 OSStatus result = noErr; | |
220 | |
221 if (!is_running_) { | |
222 first_input_time_ = -1; | |
223 | |
224 result = AudioOutputUnitStart(input_unit_); | |
225 OSSTATUS_DCHECK(result == noErr, result); | |
226 | |
227 if (result == noErr) { | |
228 result = AudioOutputUnitStart(output_unit_); | |
229 OSSTATUS_DCHECK(result == noErr, result); | |
230 } | |
231 } | |
232 | |
233 is_running_ = true; | |
234 } | |
235 | |
236 void AudioSynchronizedStream::Stop() { | |
237 OSStatus result = noErr; | |
238 if (is_running_) { | |
239 result = AudioOutputUnitStop(input_unit_); | |
240 OSSTATUS_DCHECK(result == noErr, result); | |
241 | |
242 if (result == noErr) { | |
243 result = AudioOutputUnitStop(output_unit_); | |
244 OSSTATUS_DCHECK(result == noErr, result); | |
245 } | |
246 } | |
247 | |
248 if (result == noErr) | |
249 is_running_ = false; | |
250 } | |
251 | |
252 bool AudioSynchronizedStream::IsRunning() { | |
253 return is_running_; | |
254 } | |
255 | |
256 // TODO(crogers): implement - or remove from AudioOutputStream. | |
257 void AudioSynchronizedStream::SetVolume(double volume) {} | |
258 void AudioSynchronizedStream::GetVolume(double* volume) {} | |
259 | |
260 OSStatus AudioSynchronizedStream::SetOutputDeviceAsCurrent( | |
261 AudioDeviceID output_id) { | |
262 OSStatus result = noErr; | |
263 | |
264 // Get the default output device if device is unknown. | |
265 if (output_id == kAudioDeviceUnknown) { | |
266 AudioObjectPropertyAddress pa; | |
267 pa.mSelector = kAudioHardwarePropertyDefaultOutputDevice; | |
268 pa.mScope = kAudioObjectPropertyScopeGlobal; | |
269 pa.mElement = kAudioObjectPropertyElementMaster; | |
270 UInt32 size = sizeof(output_id); | |
271 | |
272 result = AudioObjectGetPropertyData( | |
273 kAudioObjectSystemObject, | |
274 &pa, | |
275 0, | |
276 0, | |
277 &size, | |
278 &output_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_buffer_size_; | |
287 AudioObjectPropertyAddress pa; | |
288 pa.mSelector = kAudioDevicePropertyBufferFrameSize; | |
289 pa.mScope = kAudioDevicePropertyScopeInput; | |
290 pa.mElement = kAudioObjectPropertyElementMaster; | |
291 result = AudioObjectSetPropertyData( | |
292 output_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 output_info_.Initialize(output_id, false); | |
304 | |
305 // Set the Current Device to the Default Output Unit. | |
306 result = AudioUnitSetProperty( | |
307 output_unit_, | |
308 kAudioOutputUnitProperty_CurrentDevice, | |
309 kAudioUnitScope_Global, | |
310 0, | |
311 &output_info_.id_, | |
312 sizeof(output_info_.id_)); | |
313 | |
314 OSSTATUS_DCHECK(result == noErr, result); | |
315 return result; | |
316 } | |
317 | |
318 OSStatus AudioSynchronizedStream::SetInputDeviceAsCurrent( | |
319 AudioDeviceID input_id) { | |
320 OSStatus result = noErr; | |
321 | |
322 // Get the default input device if device is unknown. | |
323 if (input_id == kAudioDeviceUnknown) { | |
324 AudioObjectPropertyAddress pa; | |
325 pa.mSelector = kAudioHardwarePropertyDefaultInputDevice; | |
326 pa.mScope = kAudioObjectPropertyScopeGlobal; | |
327 pa.mElement = kAudioObjectPropertyElementMaster; | |
328 UInt32 size = sizeof(input_id); | |
329 | |
330 result = AudioObjectGetPropertyData( | |
331 kAudioObjectSystemObject, | |
332 &pa, | |
333 0, | |
334 0, | |
335 &size, | |
336 &input_id); | |
337 | |
338 OSSTATUS_DCHECK(result == noErr, result); | |
339 if (result != noErr) | |
340 return result; | |
341 } | |
342 | |
343 // Set the render frame size. | |
344 UInt32 frame_size = hardware_buffer_size_; | |
345 AudioObjectPropertyAddress pa; | |
346 pa.mSelector = kAudioDevicePropertyBufferFrameSize; | |
347 pa.mScope = kAudioDevicePropertyScopeInput; | |
348 pa.mElement = kAudioObjectPropertyElementMaster; | |
349 result = AudioObjectSetPropertyData( | |
350 input_id, | |
351 &pa, | |
352 0, | |
353 0, | |
354 sizeof(frame_size), | |
355 &frame_size); | |
356 | |
357 OSSTATUS_DCHECK(result == noErr, result); | |
358 if (result != noErr) | |
359 return result; | |
360 | |
361 input_info_.Initialize(input_id, true); | |
362 | |
363 // Set the Current Device to the AUHAL. | |
364 // This should be done only after I/O has been enabled on the AUHAL. | |
365 result = AudioUnitSetProperty( | |
366 input_unit_, | |
367 kAudioOutputUnitProperty_CurrentDevice, | |
368 kAudioUnitScope_Global, | |
369 0, | |
370 &input_info_.id_, | |
371 sizeof(input_info_.id_)); | |
372 | |
373 OSSTATUS_DCHECK(result == noErr, result); | |
374 return result; | |
375 } | |
376 | |
377 OSStatus AudioSynchronizedStream::CreateAudioUnits() { | |
378 // Q: Why do we need a varispeed unit? | |
379 // A: If the input device and the output device are running at | |
380 // different sample rates and/or on different clocks, we will need | |
381 // to compensate to avoid a pitch change and | |
382 // to avoid buffer under and over runs. | |
383 ComponentDescription varispeed_desc; | |
384 varispeed_desc.componentType = kAudioUnitType_FormatConverter; | |
385 varispeed_desc.componentSubType = kAudioUnitSubType_Varispeed; | |
386 varispeed_desc.componentManufacturer = kAudioUnitManufacturer_Apple; | |
387 varispeed_desc.componentFlags = 0; | |
388 varispeed_desc.componentFlagsMask = 0; | |
389 | |
390 Component varispeed_comp = FindNextComponent(NULL, &varispeed_desc); | |
391 if (varispeed_comp == NULL) | |
392 return -1; | |
393 | |
394 OSStatus result = OpenAComponent(varispeed_comp, &varispeed_unit_); | |
395 OSSTATUS_DCHECK(result == noErr, result); | |
396 if (result != noErr) | |
397 return result; | |
398 | |
399 // Open input AudioUnit. | |
400 ComponentDescription input_desc; | |
401 input_desc.componentType = kAudioUnitType_Output; | |
402 input_desc.componentSubType = kAudioUnitSubType_HALOutput; | |
403 input_desc.componentManufacturer = kAudioUnitManufacturer_Apple; | |
404 input_desc.componentFlags = 0; | |
405 input_desc.componentFlagsMask = 0; | |
406 | |
407 Component input_comp = FindNextComponent(NULL, &input_desc); | |
408 if (input_comp == NULL) | |
409 return -1; | |
410 | |
411 result = OpenAComponent(input_comp, &input_unit_); | |
412 OSSTATUS_DCHECK(result == noErr, result); | |
413 if (result != noErr) | |
414 return result; | |
415 | |
416 // Open output AudioUnit. | |
417 ComponentDescription output_desc; | |
418 output_desc.componentType = kAudioUnitType_Output; | |
419 output_desc.componentSubType = kAudioUnitSubType_DefaultOutput; | |
420 output_desc.componentManufacturer = kAudioUnitManufacturer_Apple; | |
421 output_desc.componentFlags = 0; | |
422 output_desc.componentFlagsMask = 0; | |
423 | |
424 Component output_comp = FindNextComponent(NULL, &output_desc); | |
425 if (output_comp == NULL) | |
426 return -1; | |
427 | |
428 result = OpenAComponent(output_comp, &output_unit_); | |
429 OSSTATUS_DCHECK(result == noErr, result); | |
430 if (result != noErr) | |
431 return result; | |
432 | |
433 return noErr; | |
434 } | |
435 | |
436 OSStatus AudioSynchronizedStream::SetupInput(AudioDeviceID input_id) { | |
437 // The AUHAL used for input needs to be initialized | |
438 // before anything is done to it. | |
439 OSStatus result = AudioUnitInitialize(input_unit_); | |
440 OSSTATUS_DCHECK(result == noErr, result); | |
441 if (result != noErr) | |
442 return result; | |
443 | |
444 // We must enable the Audio Unit (AUHAL) for input and disable output | |
445 // BEFORE setting the AUHAL's current device. | |
446 result = EnableIO(); | |
447 OSSTATUS_DCHECK(result == noErr, result); | |
448 if (result != noErr) | |
449 return result; | |
450 | |
451 result = SetInputDeviceAsCurrent(input_id); | |
452 OSSTATUS_DCHECK(result == noErr, result); | |
453 | |
454 return result; | |
455 } | |
456 | |
457 OSStatus AudioSynchronizedStream::EnableIO() { | |
458 // Enable input on the AUHAL. | |
459 UInt32 enable_io = 1; | |
460 OSStatus result = AudioUnitSetProperty( | |
461 input_unit_, | |
462 kAudioOutputUnitProperty_EnableIO, | |
463 kAudioUnitScope_Input, | |
464 1, // input element | |
465 &enable_io, | |
466 sizeof(enable_io)); | |
467 | |
468 OSSTATUS_DCHECK(result == noErr, result); | |
469 if (result != noErr) | |
470 return result; | |
471 | |
472 // Disable Output on the AUHAL. | |
473 enable_io = 0; | |
474 result = AudioUnitSetProperty( | |
475 input_unit_, | |
476 kAudioOutputUnitProperty_EnableIO, | |
477 kAudioUnitScope_Output, | |
478 0, // output element | |
479 &enable_io, | |
480 sizeof(enable_io)); | |
481 | |
482 OSSTATUS_DCHECK(result == noErr, result); | |
483 return result; | |
484 } | |
485 | |
486 OSStatus AudioSynchronizedStream::SetupOutput(AudioDeviceID output_id) { | |
487 OSStatus result = noErr; | |
488 | |
489 result = SetOutputDeviceAsCurrent(output_id); | |
490 OSSTATUS_DCHECK(result == noErr, result); | |
491 if (result != noErr) | |
492 return result; | |
493 | |
494 // Tell the output unit not to reset timestamps. | |
495 // Otherwise sample rate changes will cause sync loss. | |
496 UInt32 start_at_zero = 0; | |
497 result = AudioUnitSetProperty( | |
498 output_unit_, | |
499 kAudioOutputUnitProperty_StartTimestampsAtZero, | |
500 kAudioUnitScope_Global, | |
501 0, | |
502 &start_at_zero, | |
503 sizeof(start_at_zero)); | |
504 | |
505 OSSTATUS_DCHECK(result == noErr, result); | |
506 | |
507 return result; | |
508 } | |
509 | |
510 OSStatus AudioSynchronizedStream::SetupCallbacks() { | |
511 // Set the input callback. | |
512 AURenderCallbackStruct callback; | |
513 callback.inputProc = InputProc; | |
514 callback.inputProcRefCon = this; | |
515 OSStatus result = AudioUnitSetProperty( | |
516 input_unit_, | |
517 kAudioOutputUnitProperty_SetInputCallback, | |
518 kAudioUnitScope_Global, | |
519 0, | |
520 &callback, | |
521 sizeof(callback)); | |
522 | |
523 OSSTATUS_DCHECK(result == noErr, result); | |
524 if (result != noErr) | |
525 return result; | |
526 | |
527 // Set the output callback. | |
528 callback.inputProc = OutputProc; | |
529 callback.inputProcRefCon = this; | |
530 result = AudioUnitSetProperty( | |
531 output_unit_, | |
532 kAudioUnitProperty_SetRenderCallback, | |
533 kAudioUnitScope_Input, | |
534 0, | |
535 &callback, | |
536 sizeof(callback)); | |
537 | |
538 OSSTATUS_DCHECK(result == noErr, result); | |
539 if (result != noErr) | |
540 return result; | |
541 | |
542 // Set the varispeed callback. | |
543 callback.inputProc = VarispeedProc; | |
544 callback.inputProcRefCon = this; | |
545 result = AudioUnitSetProperty( | |
546 varispeed_unit_, | |
547 kAudioUnitProperty_SetRenderCallback, | |
548 kAudioUnitScope_Input, | |
549 0, | |
550 &callback, | |
551 sizeof(callback)); | |
552 | |
553 OSSTATUS_DCHECK(result == noErr, result); | |
554 | |
555 return result; | |
556 } | |
557 | |
558 OSStatus AudioSynchronizedStream::SetupStreamFormats() { | |
559 AudioStreamBasicDescription asbd, asbd_dev1_in, asbd_dev2_out; | |
560 | |
561 // Get the Stream Format (Output client side). | |
562 UInt32 property_size = sizeof(asbd_dev1_in); | |
563 OSStatus result = AudioUnitGetProperty( | |
564 input_unit_, | |
565 kAudioUnitProperty_StreamFormat, | |
566 kAudioUnitScope_Input, | |
567 1, | |
568 &asbd_dev1_in, | |
569 &property_size); | |
570 | |
571 OSSTATUS_DCHECK(result == noErr, result); | |
572 if (result != noErr) | |
573 return result; | |
574 | |
575 // Get the Stream Format (client side). | |
576 property_size = sizeof(asbd); | |
577 result = AudioUnitGetProperty( | |
578 input_unit_, | |
579 kAudioUnitProperty_StreamFormat, | |
580 kAudioUnitScope_Output, | |
581 1, | |
582 &asbd, | |
583 &property_size); | |
584 | |
585 OSSTATUS_DCHECK(result == noErr, result); | |
586 if (result != noErr) | |
587 return result; | |
588 | |
589 // Get the Stream Format (Output client side). | |
590 property_size = sizeof(asbd_dev2_out); | |
591 result = AudioUnitGetProperty( | |
592 output_unit_, | |
593 kAudioUnitProperty_StreamFormat, | |
594 kAudioUnitScope_Output, | |
595 0, | |
596 &asbd_dev2_out, | |
597 &property_size); | |
598 | |
599 OSSTATUS_DCHECK(result == noErr, result); | |
600 if (result != noErr) | |
601 return result; | |
602 | |
603 // Set the format of all the AUs to the input/output devices channel count. | |
604 // For a simple case, you want to set this to | |
605 // the lower of count of the channels in the input device vs output device. | |
606 asbd.mChannelsPerFrame = std::min(asbd_dev1_in.mChannelsPerFrame, | |
607 asbd_dev2_out.mChannelsPerFrame); | |
608 | |
609 // We must get the sample rate of the input device and set it to the | |
610 // stream format of AUHAL. | |
611 Float64 rate = 0; | |
612 property_size = sizeof(rate); | |
613 | |
614 AudioObjectPropertyAddress pa; | |
615 pa.mSelector = kAudioDevicePropertyNominalSampleRate; | |
616 pa.mScope = kAudioObjectPropertyScopeWildcard; | |
617 pa.mElement = kAudioObjectPropertyElementMaster; | |
618 result = AudioObjectGetPropertyData( | |
619 input_info_.id_, | |
620 &pa, | |
621 0, | |
622 0, | |
623 &property_size, | |
624 &rate); | |
625 | |
626 OSSTATUS_DCHECK(result == noErr, result); | |
627 if (result != noErr) | |
628 return result; | |
629 | |
630 input_sample_rate_ = rate; | |
631 | |
632 asbd.mSampleRate = rate; | |
633 property_size = sizeof(asbd); | |
634 | |
635 // Set the new formats to the AUs... | |
636 result = AudioUnitSetProperty( | |
637 input_unit_, | |
638 kAudioUnitProperty_StreamFormat, | |
639 kAudioUnitScope_Output, | |
640 1, | |
641 &asbd, | |
642 property_size); | |
643 | |
644 OSSTATUS_DCHECK(result == noErr, result); | |
645 if (result != noErr) | |
646 return result; | |
647 | |
648 result = AudioUnitSetProperty( | |
649 varispeed_unit_, | |
650 kAudioUnitProperty_StreamFormat, | |
651 kAudioUnitScope_Input, | |
652 0, | |
653 &asbd, | |
654 property_size); | |
655 | |
656 OSSTATUS_DCHECK(result == noErr, result); | |
657 if (result != noErr) | |
658 return result; | |
659 | |
660 // Set the correct sample rate for the output device, | |
661 // but keep the channel count the same. | |
662 property_size = sizeof(rate); | |
663 | |
664 pa.mSelector = kAudioDevicePropertyNominalSampleRate; | |
665 pa.mScope = kAudioObjectPropertyScopeWildcard; | |
666 pa.mElement = kAudioObjectPropertyElementMaster; | |
667 result = AudioObjectGetPropertyData( | |
668 output_info_.id_, | |
669 &pa, | |
670 0, | |
671 0, | |
672 &property_size, | |
673 &rate); | |
674 | |
675 OSSTATUS_DCHECK(result == noErr, result); | |
676 if (result != noErr) | |
677 return result; | |
678 | |
679 output_sample_rate_ = rate; | |
680 | |
681 // The requested sample-rate must match the hardware sample-rate. | |
682 if (output_sample_rate_ != params_.sample_rate()) { | |
683 LOG(ERROR) << "Requested sample-rate: " << params_.sample_rate() | |
684 << " must match the hardware sample-rate: " << output_sample_rate_; | |
685 return kAudioDeviceUnsupportedFormatError; | |
686 } | |
687 | |
688 asbd.mSampleRate = rate; | |
689 property_size = sizeof(asbd); | |
690 | |
691 // Set the new audio stream formats for the rest of the AUs... | |
692 result = AudioUnitSetProperty( | |
693 varispeed_unit_, | |
694 kAudioUnitProperty_StreamFormat, | |
695 kAudioUnitScope_Output, | |
696 0, | |
697 &asbd, | |
698 property_size); | |
699 | |
700 OSSTATUS_DCHECK(result == noErr, result); | |
701 if (result != noErr) | |
702 return result; | |
703 | |
704 result = AudioUnitSetProperty( | |
705 output_unit_, | |
706 kAudioUnitProperty_StreamFormat, | |
707 kAudioUnitScope_Input, | |
708 0, | |
709 &asbd, | |
710 property_size); | |
711 | |
712 OSSTATUS_DCHECK(result == noErr, result); | |
713 return result; | |
714 } | |
715 | |
716 void AudioSynchronizedStream::AllocateInputData() { | |
717 // Get the native number of input channels that the hardware supports. | |
718 int hardware_channels = 0; | |
719 bool got_hardware_channels = AudioManagerMac::GetDeviceChannels( | |
720 input_id_, kAudioDevicePropertyScopeInput, &hardware_channels); | |
721 if (!got_hardware_channels || hardware_channels > 2) { | |
722 // Only mono and stereo are supported on the input side. When it fails to | |
723 // get the native channel number or the native channel number is bigger | |
724 // than 2, we open the device in stereo mode. | |
725 hardware_channels = 2; | |
726 } | |
727 | |
728 // Allocate storage for the AudioBufferList used for the | |
729 // input data from the input AudioUnit. | |
730 // We allocate enough space for with one AudioBuffer per channel. | |
731 size_t malloc_size = offsetof(AudioBufferList, mBuffers[0]) + | |
732 (sizeof(AudioBuffer) * hardware_channels); | |
733 | |
734 input_buffer_list_ = static_cast<AudioBufferList*>(malloc(malloc_size)); | |
735 input_buffer_list_->mNumberBuffers = hardware_channels; | |
736 | |
737 input_bus_ = AudioBus::Create(hardware_channels, hardware_buffer_size_); | |
738 wrapper_bus_ = AudioBus::CreateWrapper(channels_); | |
739 if (hardware_channels != params_.input_channels()) { | |
740 ChannelLayout hardware_channel_layout = | |
741 GuessChannelLayout(hardware_channels); | |
742 ChannelLayout requested_channel_layout = | |
743 GuessChannelLayout(params_.input_channels()); | |
744 channel_mixer_.reset(new ChannelMixer(hardware_channel_layout, | |
745 requested_channel_layout)); | |
746 mixer_bus_ = AudioBus::Create(params_.input_channels(), | |
747 hardware_buffer_size_); | |
748 } | |
749 | |
750 // Allocate buffers for AudioBufferList. | |
751 UInt32 buffer_size_bytes = input_bus_->frames() * sizeof(Float32); | |
752 for (size_t i = 0; i < input_buffer_list_->mNumberBuffers; ++i) { | |
753 input_buffer_list_->mBuffers[i].mNumberChannels = 1; | |
754 input_buffer_list_->mBuffers[i].mDataByteSize = buffer_size_bytes; | |
755 input_buffer_list_->mBuffers[i].mData = input_bus_->channel(i); | |
756 } | |
757 } | |
758 | |
759 OSStatus AudioSynchronizedStream::HandleInputCallback( | |
760 AudioUnitRenderActionFlags* io_action_flags, | |
761 const AudioTimeStamp* time_stamp, | |
762 UInt32 bus_number, | |
763 UInt32 number_of_frames, | |
764 AudioBufferList* io_data) { | |
765 TRACE_EVENT0("audio", "AudioSynchronizedStream::HandleInputCallback"); | |
766 | |
767 if (first_input_time_ < 0.0) | |
768 first_input_time_ = time_stamp->mSampleTime; | |
769 | |
770 // Get the new audio input data. | |
771 OSStatus result = AudioUnitRender( | |
772 input_unit_, | |
773 io_action_flags, | |
774 time_stamp, | |
775 bus_number, | |
776 number_of_frames, | |
777 input_buffer_list_); | |
778 | |
779 // TODO(xians): Add back the DCHECK after synchronize IO supports all | |
780 // combination of input and output params. See http://issue/246521. | |
781 if (result != noErr) | |
782 return result; | |
783 | |
784 // Buffer input into FIFO. | |
785 int available_frames = fifo_.max_frames() - fifo_.frames(); | |
786 if (input_bus_->frames() <= available_frames) { | |
787 if (channel_mixer_) { | |
788 channel_mixer_->Transform(input_bus_.get(), mixer_bus_.get()); | |
789 fifo_.Push(mixer_bus_.get()); | |
790 } else { | |
791 fifo_.Push(input_bus_.get()); | |
792 } | |
793 } | |
794 | |
795 return result; | |
796 } | |
797 | |
798 OSStatus AudioSynchronizedStream::HandleVarispeedCallback( | |
799 AudioUnitRenderActionFlags* io_action_flags, | |
800 const AudioTimeStamp* time_stamp, | |
801 UInt32 bus_number, | |
802 UInt32 number_of_frames, | |
803 AudioBufferList* io_data) { | |
804 // Create a wrapper bus on the AudioBufferList. | |
805 WrapBufferList(io_data, wrapper_bus_.get(), number_of_frames); | |
806 | |
807 if (fifo_.frames() < static_cast<int>(number_of_frames)) { | |
808 // We don't DCHECK here, since this is a possible run-time condition | |
809 // if the machine is bogged down. | |
810 wrapper_bus_->Zero(); | |
811 return noErr; | |
812 } | |
813 | |
814 // Read from the FIFO to feed the varispeed. | |
815 fifo_.Consume(wrapper_bus_.get(), 0, number_of_frames); | |
816 | |
817 return noErr; | |
818 } | |
819 | |
820 OSStatus AudioSynchronizedStream::HandleOutputCallback( | |
821 AudioUnitRenderActionFlags* io_action_flags, | |
822 const AudioTimeStamp* time_stamp, | |
823 UInt32 bus_number, | |
824 UInt32 number_of_frames, | |
825 AudioBufferList* io_data) { | |
826 // Input callback hasn't run yet or we've suddenly changed sample-rates | |
827 // -> silence. | |
828 if (first_input_time_ < 0.0 || | |
829 static_cast<int>(number_of_frames) != params_.frames_per_buffer()) { | |
830 ZeroBufferList(io_data); | |
831 return noErr; | |
832 } | |
833 | |
834 // Use the varispeed playback rate to offset small discrepancies | |
835 // in hardware clocks, and also any differences in sample-rate | |
836 // between input and output devices. | |
837 | |
838 // Calculate a varispeed rate scalar factor to compensate for drift between | |
839 // input and output. We use the actual number of frames still in the FIFO | |
840 // compared with the ideal value of |target_fifo_frames_|. | |
841 int delta = fifo_.frames() - target_fifo_frames_; | |
842 | |
843 // Average |delta| because it can jitter back/forth quite frequently | |
844 // by +/- the hardware buffer-size *if* the input and output callbacks are | |
845 // happening at almost exactly the same time. Also, if the input and output | |
846 // sample-rates are different then |delta| will jitter quite a bit due to | |
847 // the rate conversion happening in the varispeed, plus the jittering of | |
848 // the callbacks. The average value is what's important here. | |
849 average_delta_ += (delta - average_delta_) * 0.1; | |
850 | |
851 // Compute a rate compensation which always attracts us back to the | |
852 // |target_fifo_frames_| over a period of kCorrectionTimeSeconds. | |
853 const double kCorrectionTimeSeconds = 0.1; | |
854 double correction_time_frames = kCorrectionTimeSeconds * output_sample_rate_; | |
855 fifo_rate_compensation_ = | |
856 (correction_time_frames + average_delta_) / correction_time_frames; | |
857 | |
858 // Adjust for FIFO drift. | |
859 OSStatus result = AudioUnitSetParameter( | |
860 varispeed_unit_, | |
861 kVarispeedParam_PlaybackRate, | |
862 kAudioUnitScope_Global, | |
863 0, | |
864 fifo_rate_compensation_, | |
865 0); | |
866 | |
867 OSSTATUS_DCHECK(result == noErr, result); | |
868 if (result != noErr) | |
869 return result; | |
870 | |
871 // Render to the output using the varispeed. | |
872 result = AudioUnitRender( | |
873 varispeed_unit_, | |
874 io_action_flags, | |
875 time_stamp, | |
876 0, | |
877 number_of_frames, | |
878 io_data); | |
879 | |
880 OSSTATUS_DCHECK(result == noErr, result); | |
881 if (result != noErr) | |
882 return result; | |
883 | |
884 // Create a wrapper bus on the AudioBufferList. | |
885 WrapBufferList(io_data, wrapper_bus_.get(), number_of_frames); | |
886 | |
887 // Process in-place! | |
888 source_->OnMoreIOData(wrapper_bus_.get(), | |
889 wrapper_bus_.get(), | |
890 AudioBuffersState(0, 0)); | |
891 | |
892 return noErr; | |
893 } | |
894 | |
895 OSStatus AudioSynchronizedStream::InputProc( | |
896 void* user_data, | |
897 AudioUnitRenderActionFlags* io_action_flags, | |
898 const AudioTimeStamp* time_stamp, | |
899 UInt32 bus_number, | |
900 UInt32 number_of_frames, | |
901 AudioBufferList* io_data) { | |
902 AudioSynchronizedStream* stream = | |
903 static_cast<AudioSynchronizedStream*>(user_data); | |
904 DCHECK(stream); | |
905 | |
906 return stream->HandleInputCallback( | |
907 io_action_flags, | |
908 time_stamp, | |
909 bus_number, | |
910 number_of_frames, | |
911 io_data); | |
912 } | |
913 | |
914 OSStatus AudioSynchronizedStream::VarispeedProc( | |
915 void* user_data, | |
916 AudioUnitRenderActionFlags* io_action_flags, | |
917 const AudioTimeStamp* time_stamp, | |
918 UInt32 bus_number, | |
919 UInt32 number_of_frames, | |
920 AudioBufferList* io_data) { | |
921 AudioSynchronizedStream* stream = | |
922 static_cast<AudioSynchronizedStream*>(user_data); | |
923 DCHECK(stream); | |
924 | |
925 return stream->HandleVarispeedCallback( | |
926 io_action_flags, | |
927 time_stamp, | |
928 bus_number, | |
929 number_of_frames, | |
930 io_data); | |
931 } | |
932 | |
933 OSStatus AudioSynchronizedStream::OutputProc( | |
934 void* user_data, | |
935 AudioUnitRenderActionFlags* io_action_flags, | |
936 const AudioTimeStamp* time_stamp, | |
937 UInt32 bus_number, | |
938 UInt32 number_of_frames, | |
939 AudioBufferList* io_data) { | |
940 AudioSynchronizedStream* stream = | |
941 static_cast<AudioSynchronizedStream*>(user_data); | |
942 DCHECK(stream); | |
943 | |
944 return stream->HandleOutputCallback( | |
945 io_action_flags, | |
946 time_stamp, | |
947 bus_number, | |
948 number_of_frames, | |
949 io_data); | |
950 } | |
951 | |
952 void AudioSynchronizedStream::AudioDeviceInfo::Initialize( | |
953 AudioDeviceID id, bool is_input) { | |
954 id_ = id; | |
955 is_input_ = is_input; | |
956 if (id_ == kAudioDeviceUnknown) | |
957 return; | |
958 | |
959 UInt32 property_size = sizeof(buffer_size_frames_); | |
960 | |
961 AudioObjectPropertyAddress pa; | |
962 pa.mSelector = kAudioDevicePropertyBufferFrameSize; | |
963 pa.mScope = kAudioObjectPropertyScopeWildcard; | |
964 pa.mElement = kAudioObjectPropertyElementMaster; | |
965 OSStatus result = AudioObjectGetPropertyData( | |
966 id_, | |
967 &pa, | |
968 0, | |
969 0, | |
970 &property_size, | |
971 &buffer_size_frames_); | |
972 | |
973 OSSTATUS_DCHECK(result == noErr, result); | |
974 } | |
975 | |
976 } // namespace media | |
OLD | NEW |