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