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