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

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 "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
OLDNEW
« media/audio/mac/audio_synchronized_mac.h ('K') | « media/audio/mac/audio_synchronized_mac.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698