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