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.
5 #include "media/audio/mac/audio_synchronized_mac.h"
7 #include <CoreServices/CoreServices.h>
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/audio_util.h"
15 #include "media/audio/mac/audio_manager_mac.h"
19 static const int kHardwareBufferSize
= 128;
20 static const int kFifoSize
= 16384;
22 // TODO(crogers): handle the non-stereo case.
23 static const int kChannels
= 2;
25 // This value was determined empirically for minimum latency while still
26 // guarding against FIFO under-runs.
27 static const int kBaseTargetFifoFrames
= 256 + 64;
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;
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
,
38 buffer_list
->mBuffers
[i
].mDataByteSize
);
41 static void WrapBufferList(AudioBufferList
* buffer_list
,
46 int channels
= bus
->channels();
47 int buffer_list_channels
= buffer_list
->mNumberBuffers
;
49 // Copy pointers from AudioBufferList.
51 for (int i
= 0; i
< channels
; ++i
) {
53 i
, static_cast<float*>(buffer_list
->mBuffers
[source_idx
].mData
));
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)
61 // Finally set the actual length.
62 bus
->set_frames(frames
);
65 AudioSynchronizedStream::AudioSynchronizedStream(
66 AudioManagerMac
* manager
,
67 const AudioParameters
& params
,
68 AudioDeviceID input_id
,
69 AudioDeviceID output_id
)
72 input_sample_rate_(0),
73 output_sample_rate_(0),
75 output_id_(output_id
),
76 input_buffer_list_(NULL
),
77 fifo_(kChannels
, kFifoSize
),
78 target_fifo_frames_(kBaseTargetFifoFrames
),
80 fifo_rate_compensation_(1.0),
84 first_input_time_(-1),
86 hardware_buffer_size_(kHardwareBufferSize
),
87 channels_(kChannels
) {
90 AudioSynchronizedStream::~AudioSynchronizedStream() {
92 DCHECK(!output_unit_
);
93 DCHECK(!varispeed_unit_
);
96 bool AudioSynchronizedStream::Open() {
97 if (params_
.channels() != kChannels
) {
98 LOG(ERROR
) << "Only stereo output is currently supported.";
102 // Create the input, output, and varispeed AudioUnits.
103 OSStatus result
= CreateAudioUnits();
104 if (result
!= noErr
) {
105 LOG(ERROR
) << "Cannot create AudioUnits.";
109 result
= SetupInput(input_id_
);
110 if (result
!= noErr
) {
111 LOG(ERROR
) << "Error configuring input AudioUnit.";
115 result
= SetupOutput(output_id_
);
116 if (result
!= noErr
) {
117 LOG(ERROR
) << "Error configuring output AudioUnit.";
121 result
= SetupCallbacks();
122 if (result
!= noErr
) {
123 LOG(ERROR
) << "Error setting up callbacks on AudioUnits.";
127 result
= SetupStreamFormats();
128 if (result
!= noErr
) {
129 LOG(ERROR
) << "Error configuring stream formats on AudioUnits.";
135 // Final initialization of the AudioUnits.
136 result
= AudioUnitInitialize(input_unit_
);
137 if (result
!= noErr
) {
138 LOG(ERROR
) << "Error initializing input AudioUnit.";
142 result
= AudioUnitInitialize(output_unit_
);
143 if (result
!= noErr
) {
144 LOG(ERROR
) << "Error initializing output AudioUnit.";
148 result
= AudioUnitInitialize(varispeed_unit_
);
149 if (result
!= noErr
) {
150 LOG(ERROR
) << "Error initializing varispeed AudioUnit.";
154 if (input_sample_rate_
!= output_sample_rate_
) {
155 // Add extra safety margin.
156 target_fifo_frames_
+= kAdditionalTargetFifoFrames
;
159 // Buffer initial silence corresponding to target I/O buffering.
161 scoped_ptr
<AudioBus
> silence
=
162 AudioBus::Create(channels_
, target_fifo_frames_
);
164 fifo_
.Push(silence
.get());
169 void AudioSynchronizedStream::Close() {
170 DCHECK(!is_running_
);
172 if (input_buffer_list_
) {
173 free(input_buffer_list_
);
174 input_buffer_list_
= 0;
175 input_bus_
.reset(NULL
);
176 wrapper_bus_
.reset(NULL
);
180 AudioUnitUninitialize(input_unit_
);
181 CloseComponent(input_unit_
);
185 AudioUnitUninitialize(output_unit_
);
186 CloseComponent(output_unit_
);
189 if (varispeed_unit_
) {
190 AudioUnitUninitialize(varispeed_unit_
);
191 CloseComponent(varispeed_unit_
);
196 varispeed_unit_
= NULL
;
198 // Inform the audio manager that we have been closed. This can cause our
200 manager_
->ReleaseOutputStream(this);
203 void AudioSynchronizedStream::Start(AudioSourceCallback
* callback
) {
206 DCHECK(output_unit_
);
207 DCHECK(varispeed_unit_
);
209 if (is_running_
|| !input_unit_
|| !output_unit_
|| !varispeed_unit_
)
214 // Reset state variables each time we Start().
215 fifo_rate_compensation_
= 1.0;
216 average_delta_
= 0.0;
218 OSStatus result
= noErr
;
221 first_input_time_
= -1;
223 result
= AudioOutputUnitStart(input_unit_
);
224 OSSTATUS_DCHECK(result
== noErr
, result
);
226 if (result
== noErr
) {
227 result
= AudioOutputUnitStart(output_unit_
);
228 OSSTATUS_DCHECK(result
== noErr
, result
);
235 void AudioSynchronizedStream::Stop() {
236 OSStatus result
= noErr
;
238 result
= AudioOutputUnitStop(input_unit_
);
239 OSSTATUS_DCHECK(result
== noErr
, result
);
241 if (result
== noErr
) {
242 result
= AudioOutputUnitStop(output_unit_
);
243 OSSTATUS_DCHECK(result
== noErr
, result
);
251 bool AudioSynchronizedStream::IsRunning() {
255 // TODO(crogers): implement - or remove from AudioOutputStream.
256 void AudioSynchronizedStream::SetVolume(double volume
) {}
257 void AudioSynchronizedStream::GetVolume(double* volume
) {}
259 OSStatus
AudioSynchronizedStream::SetOutputDeviceAsCurrent(
260 AudioDeviceID output_id
) {
261 OSStatus result
= noErr
;
263 // Get the default output device if device is unknown.
264 if (output_id
== kAudioDeviceUnknown
) {
265 AudioObjectPropertyAddress pa
;
266 pa
.mSelector
= kAudioHardwarePropertyDefaultOutputDevice
;
267 pa
.mScope
= kAudioObjectPropertyScopeGlobal
;
268 pa
.mElement
= kAudioObjectPropertyElementMaster
;
269 UInt32 size
= sizeof(output_id
);
271 result
= AudioObjectGetPropertyData(
272 kAudioObjectSystemObject
,
279 OSSTATUS_DCHECK(result
== noErr
, result
);
284 // Set the render frame size.
285 UInt32 frame_size
= hardware_buffer_size_
;
286 AudioObjectPropertyAddress pa
;
287 pa
.mSelector
= kAudioDevicePropertyBufferFrameSize
;
288 pa
.mScope
= kAudioDevicePropertyScopeInput
;
289 pa
.mElement
= kAudioObjectPropertyElementMaster
;
290 result
= AudioObjectSetPropertyData(
298 OSSTATUS_DCHECK(result
== noErr
, result
);
302 output_info_
.Initialize(output_id
, false);
304 // Set the Current Device to the Default Output Unit.
305 result
= AudioUnitSetProperty(
307 kAudioOutputUnitProperty_CurrentDevice
,
308 kAudioUnitScope_Global
,
311 sizeof(output_info_
.id_
));
313 OSSTATUS_DCHECK(result
== noErr
, result
);
317 OSStatus
AudioSynchronizedStream::SetInputDeviceAsCurrent(
318 AudioDeviceID input_id
) {
319 OSStatus result
= noErr
;
321 // Get the default input device if device is unknown.
322 if (input_id
== kAudioDeviceUnknown
) {
323 AudioObjectPropertyAddress pa
;
324 pa
.mSelector
= kAudioHardwarePropertyDefaultInputDevice
;
325 pa
.mScope
= kAudioObjectPropertyScopeGlobal
;
326 pa
.mElement
= kAudioObjectPropertyElementMaster
;
327 UInt32 size
= sizeof(input_id
);
329 result
= AudioObjectGetPropertyData(
330 kAudioObjectSystemObject
,
337 OSSTATUS_DCHECK(result
== noErr
, result
);
342 // Set the render frame size.
343 UInt32 frame_size
= hardware_buffer_size_
;
344 AudioObjectPropertyAddress pa
;
345 pa
.mSelector
= kAudioDevicePropertyBufferFrameSize
;
346 pa
.mScope
= kAudioDevicePropertyScopeInput
;
347 pa
.mElement
= kAudioObjectPropertyElementMaster
;
348 result
= AudioObjectSetPropertyData(
356 OSSTATUS_DCHECK(result
== noErr
, result
);
360 input_info_
.Initialize(input_id
, true);
362 // Set the Current Device to the AUHAL.
363 // This should be done only after I/O has been enabled on the AUHAL.
364 result
= AudioUnitSetProperty(
366 kAudioOutputUnitProperty_CurrentDevice
,
367 kAudioUnitScope_Global
,
370 sizeof(input_info_
.id_
));
372 OSSTATUS_DCHECK(result
== noErr
, result
);
376 OSStatus
AudioSynchronizedStream::CreateAudioUnits() {
377 // Q: Why do we need a varispeed unit?
378 // A: If the input device and the output device are running at
379 // different sample rates and/or on different clocks, we will need
380 // to compensate to avoid a pitch change and
381 // to avoid buffer under and over runs.
382 ComponentDescription varispeed_desc
;
383 varispeed_desc
.componentType
= kAudioUnitType_FormatConverter
;
384 varispeed_desc
.componentSubType
= kAudioUnitSubType_Varispeed
;
385 varispeed_desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
386 varispeed_desc
.componentFlags
= 0;
387 varispeed_desc
.componentFlagsMask
= 0;
389 Component varispeed_comp
= FindNextComponent(NULL
, &varispeed_desc
);
390 if (varispeed_comp
== NULL
)
393 OSStatus result
= OpenAComponent(varispeed_comp
, &varispeed_unit_
);
394 OSSTATUS_DCHECK(result
== noErr
, result
);
398 // Open input AudioUnit.
399 ComponentDescription input_desc
;
400 input_desc
.componentType
= kAudioUnitType_Output
;
401 input_desc
.componentSubType
= kAudioUnitSubType_HALOutput
;
402 input_desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
403 input_desc
.componentFlags
= 0;
404 input_desc
.componentFlagsMask
= 0;
406 Component input_comp
= FindNextComponent(NULL
, &input_desc
);
407 if (input_comp
== NULL
)
410 result
= OpenAComponent(input_comp
, &input_unit_
);
411 OSSTATUS_DCHECK(result
== noErr
, result
);
415 // Open output AudioUnit.
416 ComponentDescription output_desc
;
417 output_desc
.componentType
= kAudioUnitType_Output
;
418 output_desc
.componentSubType
= kAudioUnitSubType_DefaultOutput
;
419 output_desc
.componentManufacturer
= kAudioUnitManufacturer_Apple
;
420 output_desc
.componentFlags
= 0;
421 output_desc
.componentFlagsMask
= 0;
423 Component output_comp
= FindNextComponent(NULL
, &output_desc
);
424 if (output_comp
== NULL
)
427 result
= OpenAComponent(output_comp
, &output_unit_
);
428 OSSTATUS_DCHECK(result
== noErr
, result
);
435 OSStatus
AudioSynchronizedStream::SetupInput(AudioDeviceID input_id
) {
436 // The AUHAL used for input needs to be initialized
437 // before anything is done to it.
438 OSStatus result
= AudioUnitInitialize(input_unit_
);
439 OSSTATUS_DCHECK(result
== noErr
, result
);
443 // We must enable the Audio Unit (AUHAL) for input and disable output
444 // BEFORE setting the AUHAL's current device.
446 OSSTATUS_DCHECK(result
== noErr
, result
);
450 result
= SetInputDeviceAsCurrent(input_id
);
451 OSSTATUS_DCHECK(result
== noErr
, result
);
456 OSStatus
AudioSynchronizedStream::EnableIO() {
457 // Enable input on the AUHAL.
458 UInt32 enable_io
= 1;
459 OSStatus result
= AudioUnitSetProperty(
461 kAudioOutputUnitProperty_EnableIO
,
462 kAudioUnitScope_Input
,
467 OSSTATUS_DCHECK(result
== noErr
, result
);
471 // Disable Output on the AUHAL.
473 result
= AudioUnitSetProperty(
475 kAudioOutputUnitProperty_EnableIO
,
476 kAudioUnitScope_Output
,
481 OSSTATUS_DCHECK(result
== noErr
, result
);
485 OSStatus
AudioSynchronizedStream::SetupOutput(AudioDeviceID output_id
) {
486 OSStatus result
= noErr
;
488 result
= SetOutputDeviceAsCurrent(output_id
);
489 OSSTATUS_DCHECK(result
== noErr
, result
);
493 // Tell the output unit not to reset timestamps.
494 // Otherwise sample rate changes will cause sync loss.
495 UInt32 start_at_zero
= 0;
496 result
= AudioUnitSetProperty(
498 kAudioOutputUnitProperty_StartTimestampsAtZero
,
499 kAudioUnitScope_Global
,
502 sizeof(start_at_zero
));
504 OSSTATUS_DCHECK(result
== noErr
, result
);
509 OSStatus
AudioSynchronizedStream::SetupCallbacks() {
510 // Set the input callback.
511 AURenderCallbackStruct callback
;
512 callback
.inputProc
= InputProc
;
513 callback
.inputProcRefCon
= this;
514 OSStatus result
= AudioUnitSetProperty(
516 kAudioOutputUnitProperty_SetInputCallback
,
517 kAudioUnitScope_Global
,
522 OSSTATUS_DCHECK(result
== noErr
, result
);
526 // Set the output callback.
527 callback
.inputProc
= OutputProc
;
528 callback
.inputProcRefCon
= this;
529 result
= AudioUnitSetProperty(
531 kAudioUnitProperty_SetRenderCallback
,
532 kAudioUnitScope_Input
,
537 OSSTATUS_DCHECK(result
== noErr
, result
);
541 // Set the varispeed callback.
542 callback
.inputProc
= VarispeedProc
;
543 callback
.inputProcRefCon
= this;
544 result
= AudioUnitSetProperty(
546 kAudioUnitProperty_SetRenderCallback
,
547 kAudioUnitScope_Input
,
552 OSSTATUS_DCHECK(result
== noErr
, result
);
557 OSStatus
AudioSynchronizedStream::SetupStreamFormats() {
558 AudioStreamBasicDescription asbd
, asbd_dev1_in
, asbd_dev2_out
;
560 // Get the Stream Format (Output client side).
561 UInt32 property_size
= sizeof(asbd_dev1_in
);
562 OSStatus result
= AudioUnitGetProperty(
564 kAudioUnitProperty_StreamFormat
,
565 kAudioUnitScope_Input
,
570 OSSTATUS_DCHECK(result
== noErr
, result
);
574 // Get the Stream Format (client side).
575 property_size
= sizeof(asbd
);
576 result
= AudioUnitGetProperty(
578 kAudioUnitProperty_StreamFormat
,
579 kAudioUnitScope_Output
,
584 OSSTATUS_DCHECK(result
== noErr
, result
);
588 // Get the Stream Format (Output client side).
589 property_size
= sizeof(asbd_dev2_out
);
590 result
= AudioUnitGetProperty(
592 kAudioUnitProperty_StreamFormat
,
593 kAudioUnitScope_Output
,
598 OSSTATUS_DCHECK(result
== noErr
, result
);
602 // Set the format of all the AUs to the input/output devices channel count.
603 // For a simple case, you want to set this to
604 // the lower of count of the channels in the input device vs output device.
605 asbd
.mChannelsPerFrame
= std::min(asbd_dev1_in
.mChannelsPerFrame
,
606 asbd_dev2_out
.mChannelsPerFrame
);
608 // We must get the sample rate of the input device and set it to the
609 // stream format of AUHAL.
611 property_size
= sizeof(rate
);
613 AudioObjectPropertyAddress pa
;
614 pa
.mSelector
= kAudioDevicePropertyNominalSampleRate
;
615 pa
.mScope
= kAudioObjectPropertyScopeWildcard
;
616 pa
.mElement
= kAudioObjectPropertyElementMaster
;
617 result
= AudioObjectGetPropertyData(
625 OSSTATUS_DCHECK(result
== noErr
, result
);
629 input_sample_rate_
= rate
;
631 asbd
.mSampleRate
= rate
;
632 property_size
= sizeof(asbd
);
634 // Set the new formats to the AUs...
635 result
= AudioUnitSetProperty(
637 kAudioUnitProperty_StreamFormat
,
638 kAudioUnitScope_Output
,
643 OSSTATUS_DCHECK(result
== noErr
, result
);
647 result
= AudioUnitSetProperty(
649 kAudioUnitProperty_StreamFormat
,
650 kAudioUnitScope_Input
,
655 OSSTATUS_DCHECK(result
== noErr
, result
);
659 // Set the correct sample rate for the output device,
660 // but keep the channel count the same.
661 property_size
= sizeof(rate
);
663 pa
.mSelector
= kAudioDevicePropertyNominalSampleRate
;
664 pa
.mScope
= kAudioObjectPropertyScopeWildcard
;
665 pa
.mElement
= kAudioObjectPropertyElementMaster
;
666 result
= AudioObjectGetPropertyData(
674 OSSTATUS_DCHECK(result
== noErr
, result
);
678 output_sample_rate_
= rate
;
680 // The requested sample-rate must match the hardware sample-rate.
681 if (output_sample_rate_
!= params_
.sample_rate()) {
682 LOG(ERROR
) << "Requested sample-rate: " << params_
.sample_rate()
683 << " must match the hardware sample-rate: " << output_sample_rate_
;
684 return kAudioDeviceUnsupportedFormatError
;
687 asbd
.mSampleRate
= rate
;
688 property_size
= sizeof(asbd
);
690 // Set the new audio stream formats for the rest of the AUs...
691 result
= AudioUnitSetProperty(
693 kAudioUnitProperty_StreamFormat
,
694 kAudioUnitScope_Output
,
699 OSSTATUS_DCHECK(result
== noErr
, result
);
703 result
= AudioUnitSetProperty(
705 kAudioUnitProperty_StreamFormat
,
706 kAudioUnitScope_Input
,
711 OSSTATUS_DCHECK(result
== noErr
, result
);
715 void AudioSynchronizedStream::AllocateInputData() {
716 // Allocate storage for the AudioBufferList used for the
717 // input data from the input AudioUnit.
718 // We allocate enough space for with one AudioBuffer per channel.
719 size_t malloc_size
= offsetof(AudioBufferList
, mBuffers
[0]) +
720 (sizeof(AudioBuffer
) * channels_
);
722 input_buffer_list_
= static_cast<AudioBufferList
*>(malloc(malloc_size
));
723 input_buffer_list_
->mNumberBuffers
= channels_
;
725 input_bus_
= AudioBus::Create(channels_
, hardware_buffer_size_
);
726 wrapper_bus_
= AudioBus::CreateWrapper(channels_
);
728 // Allocate buffers for AudioBufferList.
729 UInt32 buffer_size_bytes
= input_bus_
->frames() * sizeof(Float32
);
730 for (size_t i
= 0; i
< input_buffer_list_
->mNumberBuffers
; ++i
) {
731 input_buffer_list_
->mBuffers
[i
].mNumberChannels
= 1;
732 input_buffer_list_
->mBuffers
[i
].mDataByteSize
= buffer_size_bytes
;
733 input_buffer_list_
->mBuffers
[i
].mData
= input_bus_
->channel(i
);
737 OSStatus
AudioSynchronizedStream::HandleInputCallback(
738 AudioUnitRenderActionFlags
* io_action_flags
,
739 const AudioTimeStamp
* time_stamp
,
741 UInt32 number_of_frames
,
742 AudioBufferList
* io_data
) {
743 TRACE_EVENT0("audio", "AudioSynchronizedStream::HandleInputCallback");
745 if (first_input_time_
< 0.0)
746 first_input_time_
= time_stamp
->mSampleTime
;
748 // Get the new audio input data.
749 OSStatus result
= AudioUnitRender(
757 OSSTATUS_DCHECK(result
== noErr
, result
);
761 // Buffer input into FIFO.
762 int available_frames
= fifo_
.max_frames() - fifo_
.frames();
763 if (input_bus_
->frames() <= available_frames
)
764 fifo_
.Push(input_bus_
.get());
769 OSStatus
AudioSynchronizedStream::HandleVarispeedCallback(
770 AudioUnitRenderActionFlags
* io_action_flags
,
771 const AudioTimeStamp
* time_stamp
,
773 UInt32 number_of_frames
,
774 AudioBufferList
* io_data
) {
775 // Create a wrapper bus on the AudioBufferList.
776 WrapBufferList(io_data
, wrapper_bus_
.get(), number_of_frames
);
778 if (fifo_
.frames() < static_cast<int>(number_of_frames
)) {
779 // We don't DCHECK here, since this is a possible run-time condition
780 // if the machine is bogged down.
781 wrapper_bus_
->Zero();
785 // Read from the FIFO to feed the varispeed.
786 fifo_
.Consume(wrapper_bus_
.get(), 0, number_of_frames
);
791 OSStatus
AudioSynchronizedStream::HandleOutputCallback(
792 AudioUnitRenderActionFlags
* io_action_flags
,
793 const AudioTimeStamp
* time_stamp
,
795 UInt32 number_of_frames
,
796 AudioBufferList
* io_data
) {
797 if (first_input_time_
< 0.0) {
798 // Input callback hasn't run yet -> silence.
799 ZeroBufferList(io_data
);
803 // Use the varispeed playback rate to offset small discrepancies
804 // in hardware clocks, and also any differences in sample-rate
805 // between input and output devices.
807 // Calculate a varispeed rate scalar factor to compensate for drift between
808 // input and output. We use the actual number of frames still in the FIFO
809 // compared with the ideal value of |target_fifo_frames_|.
810 int delta
= fifo_
.frames() - target_fifo_frames_
;
812 // Average |delta| because it can jitter back/forth quite frequently
813 // by +/- the hardware buffer-size *if* the input and output callbacks are
814 // happening at almost exactly the same time. Also, if the input and output
815 // sample-rates are different then |delta| will jitter quite a bit due to
816 // the rate conversion happening in the varispeed, plus the jittering of
817 // the callbacks. The average value is what's important here.
818 average_delta_
+= (delta
- average_delta_
) * 0.1;
820 // Compute a rate compensation which always attracts us back to the
821 // |target_fifo_frames_| over a period of kCorrectionTimeSeconds.
822 const double kCorrectionTimeSeconds
= 0.1;
823 double correction_time_frames
= kCorrectionTimeSeconds
* output_sample_rate_
;
824 fifo_rate_compensation_
=
825 (correction_time_frames
+ average_delta_
) / correction_time_frames
;
827 // Adjust for FIFO drift.
828 OSStatus result
= AudioUnitSetParameter(
830 kVarispeedParam_PlaybackRate
,
831 kAudioUnitScope_Global
,
833 fifo_rate_compensation_
,
836 OSSTATUS_DCHECK(result
== noErr
, result
);
840 // Render to the output using the varispeed.
841 result
= AudioUnitRender(
849 OSSTATUS_DCHECK(result
== noErr
, result
);
853 // Create a wrapper bus on the AudioBufferList.
854 WrapBufferList(io_data
, wrapper_bus_
.get(), number_of_frames
);
857 source_
->OnMoreIOData(wrapper_bus_
.get(),
859 AudioBuffersState(0, 0));
864 OSStatus
AudioSynchronizedStream::InputProc(
866 AudioUnitRenderActionFlags
* io_action_flags
,
867 const AudioTimeStamp
* time_stamp
,
869 UInt32 number_of_frames
,
870 AudioBufferList
* io_data
) {
871 AudioSynchronizedStream
* stream
=
872 static_cast<AudioSynchronizedStream
*>(user_data
);
875 return stream
->HandleInputCallback(
883 OSStatus
AudioSynchronizedStream::VarispeedProc(
885 AudioUnitRenderActionFlags
* io_action_flags
,
886 const AudioTimeStamp
* time_stamp
,
888 UInt32 number_of_frames
,
889 AudioBufferList
* io_data
) {
890 AudioSynchronizedStream
* stream
=
891 static_cast<AudioSynchronizedStream
*>(user_data
);
894 return stream
->HandleVarispeedCallback(
902 OSStatus
AudioSynchronizedStream::OutputProc(
904 AudioUnitRenderActionFlags
* io_action_flags
,
905 const AudioTimeStamp
* time_stamp
,
907 UInt32 number_of_frames
,
908 AudioBufferList
* io_data
) {
909 AudioSynchronizedStream
* stream
=
910 static_cast<AudioSynchronizedStream
*>(user_data
);
913 return stream
->HandleOutputCallback(
921 void AudioSynchronizedStream::AudioDeviceInfo::Initialize(
922 AudioDeviceID id
, bool is_input
) {
924 is_input_
= is_input
;
925 if (id_
== kAudioDeviceUnknown
)
928 UInt32 property_size
= sizeof(buffer_size_frames_
);
930 AudioObjectPropertyAddress pa
;
931 pa
.mSelector
= kAudioDevicePropertyBufferFrameSize
;
932 pa
.mScope
= kAudioObjectPropertyScopeWildcard
;
933 pa
.mElement
= kAudioObjectPropertyElementMaster
;
934 OSStatus result
= AudioObjectGetPropertyData(
940 &buffer_size_frames_
);
942 OSSTATUS_DCHECK(result
== noErr
, result
);