audio_mixer_manager_mac.cc (28486B)
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(::webrtc::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(::webrtc::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(::webrtc::LS_WARNING, "Error in " #expr, (const char*)&err); \ 41 } \ 42 } while (0) 43 44 AudioMixerManagerMac::AudioMixerManagerMac() 45 : _inputDeviceID(kAudioObjectUnknown), 46 _outputDeviceID(kAudioObjectUnknown), 47 _noInputChannels(0), 48 _noOutputChannels(0) { 49 RTC_DLOG(LS_INFO) << __FUNCTION__ << " created"; 50 } 51 52 AudioMixerManagerMac::~AudioMixerManagerMac() { 53 RTC_DLOG(LS_INFO) << __FUNCTION__ << " destroyed"; 54 Close(); 55 } 56 57 // ============================================================================ 58 // PUBLIC METHODS 59 // ============================================================================ 60 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 72 int32_t AudioMixerManagerMac::CloseSpeaker() { 73 MutexLock lock(&mutex_); 74 return CloseSpeakerLocked(); 75 } 76 77 int32_t AudioMixerManagerMac::CloseSpeakerLocked() { 78 RTC_DLOG(LS_VERBOSE) << __FUNCTION__; 79 80 _outputDeviceID = kAudioObjectUnknown; 81 _noOutputChannels = 0; 82 83 return 0; 84 } 85 86 int32_t AudioMixerManagerMac::CloseMicrophone() { 87 MutexLock lock(&mutex_); 88 return CloseMicrophoneLocked(); 89 } 90 91 int32_t AudioMixerManagerMac::CloseMicrophoneLocked() { 92 RTC_DLOG(LS_VERBOSE) << __FUNCTION__; 93 94 _inputDeviceID = kAudioObjectUnknown; 95 _noInputChannels = 0; 96 97 return 0; 98 } 99 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 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 198 bool AudioMixerManagerMac::SpeakerIsInitialized() const { 199 RTC_DLOG(LS_INFO) << __FUNCTION__; 200 201 return (_outputDeviceID != kAudioObjectUnknown); 202 } 203 204 bool AudioMixerManagerMac::MicrophoneIsInitialized() const { 205 RTC_DLOG(LS_INFO) << __FUNCTION__; 206 207 return (_inputDeviceID != kAudioObjectUnknown); 208 } 209 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 267 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 324 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 337 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 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 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 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 479 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 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 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 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 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 647 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 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 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 799 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 857 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 870 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. 888 void AudioMixerManagerMac::logCAMsg(const LoggingSeverity sev, 889 const char* msg, 890 const char* err) { 891 RTC_DCHECK(msg != NULL); 892 RTC_DCHECK(err != NULL); 893 RTC_DCHECK(sev == LS_ERROR || sev == LS_WARNING); 894 895 #ifdef WEBRTC_ARCH_BIG_ENDIAN 896 switch (sev) { 897 case LS_ERROR: 898 RTC_LOG(LS_ERROR) << msg << ": " << err[0] << err[1] << err[2] << err[3]; 899 break; 900 case 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 LS_ERROR: 911 RTC_LOG(LS_ERROR) << msg << ": " << err[3] << err[2] << err[1] << err[0]; 912 break; 913 case 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