1 // Copyright 2017 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/scoped_audio_unit.h"
6 
7 #include "base/mac/mac_logging.h"
8 
9 namespace media {
10 
11 constexpr AudioComponentDescription desc = {kAudioUnitType_Output,
12                                             kAudioUnitSubType_HALOutput,
13                                             kAudioUnitManufacturer_Apple, 0, 0};
14 
DestroyAudioUnit(AudioUnit audio_unit)15 static void DestroyAudioUnit(AudioUnit audio_unit) {
16   OSStatus result = AudioUnitUninitialize(audio_unit);
17   OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
18       << "AudioUnitUninitialize() failed : " << audio_unit;
19   result = AudioComponentInstanceDispose(audio_unit);
20   OSSTATUS_DLOG_IF(ERROR, result != noErr, result)
21       << "AudioComponentInstanceDispose() failed : " << audio_unit;
22 }
23 
ScopedAudioUnit(AudioDeviceID device,AUElement element)24 ScopedAudioUnit::ScopedAudioUnit(AudioDeviceID device, AUElement element) {
25   AudioComponent comp = AudioComponentFindNext(0, &desc);
26   if (!comp)
27     return;
28 
29   AudioUnit audio_unit;
30   OSStatus result = AudioComponentInstanceNew(comp, &audio_unit);
31   if (result != noErr) {
32     OSSTATUS_DLOG(ERROR, result) << "AudioComponentInstanceNew() failed.";
33     return;
34   }
35 
36   const UInt32 enable_input_io = element == AUElement::INPUT ? 1 : 0;
37   result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO,
38                                 kAudioUnitScope_Input, AUElement::INPUT,
39                                 &enable_input_io, sizeof(enable_input_io));
40   if (result != noErr) {
41     OSSTATUS_DLOG(ERROR, result)
42         << "Failed to set input enable IO for audio unit.";
43     DestroyAudioUnit(audio_unit);
44     return;
45   }
46 
47   const UInt32 enable_output_io = !enable_input_io;
48   result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO,
49                                 kAudioUnitScope_Output, AUElement::OUTPUT,
50                                 &enable_output_io, sizeof(enable_output_io));
51   if (result != noErr) {
52     OSSTATUS_DLOG(ERROR, result)
53         << "Failed to set output enable IO for audio unit.";
54     DestroyAudioUnit(audio_unit);
55     return;
56   }
57 
58   result = AudioUnitSetProperty(
59       audio_unit, kAudioOutputUnitProperty_CurrentDevice,
60       kAudioUnitScope_Global, 0, &device, sizeof(AudioDeviceID));
61   if (result != noErr) {
62     OSSTATUS_DLOG(ERROR, result)
63         << "Failed to set current device for audio unit.";
64     DestroyAudioUnit(audio_unit);
65     return;
66   }
67 
68   audio_unit_ = audio_unit;
69 }
70 
~ScopedAudioUnit()71 ScopedAudioUnit::~ScopedAudioUnit() {
72   if (audio_unit_)
73     DestroyAudioUnit(audio_unit_);
74 }
75 
76 }  // namespace media
77