Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(949)

Side by Side Diff: media/audio/mac/audio_synchronized_mac.cc

Issue 10909185: Add Mac OS X synchronized audio I/O back-end (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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/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
16 namespace media {
17
18 static const int kHardwareBufferSize = 128;
19 static const int kFifoSize = 16384;
20
21 // TODO(crogers): handle the non-stereo case.
22 static const int kChannels = 2;
23
24 // This value was determined empirically for minimum latency while still
25 // guarding against FIFO under-runs.
26 // TODO(crogers): refine this, taking into account different input/output
27 // sample-rate combinations.
28 static const int kTargetDelayFrames = 256;
29
30 static void ZeroBufferList(AudioBufferList* io_data) {
31 for (UInt32 i = 0; i < io_data->mNumberBuffers; ++i)
32 memset(io_data->mBuffers[i].mData, 0, io_data->mBuffers[i].mDataByteSize);
33 }
34
35 AudioSynchronizedStream::AudioSynchronizedStream(
36 AudioManagerMac* manager,
37 const AudioParameters&,
38 AudioDeviceID input_id,
39 AudioDeviceID output_id)
40 : manager_(manager),
41 input_id_(input_id),
42 output_id_(output_id),
43 input_data_(NULL),
44 fifo_(kChannels, kFifoSize),
45 is_fifo_initialized_(false),
46 fifo_rate_compensation_(1.0),
47 output_sample_rate_(0),
48 input_unit_(0),
49 varispeed_unit_(0),
50 output_unit_(0),
51 first_input_time_(-1),
52 first_output_time_(-1),
53 in_to_out_sample_offset_(0),
54 is_running_(false),
55 hardware_buffer_size_(kHardwareBufferSize),
56 channels_(kChannels) {
57 // TODO(crogers): actually do something with |params|.
58 // We at least need to verify the sample-rate matches the hardware
59 // sample-rate of the output device, and we should take into account
60 // the |channels|. For now we're limited to stereo output.
61 }
62
63 AudioSynchronizedStream::~AudioSynchronizedStream() {
64 DCHECK(!input_unit_);
65 DCHECK(!output_unit_);
66 DCHECK(!varispeed_unit_);
67 }
68
69 bool AudioSynchronizedStream::Open() {
70 // Create the input, output, and varispeed AudioUnits.
71 OSStatus result = CreateAudioUnits();
72 if (result != noErr) {
73 LOG(ERROR) << "Cannot create AudioUnits.";
74 return false;
75 }
76
77 result = SetupInput(input_id_);
78 if (result != noErr) {
79 LOG(ERROR) << "Error configuring input AudioUnit.";
80 return false;
81 }
82
83 result = SetupOutput(output_id_);
84 if (result != noErr) {
85 LOG(ERROR) << "Error configuring output AudioUnit.";
86 return false;
87 }
88
89 result = SetupCallbacks();
90 if (result != noErr) {
91 LOG(ERROR) << "Error setting up callbacks on AudioUnits.";
92 return false;
93 }
94
95 result = SetupStreamFormats();
96 if (result != noErr) {
97 LOG(ERROR) << "Error configuring stream formats on AudioUnits.";
98 return false;
99 }
100
101 AllocateInputData();
102
103 // Final initialization of the AudioUnits.
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_data_) {
131 for (UInt32 i = 0; i < input_data_->mNumberBuffers; ++i)
132 free(input_data_->mBuffers[i].mData);
133 free(input_data_);
134 input_data_ = 0;
135 }
136
137 if (input_unit_) {
138 AudioUnitUninitialize(input_unit_);
139 CloseComponent(input_unit_);
140 }
141
142 if (output_unit_) {
143 AudioUnitUninitialize(output_unit_);
144 CloseComponent(output_unit_);
145 }
146
147 if (varispeed_unit_) {
148 AudioUnitUninitialize(varispeed_unit_);
149 CloseComponent(varispeed_unit_);
150 }
151
152 input_unit_ = NULL;
153 output_unit_ = NULL;
154 varispeed_unit_ = NULL;
155
156 // Inform the audio manager that we have been closed. This can cause our
157 // destruction.
158 manager_->ReleaseOutputStream(this);
159 }
160
161 void AudioSynchronizedStream::Start(AudioSourceCallback* callback) {
162 DCHECK(callback);
163 DCHECK(input_unit_);
164 DCHECK(output_unit_);
165 DCHECK(varispeed_unit_);
166
167 if (is_running_ || !input_unit_ || !output_unit_ || !varispeed_unit_)
168 return;
169
170 source_ = callback;
171
172 OSStatus result = noErr;
173
174 if (!is_running_) {
175 first_input_time_ = -1;
176 first_output_time_ = -1;
177
178 result = AudioOutputUnitStart(input_unit_);
179 OSSTATUS_DCHECK(result == noErr, result);
180
181 if (result == noErr) {
182 result = AudioOutputUnitStart(output_unit_);
183 OSSTATUS_DCHECK(result == noErr, result);
184 }
185 }
186
187 is_running_ = true;
188 }
189
190 void AudioSynchronizedStream::Stop() {
191 OSStatus result = noErr;
192 if (is_running_) {
193 result = AudioOutputUnitStop(input_unit_);
194 OSSTATUS_DCHECK(result == noErr, result);
195
196 if (result == noErr) {
197 result = AudioOutputUnitStop(output_unit_);
198 OSSTATUS_DCHECK(result == noErr, result);
199 }
200 }
201
202 if (result == noErr)
203 is_running_ = false;
204 }
205
206 bool AudioSynchronizedStream::IsRunning() {
207 return is_running_;
208 }
209
210 // TODO(crogers):
211 // implement - or remove SetVolume()/GetVolume() from AudioOutputStream.
scherkus (not reviewing) 2012/09/13 13:06:03 nit: some of this comment can go on previous line
Chris Rogers 2012/09/15 00:06:08 Done.
212 void AudioSynchronizedStream::SetVolume(double volume) {}
213 void AudioSynchronizedStream::GetVolume(double* volume) {}
214
215 OSStatus AudioSynchronizedStream::SetOutputDeviceAsCurrent(
216 AudioDeviceID output_id) {
217 OSStatus result = noErr;
218
219 // Get the default output device if device is unknown.
220 if (output_id == kAudioDeviceUnknown) {
221 AudioObjectPropertyAddress pa;
222 pa.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
223 pa.mScope = kAudioObjectPropertyScopeGlobal;
224 pa.mElement = kAudioObjectPropertyElementMaster;
225 UInt32 size = sizeof(output_id);
226
227 result = AudioObjectGetPropertyData(
228 kAudioObjectSystemObject,
229 &pa,
230 0,
231 0,
232 &size,
233 &output_id);
234
235 OSSTATUS_DCHECK(result == noErr, result);
236 if (result != noErr)
237 return result;
238 }
239
240 // Set the render frame size.
241 UInt32 frame_size = hardware_buffer_size_;
242 AudioObjectPropertyAddress pa;
243 pa.mSelector = kAudioDevicePropertyBufferFrameSize;
244 pa.mScope = kAudioDevicePropertyScopeInput;
245 pa.mElement = kAudioObjectPropertyElementMaster;
246 result = AudioObjectSetPropertyData(
247 output_id,
248 &pa,
249 0,
250 0,
251 sizeof(frame_size),
252 &frame_size);
253
254 OSSTATUS_DCHECK(result == noErr, result);
255 if (result != noErr)
256 return result;
257
258 output_info_.Initialize(output_id, false);
259
260 // Set the Current Device to the Default Output Unit.
261 result = AudioUnitSetProperty(
262 output_unit_,
263 kAudioOutputUnitProperty_CurrentDevice,
264 kAudioUnitScope_Global,
265 0,
266 &output_info_.id_,
267 sizeof(output_info_.id_));
268
269 OSSTATUS_DCHECK(result == noErr, result);
270 return result;
271 }
272
273 OSStatus AudioSynchronizedStream::SetInputDeviceAsCurrent(
274 AudioDeviceID input_id) {
275 OSStatus result = noErr;
276
277 // Get the default input device if device is unknown.
278 if (input_id == kAudioDeviceUnknown) {
279 AudioObjectPropertyAddress pa;
280 pa.mSelector = kAudioHardwarePropertyDefaultInputDevice;
281 pa.mScope = kAudioObjectPropertyScopeGlobal;
282 pa.mElement = kAudioObjectPropertyElementMaster;
283 UInt32 size = sizeof(input_id);
284
285 result = AudioObjectGetPropertyData(
286 kAudioObjectSystemObject,
287 &pa,
288 0,
289 0,
290 &size,
291 &input_id);
292
293 OSSTATUS_DCHECK(result == noErr, result);
294 if (result != noErr)
295 return result;
296 }
297
298 // Set the render frame size.
299 UInt32 frame_size = hardware_buffer_size_;
300 AudioObjectPropertyAddress pa;
301 pa.mSelector = kAudioDevicePropertyBufferFrameSize;
302 pa.mScope = kAudioDevicePropertyScopeInput;
303 pa.mElement = kAudioObjectPropertyElementMaster;
304 result = AudioObjectSetPropertyData(
305 input_id,
306 &pa,
307 0,
308 0,
309 sizeof(frame_size),
310 &frame_size);
311
312 OSSTATUS_DCHECK(result == noErr, result);
313 if (result != noErr)
314 return result;
315
316 input_info_.Initialize(input_id, true);
317
318 // Set the Current Device to the AUHAL.
319 // This should be done only after I/O has been enabled on the AUHAL.
320 result = AudioUnitSetProperty(
321 input_unit_,
322 kAudioOutputUnitProperty_CurrentDevice,
323 kAudioUnitScope_Global,
324 0,
325 &input_info_.id_,
326 sizeof(input_info_.id_));
327
328 OSSTATUS_DCHECK(result == noErr, result);
329 return result;
330 }
331
332 OSStatus AudioSynchronizedStream::CreateAudioUnits() {
333 // Q: Why do we need a varispeed unit?
334 // A: If the input device and the output device are running at
335 // different sample rates and/or on different clocks, we will need
336 // to compensate to avoid a pitch change and
337 // to avoid buffer under and over runs.
338 ComponentDescription varispeed_desc;
339 varispeed_desc.componentType = kAudioUnitType_FormatConverter;
340 varispeed_desc.componentSubType = kAudioUnitSubType_Varispeed;
341 varispeed_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
342 varispeed_desc.componentFlags = 0;
343 varispeed_desc.componentFlagsMask = 0;
344
345 Component varispeed_comp = FindNextComponent(NULL, &varispeed_desc);
346 if (varispeed_comp == NULL)
347 return -1;
348
349 OSStatus result = OpenAComponent(varispeed_comp, &varispeed_unit_);
350 OSSTATUS_DCHECK(result == noErr, result);
351 if (result != noErr)
352 return result;
353
354 // Open input AudioUnit.
355 ComponentDescription input_desc;
356 input_desc.componentType = kAudioUnitType_Output;
357 input_desc.componentSubType = kAudioUnitSubType_HALOutput;
358 input_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
359 input_desc.componentFlags = 0;
360 input_desc.componentFlagsMask = 0;
361
362 Component input_comp = FindNextComponent(NULL, &input_desc);
363 if (input_comp == NULL)
364 return -1;
365
366 result = OpenAComponent(input_comp, &input_unit_);
367 OSSTATUS_DCHECK(result == noErr, result);
368 if (result != noErr)
369 return result;
370
371 // Open output AudioUnit.
372 ComponentDescription output_desc;
373 output_desc.componentType = kAudioUnitType_Output;
374 output_desc.componentSubType = kAudioUnitSubType_DefaultOutput;
375 output_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
376 output_desc.componentFlags = 0;
377 output_desc.componentFlagsMask = 0;
378
379 Component output_comp = FindNextComponent(NULL, &output_desc);
380 if (output_comp == NULL)
381 return -1;
382
383 result = OpenAComponent(output_comp, &output_unit_);
384 OSSTATUS_DCHECK(result == noErr, result);
385 if (result != noErr)
386 return result;
387
388 return noErr;
389 }
390
391 OSStatus AudioSynchronizedStream::SetupInput(AudioDeviceID input_id) {
392 // The AUHAL used for input needs to be initialized
393 // before anything is done to it.
394 OSStatus result = AudioUnitInitialize(input_unit_);
395 OSSTATUS_DCHECK(result == noErr, result);
396 if (result != noErr)
397 return result;
398
399 // We must enable the Audio Unit (AUHAL) for input and disable output
400 // BEFORE setting the AUHAL's current device.
401 result = EnableIO();
402 OSSTATUS_DCHECK(result == noErr, result);
403 if (result != noErr)
404 return result;
405
406 result = SetInputDeviceAsCurrent(input_id);
407 OSSTATUS_DCHECK(result == noErr, result);
408
409 return result;
410 }
411
412 OSStatus AudioSynchronizedStream::EnableIO() {
413 // Enable input on the AUHAL.
414 UInt32 enable_io = 1;
415 OSStatus result = AudioUnitSetProperty(
416 input_unit_,
417 kAudioOutputUnitProperty_EnableIO,
418 kAudioUnitScope_Input,
419 1, // input element
420 &enable_io,
421 sizeof(enable_io));
422
423 OSSTATUS_DCHECK(result == noErr, result);
424 if (result != noErr)
425 return result;
426
427 // Disable Output on the AUHAL.
428 enable_io = 0;
429 result = AudioUnitSetProperty(
430 input_unit_,
431 kAudioOutputUnitProperty_EnableIO,
432 kAudioUnitScope_Output,
433 0, // output element
434 &enable_io,
435 sizeof(enable_io));
436
437 OSSTATUS_DCHECK(result == noErr, result);
438 return result;
439 }
440
441 OSStatus AudioSynchronizedStream::SetupOutput(AudioDeviceID output_id) {
442 OSStatus result = noErr;
443
444 result = SetOutputDeviceAsCurrent(output_id);
445 OSSTATUS_DCHECK(result == noErr, result);
446 if (result != noErr)
447 return result;
448
449 // Tell the output unit not to reset timestamps.
450 // Otherwise sample rate changes will cause sync loss.
451 UInt32 start_at_zero = 0;
452 result = AudioUnitSetProperty(
453 output_unit_,
454 kAudioOutputUnitProperty_StartTimestampsAtZero,
455 kAudioUnitScope_Global,
456 0,
457 &start_at_zero,
458 sizeof(start_at_zero));
459
460 OSSTATUS_DCHECK(result == noErr, result);
461
462 return result;
463 }
464
465 OSStatus AudioSynchronizedStream::SetupCallbacks() {
466 // Set the input callback.
467 AURenderCallbackStruct callback;
468 callback.inputProc = InputProc;
469 callback.inputProcRefCon = this;
470 OSStatus result = AudioUnitSetProperty(
471 input_unit_,
472 kAudioOutputUnitProperty_SetInputCallback,
473 kAudioUnitScope_Global,
474 0,
475 &callback,
476 sizeof(callback));
477
478 OSSTATUS_DCHECK(result == noErr, result);
479 if (result != noErr)
480 return result;
481
482 // Set the output callback.
483 callback.inputProc = OutputProc;
484 callback.inputProcRefCon = this;
485 result = AudioUnitSetProperty(
486 output_unit_,
487 kAudioUnitProperty_SetRenderCallback,
488 kAudioUnitScope_Input,
489 0,
490 &callback,
491 sizeof(callback));
492
493 OSSTATUS_DCHECK(result == noErr, result);
494 if (result != noErr)
495 return result;
496
497 // Set the varispeed callback.
498 callback.inputProc = VarispeedProc;
499 callback.inputProcRefCon = this;
500 result = AudioUnitSetProperty(
501 varispeed_unit_,
502 kAudioUnitProperty_SetRenderCallback,
503 kAudioUnitScope_Input,
504 0,
505 &callback,
506 sizeof(callback));
507
508 OSSTATUS_DCHECK(result == noErr, result);
509
510 return result;
511 }
512
513 OSStatus AudioSynchronizedStream::SetupStreamFormats() {
514 AudioStreamBasicDescription asbd, asbd_dev1_in, asbd_dev2_out;
515
516 // Get the Stream Format (Output client side).
517 UInt32 property_size = sizeof(asbd_dev1_in);
518 OSStatus result = AudioUnitGetProperty(
519 input_unit_,
520 kAudioUnitProperty_StreamFormat,
521 kAudioUnitScope_Input,
522 1,
523 &asbd_dev1_in,
524 &property_size);
525
526 OSSTATUS_DCHECK(result == noErr, result);
527 if (result != noErr)
528 return result;
529
530 // Get the Stream Format (client side).
531 property_size = sizeof(asbd);
532 result = AudioUnitGetProperty(
533 input_unit_,
534 kAudioUnitProperty_StreamFormat,
535 kAudioUnitScope_Output,
536 1,
537 &asbd,
538 &property_size);
539
540 OSSTATUS_DCHECK(result == noErr, result);
541 if (result != noErr)
542 return result;
543
544 // Get the Stream Format (Output client side).
545 property_size = sizeof(asbd_dev2_out);
546 result = AudioUnitGetProperty(
547 output_unit_,
548 kAudioUnitProperty_StreamFormat,
549 kAudioUnitScope_Output,
550 0,
551 &asbd_dev2_out,
552 &property_size);
553
554 OSSTATUS_DCHECK(result == noErr, result);
555 if (result != noErr)
556 return result;
557
558 // Set the format of all the AUs to the input/output devices channel count.
559 // For a simple case, you want to set this to
560 // the lower of count of the channels in the input device vs output device.
561 asbd.mChannelsPerFrame = std::min(asbd_dev1_in.mChannelsPerFrame,
562 asbd_dev2_out.mChannelsPerFrame);
563
564 // We must get the sample rate of the input device and set it to the
565 // stream format of AUHAL.
566 Float64 rate = 0;
567 property_size = sizeof(rate);
568
569 AudioObjectPropertyAddress pa;
570 pa.mSelector = kAudioDevicePropertyNominalSampleRate;
571 pa.mScope = kAudioObjectPropertyScopeWildcard;
572 pa.mElement = kAudioObjectPropertyElementMaster;
573 result = AudioObjectGetPropertyData(
574 input_info_.id_,
575 &pa,
576 0,
577 0,
578 &property_size,
579 &rate);
580
581 OSSTATUS_DCHECK(result == noErr, result);
582 if (result != noErr)
583 return result;
584
585 asbd.mSampleRate = rate;
586 property_size = sizeof(asbd);
587
588 // Set the new formats to the AUs...
589 result = AudioUnitSetProperty(
590 input_unit_,
591 kAudioUnitProperty_StreamFormat,
592 kAudioUnitScope_Output,
593 1,
594 &asbd,
595 property_size);
596
597 OSSTATUS_DCHECK(result == noErr, result);
598 if (result != noErr)
599 return result;
600
601 result = AudioUnitSetProperty(
602 varispeed_unit_,
603 kAudioUnitProperty_StreamFormat,
604 kAudioUnitScope_Input,
605 0,
606 &asbd,
607 property_size);
608
609 OSSTATUS_DCHECK(result == noErr, result);
610 if (result != noErr)
611 return result;
612
613 // Set the correct sample rate for the output device,
614 // but keep the channel count the same.
615 property_size = sizeof(rate);
616
617 pa.mSelector = kAudioDevicePropertyNominalSampleRate;
618 pa.mScope = kAudioObjectPropertyScopeWildcard;
619 pa.mElement = kAudioObjectPropertyElementMaster;
620 result = AudioObjectGetPropertyData(
621 output_info_.id_,
622 &pa,
623 0,
624 0,
625 &property_size,
626 &rate);
627
628 OSSTATUS_DCHECK(result == noErr, result);
629 if (result != noErr)
630 return result;
631
632 output_sample_rate_ = rate;
633
634 asbd.mSampleRate = rate;
635 property_size = sizeof(asbd);
636
637 // Set the new audio stream formats for the rest of the AUs...
638 result = AudioUnitSetProperty(
639 varispeed_unit_,
640 kAudioUnitProperty_StreamFormat,
641 kAudioUnitScope_Output,
642 0,
643 &asbd,
644 property_size);
645
646 OSSTATUS_DCHECK(result == noErr, result);
647 if (result != noErr)
648 return result;
649
650 result = AudioUnitSetProperty(
651 output_unit_,
652 kAudioUnitProperty_StreamFormat,
653 kAudioUnitScope_Input,
654 0,
655 &asbd,
656 property_size);
657
658 OSSTATUS_DCHECK(result == noErr, result);
659 return result;
660 }
661
662 void AudioSynchronizedStream::AllocateInputData() {
663 // Allocate storage for the AudioBufferList used for the
664 // input data from the input AudioUnit.
665 // We allocate enough space for with one AudioBuffer per channel.
666 UInt32 malloc_size = offsetof(AudioBufferList, mBuffers[0]) +
scherkus (not reviewing) 2012/09/13 13:06:03 for the sake of other chromium developers can you
Chris Rogers 2012/09/15 00:06:08 Done.
667 (sizeof(AudioBuffer) * channels_);
668
669 input_data_ = static_cast<AudioBufferList*>(malloc(malloc_size));
scherkus (not reviewing) 2012/09/13 13:06:03 any reason to prefer malloc/free over new/delete h
Chris Rogers 2012/09/15 00:06:08 For the AudioBufferList allocation, I'm using mall
670 input_data_->mNumberBuffers = channels_;
671
672 // Allocate buffers for AudioBufferList.
673 UInt32 buffer_size_bytes = hardware_buffer_size_ * sizeof(Float32);
674 for (UInt32 i = 0; i < input_data_->mNumberBuffers; ++i) {
675 input_data_->mBuffers[i].mNumberChannels = 1;
676 input_data_->mBuffers[i].mDataByteSize = buffer_size_bytes;
677 input_data_->mBuffers[i].mData = malloc(buffer_size_bytes);
Chris Rogers 2012/09/15 00:06:08 I've removed the malloc() here and am now using an
678 }
679 }
680
681 void AudioSynchronizedStream::ComputeThruOffset() {
682 // The initial latency will at least be the safety offsets
683 // of the devices + the buffer sizes.
684 in_to_out_sample_offset_ = SInt32(input_info_.buffer_size_frames_ +
685 output_info_.buffer_size_frames_);
686 }
687
688 OSStatus AudioSynchronizedStream::HandleInputCallback(
689 AudioUnitRenderActionFlags* io_action_flags,
690 const AudioTimeStamp* time_stamp,
691 UInt32 bus_number,
692 UInt32 number_of_frames,
693 AudioBufferList* io_data) {
694 if (first_input_time_ < 0.0)
695 first_input_time_ = time_stamp->mSampleTime;
696
697 // Get the new audio input data.
698 OSStatus result = AudioUnitRender(
699 input_unit_,
700 io_action_flags,
701 time_stamp,
702 bus_number,
703 number_of_frames,
704 input_data_);
705
706 OSSTATUS_DCHECK(result == noErr, result);
707 if (result != noErr)
708 return result;
709
710 // Buffer input into FIFO.
711 if (is_fifo_initialized_) {
712 // TODO(crogers): remove this locking once AudioFifo becomes thread-safe.
713 if (fifo_lock_.Try()) {
714 AudioBus bus(channels_, number_of_frames, input_data_);
715 fifo_.Push(&bus);
716 fifo_lock_.Release();
717 }
718 }
719
720 return result;
721 }
722
723 OSStatus AudioSynchronizedStream::HandleVarispeedCallback(
724 AudioUnitRenderActionFlags* io_action_flags,
725 const AudioTimeStamp* time_stamp,
726 UInt32 bus_number,
727 UInt32 number_of_frames,
728 AudioBufferList* io_data) {
729 // Create a wrapper bus on the AudioBufferList.
730 AudioBus bus(channels_, number_of_frames, io_data);
731
732 if (fifo_.frames() < static_cast<int>(number_of_frames)) {
scherkus (not reviewing) 2012/09/13 13:06:03 FYI: fifo_ not locked See my comment on line 817
Chris Rogers 2012/09/15 00:06:08 I've completely removed this fifo_lock_ and have a
733 // We don't DCHECK here, since this is a possible run-time condition
734 // if the machine is bogged down.
735 bus.Zero();
736 return noErr;
737 }
738
739 // TODO(crogers): remove this locking once AudioFifo becomes thread-safe.
740 if (fifo_lock_.Try()) {
741 // Read from the FIFO to feed the varispeed.
742 fifo_.Consume(&bus, 0, number_of_frames);
743 fifo_lock_.Release();
744 }
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 = fifo_.frames();
scherkus (not reviewing) 2012/09/13 13:06:03 not locked
Chris Rogers 2012/09/15 00:06:08 Fixed in AudioFifo thread-safety
750 int delta = n - kTargetDelayFrames;
751 double sample_rate = output_sample_rate_;
752 double x = (sample_rate + delta) / sample_rate;
753
754 fifo_rate_compensation_ = x;
755
756 return noErr;
757 }
758
759 OSStatus AudioSynchronizedStream::HandleOutputCallback(
760 AudioUnitRenderActionFlags* io_action_flags,
761 const AudioTimeStamp* time_stamp,
762 UInt32 bus_number,
763 UInt32 number_of_frames,
764 AudioBufferList* io_data) {
765 if (first_input_time_ < 0.0) {
766 // Input callback hasn't run yet -> silence.
767 ZeroBufferList(io_data);
768 return noErr;
769 }
770
771 AudioTimeStamp input_ts;
772 OSStatus result = AudioDeviceGetCurrentTime(input_info_.id_, &input_ts);
773
774 if (result != noErr) {
775 ZeroBufferList(io_data);
776 return noErr;
777 }
778
779 AudioTimeStamp output_ts;
780 result = AudioDeviceGetCurrentTime(output_info_.id_, &output_ts);
781
782 OSSTATUS_DCHECK(result == noErr, result);
783 if (result != noErr)
784 return result;
785
786 // Use the varispeed playback rate to offset small discrepancies
787 // in hardware clocks, and also any differences in sample-rate
788 // between input and output devices.
789
790 // Adjust for rate scalars of the input and output devices.
791 double rate = input_ts.mRateScalar / output_ts.mRateScalar;
792
793 // Adjust for FIFO drift.
794 rate *= fifo_rate_compensation_;
795
796 result = AudioUnitSetParameter(
797 varispeed_unit_,
798 kVarispeedParam_PlaybackRate,
799 kAudioUnitScope_Global,
800 0,
801 rate,
802 0);
803
804 OSSTATUS_DCHECK(result == noErr, result);
805 if (result != noErr)
806 return result;
807
808 // Get the delta between the devices and add it to the offset.
809 if (first_output_time_ < 0.) {
scherkus (not reviewing) 2012/09/13 13:06:03 0.0
Chris Rogers 2012/09/15 00:06:08 This code has now been removed.
810 first_output_time_ = time_stamp->mSampleTime;
811 ComputeThruOffset();
812
813 // Buffer initial silence corresponding to I/O delay.
814 unsigned n = static_cast<unsigned>(in_to_out_sample_offset_);
scherkus (not reviewing) 2012/09/13 13:06:03 s/unsigned/int
Chris Rogers 2012/09/15 00:06:08 Removed/simplified
815 AudioBus silence(channels_, n);
816 silence.Zero();
817 fifo_.Push(&silence);
scherkus (not reviewing) 2012/09/13 13:06:03 FYI: not locked to confirm.. the output+varispeed
Chris Rogers 2012/09/15 00:06:08 The AudioFifo now has changes to make it lock-free
818 is_fifo_initialized_ = true;
819
820 ZeroBufferList(io_data);
821 return noErr;
822 }
823
824 // Render to the output using the varispeed.
825 result = AudioUnitRender(
826 varispeed_unit_,
827 io_action_flags,
828 time_stamp,
829 0,
830 number_of_frames,
831 io_data);
832
833 OSSTATUS_DCHECK(result == noErr, result);
834 if (result != noErr)
835 return result;
836
837 // Create a wrapper bus on the AudioBufferList.
838 AudioBus bus(channels_, number_of_frames, io_data);
839
840 // Process in-place!
841 source_->OnMoreIOData(&bus, &bus, AudioBuffersState(0, 0));
842
843 return noErr;
844 }
845
846 OSStatus AudioSynchronizedStream::InputProc(
847 void* user_data,
848 AudioUnitRenderActionFlags* io_action_flags,
849 const AudioTimeStamp* time_stamp,
850 UInt32 bus_number,
851 UInt32 number_of_frames,
852 AudioBufferList* io_data) {
853 AudioSynchronizedStream* stream =
854 static_cast<AudioSynchronizedStream*>(user_data);
855 DCHECK(stream);
856
857 return stream->HandleInputCallback(
858 io_action_flags,
859 time_stamp,
860 bus_number,
861 number_of_frames,
862 io_data);
863 }
864
865 OSStatus AudioSynchronizedStream::VarispeedProc(
866 void* user_data,
867 AudioUnitRenderActionFlags* io_action_flags,
868 const AudioTimeStamp* time_stamp,
869 UInt32 bus_number,
870 UInt32 number_of_frames,
871 AudioBufferList* io_data) {
872 AudioSynchronizedStream* stream =
873 static_cast<AudioSynchronizedStream*>(user_data);
874 DCHECK(stream);
875
876 return stream->HandleVarispeedCallback(
877 io_action_flags,
878 time_stamp,
879 bus_number,
880 number_of_frames,
881 io_data);
882 }
883
884 OSStatus AudioSynchronizedStream::OutputProc(
885 void* user_data,
886 AudioUnitRenderActionFlags* io_action_flags,
887 const AudioTimeStamp* time_stamp,
888 UInt32 bus_number,
889 UInt32 number_of_frames,
890 AudioBufferList* io_data) {
891 AudioSynchronizedStream* stream =
892 static_cast<AudioSynchronizedStream*>(user_data);
893 DCHECK(stream);
894
895 return stream->HandleOutputCallback(
896 io_action_flags,
897 time_stamp,
898 bus_number,
899 number_of_frames,
900 io_data);
901 }
902
903 void AudioSynchronizedStream::AudioDeviceInfo::Initialize(
904 AudioDeviceID id, bool is_input) {
905 id_ = id;
906 is_input_ = is_input;
907 if (id_ == kAudioDeviceUnknown)
908 return;
909
910 UInt32 property_size = sizeof(buffer_size_frames_);
911
912 AudioObjectPropertyAddress pa;
913 pa.mSelector = kAudioDevicePropertyBufferFrameSize;
914 pa.mScope = kAudioObjectPropertyScopeWildcard;
915 pa.mElement = kAudioObjectPropertyElementMaster;
916 OSStatus result = AudioObjectGetPropertyData(
917 id_,
918 &pa,
919 0,
920 0,
921 &property_size,
922 &buffer_size_frames_);
923
924 OSSTATUS_DCHECK(result == noErr, result);
925 }
926
927 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698