1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "webrtc/modules/audio_device/mac/audio_mixer_manager_mac.h"
12 #include "webrtc/system_wrappers/include/trace.h"
13 
14 #include <unistd.h>  // getpid()
15 
16 namespace webrtc {
17 
18 #define WEBRTC_CA_RETURN_ON_ERR(expr)                                  \
19   do {                                                                 \
20     err = expr;                                                        \
21     if (err != noErr) {                                                \
22       logCAMsg(kTraceError, kTraceAudioDevice, _id, "Error in " #expr, \
23                (const char*) & err);                                   \
24       return -1;                                                       \
25     }                                                                  \
26   } while (0)
27 
28 #define WEBRTC_CA_LOG_ERR(expr)                                        \
29   do {                                                                 \
30     err = expr;                                                        \
31     if (err != noErr) {                                                \
32       logCAMsg(kTraceError, kTraceAudioDevice, _id, "Error in " #expr, \
33                (const char*) & err);                                   \
34     }                                                                  \
35   } while (0)
36 
37 #define WEBRTC_CA_LOG_WARN(expr)                                         \
38   do {                                                                   \
39     err = expr;                                                          \
40     if (err != noErr) {                                                  \
41       logCAMsg(kTraceWarning, kTraceAudioDevice, _id, "Error in " #expr, \
42                (const char*) & err);                                     \
43     }                                                                    \
44   } while (0)
45 
AudioMixerManagerMac(const int32_t id)46 AudioMixerManagerMac::AudioMixerManagerMac(const int32_t id)
47     : _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
48       _id(id),
49       _inputDeviceID(kAudioObjectUnknown),
50       _outputDeviceID(kAudioObjectUnknown),
51       _noInputChannels(0),
52       _noOutputChannels(0) {
53   WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s constructed",
54                __FUNCTION__);
55 }
56 
~AudioMixerManagerMac()57 AudioMixerManagerMac::~AudioMixerManagerMac() {
58   WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destructed",
59                __FUNCTION__);
60 
61   Close();
62 
63   delete &_critSect;
64 }
65 
66 // ============================================================================
67 //	                                PUBLIC METHODS
68 // ============================================================================
69 
Close()70 int32_t AudioMixerManagerMac::Close() {
71   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
72 
73   CriticalSectionScoped lock(&_critSect);
74 
75   CloseSpeaker();
76   CloseMicrophone();
77 
78   return 0;
79 }
80 
CloseSpeaker()81 int32_t AudioMixerManagerMac::CloseSpeaker() {
82   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
83 
84   CriticalSectionScoped lock(&_critSect);
85 
86   _outputDeviceID = kAudioObjectUnknown;
87   _noOutputChannels = 0;
88 
89   return 0;
90 }
91 
CloseMicrophone()92 int32_t AudioMixerManagerMac::CloseMicrophone() {
93   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
94 
95   CriticalSectionScoped lock(&_critSect);
96 
97   _inputDeviceID = kAudioObjectUnknown;
98   _noInputChannels = 0;
99 
100   return 0;
101 }
102 
OpenSpeaker(AudioDeviceID deviceID)103 int32_t AudioMixerManagerMac::OpenSpeaker(AudioDeviceID deviceID) {
104   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
105                "AudioMixerManagerMac::OpenSpeaker(id=%d)", deviceID);
106 
107   CriticalSectionScoped lock(&_critSect);
108 
109   OSStatus err = noErr;
110   UInt32 size = 0;
111   pid_t hogPid = -1;
112 
113   _outputDeviceID = deviceID;
114 
115   // Check which process, if any, has hogged the device.
116   AudioObjectPropertyAddress propertyAddress = {
117       kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeOutput, 0};
118 
119   size = sizeof(hogPid);
120   WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
121       _outputDeviceID, &propertyAddress, 0, NULL, &size, &hogPid));
122 
123   if (hogPid == -1) {
124     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
125                  " No process has hogged the input device");
126   }
127   // getpid() is apparently "always successful"
128   else if (hogPid == getpid()) {
129     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
130                  " Our process has hogged the input device");
131   } else {
132     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
133                  " Another process (pid = %d) has hogged the input device",
134                  static_cast<int>(hogPid));
135 
136     return -1;
137   }
138 
139   // get number of channels from stream format
140   propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
141 
142   // Get the stream format, to be able to read the number of channels.
143   AudioStreamBasicDescription streamFormat;
144   size = sizeof(AudioStreamBasicDescription);
145   memset(&streamFormat, 0, size);
146   WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
147       _outputDeviceID, &propertyAddress, 0, NULL, &size, &streamFormat));
148 
149   _noOutputChannels = streamFormat.mChannelsPerFrame;
150 
151   return 0;
152 }
153 
OpenMicrophone(AudioDeviceID deviceID)154 int32_t AudioMixerManagerMac::OpenMicrophone(AudioDeviceID deviceID) {
155   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
156                "AudioMixerManagerMac::OpenMicrophone(id=%d)", deviceID);
157 
158   CriticalSectionScoped lock(&_critSect);
159 
160   OSStatus err = noErr;
161   UInt32 size = 0;
162   pid_t hogPid = -1;
163 
164   _inputDeviceID = deviceID;
165 
166   // Check which process, if any, has hogged the device.
167   AudioObjectPropertyAddress propertyAddress = {
168       kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeInput, 0};
169   size = sizeof(hogPid);
170   WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
171       _inputDeviceID, &propertyAddress, 0, NULL, &size, &hogPid));
172   if (hogPid == -1) {
173     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
174                  " No process has hogged the input device");
175   }
176   // getpid() is apparently "always successful"
177   else if (hogPid == getpid()) {
178     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
179                  " Our process has hogged the input device");
180   } else {
181     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
182                  " Another process (pid = %d) has hogged the input device",
183                  static_cast<int>(hogPid));
184 
185     return -1;
186   }
187 
188   // get number of channels from stream format
189   propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
190 
191   // Get the stream format, to be able to read the number of channels.
192   AudioStreamBasicDescription streamFormat;
193   size = sizeof(AudioStreamBasicDescription);
194   memset(&streamFormat, 0, size);
195   WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
196       _inputDeviceID, &propertyAddress, 0, NULL, &size, &streamFormat));
197 
198   _noInputChannels = streamFormat.mChannelsPerFrame;
199 
200   return 0;
201 }
202 
SpeakerIsInitialized() const203 bool AudioMixerManagerMac::SpeakerIsInitialized() const {
204   WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__);
205 
206   return (_outputDeviceID != kAudioObjectUnknown);
207 }
208 
MicrophoneIsInitialized() const209 bool AudioMixerManagerMac::MicrophoneIsInitialized() const {
210   WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__);
211 
212   return (_inputDeviceID != kAudioObjectUnknown);
213 }
214 
SetSpeakerVolume(uint32_t volume)215 int32_t AudioMixerManagerMac::SetSpeakerVolume(uint32_t volume) {
216   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
217                "AudioMixerManagerMac::SetSpeakerVolume(volume=%u)", volume);
218 
219   CriticalSectionScoped lock(&_critSect);
220 
221   if (_outputDeviceID == kAudioObjectUnknown) {
222     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
223                  "  device ID has not been set");
224     return -1;
225   }
226 
227   OSStatus err = noErr;
228   UInt32 size = 0;
229   bool success = false;
230 
231   // volume range is 0.0 - 1.0, convert from 0 -255
232   const Float32 vol = (Float32)(volume / 255.0);
233 
234   assert(vol <= 1.0 && vol >= 0.0);
235 
236   // Does the capture device have a master volume control?
237   // If so, use it exclusively.
238   AudioObjectPropertyAddress propertyAddress = {
239       kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 0};
240   Boolean isSettable = false;
241   err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
242                                       &isSettable);
243   if (err == noErr && isSettable) {
244     size = sizeof(vol);
245     WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
246         _outputDeviceID, &propertyAddress, 0, NULL, size, &vol));
247 
248     return 0;
249   }
250 
251   // Otherwise try to set each channel.
252   for (UInt32 i = 1; i <= _noOutputChannels; i++) {
253     propertyAddress.mElement = i;
254     isSettable = false;
255     err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
256                                         &isSettable);
257     if (err == noErr && isSettable) {
258       size = sizeof(vol);
259       WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
260           _outputDeviceID, &propertyAddress, 0, NULL, size, &vol));
261     }
262     success = true;
263   }
264 
265   if (!success) {
266     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
267                  " Unable to set a volume on any output channel");
268     return -1;
269   }
270 
271   return 0;
272 }
273 
SpeakerVolume(uint32_t & volume) const274 int32_t AudioMixerManagerMac::SpeakerVolume(uint32_t& volume) const {
275   if (_outputDeviceID == kAudioObjectUnknown) {
276     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
277                  "  device ID has not been set");
278     return -1;
279   }
280 
281   OSStatus err = noErr;
282   UInt32 size = 0;
283   unsigned int channels = 0;
284   Float32 channelVol = 0;
285   Float32 vol = 0;
286 
287   // Does the device have a master volume control?
288   // If so, use it exclusively.
289   AudioObjectPropertyAddress propertyAddress = {
290       kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 0};
291   Boolean hasProperty =
292       AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
293   if (hasProperty) {
294     size = sizeof(vol);
295     WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
296         _outputDeviceID, &propertyAddress, 0, NULL, &size, &vol));
297 
298     // vol 0.0 to 1.0 -> convert to 0 - 255
299     volume = static_cast<uint32_t>(vol * 255 + 0.5);
300   } else {
301     // Otherwise get the average volume across channels.
302     vol = 0;
303     for (UInt32 i = 1; i <= _noOutputChannels; i++) {
304       channelVol = 0;
305       propertyAddress.mElement = i;
306       hasProperty = AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
307       if (hasProperty) {
308         size = sizeof(channelVol);
309         WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
310             _outputDeviceID, &propertyAddress, 0, NULL, &size, &channelVol));
311 
312         vol += channelVol;
313         channels++;
314       }
315     }
316 
317     if (channels == 0) {
318       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
319                    " Unable to get a volume on any channel");
320       return -1;
321     }
322 
323     assert(channels > 0);
324     // vol 0.0 to 1.0 -> convert to 0 - 255
325     volume = static_cast<uint32_t>(255 * vol / channels + 0.5);
326   }
327 
328   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
329                "     AudioMixerManagerMac::SpeakerVolume() => vol=%i", vol);
330 
331   return 0;
332 }
333 
MaxSpeakerVolume(uint32_t & maxVolume) const334 int32_t AudioMixerManagerMac::MaxSpeakerVolume(uint32_t& maxVolume) const {
335   if (_outputDeviceID == kAudioObjectUnknown) {
336     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
337                  "  device ID has not been set");
338     return -1;
339   }
340 
341   // volume range is 0.0 to 1.0
342   // we convert that to 0 - 255
343   maxVolume = 255;
344 
345   return 0;
346 }
347 
MinSpeakerVolume(uint32_t & minVolume) const348 int32_t AudioMixerManagerMac::MinSpeakerVolume(uint32_t& minVolume) const {
349   if (_outputDeviceID == kAudioObjectUnknown) {
350     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
351                  "  device ID has not been set");
352     return -1;
353   }
354 
355   // volume range is 0.0 to 1.0
356   // we convert that to 0 - 255
357   minVolume = 0;
358 
359   return 0;
360 }
361 
SpeakerVolumeStepSize(uint16_t & stepSize) const362 int32_t AudioMixerManagerMac::SpeakerVolumeStepSize(uint16_t& stepSize) const {
363   if (_outputDeviceID == kAudioObjectUnknown) {
364     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
365                  "  device ID has not been set");
366     return -1;
367   }
368 
369   // volume range is 0.0 to 1.0
370   // we convert that to 0 - 255
371   stepSize = 1;
372 
373   return 0;
374 }
375 
SpeakerVolumeIsAvailable(bool & available)376 int32_t AudioMixerManagerMac::SpeakerVolumeIsAvailable(bool& available) {
377   if (_outputDeviceID == kAudioObjectUnknown) {
378     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
379                  "  device ID has not been set");
380     return -1;
381   }
382 
383   OSStatus err = noErr;
384 
385   // Does the capture device have a master volume control?
386   // If so, use it exclusively.
387   AudioObjectPropertyAddress propertyAddress = {
388       kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 0};
389   Boolean isSettable = false;
390   err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
391                                       &isSettable);
392   if (err == noErr && isSettable) {
393     available = true;
394     return 0;
395   }
396 
397   // Otherwise try to set each channel.
398   for (UInt32 i = 1; i <= _noOutputChannels; i++) {
399     propertyAddress.mElement = i;
400     isSettable = false;
401     err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
402                                         &isSettable);
403     if (err != noErr || !isSettable) {
404       available = false;
405       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
406                    " Volume cannot be set for output channel %d, err=%d", i,
407                    err);
408       return -1;
409     }
410   }
411 
412   available = true;
413   return 0;
414 }
415 
SpeakerMuteIsAvailable(bool & available)416 int32_t AudioMixerManagerMac::SpeakerMuteIsAvailable(bool& available) {
417   if (_outputDeviceID == kAudioObjectUnknown) {
418     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
419                  "  device ID has not been set");
420     return -1;
421   }
422 
423   OSStatus err = noErr;
424 
425   // Does the capture device have a master mute control?
426   // If so, use it exclusively.
427   AudioObjectPropertyAddress propertyAddress = {
428       kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, 0};
429   Boolean isSettable = false;
430   err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
431                                       &isSettable);
432   if (err == noErr && isSettable) {
433     available = true;
434     return 0;
435   }
436 
437   // Otherwise try to set each channel.
438   for (UInt32 i = 1; i <= _noOutputChannels; i++) {
439     propertyAddress.mElement = i;
440     isSettable = false;
441     err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
442                                         &isSettable);
443     if (err != noErr || !isSettable) {
444       available = false;
445       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
446                    " Mute cannot be set for output channel %d, err=%d", i, err);
447       return -1;
448     }
449   }
450 
451   available = true;
452   return 0;
453 }
454 
SetSpeakerMute(bool enable)455 int32_t AudioMixerManagerMac::SetSpeakerMute(bool enable) {
456   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
457                "AudioMixerManagerMac::SetSpeakerMute(enable=%u)", enable);
458 
459   CriticalSectionScoped lock(&_critSect);
460 
461   if (_outputDeviceID == kAudioObjectUnknown) {
462     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
463                  "  device ID has not been set");
464     return -1;
465   }
466 
467   OSStatus err = noErr;
468   UInt32 size = 0;
469   UInt32 mute = enable ? 1 : 0;
470   bool success = false;
471 
472   // Does the render device have a master mute control?
473   // If so, use it exclusively.
474   AudioObjectPropertyAddress propertyAddress = {
475       kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, 0};
476   Boolean isSettable = false;
477   err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
478                                       &isSettable);
479   if (err == noErr && isSettable) {
480     size = sizeof(mute);
481     WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
482         _outputDeviceID, &propertyAddress, 0, NULL, size, &mute));
483 
484     return 0;
485   }
486 
487   // Otherwise try to set each channel.
488   for (UInt32 i = 1; i <= _noOutputChannels; i++) {
489     propertyAddress.mElement = i;
490     isSettable = false;
491     err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
492                                         &isSettable);
493     if (err == noErr && isSettable) {
494       size = sizeof(mute);
495       WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
496           _outputDeviceID, &propertyAddress, 0, NULL, size, &mute));
497     }
498     success = true;
499   }
500 
501   if (!success) {
502     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
503                  " Unable to set mute on any input channel");
504     return -1;
505   }
506 
507   return 0;
508 }
509 
SpeakerMute(bool & enabled) const510 int32_t AudioMixerManagerMac::SpeakerMute(bool& enabled) const {
511   if (_outputDeviceID == kAudioObjectUnknown) {
512     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
513                  "  device ID has not been set");
514     return -1;
515   }
516 
517   OSStatus err = noErr;
518   UInt32 size = 0;
519   unsigned int channels = 0;
520   UInt32 channelMuted = 0;
521   UInt32 muted = 0;
522 
523   // Does the device have a master volume control?
524   // If so, use it exclusively.
525   AudioObjectPropertyAddress propertyAddress = {
526       kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, 0};
527   Boolean hasProperty =
528       AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
529   if (hasProperty) {
530     size = sizeof(muted);
531     WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
532         _outputDeviceID, &propertyAddress, 0, NULL, &size, &muted));
533 
534     // 1 means muted
535     enabled = static_cast<bool>(muted);
536   } else {
537     // Otherwise check if all channels are muted.
538     for (UInt32 i = 1; i <= _noOutputChannels; i++) {
539       muted = 0;
540       propertyAddress.mElement = i;
541       hasProperty = AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
542       if (hasProperty) {
543         size = sizeof(channelMuted);
544         WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
545             _outputDeviceID, &propertyAddress, 0, NULL, &size, &channelMuted));
546 
547         muted = (muted && channelMuted);
548         channels++;
549       }
550     }
551 
552     if (channels == 0) {
553       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
554                    " Unable to get mute for any channel");
555       return -1;
556     }
557 
558     assert(channels > 0);
559     // 1 means muted
560     enabled = static_cast<bool>(muted);
561   }
562 
563   WEBRTC_TRACE(
564       kTraceInfo, kTraceAudioDevice, _id,
565       "     AudioMixerManagerMac::SpeakerMute() => enabled=%d, enabled");
566 
567   return 0;
568 }
569 
StereoPlayoutIsAvailable(bool & available)570 int32_t AudioMixerManagerMac::StereoPlayoutIsAvailable(bool& available) {
571   if (_outputDeviceID == kAudioObjectUnknown) {
572     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
573                  "  device ID has not been set");
574     return -1;
575   }
576 
577   available = (_noOutputChannels == 2);
578   return 0;
579 }
580 
StereoRecordingIsAvailable(bool & available)581 int32_t AudioMixerManagerMac::StereoRecordingIsAvailable(bool& available) {
582   if (_inputDeviceID == kAudioObjectUnknown) {
583     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
584                  "  device ID has not been set");
585     return -1;
586   }
587 
588   available = (_noInputChannels == 2);
589   return 0;
590 }
591 
MicrophoneMuteIsAvailable(bool & available)592 int32_t AudioMixerManagerMac::MicrophoneMuteIsAvailable(bool& available) {
593   if (_inputDeviceID == kAudioObjectUnknown) {
594     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
595                  "  device ID has not been set");
596     return -1;
597   }
598 
599   OSStatus err = noErr;
600 
601   // Does the capture device have a master mute control?
602   // If so, use it exclusively.
603   AudioObjectPropertyAddress propertyAddress = {
604       kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput, 0};
605   Boolean isSettable = false;
606   err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
607                                       &isSettable);
608   if (err == noErr && isSettable) {
609     available = true;
610     return 0;
611   }
612 
613   // Otherwise try to set each channel.
614   for (UInt32 i = 1; i <= _noInputChannels; i++) {
615     propertyAddress.mElement = i;
616     isSettable = false;
617     err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
618                                         &isSettable);
619     if (err != noErr || !isSettable) {
620       available = false;
621       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
622                    " Mute cannot be set for output channel %d, err=%d", i, err);
623       return -1;
624     }
625   }
626 
627   available = true;
628   return 0;
629 }
630 
SetMicrophoneMute(bool enable)631 int32_t AudioMixerManagerMac::SetMicrophoneMute(bool enable) {
632   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
633                "AudioMixerManagerMac::SetMicrophoneMute(enable=%u)", enable);
634 
635   CriticalSectionScoped lock(&_critSect);
636 
637   if (_inputDeviceID == kAudioObjectUnknown) {
638     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
639                  "  device ID has not been set");
640     return -1;
641   }
642 
643   OSStatus err = noErr;
644   UInt32 size = 0;
645   UInt32 mute = enable ? 1 : 0;
646   bool success = false;
647 
648   // Does the capture device have a master mute control?
649   // If so, use it exclusively.
650   AudioObjectPropertyAddress propertyAddress = {
651       kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput, 0};
652   Boolean isSettable = false;
653   err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
654                                       &isSettable);
655   if (err == noErr && isSettable) {
656     size = sizeof(mute);
657     WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
658         _inputDeviceID, &propertyAddress, 0, NULL, size, &mute));
659 
660     return 0;
661   }
662 
663   // Otherwise try to set each channel.
664   for (UInt32 i = 1; i <= _noInputChannels; i++) {
665     propertyAddress.mElement = i;
666     isSettable = false;
667     err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
668                                         &isSettable);
669     if (err == noErr && isSettable) {
670       size = sizeof(mute);
671       WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
672           _inputDeviceID, &propertyAddress, 0, NULL, size, &mute));
673     }
674     success = true;
675   }
676 
677   if (!success) {
678     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
679                  " Unable to set mute on any input channel");
680     return -1;
681   }
682 
683   return 0;
684 }
685 
MicrophoneMute(bool & enabled) const686 int32_t AudioMixerManagerMac::MicrophoneMute(bool& enabled) const {
687   if (_inputDeviceID == kAudioObjectUnknown) {
688     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
689                  "  device ID has not been set");
690     return -1;
691   }
692 
693   OSStatus err = noErr;
694   UInt32 size = 0;
695   unsigned int channels = 0;
696   UInt32 channelMuted = 0;
697   UInt32 muted = 0;
698 
699   // Does the device have a master volume control?
700   // If so, use it exclusively.
701   AudioObjectPropertyAddress propertyAddress = {
702       kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput, 0};
703   Boolean hasProperty =
704       AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
705   if (hasProperty) {
706     size = sizeof(muted);
707     WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
708         _inputDeviceID, &propertyAddress, 0, NULL, &size, &muted));
709 
710     // 1 means muted
711     enabled = static_cast<bool>(muted);
712   } else {
713     // Otherwise check if all channels are muted.
714     for (UInt32 i = 1; i <= _noInputChannels; i++) {
715       muted = 0;
716       propertyAddress.mElement = i;
717       hasProperty = AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
718       if (hasProperty) {
719         size = sizeof(channelMuted);
720         WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
721             _inputDeviceID, &propertyAddress, 0, NULL, &size, &channelMuted));
722 
723         muted = (muted && channelMuted);
724         channels++;
725       }
726     }
727 
728     if (channels == 0) {
729       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
730                    " Unable to get mute for any channel");
731       return -1;
732     }
733 
734     assert(channels > 0);
735     // 1 means muted
736     enabled = static_cast<bool>(muted);
737   }
738 
739   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
740                "     AudioMixerManagerMac::MicrophoneMute() => enabled=%d",
741                enabled);
742 
743   return 0;
744 }
745 
MicrophoneBoostIsAvailable(bool & available)746 int32_t AudioMixerManagerMac::MicrophoneBoostIsAvailable(bool& available) {
747   if (_inputDeviceID == kAudioObjectUnknown) {
748     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
749                  "  device ID has not been set");
750     return -1;
751   }
752 
753   available = false;  // No AudioObjectPropertySelector value for Mic Boost
754 
755   return 0;
756 }
757 
SetMicrophoneBoost(bool enable)758 int32_t AudioMixerManagerMac::SetMicrophoneBoost(bool enable) {
759   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
760                "AudioMixerManagerMac::SetMicrophoneBoost(enable=%u)", enable);
761 
762   CriticalSectionScoped lock(&_critSect);
763 
764   if (_inputDeviceID == kAudioObjectUnknown) {
765     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
766                  "  device ID has not been set");
767     return -1;
768   }
769 
770   // Ensure that the selected microphone has a valid boost control.
771   bool available(false);
772   MicrophoneBoostIsAvailable(available);
773   if (!available) {
774     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
775                  "  it is not possible to enable microphone boost");
776     return -1;
777   }
778 
779   // It is assumed that the call above fails!
780   return 0;
781 }
782 
MicrophoneBoost(bool & enabled) const783 int32_t AudioMixerManagerMac::MicrophoneBoost(bool& enabled) const {
784   if (_inputDeviceID == kAudioObjectUnknown) {
785     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
786                  "  device ID has not been set");
787     return -1;
788   }
789 
790   // Microphone boost cannot be enabled on this platform!
791   enabled = false;
792 
793   return 0;
794 }
795 
MicrophoneVolumeIsAvailable(bool & available)796 int32_t AudioMixerManagerMac::MicrophoneVolumeIsAvailable(bool& available) {
797   if (_inputDeviceID == kAudioObjectUnknown) {
798     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
799                  "  device ID has not been set");
800     return -1;
801   }
802 
803   OSStatus err = noErr;
804 
805   // Does the capture device have a master volume control?
806   // If so, use it exclusively.
807   AudioObjectPropertyAddress propertyAddress = {
808       kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, 0};
809   Boolean isSettable = false;
810   err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
811                                       &isSettable);
812   if (err == noErr && isSettable) {
813     available = true;
814     return 0;
815   }
816 
817   // Otherwise try to set each channel.
818   for (UInt32 i = 1; i <= _noInputChannels; i++) {
819     propertyAddress.mElement = i;
820     isSettable = false;
821     err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
822                                         &isSettable);
823     if (err != noErr || !isSettable) {
824       available = false;
825       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
826                    " Volume cannot be set for input channel %d, err=%d", i,
827                    err);
828       return -1;
829     }
830   }
831 
832   available = true;
833   return 0;
834 }
835 
SetMicrophoneVolume(uint32_t volume)836 int32_t AudioMixerManagerMac::SetMicrophoneVolume(uint32_t volume) {
837   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
838                "AudioMixerManagerMac::SetMicrophoneVolume(volume=%u)", volume);
839 
840   CriticalSectionScoped lock(&_critSect);
841 
842   if (_inputDeviceID == kAudioObjectUnknown) {
843     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
844                  "  device ID has not been set");
845     return -1;
846   }
847 
848   OSStatus err = noErr;
849   UInt32 size = 0;
850   bool success = false;
851 
852   // volume range is 0.0 - 1.0, convert from 0 - 255
853   const Float32 vol = (Float32)(volume / 255.0);
854 
855   assert(vol <= 1.0 && vol >= 0.0);
856 
857   // Does the capture device have a master volume control?
858   // If so, use it exclusively.
859   AudioObjectPropertyAddress propertyAddress = {
860       kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, 0};
861   Boolean isSettable = false;
862   err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
863                                       &isSettable);
864   if (err == noErr && isSettable) {
865     size = sizeof(vol);
866     WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
867         _inputDeviceID, &propertyAddress, 0, NULL, size, &vol));
868 
869     return 0;
870   }
871 
872   // Otherwise try to set each channel.
873   for (UInt32 i = 1; i <= _noInputChannels; i++) {
874     propertyAddress.mElement = i;
875     isSettable = false;
876     err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
877                                         &isSettable);
878     if (err == noErr && isSettable) {
879       size = sizeof(vol);
880       WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
881           _inputDeviceID, &propertyAddress, 0, NULL, size, &vol));
882     }
883     success = true;
884   }
885 
886   if (!success) {
887     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
888                  " Unable to set a level on any input channel");
889     return -1;
890   }
891 
892   return 0;
893 }
894 
MicrophoneVolume(uint32_t & volume) const895 int32_t AudioMixerManagerMac::MicrophoneVolume(uint32_t& volume) const {
896   if (_inputDeviceID == kAudioObjectUnknown) {
897     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
898                  "  device ID has not been set");
899     return -1;
900   }
901 
902   OSStatus err = noErr;
903   UInt32 size = 0;
904   unsigned int channels = 0;
905   Float32 channelVol = 0;
906   Float32 volFloat32 = 0;
907 
908   // Does the device have a master volume control?
909   // If so, use it exclusively.
910   AudioObjectPropertyAddress propertyAddress = {
911       kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, 0};
912   Boolean hasProperty =
913       AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
914   if (hasProperty) {
915     size = sizeof(volFloat32);
916     WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
917         _inputDeviceID, &propertyAddress, 0, NULL, &size, &volFloat32));
918 
919     // vol 0.0 to 1.0 -> convert to 0 - 255
920     volume = static_cast<uint32_t>(volFloat32 * 255 + 0.5);
921   } else {
922     // Otherwise get the average volume across channels.
923     volFloat32 = 0;
924     for (UInt32 i = 1; i <= _noInputChannels; i++) {
925       channelVol = 0;
926       propertyAddress.mElement = i;
927       hasProperty = AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
928       if (hasProperty) {
929         size = sizeof(channelVol);
930         WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
931             _inputDeviceID, &propertyAddress, 0, NULL, &size, &channelVol));
932 
933         volFloat32 += channelVol;
934         channels++;
935       }
936     }
937 
938     if (channels == 0) {
939       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
940                    " Unable to get a level on any channel");
941       return -1;
942     }
943 
944     assert(channels > 0);
945     // vol 0.0 to 1.0 -> convert to 0 - 255
946     volume = static_cast<uint32_t>(255 * volFloat32 / channels + 0.5);
947   }
948 
949   WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
950                "     AudioMixerManagerMac::MicrophoneVolume() => vol=%u",
951                volume);
952 
953   return 0;
954 }
955 
MaxMicrophoneVolume(uint32_t & maxVolume) const956 int32_t AudioMixerManagerMac::MaxMicrophoneVolume(uint32_t& maxVolume) const {
957   if (_inputDeviceID == kAudioObjectUnknown) {
958     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
959                  "  device ID has not been set");
960     return -1;
961   }
962 
963   // volume range is 0.0 to 1.0
964   // we convert that to 0 - 255
965   maxVolume = 255;
966 
967   return 0;
968 }
969 
MinMicrophoneVolume(uint32_t & minVolume) const970 int32_t AudioMixerManagerMac::MinMicrophoneVolume(uint32_t& minVolume) const {
971   if (_inputDeviceID == kAudioObjectUnknown) {
972     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
973                  "  device ID has not been set");
974     return -1;
975   }
976 
977   // volume range is 0.0 to 1.0
978   // we convert that to 0 - 10
979   minVolume = 0;
980 
981   return 0;
982 }
983 
MicrophoneVolumeStepSize(uint16_t & stepSize) const984 int32_t AudioMixerManagerMac::MicrophoneVolumeStepSize(
985     uint16_t& stepSize) const {
986   if (_inputDeviceID == kAudioObjectUnknown) {
987     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
988                  "  device ID has not been set");
989     return -1;
990   }
991 
992   // volume range is 0.0 to 1.0
993   // we convert that to 0 - 10
994   stepSize = 1;
995 
996   return 0;
997 }
998 
999 // ============================================================================
1000 //                                 Private Methods
1001 // ============================================================================
1002 
1003 // CoreAudio errors are best interpreted as four character strings.
logCAMsg(const TraceLevel level,const TraceModule module,const int32_t id,const char * msg,const char * err)1004 void AudioMixerManagerMac::logCAMsg(const TraceLevel level,
1005                                     const TraceModule module,
1006                                     const int32_t id,
1007                                     const char* msg,
1008                                     const char* err) {
1009   assert(msg != NULL);
1010   assert(err != NULL);
1011 
1012 #ifdef WEBRTC_ARCH_BIG_ENDIAN
1013   WEBRTC_TRACE(level, module, id, "%s: %.4s", msg, err);
1014 #else
1015   // We need to flip the characters in this case.
1016   WEBRTC_TRACE(level, module, id, "%s: %.1s%.1s%.1s%.1s", msg, err + 3, err + 2,
1017                err + 1, err);
1018 #endif
1019 }
1020 
1021 }  // namespace webrtc
1022 // EOF
1023