audio_low_latency_input_mac.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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_low_latency_input_mac.h"
6
7#include <CoreServices/CoreServices.h>
8
9#include "base/basictypes.h"
10#include "base/logging.h"
11#include "base/mac/mac_logging.h"
12#include "media/audio/audio_util.h"
13#include "media/audio/mac/audio_manager_mac.h"
14#include "media/base/data_buffer.h"
15
16namespace media {
17
18static const int kMinIntervalBetweenVolumeUpdatesMs = 1000;
19
20static std::ostream& operator<<(std::ostream& os,
21                                const AudioStreamBasicDescription& format) {
22  os << "sample rate       : " << format.mSampleRate << std::endl
23     << "format ID         : " << format.mFormatID << std::endl
24     << "format flags      : " << format.mFormatFlags << std::endl
25     << "bytes per packet  : " << format.mBytesPerPacket << std::endl
26     << "frames per packet : " << format.mFramesPerPacket << std::endl
27     << "bytes per frame   : " << format.mBytesPerFrame << std::endl
28     << "channels per frame: " << format.mChannelsPerFrame << std::endl
29     << "bits per channel  : " << format.mBitsPerChannel;
30  return os;
31}
32
33// See "Technical Note TN2091 - Device input using the HAL Output Audio Unit"
34// http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
35// for more details and background regarding this implementation.
36
37AUAudioInputStream::AUAudioInputStream(
38    AudioManagerMac* manager, const AudioParameters& params,
39    AudioDeviceID audio_device_id)
40    : manager_(manager),
41      sink_(NULL),
42      audio_unit_(0),
43      input_device_id_(audio_device_id),
44      started_(false),
45      hardware_latency_frames_(0),
46      number_of_channels_in_frame_(0) {
47  DCHECK(manager_);
48
49  // Set up the desired (output) format specified by the client.
50  format_.mSampleRate = params.sample_rate();
51  format_.mFormatID = kAudioFormatLinearPCM;
52  format_.mFormatFlags = kLinearPCMFormatFlagIsPacked |
53                         kLinearPCMFormatFlagIsSignedInteger;
54  format_.mBitsPerChannel = params.bits_per_sample();
55  format_.mChannelsPerFrame = params.channels();
56  format_.mFramesPerPacket = 1;  // uncompressed audio
57  format_.mBytesPerPacket = (format_.mBitsPerChannel *
58                             params.channels()) / 8;
59  format_.mBytesPerFrame = format_.mBytesPerPacket;
60  format_.mReserved = 0;
61
62  DVLOG(1) << "Desired ouput format: " << format_;
63
64  // Set number of sample frames per callback used by the internal audio layer.
65  // An internal FIFO is then utilized to adapt the internal size to the size
66  // requested by the client.
67  // Note that we  use the same native buffer size as for the output side here
68  // since the AUHAL implementation requires that both capture and render side
69  // use the same buffer size. See http://crbug.com/154352 for more details.
70  // TODO(xians): Get the audio parameters from the right device.
71  const AudioParameters parameters =
72      manager_->GetInputStreamParameters(AudioManagerBase::kDefaultDeviceId);
73  number_of_frames_ = parameters.frames_per_buffer();
74  DVLOG(1) << "Size of data buffer in frames : " << number_of_frames_;
75
76  // Derive size (in bytes) of the buffers that we will render to.
77  UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame;
78  DVLOG(1) << "Size of data buffer in bytes : " << data_byte_size;
79
80  // Allocate AudioBuffers to be used as storage for the received audio.
81  // The AudioBufferList structure works as a placeholder for the
82  // AudioBuffer structure, which holds a pointer to the actual data buffer.
83  audio_data_buffer_.reset(new uint8[data_byte_size]);
84  audio_buffer_list_.mNumberBuffers = 1;
85
86  AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers;
87  audio_buffer->mNumberChannels = params.channels();
88  audio_buffer->mDataByteSize = data_byte_size;
89  audio_buffer->mData = audio_data_buffer_.get();
90
91  // Set up an internal FIFO buffer that will accumulate recorded audio frames
92  // until a requested size is ready to be sent to the client.
93  // It is not possible to ask for less than |kAudioFramesPerCallback| number of
94  // audio frames.
95  const size_t requested_size_frames =
96      params.GetBytesPerBuffer() / format_.mBytesPerPacket;
97  DCHECK_GE(requested_size_frames, number_of_frames_);
98  requested_size_bytes_ = requested_size_frames * format_.mBytesPerFrame;
99  DVLOG(1) << "Requested buffer size in bytes : " << requested_size_bytes_;
100  DLOG_IF(INFO, requested_size_frames > number_of_frames_) << "FIFO is used";
101
102  // Allocate some extra memory to avoid memory reallocations.
103  // Ensure that the size is an even multiple of |number_of_frames_ and
104  // larger than |requested_size_frames|.
105  // Example: number_of_frames_=128, requested_size_frames=480 =>
106  // allocated space equals 4*128=512 audio frames
107  const int max_forward_capacity = format_.mBytesPerFrame * number_of_frames_ *
108      ((requested_size_frames / number_of_frames_) + 1);
109  fifo_.reset(new media::SeekableBuffer(0, max_forward_capacity));
110
111  data_ = new media::DataBuffer(requested_size_bytes_);
112}
113
114AUAudioInputStream::~AUAudioInputStream() {}
115
116// Obtain and open the AUHAL AudioOutputUnit for recording.
117bool AUAudioInputStream::Open() {
118  // Verify that we are not already opened.
119  if (audio_unit_)
120    return false;
121
122  // Verify that we have a valid device.
123  if (input_device_id_ == kAudioObjectUnknown) {
124    NOTREACHED() << "Device ID is unknown";
125    return false;
126  }
127
128  // Start by obtaining an AudioOuputUnit using an AUHAL component description.
129
130  Component comp;
131  ComponentDescription desc;
132
133  // Description for the Audio Unit we want to use (AUHAL in this case).
134  desc.componentType = kAudioUnitType_Output;
135  desc.componentSubType = kAudioUnitSubType_HALOutput;
136  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
137  desc.componentFlags = 0;
138  desc.componentFlagsMask = 0;
139  comp = FindNextComponent(0, &desc);
140  DCHECK(comp);
141
142  // Get access to the service provided by the specified Audio Unit.
143  OSStatus result = OpenAComponent(comp, &audio_unit_);
144  if (result) {
145    HandleError(result);
146    return false;
147  }
148
149  // Enable IO on the input scope of the Audio Unit.
150
151  // After creating the AUHAL object, we must enable IO on the input scope
152  // of the Audio Unit to obtain the device input. Input must be explicitly
153  // enabled with the kAudioOutputUnitProperty_EnableIO property on Element 1
154  // of the AUHAL. Beacause the AUHAL can be used for both input and output,
155  // we must also disable IO on the output scope.
156
157  UInt32 enableIO = 1;
158
159  // Enable input on the AUHAL.
160  result = AudioUnitSetProperty(audio_unit_,
161                                kAudioOutputUnitProperty_EnableIO,
162                                kAudioUnitScope_Input,
163                                1,          // input element 1
164                                &enableIO,  // enable
165                                sizeof(enableIO));
166  if (result) {
167    HandleError(result);
168    return false;
169  }
170
171  // Disable output on the AUHAL.
172  enableIO = 0;
173  result = AudioUnitSetProperty(audio_unit_,
174                                kAudioOutputUnitProperty_EnableIO,
175                                kAudioUnitScope_Output,
176                                0,          // output element 0
177                                &enableIO,  // disable
178                                sizeof(enableIO));
179  if (result) {
180    HandleError(result);
181    return false;
182  }
183
184  // Next, set the audio device to be the Audio Unit's current device.
185  // Note that, devices can only be set to the AUHAL after enabling IO.
186  result = AudioUnitSetProperty(audio_unit_,
187                                kAudioOutputUnitProperty_CurrentDevice,
188                                kAudioUnitScope_Global,
189                                0,
190                                &input_device_id_,
191                                sizeof(input_device_id_));
192  if (result) {
193    HandleError(result);
194    return false;
195  }
196
197  // Register the input procedure for the AUHAL.
198  // This procedure will be called when the AUHAL has received new data
199  // from the input device.
200  AURenderCallbackStruct callback;
201  callback.inputProc = InputProc;
202  callback.inputProcRefCon = this;
203  result = AudioUnitSetProperty(audio_unit_,
204                                kAudioOutputUnitProperty_SetInputCallback,
205                                kAudioUnitScope_Global,
206                                0,
207                                &callback,
208                                sizeof(callback));
209  if (result) {
210    HandleError(result);
211    return false;
212  }
213
214  // Set up the the desired (output) format.
215  // For obtaining input from a device, the device format is always expressed
216  // on the output scope of the AUHAL's Element 1.
217  result = AudioUnitSetProperty(audio_unit_,
218                                kAudioUnitProperty_StreamFormat,
219                                kAudioUnitScope_Output,
220                                1,
221                                &format_,
222                                sizeof(format_));
223  if (result) {
224    HandleError(result);
225    return false;
226  }
227
228  // Set the desired number of frames in the IO buffer (output scope).
229  // WARNING: Setting this value changes the frame size for all audio units in
230  // the current process.  It's imperative that the input and output frame sizes
231  // be the same as the frames_per_buffer() returned by
232  // GetInputStreamParameters().
233  // TODO(henrika): Due to http://crrev.com/159666 this is currently not true
234  // and should be fixed, a CHECK() should be added at that time.
235  result = AudioUnitSetProperty(audio_unit_,
236                                kAudioDevicePropertyBufferFrameSize,
237                                kAudioUnitScope_Output,
238                                1,
239                                &number_of_frames_,  // size is set in the ctor
240                                sizeof(number_of_frames_));
241  if (result) {
242    HandleError(result);
243    return false;
244  }
245
246  // Finally, initialize the audio unit and ensure that it is ready to render.
247  // Allocates memory according to the maximum number of audio frames
248  // it can produce in response to a single render call.
249  result = AudioUnitInitialize(audio_unit_);
250  if (result) {
251    HandleError(result);
252    return false;
253  }
254
255  // The hardware latency is fixed and will not change during the call.
256  hardware_latency_frames_ = GetHardwareLatency();
257
258  // The master channel is 0, Left and right are channels 1 and 2.
259  // And the master channel is not counted in |number_of_channels_in_frame_|.
260  number_of_channels_in_frame_ = GetNumberOfChannelsFromStream();
261
262  return true;
263}
264
265void AUAudioInputStream::Start(AudioInputCallback* callback) {
266  DCHECK(callback);
267  DLOG_IF(ERROR, !audio_unit_) << "Open() has not been called successfully";
268  if (started_ || !audio_unit_)
269    return;
270  sink_ = callback;
271  StartAgc();
272  OSStatus result = AudioOutputUnitStart(audio_unit_);
273  if (result == noErr) {
274    started_ = true;
275  }
276  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
277      << "Failed to start acquiring data";
278}
279
280void AUAudioInputStream::Stop() {
281  if (!started_)
282    return;
283  StopAgc();
284  OSStatus result = AudioOutputUnitStop(audio_unit_);
285  if (result == noErr) {
286    started_ = false;
287  }
288  OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
289      << "Failed to stop acquiring data";
290}
291
292void AUAudioInputStream::Close() {
293  // It is valid to call Close() before calling open or Start().
294  // It is also valid to call Close() after Start() has been called.
295  if (started_) {
296    Stop();
297  }
298  if (audio_unit_) {
299    // Deallocate the audio unit’s resources.
300    AudioUnitUninitialize(audio_unit_);
301
302    // Terminates our connection to the AUHAL component.
303    CloseComponent(audio_unit_);
304    audio_unit_ = 0;
305  }
306  if (sink_) {
307    sink_->OnClose(this);
308    sink_ = NULL;
309  }
310
311  // Inform the audio manager that we have been closed. This can cause our
312  // destruction.
313  manager_->ReleaseInputStream(this);
314}
315
316double AUAudioInputStream::GetMaxVolume() {
317  // Verify that we have a valid device.
318  if (input_device_id_ == kAudioObjectUnknown) {
319    NOTREACHED() << "Device ID is unknown";
320    return 0.0;
321  }
322
323  // Query if any of the master, left or right channels has volume control.
324  for (int i = 0; i <= number_of_channels_in_frame_; ++i) {
325    // If the volume is settable, the  valid volume range is [0.0, 1.0].
326    if (IsVolumeSettableOnChannel(i))
327      return 1.0;
328  }
329
330  // Volume control is not available for the audio stream.
331  return 0.0;
332}
333
334void AUAudioInputStream::SetVolume(double volume) {
335  DVLOG(1) << "SetVolume(volume=" << volume << ")";
336  DCHECK_GE(volume, 0.0);
337  DCHECK_LE(volume, 1.0);
338
339  // Verify that we have a valid device.
340  if (input_device_id_ == kAudioObjectUnknown) {
341    NOTREACHED() << "Device ID is unknown";
342    return;
343  }
344
345  Float32 volume_float32 = static_cast<Float32>(volume);
346  AudioObjectPropertyAddress property_address = {
347    kAudioDevicePropertyVolumeScalar,
348    kAudioDevicePropertyScopeInput,
349    kAudioObjectPropertyElementMaster
350  };
351
352  // Try to set the volume for master volume channel.
353  if (IsVolumeSettableOnChannel(kAudioObjectPropertyElementMaster)) {
354    OSStatus result = AudioObjectSetPropertyData(input_device_id_,
355                                                 &property_address,
356                                                 0,
357                                                 NULL,
358                                                 sizeof(volume_float32),
359                                                 &volume_float32);
360    if (result != noErr) {
361      DLOG(WARNING) << "Failed to set volume to " << volume_float32;
362    }
363    return;
364  }
365
366  // There is no master volume control, try to set volume for each channel.
367  int successful_channels = 0;
368  for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
369    property_address.mElement = static_cast<UInt32>(i);
370    if (IsVolumeSettableOnChannel(i)) {
371      OSStatus result = AudioObjectSetPropertyData(input_device_id_,
372                                                   &property_address,
373                                                   0,
374                                                   NULL,
375                                                   sizeof(volume_float32),
376                                                   &volume_float32);
377      if (result == noErr)
378        ++successful_channels;
379    }
380  }
381
382  DLOG_IF(WARNING, successful_channels == 0)
383      << "Failed to set volume to " << volume_float32;
384
385  // Update the AGC volume level based on the last setting above. Note that,
386  // the volume-level resolution is not infinite and it is therefore not
387  // possible to assume that the volume provided as input parameter can be
388  // used directly. Instead, a new query to the audio hardware is required.
389  // This method does nothing if AGC is disabled.
390  UpdateAgcVolume();
391}
392
393double AUAudioInputStream::GetVolume() {
394  // Verify that we have a valid device.
395  if (input_device_id_ == kAudioObjectUnknown){
396    NOTREACHED() << "Device ID is unknown";
397    return 0.0;
398  }
399
400  AudioObjectPropertyAddress property_address = {
401    kAudioDevicePropertyVolumeScalar,
402    kAudioDevicePropertyScopeInput,
403    kAudioObjectPropertyElementMaster
404  };
405
406  if (AudioObjectHasProperty(input_device_id_, &property_address)) {
407    // The device supports master volume control, get the volume from the
408    // master channel.
409    Float32 volume_float32 = 0.0;
410    UInt32 size = sizeof(volume_float32);
411    OSStatus result = AudioObjectGetPropertyData(input_device_id_,
412                                                 &property_address,
413                                                 0,
414                                                 NULL,
415                                                 &size,
416                                                 &volume_float32);
417    if (result == noErr)
418      return static_cast<double>(volume_float32);
419  } else {
420    // There is no master volume control, try to get the average volume of
421    // all the channels.
422    Float32 volume_float32 = 0.0;
423    int successful_channels = 0;
424    for (int i = 1; i <= number_of_channels_in_frame_; ++i) {
425      property_address.mElement = static_cast<UInt32>(i);
426      if (AudioObjectHasProperty(input_device_id_, &property_address)) {
427        Float32 channel_volume = 0;
428        UInt32 size = sizeof(channel_volume);
429        OSStatus result = AudioObjectGetPropertyData(input_device_id_,
430                                                     &property_address,
431                                                     0,
432                                                     NULL,
433                                                     &size,
434                                                     &channel_volume);
435        if (result == noErr) {
436          volume_float32 += channel_volume;
437          ++successful_channels;
438        }
439      }
440    }
441
442    // Get the average volume of the channels.
443    if (successful_channels != 0)
444      return static_cast<double>(volume_float32 / successful_channels);
445  }
446
447  DLOG(WARNING) << "Failed to get volume";
448  return 0.0;
449}
450
451// AUHAL AudioDeviceOutput unit callback
452OSStatus AUAudioInputStream::InputProc(void* user_data,
453                                       AudioUnitRenderActionFlags* flags,
454                                       const AudioTimeStamp* time_stamp,
455                                       UInt32 bus_number,
456                                       UInt32 number_of_frames,
457                                       AudioBufferList* io_data) {
458  // Verify that the correct bus is used (Input bus/Element 1)
459  DCHECK_EQ(bus_number, static_cast<UInt32>(1));
460  AUAudioInputStream* audio_input =
461      reinterpret_cast<AUAudioInputStream*>(user_data);
462  DCHECK(audio_input);
463  if (!audio_input)
464    return kAudioUnitErr_InvalidElement;
465
466  // Receive audio from the AUHAL from the output scope of the Audio Unit.
467  OSStatus result = AudioUnitRender(audio_input->audio_unit(),
468                                    flags,
469                                    time_stamp,
470                                    bus_number,
471                                    number_of_frames,
472                                    audio_input->audio_buffer_list());
473  if (result)
474    return result;
475
476  // Deliver recorded data to the consumer as a callback.
477  return audio_input->Provide(number_of_frames,
478                              audio_input->audio_buffer_list(),
479                              time_stamp);
480}
481
482OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
483                                     AudioBufferList* io_data,
484                                     const AudioTimeStamp* time_stamp) {
485  // Update the capture latency.
486  double capture_latency_frames = GetCaptureLatency(time_stamp);
487
488  // The AGC volume level is updated once every second on a separate thread.
489  // Note that, |volume| is also updated each time SetVolume() is called
490  // through IPC by the render-side AGC.
491  double normalized_volume = 0.0;
492  GetAgcVolume(&normalized_volume);
493
494  AudioBuffer& buffer = io_data->mBuffers[0];
495  uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
496  uint32 capture_delay_bytes = static_cast<uint32>
497      ((capture_latency_frames + 0.5) * format_.mBytesPerFrame);
498  DCHECK(audio_data);
499  if (!audio_data)
500    return kAudioUnitErr_InvalidElement;
501
502  // Accumulate captured audio in FIFO until we can match the output size
503  // requested by the client.
504  fifo_->Append(audio_data, buffer.mDataByteSize);
505
506  // Deliver recorded data to the client as soon as the FIFO contains a
507  // sufficient amount.
508  if (fifo_->forward_bytes() >= requested_size_bytes_) {
509    // Account for the extra delay added by the FIFO.
510    capture_delay_bytes += fifo_->forward_bytes();
511
512    // Read from FIFO into temporary data buffer.
513    fifo_->Read(data_->GetWritableData(), requested_size_bytes_);
514
515    // Deliver data packet, delay estimation and volume level to the user.
516    sink_->OnData(this,
517                  data_->GetData(),
518                  requested_size_bytes_,
519                  capture_delay_bytes,
520                  normalized_volume);
521  }
522
523  return noErr;
524}
525
526int AUAudioInputStream::HardwareSampleRate() {
527  // Determine the default input device's sample-rate.
528  AudioDeviceID device_id = kAudioObjectUnknown;
529  UInt32 info_size = sizeof(device_id);
530
531  AudioObjectPropertyAddress default_input_device_address = {
532    kAudioHardwarePropertyDefaultInputDevice,
533    kAudioObjectPropertyScopeGlobal,
534    kAudioObjectPropertyElementMaster
535  };
536  OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
537                                               &default_input_device_address,
538                                               0,
539                                               0,
540                                               &info_size,
541                                               &device_id);
542  if (result != noErr)
543    return 0.0;
544
545  Float64 nominal_sample_rate;
546  info_size = sizeof(nominal_sample_rate);
547
548  AudioObjectPropertyAddress nominal_sample_rate_address = {
549    kAudioDevicePropertyNominalSampleRate,
550    kAudioObjectPropertyScopeGlobal,
551    kAudioObjectPropertyElementMaster
552  };
553  result = AudioObjectGetPropertyData(device_id,
554                                      &nominal_sample_rate_address,
555                                      0,
556                                      0,
557                                      &info_size,
558                                      &nominal_sample_rate);
559  if (result != noErr)
560    return 0.0;
561
562  return static_cast<int>(nominal_sample_rate);
563}
564
565double AUAudioInputStream::GetHardwareLatency() {
566  if (!audio_unit_ || input_device_id_ == kAudioObjectUnknown) {
567    DLOG(WARNING) << "Audio unit object is NULL or device ID is unknown";
568    return 0.0;
569  }
570
571  // Get audio unit latency.
572  Float64 audio_unit_latency_sec = 0.0;
573  UInt32 size = sizeof(audio_unit_latency_sec);
574  OSStatus result = AudioUnitGetProperty(audio_unit_,
575                                         kAudioUnitProperty_Latency,
576                                         kAudioUnitScope_Global,
577                                         0,
578                                         &audio_unit_latency_sec,
579                                         &size);
580  OSSTATUS_DLOG_IF(WARNING, result != noErr, result)
581      << "Could not get audio unit latency";
582
583  // Get input audio device latency.
584  AudioObjectPropertyAddress property_address = {
585    kAudioDevicePropertyLatency,
586    kAudioDevicePropertyScopeInput,
587    kAudioObjectPropertyElementMaster
588  };
589  UInt32 device_latency_frames = 0;
590  size = sizeof(device_latency_frames);
591  result = AudioObjectGetPropertyData(input_device_id_,
592                                      &property_address,
593                                      0,
594                                      NULL,
595                                      &size,
596                                      &device_latency_frames);
597  DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
598
599  return static_cast<double>((audio_unit_latency_sec *
600      format_.mSampleRate) + device_latency_frames);
601}
602
603double AUAudioInputStream::GetCaptureLatency(
604    const AudioTimeStamp* input_time_stamp) {
605  // Get the delay between between the actual recording instant and the time
606  // when the data packet is provided as a callback.
607  UInt64 capture_time_ns = AudioConvertHostTimeToNanos(
608      input_time_stamp->mHostTime);
609  UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
610  double delay_frames = static_cast<double>
611      (1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate);
612
613  // Total latency is composed by the dynamic latency and the fixed
614  // hardware latency.
615  return (delay_frames + hardware_latency_frames_);
616}
617
618int AUAudioInputStream::GetNumberOfChannelsFromStream() {
619  // Get the stream format, to be able to read the number of channels.
620  AudioObjectPropertyAddress property_address = {
621    kAudioDevicePropertyStreamFormat,
622    kAudioDevicePropertyScopeInput,
623    kAudioObjectPropertyElementMaster
624  };
625  AudioStreamBasicDescription stream_format;
626  UInt32 size = sizeof(stream_format);
627  OSStatus result = AudioObjectGetPropertyData(input_device_id_,
628                                               &property_address,
629                                               0,
630                                               NULL,
631                                               &size,
632                                               &stream_format);
633  if (result != noErr) {
634    DLOG(WARNING) << "Could not get stream format";
635    return 0;
636  }
637
638  return static_cast<int>(stream_format.mChannelsPerFrame);
639}
640
641void AUAudioInputStream::HandleError(OSStatus err) {
642  NOTREACHED() << "error " << GetMacOSStatusErrorString(err)
643               << " (" << err << ")";
644  if (sink_)
645    sink_->OnError(this);
646}
647
648bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) {
649  Boolean is_settable = false;
650  AudioObjectPropertyAddress property_address = {
651    kAudioDevicePropertyVolumeScalar,
652    kAudioDevicePropertyScopeInput,
653    static_cast<UInt32>(channel)
654  };
655  OSStatus result = AudioObjectIsPropertySettable(input_device_id_,
656                                                  &property_address,
657                                                  &is_settable);
658  return (result == noErr) ? is_settable : false;
659}
660
661}  // namespace media
662