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