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 <assert.h> 12 13 #include "webrtc/base/checks.h" 14 15 #include "webrtc/modules/audio_device/audio_device_config.h" 16 #include "webrtc/modules/audio_device/linux/audio_device_pulse_linux.h" 17 18 #include "webrtc/system_wrappers/include/event_wrapper.h" 19 #include "webrtc/system_wrappers/include/trace.h" 20 21 webrtc_adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable; 22 23 // Accesses Pulse functions through our late-binding symbol table instead of 24 // directly. This way we don't have to link to libpulse, which means our binary 25 // will work on systems that don't have it. 26 #define LATE(sym) \ 27 LATESYM_GET(webrtc_adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, sym) 28 29 namespace webrtc 30 { 31 32 AudioDeviceLinuxPulse::AudioDeviceLinuxPulse(const int32_t id) : 33 _ptrAudioBuffer(NULL), 34 _critSect(*CriticalSectionWrapper::CreateCriticalSection()), 35 _timeEventRec(*EventWrapper::Create()), 36 _timeEventPlay(*EventWrapper::Create()), 37 _recStartEvent(*EventWrapper::Create()), 38 _playStartEvent(*EventWrapper::Create()), 39 _id(id), 40 _mixerManager(id), 41 _inputDeviceIndex(0), 42 _outputDeviceIndex(0), 43 _inputDeviceIsSpecified(false), 44 _outputDeviceIsSpecified(false), 45 sample_rate_hz_(0), 46 _recChannels(1), 47 _playChannels(1), 48 _playBufType(AudioDeviceModule::kFixedBufferSize), 49 _initialized(false), 50 _recording(false), 51 _playing(false), 52 _recIsInitialized(false), 53 _playIsInitialized(false), 54 _startRec(false), 55 _stopRec(false), 56 _startPlay(false), 57 _stopPlay(false), 58 _AGC(false), 59 update_speaker_volume_at_startup_(false), 60 _playBufDelayFixed(20), 61 _sndCardPlayDelay(0), 62 _sndCardRecDelay(0), 63 _writeErrors(0), 64 _playWarning(0), 65 _playError(0), 66 _recWarning(0), 67 _recError(0), 68 _deviceIndex(-1), 69 _numPlayDevices(0), 70 _numRecDevices(0), 71 _playDeviceName(NULL), 72 _recDeviceName(NULL), 73 _playDisplayDeviceName(NULL), 74 _recDisplayDeviceName(NULL), 75 _playBuffer(NULL), 76 _playbackBufferSize(0), 77 _playbackBufferUnused(0), 78 _tempBufferSpace(0), 79 _recBuffer(NULL), 80 _recordBufferSize(0), 81 _recordBufferUsed(0), 82 _tempSampleData(NULL), 83 _tempSampleDataSize(0), 84 _configuredLatencyPlay(0), 85 _configuredLatencyRec(0), 86 _paDeviceIndex(-1), 87 _paStateChanged(false), 88 _paMainloop(NULL), 89 _paMainloopApi(NULL), 90 _paContext(NULL), 91 _recStream(NULL), 92 _playStream(NULL), 93 _recStreamFlags(0), 94 _playStreamFlags(0) 95 { 96 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, 97 "%s created", __FUNCTION__); 98 99 memset(_paServerVersion, 0, sizeof(_paServerVersion)); 100 memset(&_playBufferAttr, 0, sizeof(_playBufferAttr)); 101 memset(&_recBufferAttr, 0, sizeof(_recBufferAttr)); 102 memset(_oldKeyState, 0, sizeof(_oldKeyState)); 103 } 104 105 AudioDeviceLinuxPulse::~AudioDeviceLinuxPulse() 106 { 107 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, 108 "%s destroyed", __FUNCTION__); 109 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 110 Terminate(); 111 112 if (_recBuffer) 113 { 114 delete [] _recBuffer; 115 _recBuffer = NULL; 116 } 117 if (_playBuffer) 118 { 119 delete [] _playBuffer; 120 _playBuffer = NULL; 121 } 122 if (_playDeviceName) 123 { 124 delete [] _playDeviceName; 125 _playDeviceName = NULL; 126 } 127 if (_recDeviceName) 128 { 129 delete [] _recDeviceName; 130 _recDeviceName = NULL; 131 } 132 133 delete &_recStartEvent; 134 delete &_playStartEvent; 135 delete &_timeEventRec; 136 delete &_timeEventPlay; 137 delete &_critSect; 138 } 139 140 void AudioDeviceLinuxPulse::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) 141 { 142 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 143 144 _ptrAudioBuffer = audioBuffer; 145 146 // Inform the AudioBuffer about default settings for this implementation. 147 // Set all values to zero here since the actual settings will be done by 148 // InitPlayout and InitRecording later. 149 _ptrAudioBuffer->SetRecordingSampleRate(0); 150 _ptrAudioBuffer->SetPlayoutSampleRate(0); 151 _ptrAudioBuffer->SetRecordingChannels(0); 152 _ptrAudioBuffer->SetPlayoutChannels(0); 153 } 154 155 // ---------------------------------------------------------------------------- 156 // ActiveAudioLayer 157 // ---------------------------------------------------------------------------- 158 159 int32_t AudioDeviceLinuxPulse::ActiveAudioLayer( 160 AudioDeviceModule::AudioLayer& audioLayer) const 161 { 162 audioLayer = AudioDeviceModule::kLinuxPulseAudio; 163 return 0; 164 } 165 166 int32_t AudioDeviceLinuxPulse::Init() 167 { 168 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 169 if (_initialized) 170 { 171 return 0; 172 } 173 174 // Initialize PulseAudio 175 if (InitPulseAudio() < 0) 176 { 177 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 178 " failed to initialize PulseAudio"); 179 180 if (TerminatePulseAudio() < 0) 181 { 182 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 183 " failed to terminate PulseAudio"); 184 } 185 186 return -1; 187 } 188 189 _playWarning = 0; 190 _playError = 0; 191 _recWarning = 0; 192 _recError = 0; 193 194 //Get X display handle for typing detection 195 _XDisplay = XOpenDisplay(NULL); 196 if (!_XDisplay) 197 { 198 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 199 " failed to open X display, typing detection will not work"); 200 } 201 202 // RECORDING 203 _ptrThreadRec.reset(new rtc::PlatformThread( 204 RecThreadFunc, this, "webrtc_audio_module_rec_thread")); 205 206 _ptrThreadRec->Start(); 207 _ptrThreadRec->SetPriority(rtc::kRealtimePriority); 208 209 // PLAYOUT 210 _ptrThreadPlay.reset(new rtc::PlatformThread( 211 PlayThreadFunc, this, "webrtc_audio_module_play_thread")); 212 _ptrThreadPlay->Start(); 213 _ptrThreadPlay->SetPriority(rtc::kRealtimePriority); 214 215 _initialized = true; 216 217 return 0; 218 } 219 220 int32_t AudioDeviceLinuxPulse::Terminate() 221 { 222 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 223 if (!_initialized) 224 { 225 return 0; 226 } 227 228 _mixerManager.Close(); 229 230 // RECORDING 231 if (_ptrThreadRec) 232 { 233 rtc::PlatformThread* tmpThread = _ptrThreadRec.release(); 234 235 _timeEventRec.Set(); 236 tmpThread->Stop(); 237 delete tmpThread; 238 } 239 240 // PLAYOUT 241 if (_ptrThreadPlay) 242 { 243 rtc::PlatformThread* tmpThread = _ptrThreadPlay.release(); 244 245 _timeEventPlay.Set(); 246 tmpThread->Stop(); 247 delete tmpThread; 248 } 249 250 // Terminate PulseAudio 251 if (TerminatePulseAudio() < 0) 252 { 253 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 254 " failed to terminate PulseAudio"); 255 return -1; 256 } 257 258 if (_XDisplay) 259 { 260 XCloseDisplay(_XDisplay); 261 _XDisplay = NULL; 262 } 263 264 _initialized = false; 265 _outputDeviceIsSpecified = false; 266 _inputDeviceIsSpecified = false; 267 268 return 0; 269 } 270 271 bool AudioDeviceLinuxPulse::Initialized() const 272 { 273 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 274 return (_initialized); 275 } 276 277 int32_t AudioDeviceLinuxPulse::InitSpeaker() 278 { 279 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 280 281 if (_playing) 282 { 283 return -1; 284 } 285 286 if (!_outputDeviceIsSpecified) 287 { 288 return -1; 289 } 290 291 // check if default device 292 if (_outputDeviceIndex == 0) 293 { 294 uint16_t deviceIndex = 0; 295 GetDefaultDeviceInfo(false, NULL, deviceIndex); 296 _paDeviceIndex = deviceIndex; 297 } else 298 { 299 // get the PA device index from 300 // the callback 301 _deviceIndex = _outputDeviceIndex; 302 303 // get playout devices 304 PlayoutDevices(); 305 } 306 307 // the callback has now set the _paDeviceIndex to 308 // the PulseAudio index of the device 309 if (_mixerManager.OpenSpeaker(_paDeviceIndex) == -1) 310 { 311 return -1; 312 } 313 314 // clear _deviceIndex 315 _deviceIndex = -1; 316 _paDeviceIndex = -1; 317 318 return 0; 319 } 320 321 int32_t AudioDeviceLinuxPulse::InitMicrophone() 322 { 323 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 324 if (_recording) 325 { 326 return -1; 327 } 328 329 if (!_inputDeviceIsSpecified) 330 { 331 return -1; 332 } 333 334 // Check if default device 335 if (_inputDeviceIndex == 0) 336 { 337 uint16_t deviceIndex = 0; 338 GetDefaultDeviceInfo(true, NULL, deviceIndex); 339 _paDeviceIndex = deviceIndex; 340 } else 341 { 342 // Get the PA device index from 343 // the callback 344 _deviceIndex = _inputDeviceIndex; 345 346 // get recording devices 347 RecordingDevices(); 348 } 349 350 // The callback has now set the _paDeviceIndex to 351 // the PulseAudio index of the device 352 if (_mixerManager.OpenMicrophone(_paDeviceIndex) == -1) 353 { 354 return -1; 355 } 356 357 // Clear _deviceIndex 358 _deviceIndex = -1; 359 _paDeviceIndex = -1; 360 361 return 0; 362 } 363 364 bool AudioDeviceLinuxPulse::SpeakerIsInitialized() const 365 { 366 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 367 return (_mixerManager.SpeakerIsInitialized()); 368 } 369 370 bool AudioDeviceLinuxPulse::MicrophoneIsInitialized() const 371 { 372 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 373 return (_mixerManager.MicrophoneIsInitialized()); 374 } 375 376 int32_t AudioDeviceLinuxPulse::SpeakerVolumeIsAvailable(bool& available) 377 { 378 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 379 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); 380 381 // Make an attempt to open up the 382 // output mixer corresponding to the currently selected output device. 383 if (!wasInitialized && InitSpeaker() == -1) 384 { 385 // If we end up here it means that the selected speaker has no volume 386 // control. 387 available = false; 388 return 0; 389 } 390 391 // Given that InitSpeaker was successful, we know volume control exists. 392 available = true; 393 394 // Close the initialized output mixer 395 if (!wasInitialized) 396 { 397 _mixerManager.CloseSpeaker(); 398 } 399 400 return 0; 401 } 402 403 int32_t AudioDeviceLinuxPulse::SetSpeakerVolume(uint32_t volume) 404 { 405 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 406 if (!_playing) { 407 // Only update the volume if it's been set while we weren't playing. 408 update_speaker_volume_at_startup_ = true; 409 } 410 return (_mixerManager.SetSpeakerVolume(volume)); 411 } 412 413 int32_t AudioDeviceLinuxPulse::SpeakerVolume(uint32_t& volume) const 414 { 415 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 416 uint32_t level(0); 417 418 if (_mixerManager.SpeakerVolume(level) == -1) 419 { 420 return -1; 421 } 422 423 volume = level; 424 425 return 0; 426 } 427 428 int32_t AudioDeviceLinuxPulse::SetWaveOutVolume( 429 uint16_t volumeLeft, 430 uint16_t volumeRight) 431 { 432 433 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 434 " API call not supported on this platform"); 435 return -1; 436 } 437 438 int32_t AudioDeviceLinuxPulse::WaveOutVolume( 439 uint16_t& /*volumeLeft*/, 440 uint16_t& /*volumeRight*/) const 441 { 442 443 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 444 " API call not supported on this platform"); 445 return -1; 446 } 447 448 int32_t AudioDeviceLinuxPulse::MaxSpeakerVolume( 449 uint32_t& maxVolume) const 450 { 451 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 452 uint32_t maxVol(0); 453 454 if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) 455 { 456 return -1; 457 } 458 459 maxVolume = maxVol; 460 461 return 0; 462 } 463 464 int32_t AudioDeviceLinuxPulse::MinSpeakerVolume( 465 uint32_t& minVolume) const 466 { 467 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 468 uint32_t minVol(0); 469 470 if (_mixerManager.MinSpeakerVolume(minVol) == -1) 471 { 472 return -1; 473 } 474 475 minVolume = minVol; 476 477 return 0; 478 } 479 480 int32_t AudioDeviceLinuxPulse::SpeakerVolumeStepSize( 481 uint16_t& stepSize) const 482 { 483 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 484 uint16_t delta(0); 485 486 if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) 487 { 488 return -1; 489 } 490 491 stepSize = delta; 492 493 return 0; 494 } 495 496 int32_t AudioDeviceLinuxPulse::SpeakerMuteIsAvailable(bool& available) 497 { 498 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 499 bool isAvailable(false); 500 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); 501 502 // Make an attempt to open up the 503 // output mixer corresponding to the currently selected output device. 504 // 505 if (!wasInitialized && InitSpeaker() == -1) 506 { 507 // If we end up here it means that the selected speaker has no volume 508 // control, hence it is safe to state that there is no mute control 509 // already at this stage. 510 available = false; 511 return 0; 512 } 513 514 // Check if the selected speaker has a mute control 515 _mixerManager.SpeakerMuteIsAvailable(isAvailable); 516 517 available = isAvailable; 518 519 // Close the initialized output mixer 520 if (!wasInitialized) 521 { 522 _mixerManager.CloseSpeaker(); 523 } 524 525 return 0; 526 } 527 528 int32_t AudioDeviceLinuxPulse::SetSpeakerMute(bool enable) 529 { 530 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 531 return (_mixerManager.SetSpeakerMute(enable)); 532 } 533 534 int32_t AudioDeviceLinuxPulse::SpeakerMute(bool& enabled) const 535 { 536 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 537 bool muted(0); 538 if (_mixerManager.SpeakerMute(muted) == -1) 539 { 540 return -1; 541 } 542 543 enabled = muted; 544 return 0; 545 } 546 547 int32_t AudioDeviceLinuxPulse::MicrophoneMuteIsAvailable(bool& available) 548 { 549 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 550 bool isAvailable(false); 551 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); 552 553 // Make an attempt to open up the 554 // input mixer corresponding to the currently selected input device. 555 // 556 if (!wasInitialized && InitMicrophone() == -1) 557 { 558 // If we end up here it means that the selected microphone has no 559 // volume control, hence it is safe to state that there is no 560 // boost control already at this stage. 561 available = false; 562 return 0; 563 } 564 565 // Check if the selected microphone has a mute control 566 // 567 _mixerManager.MicrophoneMuteIsAvailable(isAvailable); 568 available = isAvailable; 569 570 // Close the initialized input mixer 571 // 572 if (!wasInitialized) 573 { 574 _mixerManager.CloseMicrophone(); 575 } 576 577 return 0; 578 } 579 580 int32_t AudioDeviceLinuxPulse::SetMicrophoneMute(bool enable) 581 { 582 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 583 return (_mixerManager.SetMicrophoneMute(enable)); 584 } 585 586 int32_t AudioDeviceLinuxPulse::MicrophoneMute(bool& enabled) const 587 { 588 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 589 bool muted(0); 590 if (_mixerManager.MicrophoneMute(muted) == -1) 591 { 592 return -1; 593 } 594 595 enabled = muted; 596 return 0; 597 } 598 599 int32_t AudioDeviceLinuxPulse::MicrophoneBoostIsAvailable(bool& available) 600 { 601 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 602 bool isAvailable(false); 603 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); 604 605 // Enumerate all avaliable microphone and make an attempt to open up the 606 // input mixer corresponding to the currently selected input device. 607 // 608 if (!wasInitialized && InitMicrophone() == -1) 609 { 610 // If we end up here it means that the selected microphone has no 611 // volume control, hence it is safe to state that there is no 612 // boost control already at this stage. 613 available = false; 614 return 0; 615 } 616 617 // Check if the selected microphone has a boost control 618 _mixerManager.MicrophoneBoostIsAvailable(isAvailable); 619 available = isAvailable; 620 621 // Close the initialized input mixer 622 if (!wasInitialized) 623 { 624 _mixerManager.CloseMicrophone(); 625 } 626 627 return 0; 628 } 629 630 int32_t AudioDeviceLinuxPulse::SetMicrophoneBoost(bool enable) 631 { 632 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 633 return (_mixerManager.SetMicrophoneBoost(enable)); 634 } 635 636 int32_t AudioDeviceLinuxPulse::MicrophoneBoost(bool& enabled) const 637 { 638 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 639 bool onOff(0); 640 641 if (_mixerManager.MicrophoneBoost(onOff) == -1) 642 { 643 return -1; 644 } 645 646 enabled = onOff; 647 648 return 0; 649 } 650 651 int32_t AudioDeviceLinuxPulse::StereoRecordingIsAvailable(bool& available) 652 { 653 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 654 if (_recChannels == 2 && _recording) { 655 available = true; 656 return 0; 657 } 658 659 available = false; 660 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); 661 int error = 0; 662 663 if (!wasInitialized && InitMicrophone() == -1) 664 { 665 // Cannot open the specified device 666 available = false; 667 return 0; 668 } 669 670 // Check if the selected microphone can record stereo. 671 bool isAvailable(false); 672 error = _mixerManager.StereoRecordingIsAvailable(isAvailable); 673 if (!error) 674 available = isAvailable; 675 676 // Close the initialized input mixer 677 if (!wasInitialized) 678 { 679 _mixerManager.CloseMicrophone(); 680 } 681 682 return error; 683 } 684 685 int32_t AudioDeviceLinuxPulse::SetStereoRecording(bool enable) 686 { 687 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 688 if (enable) 689 _recChannels = 2; 690 else 691 _recChannels = 1; 692 693 return 0; 694 } 695 696 int32_t AudioDeviceLinuxPulse::StereoRecording(bool& enabled) const 697 { 698 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 699 if (_recChannels == 2) 700 enabled = true; 701 else 702 enabled = false; 703 704 return 0; 705 } 706 707 int32_t AudioDeviceLinuxPulse::StereoPlayoutIsAvailable(bool& available) 708 { 709 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 710 if (_playChannels == 2 && _playing) { 711 available = true; 712 return 0; 713 } 714 715 available = false; 716 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); 717 int error = 0; 718 719 if (!wasInitialized && InitSpeaker() == -1) 720 { 721 // Cannot open the specified device. 722 return -1; 723 } 724 725 // Check if the selected speaker can play stereo. 726 bool isAvailable(false); 727 error = _mixerManager.StereoPlayoutIsAvailable(isAvailable); 728 if (!error) 729 available = isAvailable; 730 731 // Close the initialized input mixer 732 if (!wasInitialized) 733 { 734 _mixerManager.CloseSpeaker(); 735 } 736 737 return error; 738 } 739 740 int32_t AudioDeviceLinuxPulse::SetStereoPlayout(bool enable) 741 { 742 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 743 if (enable) 744 _playChannels = 2; 745 else 746 _playChannels = 1; 747 748 return 0; 749 } 750 751 int32_t AudioDeviceLinuxPulse::StereoPlayout(bool& enabled) const 752 { 753 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 754 if (_playChannels == 2) 755 enabled = true; 756 else 757 enabled = false; 758 759 return 0; 760 } 761 762 int32_t AudioDeviceLinuxPulse::SetAGC(bool enable) 763 { 764 CriticalSectionScoped lock(&_critSect); 765 _AGC = enable; 766 767 return 0; 768 } 769 770 bool AudioDeviceLinuxPulse::AGC() const 771 { 772 CriticalSectionScoped lock(&_critSect); 773 return _AGC; 774 } 775 776 int32_t AudioDeviceLinuxPulse::MicrophoneVolumeIsAvailable( 777 bool& available) 778 { 779 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 780 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); 781 782 // Make an attempt to open up the 783 // input mixer corresponding to the currently selected output device. 784 if (!wasInitialized && InitMicrophone() == -1) 785 { 786 // If we end up here it means that the selected microphone has no 787 // volume control. 788 available = false; 789 return 0; 790 } 791 792 // Given that InitMicrophone was successful, we know that a volume control 793 // exists. 794 available = true; 795 796 // Close the initialized input mixer 797 if (!wasInitialized) 798 { 799 _mixerManager.CloseMicrophone(); 800 } 801 802 return 0; 803 } 804 805 int32_t AudioDeviceLinuxPulse::SetMicrophoneVolume(uint32_t volume) 806 { 807 return (_mixerManager.SetMicrophoneVolume(volume)); 808 } 809 810 int32_t AudioDeviceLinuxPulse::MicrophoneVolume( 811 uint32_t& volume) const 812 { 813 814 uint32_t level(0); 815 816 if (_mixerManager.MicrophoneVolume(level) == -1) 817 { 818 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 819 " failed to retrive current microphone level"); 820 return -1; 821 } 822 823 volume = level; 824 825 return 0; 826 } 827 828 int32_t AudioDeviceLinuxPulse::MaxMicrophoneVolume( 829 uint32_t& maxVolume) const 830 { 831 832 uint32_t maxVol(0); 833 834 if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) 835 { 836 return -1; 837 } 838 839 maxVolume = maxVol; 840 841 return 0; 842 } 843 844 int32_t AudioDeviceLinuxPulse::MinMicrophoneVolume( 845 uint32_t& minVolume) const 846 { 847 848 uint32_t minVol(0); 849 850 if (_mixerManager.MinMicrophoneVolume(minVol) == -1) 851 { 852 return -1; 853 } 854 855 minVolume = minVol; 856 857 return 0; 858 } 859 860 int32_t AudioDeviceLinuxPulse::MicrophoneVolumeStepSize( 861 uint16_t& stepSize) const 862 { 863 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 864 uint16_t delta(0); 865 866 if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) 867 { 868 return -1; 869 } 870 871 stepSize = delta; 872 873 return 0; 874 } 875 876 int16_t AudioDeviceLinuxPulse::PlayoutDevices() 877 { 878 PaLock(); 879 880 pa_operation* paOperation = NULL; 881 _numPlayDevices = 1; // init to 1 to account for "default" 882 883 // get the whole list of devices and update _numPlayDevices 884 paOperation = LATE(pa_context_get_sink_info_list)(_paContext, 885 PaSinkInfoCallback, 886 this); 887 888 WaitForOperationCompletion(paOperation); 889 890 PaUnLock(); 891 892 return _numPlayDevices; 893 } 894 895 int32_t AudioDeviceLinuxPulse::SetPlayoutDevice(uint16_t index) 896 { 897 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 898 if (_playIsInitialized) 899 { 900 return -1; 901 } 902 903 const uint16_t nDevices = PlayoutDevices(); 904 905 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 906 " number of availiable output devices is %u", nDevices); 907 908 if (index > (nDevices - 1)) 909 { 910 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 911 " device index is out of range [0,%u]", (nDevices - 1)); 912 return -1; 913 } 914 915 _outputDeviceIndex = index; 916 _outputDeviceIsSpecified = true; 917 918 return 0; 919 } 920 921 int32_t AudioDeviceLinuxPulse::SetPlayoutDevice( 922 AudioDeviceModule::WindowsDeviceType /*device*/) 923 { 924 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 925 "WindowsDeviceType not supported"); 926 return -1; 927 } 928 929 int32_t AudioDeviceLinuxPulse::PlayoutDeviceName( 930 uint16_t index, 931 char name[kAdmMaxDeviceNameSize], 932 char guid[kAdmMaxGuidSize]) 933 { 934 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 935 const uint16_t nDevices = PlayoutDevices(); 936 937 if ((index > (nDevices - 1)) || (name == NULL)) 938 { 939 return -1; 940 } 941 942 memset(name, 0, kAdmMaxDeviceNameSize); 943 944 if (guid != NULL) 945 { 946 memset(guid, 0, kAdmMaxGuidSize); 947 } 948 949 // Check if default device 950 if (index == 0) 951 { 952 uint16_t deviceIndex = 0; 953 return GetDefaultDeviceInfo(false, name, deviceIndex); 954 } 955 956 // Tell the callback that we want 957 // The name for this device 958 _playDisplayDeviceName = name; 959 _deviceIndex = index; 960 961 // get playout devices 962 PlayoutDevices(); 963 964 // clear device name and index 965 _playDisplayDeviceName = NULL; 966 _deviceIndex = -1; 967 968 return 0; 969 } 970 971 int32_t AudioDeviceLinuxPulse::RecordingDeviceName( 972 uint16_t index, 973 char name[kAdmMaxDeviceNameSize], 974 char guid[kAdmMaxGuidSize]) 975 { 976 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 977 const uint16_t nDevices(RecordingDevices()); 978 979 if ((index > (nDevices - 1)) || (name == NULL)) 980 { 981 return -1; 982 } 983 984 memset(name, 0, kAdmMaxDeviceNameSize); 985 986 if (guid != NULL) 987 { 988 memset(guid, 0, kAdmMaxGuidSize); 989 } 990 991 // Check if default device 992 if (index == 0) 993 { 994 uint16_t deviceIndex = 0; 995 return GetDefaultDeviceInfo(true, name, deviceIndex); 996 } 997 998 // Tell the callback that we want 999 // the name for this device 1000 _recDisplayDeviceName = name; 1001 _deviceIndex = index; 1002 1003 // Get recording devices 1004 RecordingDevices(); 1005 1006 // Clear device name and index 1007 _recDisplayDeviceName = NULL; 1008 _deviceIndex = -1; 1009 1010 return 0; 1011 } 1012 1013 int16_t AudioDeviceLinuxPulse::RecordingDevices() 1014 { 1015 PaLock(); 1016 1017 pa_operation* paOperation = NULL; 1018 _numRecDevices = 1; // Init to 1 to account for "default" 1019 1020 // Get the whole list of devices and update _numRecDevices 1021 paOperation = LATE(pa_context_get_source_info_list)(_paContext, 1022 PaSourceInfoCallback, 1023 this); 1024 1025 WaitForOperationCompletion(paOperation); 1026 1027 PaUnLock(); 1028 1029 return _numRecDevices; 1030 } 1031 1032 int32_t AudioDeviceLinuxPulse::SetRecordingDevice(uint16_t index) 1033 { 1034 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1035 if (_recIsInitialized) 1036 { 1037 return -1; 1038 } 1039 1040 const uint16_t nDevices(RecordingDevices()); 1041 1042 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1043 " number of availiable input devices is %u", nDevices); 1044 1045 if (index > (nDevices - 1)) 1046 { 1047 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1048 " device index is out of range [0,%u]", (nDevices - 1)); 1049 return -1; 1050 } 1051 1052 _inputDeviceIndex = index; 1053 _inputDeviceIsSpecified = true; 1054 1055 return 0; 1056 } 1057 1058 int32_t AudioDeviceLinuxPulse::SetRecordingDevice( 1059 AudioDeviceModule::WindowsDeviceType /*device*/) 1060 { 1061 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1062 "WindowsDeviceType not supported"); 1063 return -1; 1064 } 1065 1066 int32_t AudioDeviceLinuxPulse::PlayoutIsAvailable(bool& available) 1067 { 1068 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1069 available = false; 1070 1071 // Try to initialize the playout side 1072 int32_t res = InitPlayout(); 1073 1074 // Cancel effect of initialization 1075 StopPlayout(); 1076 1077 if (res != -1) 1078 { 1079 available = true; 1080 } 1081 1082 return res; 1083 } 1084 1085 int32_t AudioDeviceLinuxPulse::RecordingIsAvailable(bool& available) 1086 { 1087 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1088 available = false; 1089 1090 // Try to initialize the playout side 1091 int32_t res = InitRecording(); 1092 1093 // Cancel effect of initialization 1094 StopRecording(); 1095 1096 if (res != -1) 1097 { 1098 available = true; 1099 } 1100 1101 return res; 1102 } 1103 1104 int32_t AudioDeviceLinuxPulse::InitPlayout() 1105 { 1106 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1107 1108 if (_playing) 1109 { 1110 return -1; 1111 } 1112 1113 if (!_outputDeviceIsSpecified) 1114 { 1115 return -1; 1116 } 1117 1118 if (_playIsInitialized) 1119 { 1120 return 0; 1121 } 1122 1123 // Initialize the speaker (devices might have been added or removed) 1124 if (InitSpeaker() == -1) 1125 { 1126 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 1127 " InitSpeaker() failed"); 1128 } 1129 1130 // Set the play sample specification 1131 pa_sample_spec playSampleSpec; 1132 playSampleSpec.channels = _playChannels; 1133 playSampleSpec.format = PA_SAMPLE_S16LE; 1134 playSampleSpec.rate = sample_rate_hz_; 1135 1136 // Create a new play stream 1137 _playStream = LATE(pa_stream_new)(_paContext, "playStream", 1138 &playSampleSpec, NULL); 1139 1140 if (!_playStream) 1141 { 1142 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1143 " failed to create play stream, err=%d", 1144 LATE(pa_context_errno)(_paContext)); 1145 return -1; 1146 } 1147 1148 // Provide the playStream to the mixer 1149 _mixerManager.SetPlayStream(_playStream); 1150 1151 if (_ptrAudioBuffer) 1152 { 1153 // Update audio buffer with the selected parameters 1154 _ptrAudioBuffer->SetPlayoutSampleRate(sample_rate_hz_); 1155 _ptrAudioBuffer->SetPlayoutChannels((uint8_t) _playChannels); 1156 } 1157 1158 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1159 " stream state %d\n", 1160 LATE(pa_stream_get_state)(_playStream)); 1161 1162 // Set stream flags 1163 _playStreamFlags = (pa_stream_flags_t) (PA_STREAM_AUTO_TIMING_UPDATE 1164 | PA_STREAM_INTERPOLATE_TIMING); 1165 1166 if (_configuredLatencyPlay != WEBRTC_PA_NO_LATENCY_REQUIREMENTS) 1167 { 1168 // If configuring a specific latency then we want to specify 1169 // PA_STREAM_ADJUST_LATENCY to make the server adjust parameters 1170 // automatically to reach that target latency. However, that flag 1171 // doesn't exist in Ubuntu 8.04 and many people still use that, 1172 // so we have to check the protocol version of libpulse. 1173 if (LATE(pa_context_get_protocol_version)(_paContext) 1174 >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) 1175 { 1176 _playStreamFlags |= PA_STREAM_ADJUST_LATENCY; 1177 } 1178 1179 const pa_sample_spec *spec = 1180 LATE(pa_stream_get_sample_spec)(_playStream); 1181 if (!spec) 1182 { 1183 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1184 " pa_stream_get_sample_spec()"); 1185 return -1; 1186 } 1187 1188 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); 1189 uint32_t latency = bytesPerSec * 1190 WEBRTC_PA_PLAYBACK_LATENCY_MINIMUM_MSECS / 1191 WEBRTC_PA_MSECS_PER_SEC; 1192 1193 // Set the play buffer attributes 1194 _playBufferAttr.maxlength = latency; // num bytes stored in the buffer 1195 _playBufferAttr.tlength = latency; // target fill level of play buffer 1196 // minimum free num bytes before server request more data 1197 _playBufferAttr.minreq = latency / WEBRTC_PA_PLAYBACK_REQUEST_FACTOR; 1198 // prebuffer tlength before starting playout 1199 _playBufferAttr.prebuf = _playBufferAttr.tlength - 1200 _playBufferAttr.minreq; 1201 1202 _configuredLatencyPlay = latency; 1203 } 1204 1205 // num samples in bytes * num channels 1206 _playbackBufferSize = sample_rate_hz_ / 100 * 2 * _playChannels; 1207 _playbackBufferUnused = _playbackBufferSize; 1208 _playBuffer = new int8_t[_playbackBufferSize]; 1209 1210 // Enable underflow callback 1211 LATE(pa_stream_set_underflow_callback)(_playStream, 1212 PaStreamUnderflowCallback, this); 1213 1214 // Set the state callback function for the stream 1215 LATE(pa_stream_set_state_callback)(_playStream, 1216 PaStreamStateCallback, this); 1217 1218 // Mark playout side as initialized 1219 _playIsInitialized = true; 1220 _sndCardPlayDelay = 0; 1221 _sndCardRecDelay = 0; 1222 1223 return 0; 1224 } 1225 1226 int32_t AudioDeviceLinuxPulse::InitRecording() 1227 { 1228 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1229 1230 if (_recording) 1231 { 1232 return -1; 1233 } 1234 1235 if (!_inputDeviceIsSpecified) 1236 { 1237 return -1; 1238 } 1239 1240 if (_recIsInitialized) 1241 { 1242 return 0; 1243 } 1244 1245 // Initialize the microphone (devices might have been added or removed) 1246 if (InitMicrophone() == -1) 1247 { 1248 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 1249 " InitMicrophone() failed"); 1250 } 1251 1252 // Set the rec sample specification 1253 pa_sample_spec recSampleSpec; 1254 recSampleSpec.channels = _recChannels; 1255 recSampleSpec.format = PA_SAMPLE_S16LE; 1256 recSampleSpec.rate = sample_rate_hz_; 1257 1258 // Create a new rec stream 1259 _recStream = LATE(pa_stream_new)(_paContext, "recStream", &recSampleSpec, 1260 NULL); 1261 if (!_recStream) 1262 { 1263 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1264 " failed to create rec stream, err=%d", 1265 LATE(pa_context_errno)(_paContext)); 1266 return -1; 1267 } 1268 1269 // Provide the recStream to the mixer 1270 _mixerManager.SetRecStream(_recStream); 1271 1272 if (_ptrAudioBuffer) 1273 { 1274 // Update audio buffer with the selected parameters 1275 _ptrAudioBuffer->SetRecordingSampleRate(sample_rate_hz_); 1276 _ptrAudioBuffer->SetRecordingChannels((uint8_t) _recChannels); 1277 } 1278 1279 if (_configuredLatencyRec != WEBRTC_PA_NO_LATENCY_REQUIREMENTS) 1280 { 1281 _recStreamFlags = (pa_stream_flags_t) (PA_STREAM_AUTO_TIMING_UPDATE 1282 | PA_STREAM_INTERPOLATE_TIMING); 1283 1284 // If configuring a specific latency then we want to specify 1285 // PA_STREAM_ADJUST_LATENCY to make the server adjust parameters 1286 // automatically to reach that target latency. However, that flag 1287 // doesn't exist in Ubuntu 8.04 and many people still use that, 1288 // so we have to check the protocol version of libpulse. 1289 if (LATE(pa_context_get_protocol_version)(_paContext) 1290 >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) 1291 { 1292 _recStreamFlags |= PA_STREAM_ADJUST_LATENCY; 1293 } 1294 1295 const pa_sample_spec *spec = 1296 LATE(pa_stream_get_sample_spec)(_recStream); 1297 if (!spec) 1298 { 1299 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1300 " pa_stream_get_sample_spec(rec)"); 1301 return -1; 1302 } 1303 1304 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); 1305 uint32_t latency = bytesPerSec 1306 * WEBRTC_PA_LOW_CAPTURE_LATENCY_MSECS / WEBRTC_PA_MSECS_PER_SEC; 1307 1308 // Set the rec buffer attributes 1309 // Note: fragsize specifies a maximum transfer size, not a minimum, so 1310 // it is not possible to force a high latency setting, only a low one. 1311 _recBufferAttr.fragsize = latency; // size of fragment 1312 _recBufferAttr.maxlength = latency + bytesPerSec 1313 * WEBRTC_PA_CAPTURE_BUFFER_EXTRA_MSECS / WEBRTC_PA_MSECS_PER_SEC; 1314 1315 _configuredLatencyRec = latency; 1316 } 1317 1318 _recordBufferSize = sample_rate_hz_ / 100 * 2 * _recChannels; 1319 _recordBufferUsed = 0; 1320 _recBuffer = new int8_t[_recordBufferSize]; 1321 1322 // Enable overflow callback 1323 LATE(pa_stream_set_overflow_callback)(_recStream, 1324 PaStreamOverflowCallback, 1325 this); 1326 1327 // Set the state callback function for the stream 1328 LATE(pa_stream_set_state_callback)(_recStream, 1329 PaStreamStateCallback, 1330 this); 1331 1332 // Mark recording side as initialized 1333 _recIsInitialized = true; 1334 1335 return 0; 1336 } 1337 1338 int32_t AudioDeviceLinuxPulse::StartRecording() 1339 { 1340 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1341 if (!_recIsInitialized) 1342 { 1343 return -1; 1344 } 1345 1346 if (_recording) 1347 { 1348 return 0; 1349 } 1350 1351 // Set state to ensure that the recording starts from the audio thread. 1352 _startRec = true; 1353 1354 // The audio thread will signal when recording has started. 1355 _timeEventRec.Set(); 1356 if (kEventTimeout == _recStartEvent.Wait(10000)) 1357 { 1358 { 1359 CriticalSectionScoped lock(&_critSect); 1360 _startRec = false; 1361 } 1362 StopRecording(); 1363 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1364 " failed to activate recording"); 1365 return -1; 1366 } 1367 1368 { 1369 CriticalSectionScoped lock(&_critSect); 1370 if (_recording) 1371 { 1372 // The recording state is set by the audio thread after recording 1373 // has started. 1374 } else 1375 { 1376 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1377 " failed to activate recording"); 1378 return -1; 1379 } 1380 } 1381 1382 return 0; 1383 } 1384 1385 int32_t AudioDeviceLinuxPulse::StopRecording() 1386 { 1387 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1388 CriticalSectionScoped lock(&_critSect); 1389 1390 if (!_recIsInitialized) 1391 { 1392 return 0; 1393 } 1394 1395 if (_recStream == NULL) 1396 { 1397 return -1; 1398 } 1399 1400 _recIsInitialized = false; 1401 _recording = false; 1402 1403 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1404 " stopping recording"); 1405 1406 // Stop Recording 1407 PaLock(); 1408 1409 DisableReadCallback(); 1410 LATE(pa_stream_set_overflow_callback)(_recStream, NULL, NULL); 1411 1412 // Unset this here so that we don't get a TERMINATED callback 1413 LATE(pa_stream_set_state_callback)(_recStream, NULL, NULL); 1414 1415 if (LATE(pa_stream_get_state)(_recStream) != PA_STREAM_UNCONNECTED) 1416 { 1417 // Disconnect the stream 1418 if (LATE(pa_stream_disconnect)(_recStream) != PA_OK) 1419 { 1420 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1421 " failed to disconnect rec stream, err=%d\n", 1422 LATE(pa_context_errno)(_paContext)); 1423 PaUnLock(); 1424 return -1; 1425 } 1426 1427 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1428 " disconnected recording"); 1429 } 1430 1431 LATE(pa_stream_unref)(_recStream); 1432 _recStream = NULL; 1433 1434 PaUnLock(); 1435 1436 // Provide the recStream to the mixer 1437 _mixerManager.SetRecStream(_recStream); 1438 1439 if (_recBuffer) 1440 { 1441 delete [] _recBuffer; 1442 _recBuffer = NULL; 1443 } 1444 1445 return 0; 1446 } 1447 1448 bool AudioDeviceLinuxPulse::RecordingIsInitialized() const 1449 { 1450 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1451 return (_recIsInitialized); 1452 } 1453 1454 bool AudioDeviceLinuxPulse::Recording() const 1455 { 1456 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1457 return (_recording); 1458 } 1459 1460 bool AudioDeviceLinuxPulse::PlayoutIsInitialized() const 1461 { 1462 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1463 return (_playIsInitialized); 1464 } 1465 1466 int32_t AudioDeviceLinuxPulse::StartPlayout() 1467 { 1468 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1469 1470 if (!_playIsInitialized) 1471 { 1472 return -1; 1473 } 1474 1475 if (_playing) 1476 { 1477 return 0; 1478 } 1479 1480 // Set state to ensure that playout starts from the audio thread. 1481 { 1482 CriticalSectionScoped lock(&_critSect); 1483 _startPlay = true; 1484 } 1485 1486 // Both |_startPlay| and |_playing| needs protction since they are also 1487 // accessed on the playout thread. 1488 1489 // The audio thread will signal when playout has started. 1490 _timeEventPlay.Set(); 1491 if (kEventTimeout == _playStartEvent.Wait(10000)) 1492 { 1493 { 1494 CriticalSectionScoped lock(&_critSect); 1495 _startPlay = false; 1496 } 1497 StopPlayout(); 1498 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1499 " failed to activate playout"); 1500 return -1; 1501 } 1502 1503 { 1504 CriticalSectionScoped lock(&_critSect); 1505 if (_playing) 1506 { 1507 // The playing state is set by the audio thread after playout 1508 // has started. 1509 } else 1510 { 1511 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1512 " failed to activate playing"); 1513 return -1; 1514 } 1515 } 1516 1517 return 0; 1518 } 1519 1520 int32_t AudioDeviceLinuxPulse::StopPlayout() 1521 { 1522 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1523 CriticalSectionScoped lock(&_critSect); 1524 1525 if (!_playIsInitialized) 1526 { 1527 return 0; 1528 } 1529 1530 if (_playStream == NULL) 1531 { 1532 return -1; 1533 } 1534 1535 _playIsInitialized = false; 1536 _playing = false; 1537 _sndCardPlayDelay = 0; 1538 _sndCardRecDelay = 0; 1539 1540 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1541 " stopping playback"); 1542 1543 // Stop Playout 1544 PaLock(); 1545 1546 DisableWriteCallback(); 1547 LATE(pa_stream_set_underflow_callback)(_playStream, NULL, NULL); 1548 1549 // Unset this here so that we don't get a TERMINATED callback 1550 LATE(pa_stream_set_state_callback)(_playStream, NULL, NULL); 1551 1552 if (LATE(pa_stream_get_state)(_playStream) != PA_STREAM_UNCONNECTED) 1553 { 1554 // Disconnect the stream 1555 if (LATE(pa_stream_disconnect)(_playStream) != PA_OK) 1556 { 1557 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1558 " failed to disconnect play stream, err=%d", 1559 LATE(pa_context_errno)(_paContext)); 1560 PaUnLock(); 1561 return -1; 1562 } 1563 1564 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1565 " disconnected playback"); 1566 } 1567 1568 LATE(pa_stream_unref)(_playStream); 1569 _playStream = NULL; 1570 1571 PaUnLock(); 1572 1573 // Provide the playStream to the mixer 1574 _mixerManager.SetPlayStream(_playStream); 1575 1576 if (_playBuffer) 1577 { 1578 delete [] _playBuffer; 1579 _playBuffer = NULL; 1580 } 1581 1582 return 0; 1583 } 1584 1585 int32_t AudioDeviceLinuxPulse::PlayoutDelay(uint16_t& delayMS) const 1586 { 1587 CriticalSectionScoped lock(&_critSect); 1588 delayMS = (uint16_t) _sndCardPlayDelay; 1589 return 0; 1590 } 1591 1592 int32_t AudioDeviceLinuxPulse::RecordingDelay(uint16_t& delayMS) const 1593 { 1594 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1595 delayMS = (uint16_t) _sndCardRecDelay; 1596 return 0; 1597 } 1598 1599 bool AudioDeviceLinuxPulse::Playing() const 1600 { 1601 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1602 return (_playing); 1603 } 1604 1605 int32_t AudioDeviceLinuxPulse::SetPlayoutBuffer( 1606 const AudioDeviceModule::BufferType type, 1607 uint16_t sizeMS) 1608 { 1609 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1610 if (type != AudioDeviceModule::kFixedBufferSize) 1611 { 1612 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1613 " Adaptive buffer size not supported on this platform"); 1614 return -1; 1615 } 1616 1617 _playBufType = type; 1618 _playBufDelayFixed = sizeMS; 1619 1620 return 0; 1621 } 1622 1623 int32_t AudioDeviceLinuxPulse::PlayoutBuffer( 1624 AudioDeviceModule::BufferType& type, 1625 uint16_t& sizeMS) const 1626 { 1627 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 1628 type = _playBufType; 1629 sizeMS = _playBufDelayFixed; 1630 1631 return 0; 1632 } 1633 1634 int32_t AudioDeviceLinuxPulse::CPULoad(uint16_t& /*load*/) const 1635 { 1636 1637 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 1638 " API call not supported on this platform"); 1639 return -1; 1640 } 1641 1642 bool AudioDeviceLinuxPulse::PlayoutWarning() const 1643 { 1644 CriticalSectionScoped lock(&_critSect); 1645 return (_playWarning > 0); 1646 } 1647 1648 bool AudioDeviceLinuxPulse::PlayoutError() const 1649 { 1650 CriticalSectionScoped lock(&_critSect); 1651 return (_playError > 0); 1652 } 1653 1654 bool AudioDeviceLinuxPulse::RecordingWarning() const 1655 { 1656 CriticalSectionScoped lock(&_critSect); 1657 return (_recWarning > 0); 1658 } 1659 1660 bool AudioDeviceLinuxPulse::RecordingError() const 1661 { 1662 CriticalSectionScoped lock(&_critSect); 1663 return (_recError > 0); 1664 } 1665 1666 void AudioDeviceLinuxPulse::ClearPlayoutWarning() 1667 { 1668 CriticalSectionScoped lock(&_critSect); 1669 _playWarning = 0; 1670 } 1671 1672 void AudioDeviceLinuxPulse::ClearPlayoutError() 1673 { 1674 CriticalSectionScoped lock(&_critSect); 1675 _playError = 0; 1676 } 1677 1678 void AudioDeviceLinuxPulse::ClearRecordingWarning() 1679 { 1680 CriticalSectionScoped lock(&_critSect); 1681 _recWarning = 0; 1682 } 1683 1684 void AudioDeviceLinuxPulse::ClearRecordingError() 1685 { 1686 CriticalSectionScoped lock(&_critSect); 1687 _recError = 0; 1688 } 1689 1690 // ============================================================================ 1691 // Private Methods 1692 // ============================================================================ 1693 1694 void AudioDeviceLinuxPulse::PaContextStateCallback(pa_context *c, void *pThis) 1695 { 1696 static_cast<AudioDeviceLinuxPulse*> (pThis)-> 1697 PaContextStateCallbackHandler(c); 1698 } 1699 1700 // ---------------------------------------------------------------------------- 1701 // PaSinkInfoCallback 1702 // ---------------------------------------------------------------------------- 1703 1704 void AudioDeviceLinuxPulse::PaSinkInfoCallback(pa_context */*c*/, 1705 const pa_sink_info *i, int eol, 1706 void *pThis) 1707 { 1708 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaSinkInfoCallbackHandler( 1709 i, eol); 1710 } 1711 1712 void AudioDeviceLinuxPulse::PaSourceInfoCallback(pa_context */*c*/, 1713 const pa_source_info *i, 1714 int eol, void *pThis) 1715 { 1716 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaSourceInfoCallbackHandler( 1717 i, eol); 1718 } 1719 1720 void AudioDeviceLinuxPulse::PaServerInfoCallback(pa_context */*c*/, 1721 const pa_server_info *i, 1722 void *pThis) 1723 { 1724 static_cast<AudioDeviceLinuxPulse*> (pThis)-> 1725 PaServerInfoCallbackHandler(i); 1726 } 1727 1728 void AudioDeviceLinuxPulse::PaStreamStateCallback(pa_stream *p, void *pThis) 1729 { 1730 static_cast<AudioDeviceLinuxPulse*> (pThis)-> 1731 PaStreamStateCallbackHandler(p); 1732 } 1733 1734 void AudioDeviceLinuxPulse::PaContextStateCallbackHandler(pa_context *c) 1735 { 1736 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1737 " context state cb"); 1738 1739 pa_context_state_t state = LATE(pa_context_get_state)(c); 1740 switch (state) 1741 { 1742 case PA_CONTEXT_UNCONNECTED: 1743 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1744 " unconnected"); 1745 break; 1746 case PA_CONTEXT_CONNECTING: 1747 case PA_CONTEXT_AUTHORIZING: 1748 case PA_CONTEXT_SETTING_NAME: 1749 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1750 " no state"); 1751 break; 1752 case PA_CONTEXT_FAILED: 1753 case PA_CONTEXT_TERMINATED: 1754 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1755 " failed"); 1756 _paStateChanged = true; 1757 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1758 break; 1759 case PA_CONTEXT_READY: 1760 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1761 " ready"); 1762 _paStateChanged = true; 1763 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1764 break; 1765 } 1766 } 1767 1768 void AudioDeviceLinuxPulse::PaSinkInfoCallbackHandler(const pa_sink_info *i, 1769 int eol) 1770 { 1771 if (eol) 1772 { 1773 // Signal that we are done 1774 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1775 return; 1776 } 1777 1778 if (_numPlayDevices == _deviceIndex) 1779 { 1780 // Convert the device index to the one of the sink 1781 _paDeviceIndex = i->index; 1782 1783 if (_playDeviceName) 1784 { 1785 // Copy the sink name 1786 strncpy(_playDeviceName, i->name, kAdmMaxDeviceNameSize); 1787 _playDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1788 } 1789 if (_playDisplayDeviceName) 1790 { 1791 // Copy the sink display name 1792 strncpy(_playDisplayDeviceName, i->description, 1793 kAdmMaxDeviceNameSize); 1794 _playDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1795 } 1796 } 1797 1798 _numPlayDevices++; 1799 } 1800 1801 void AudioDeviceLinuxPulse::PaSourceInfoCallbackHandler( 1802 const pa_source_info *i, 1803 int eol) 1804 { 1805 if (eol) 1806 { 1807 // Signal that we are done 1808 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1809 return; 1810 } 1811 1812 // We don't want to list output devices 1813 if (i->monitor_of_sink == PA_INVALID_INDEX) 1814 { 1815 if (_numRecDevices == _deviceIndex) 1816 { 1817 // Convert the device index to the one of the source 1818 _paDeviceIndex = i->index; 1819 1820 if (_recDeviceName) 1821 { 1822 // copy the source name 1823 strncpy(_recDeviceName, i->name, kAdmMaxDeviceNameSize); 1824 _recDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1825 } 1826 if (_recDisplayDeviceName) 1827 { 1828 // Copy the source display name 1829 strncpy(_recDisplayDeviceName, i->description, 1830 kAdmMaxDeviceNameSize); 1831 _recDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1832 } 1833 } 1834 1835 _numRecDevices++; 1836 } 1837 } 1838 1839 void AudioDeviceLinuxPulse::PaServerInfoCallbackHandler( 1840 const pa_server_info *i) 1841 { 1842 // Use PA native sampling rate 1843 sample_rate_hz_ = i->sample_spec.rate; 1844 1845 // Copy the PA server version 1846 strncpy(_paServerVersion, i->server_version, 31); 1847 _paServerVersion[31] = '\0'; 1848 1849 if (_recDisplayDeviceName) 1850 { 1851 // Copy the source name 1852 strncpy(_recDisplayDeviceName, i->default_source_name, 1853 kAdmMaxDeviceNameSize); 1854 _recDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1855 } 1856 1857 if (_playDisplayDeviceName) 1858 { 1859 // Copy the sink name 1860 strncpy(_playDisplayDeviceName, i->default_sink_name, 1861 kAdmMaxDeviceNameSize); 1862 _playDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1863 } 1864 1865 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1866 } 1867 1868 void AudioDeviceLinuxPulse::PaStreamStateCallbackHandler(pa_stream *p) 1869 { 1870 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1871 " stream state cb"); 1872 1873 pa_stream_state_t state = LATE(pa_stream_get_state)(p); 1874 switch (state) 1875 { 1876 case PA_STREAM_UNCONNECTED: 1877 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1878 " unconnected"); 1879 break; 1880 case PA_STREAM_CREATING: 1881 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1882 " creating"); 1883 break; 1884 case PA_STREAM_FAILED: 1885 case PA_STREAM_TERMINATED: 1886 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1887 " failed"); 1888 break; 1889 case PA_STREAM_READY: 1890 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1891 " ready"); 1892 break; 1893 } 1894 1895 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1896 } 1897 1898 int32_t AudioDeviceLinuxPulse::CheckPulseAudioVersion() 1899 { 1900 PaLock(); 1901 1902 pa_operation* paOperation = NULL; 1903 1904 // get the server info and update deviceName 1905 paOperation = LATE(pa_context_get_server_info)(_paContext, 1906 PaServerInfoCallback, 1907 this); 1908 1909 WaitForOperationCompletion(paOperation); 1910 1911 PaUnLock(); 1912 1913 WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, 1914 " checking PulseAudio version: %s", _paServerVersion); 1915 1916 return 0; 1917 } 1918 1919 int32_t AudioDeviceLinuxPulse::InitSamplingFrequency() 1920 { 1921 PaLock(); 1922 1923 pa_operation* paOperation = NULL; 1924 1925 // Get the server info and update sample_rate_hz_ 1926 paOperation = LATE(pa_context_get_server_info)(_paContext, 1927 PaServerInfoCallback, 1928 this); 1929 1930 WaitForOperationCompletion(paOperation); 1931 1932 PaUnLock(); 1933 1934 return 0; 1935 } 1936 1937 int32_t AudioDeviceLinuxPulse::GetDefaultDeviceInfo(bool recDevice, 1938 char* name, 1939 uint16_t& index) 1940 { 1941 char tmpName[kAdmMaxDeviceNameSize] = {0}; 1942 // subtract length of "default: " 1943 uint16_t nameLen = kAdmMaxDeviceNameSize - 9; 1944 char* pName = NULL; 1945 1946 if (name) 1947 { 1948 // Add "default: " 1949 strcpy(name, "default: "); 1950 pName = &name[9]; 1951 } 1952 1953 // Tell the callback that we want 1954 // the name for this device 1955 if (recDevice) 1956 { 1957 _recDisplayDeviceName = tmpName; 1958 } else 1959 { 1960 _playDisplayDeviceName = tmpName; 1961 } 1962 1963 // Set members 1964 _paDeviceIndex = -1; 1965 _deviceIndex = 0; 1966 _numPlayDevices = 0; 1967 _numRecDevices = 0; 1968 1969 PaLock(); 1970 1971 pa_operation* paOperation = NULL; 1972 1973 // Get the server info and update deviceName 1974 paOperation = LATE(pa_context_get_server_info)(_paContext, 1975 PaServerInfoCallback, 1976 this); 1977 1978 WaitForOperationCompletion(paOperation); 1979 1980 // Get the device index 1981 if (recDevice) 1982 { 1983 paOperation 1984 = LATE(pa_context_get_source_info_by_name)(_paContext, 1985 (char *) tmpName, 1986 PaSourceInfoCallback, 1987 this); 1988 } else 1989 { 1990 paOperation 1991 = LATE(pa_context_get_sink_info_by_name)(_paContext, 1992 (char *) tmpName, 1993 PaSinkInfoCallback, 1994 this); 1995 } 1996 1997 WaitForOperationCompletion(paOperation); 1998 1999 PaUnLock(); 2000 2001 // Set the index 2002 index = _paDeviceIndex; 2003 2004 if (name) 2005 { 2006 // Copy to name string 2007 strncpy(pName, tmpName, nameLen); 2008 } 2009 2010 // Clear members 2011 _playDisplayDeviceName = NULL; 2012 _recDisplayDeviceName = NULL; 2013 _paDeviceIndex = -1; 2014 _deviceIndex = -1; 2015 _numPlayDevices = 0; 2016 _numRecDevices = 0; 2017 2018 return 0; 2019 } 2020 2021 int32_t AudioDeviceLinuxPulse::InitPulseAudio() 2022 { 2023 int retVal = 0; 2024 2025 // Load libpulse 2026 if (!PaSymbolTable.Load()) 2027 { 2028 // Most likely the Pulse library and sound server are not installed on 2029 // this system 2030 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2031 " failed to load symbol table"); 2032 return -1; 2033 } 2034 2035 // Create a mainloop API and connection to the default server 2036 // the mainloop is the internal asynchronous API event loop 2037 if (_paMainloop) { 2038 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2039 " PA mainloop has already existed"); 2040 return -1; 2041 } 2042 _paMainloop = LATE(pa_threaded_mainloop_new)(); 2043 if (!_paMainloop) 2044 { 2045 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2046 " could not create mainloop"); 2047 return -1; 2048 } 2049 2050 // Start the threaded main loop 2051 retVal = LATE(pa_threaded_mainloop_start)(_paMainloop); 2052 if (retVal != PA_OK) 2053 { 2054 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2055 " failed to start main loop, error=%d", retVal); 2056 return -1; 2057 } 2058 2059 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2060 " mainloop running!"); 2061 2062 PaLock(); 2063 2064 _paMainloopApi = LATE(pa_threaded_mainloop_get_api)(_paMainloop); 2065 if (!_paMainloopApi) 2066 { 2067 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2068 " could not create mainloop API"); 2069 PaUnLock(); 2070 return -1; 2071 } 2072 2073 // Create a new PulseAudio context 2074 if (_paContext){ 2075 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2076 " PA context has already existed"); 2077 PaUnLock(); 2078 return -1; 2079 } 2080 _paContext = LATE(pa_context_new)(_paMainloopApi, "WEBRTC VoiceEngine"); 2081 2082 if (!_paContext) 2083 { 2084 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2085 " could not create context"); 2086 PaUnLock(); 2087 return -1; 2088 } 2089 2090 // Set state callback function 2091 LATE(pa_context_set_state_callback)(_paContext, PaContextStateCallback, 2092 this); 2093 2094 // Connect the context to a server (default) 2095 _paStateChanged = false; 2096 retVal = LATE(pa_context_connect)(_paContext, 2097 NULL, 2098 PA_CONTEXT_NOAUTOSPAWN, 2099 NULL); 2100 2101 if (retVal != PA_OK) 2102 { 2103 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2104 " failed to connect context, error=%d", retVal); 2105 PaUnLock(); 2106 return -1; 2107 } 2108 2109 // Wait for state change 2110 while (!_paStateChanged) 2111 { 2112 LATE(pa_threaded_mainloop_wait)(_paMainloop); 2113 } 2114 2115 // Now check to see what final state we reached. 2116 pa_context_state_t state = LATE(pa_context_get_state)(_paContext); 2117 2118 if (state != PA_CONTEXT_READY) 2119 { 2120 if (state == PA_CONTEXT_FAILED) 2121 { 2122 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2123 " failed to connect to PulseAudio sound server"); 2124 } else if (state == PA_CONTEXT_TERMINATED) 2125 { 2126 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2127 " PulseAudio connection terminated early"); 2128 } else 2129 { 2130 // Shouldn't happen, because we only signal on one of those three 2131 // states 2132 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2133 " unknown problem connecting to PulseAudio"); 2134 } 2135 PaUnLock(); 2136 return -1; 2137 } 2138 2139 PaUnLock(); 2140 2141 // Give the objects to the mixer manager 2142 _mixerManager.SetPulseAudioObjects(_paMainloop, _paContext); 2143 2144 // Check the version 2145 if (CheckPulseAudioVersion() < 0) 2146 { 2147 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2148 " PulseAudio version %s not supported", 2149 _paServerVersion); 2150 return -1; 2151 } 2152 2153 // Initialize sampling frequency 2154 if (InitSamplingFrequency() < 0 || sample_rate_hz_ == 0) 2155 { 2156 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2157 " failed to initialize sampling frequency," 2158 " set to %d Hz", 2159 sample_rate_hz_); 2160 return -1; 2161 } 2162 2163 return 0; 2164 } 2165 2166 int32_t AudioDeviceLinuxPulse::TerminatePulseAudio() 2167 { 2168 // Do nothing if the instance doesn't exist 2169 // likely PaSymbolTable.Load() fails 2170 if (!_paMainloop) { 2171 return 0; 2172 } 2173 2174 PaLock(); 2175 2176 // Disconnect the context 2177 if (_paContext) 2178 { 2179 LATE(pa_context_disconnect)(_paContext); 2180 } 2181 2182 // Unreference the context 2183 if (_paContext) 2184 { 2185 LATE(pa_context_unref)(_paContext); 2186 } 2187 2188 PaUnLock(); 2189 _paContext = NULL; 2190 2191 // Stop the threaded main loop 2192 if (_paMainloop) 2193 { 2194 LATE(pa_threaded_mainloop_stop)(_paMainloop); 2195 } 2196 2197 // Free the mainloop 2198 if (_paMainloop) 2199 { 2200 LATE(pa_threaded_mainloop_free)(_paMainloop); 2201 } 2202 2203 _paMainloop = NULL; 2204 2205 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2206 " PulseAudio terminated"); 2207 2208 return 0; 2209 } 2210 2211 void AudioDeviceLinuxPulse::PaLock() 2212 { 2213 LATE(pa_threaded_mainloop_lock)(_paMainloop); 2214 } 2215 2216 void AudioDeviceLinuxPulse::PaUnLock() 2217 { 2218 LATE(pa_threaded_mainloop_unlock)(_paMainloop); 2219 } 2220 2221 void AudioDeviceLinuxPulse::WaitForOperationCompletion( 2222 pa_operation* paOperation) const 2223 { 2224 if (!paOperation) 2225 { 2226 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2227 "paOperation NULL in WaitForOperationCompletion"); 2228 return; 2229 } 2230 2231 while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING) 2232 { 2233 LATE(pa_threaded_mainloop_wait)(_paMainloop); 2234 } 2235 2236 LATE(pa_operation_unref)(paOperation); 2237 } 2238 2239 // ============================================================================ 2240 // Thread Methods 2241 // ============================================================================ 2242 2243 void AudioDeviceLinuxPulse::EnableWriteCallback() 2244 { 2245 if (LATE(pa_stream_get_state)(_playStream) == PA_STREAM_READY) 2246 { 2247 // May already have available space. Must check. 2248 _tempBufferSpace = LATE(pa_stream_writable_size)(_playStream); 2249 if (_tempBufferSpace > 0) 2250 { 2251 // Yup, there is already space available, so if we register a 2252 // write callback then it will not receive any event. So dispatch 2253 // one ourself instead. 2254 _timeEventPlay.Set(); 2255 return; 2256 } 2257 } 2258 2259 LATE(pa_stream_set_write_callback)(_playStream, &PaStreamWriteCallback, 2260 this); 2261 } 2262 2263 void AudioDeviceLinuxPulse::DisableWriteCallback() 2264 { 2265 LATE(pa_stream_set_write_callback)(_playStream, NULL, NULL); 2266 } 2267 2268 void AudioDeviceLinuxPulse::PaStreamWriteCallback(pa_stream */*unused*/, 2269 size_t buffer_space, 2270 void *pThis) 2271 { 2272 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaStreamWriteCallbackHandler( 2273 buffer_space); 2274 } 2275 2276 void AudioDeviceLinuxPulse::PaStreamWriteCallbackHandler(size_t bufferSpace) 2277 { 2278 _tempBufferSpace = bufferSpace; 2279 2280 // Since we write the data asynchronously on a different thread, we have 2281 // to temporarily disable the write callback or else Pulse will call it 2282 // continuously until we write the data. We re-enable it below. 2283 DisableWriteCallback(); 2284 _timeEventPlay.Set(); 2285 } 2286 2287 void AudioDeviceLinuxPulse::PaStreamUnderflowCallback(pa_stream */*unused*/, 2288 void *pThis) 2289 { 2290 static_cast<AudioDeviceLinuxPulse*> (pThis)-> 2291 PaStreamUnderflowCallbackHandler(); 2292 } 2293 2294 void AudioDeviceLinuxPulse::PaStreamUnderflowCallbackHandler() 2295 { 2296 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 2297 " Playout underflow"); 2298 2299 if (_configuredLatencyPlay == WEBRTC_PA_NO_LATENCY_REQUIREMENTS) 2300 { 2301 // We didn't configure a pa_buffer_attr before, so switching to 2302 // one now would be questionable. 2303 return; 2304 } 2305 2306 // Otherwise reconfigure the stream with a higher target latency. 2307 2308 const pa_sample_spec *spec = LATE(pa_stream_get_sample_spec)(_playStream); 2309 if (!spec) 2310 { 2311 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2312 " pa_stream_get_sample_spec()"); 2313 return; 2314 } 2315 2316 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); 2317 uint32_t newLatency = _configuredLatencyPlay + bytesPerSec * 2318 WEBRTC_PA_PLAYBACK_LATENCY_INCREMENT_MSECS / 2319 WEBRTC_PA_MSECS_PER_SEC; 2320 2321 // Set the play buffer attributes 2322 _playBufferAttr.maxlength = newLatency; 2323 _playBufferAttr.tlength = newLatency; 2324 _playBufferAttr.minreq = newLatency / WEBRTC_PA_PLAYBACK_REQUEST_FACTOR; 2325 _playBufferAttr.prebuf = _playBufferAttr.tlength - _playBufferAttr.minreq; 2326 2327 pa_operation *op = LATE(pa_stream_set_buffer_attr)(_playStream, 2328 &_playBufferAttr, NULL, 2329 NULL); 2330 if (!op) 2331 { 2332 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2333 " pa_stream_set_buffer_attr()"); 2334 return; 2335 } 2336 2337 // Don't need to wait for this to complete. 2338 LATE(pa_operation_unref)(op); 2339 2340 // Save the new latency in case we underflow again. 2341 _configuredLatencyPlay = newLatency; 2342 } 2343 2344 void AudioDeviceLinuxPulse::EnableReadCallback() 2345 { 2346 LATE(pa_stream_set_read_callback)(_recStream, 2347 &PaStreamReadCallback, 2348 this); 2349 } 2350 2351 void AudioDeviceLinuxPulse::DisableReadCallback() 2352 { 2353 LATE(pa_stream_set_read_callback)(_recStream, NULL, NULL); 2354 } 2355 2356 void AudioDeviceLinuxPulse::PaStreamReadCallback(pa_stream */*unused1*/, 2357 size_t /*unused2*/, 2358 void *pThis) 2359 { 2360 static_cast<AudioDeviceLinuxPulse*> (pThis)-> 2361 PaStreamReadCallbackHandler(); 2362 } 2363 2364 void AudioDeviceLinuxPulse::PaStreamReadCallbackHandler() 2365 { 2366 // We get the data pointer and size now in order to save one Lock/Unlock 2367 // in the worker thread. 2368 if (LATE(pa_stream_peek)(_recStream, 2369 &_tempSampleData, 2370 &_tempSampleDataSize) != 0) 2371 { 2372 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2373 " Can't read data!"); 2374 return; 2375 } 2376 2377 // Since we consume the data asynchronously on a different thread, we have 2378 // to temporarily disable the read callback or else Pulse will call it 2379 // continuously until we consume the data. We re-enable it below. 2380 DisableReadCallback(); 2381 _timeEventRec.Set(); 2382 } 2383 2384 void AudioDeviceLinuxPulse::PaStreamOverflowCallback(pa_stream */*unused*/, 2385 void *pThis) 2386 { 2387 static_cast<AudioDeviceLinuxPulse*> (pThis)-> 2388 PaStreamOverflowCallbackHandler(); 2389 } 2390 2391 void AudioDeviceLinuxPulse::PaStreamOverflowCallbackHandler() 2392 { 2393 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 2394 " Recording overflow"); 2395 } 2396 2397 int32_t AudioDeviceLinuxPulse::LatencyUsecs(pa_stream *stream) 2398 { 2399 if (!WEBRTC_PA_REPORT_LATENCY) 2400 { 2401 return 0; 2402 } 2403 2404 if (!stream) 2405 { 2406 return 0; 2407 } 2408 2409 pa_usec_t latency; 2410 int negative; 2411 if (LATE(pa_stream_get_latency)(stream, &latency, &negative) != 0) 2412 { 2413 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2414 " Can't query latency"); 2415 // We'd rather continue playout/capture with an incorrect delay than 2416 // stop it altogether, so return a valid value. 2417 return 0; 2418 } 2419 2420 if (negative) 2421 { 2422 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2423 " warning: pa_stream_get_latency reported negative " 2424 "delay"); 2425 2426 // The delay can be negative for monitoring streams if the captured 2427 // samples haven't been played yet. In such a case, "latency" 2428 // contains the magnitude, so we must negate it to get the real value. 2429 int32_t tmpLatency = (int32_t) -latency; 2430 if (tmpLatency < 0) 2431 { 2432 // Make sure that we don't use a negative delay. 2433 tmpLatency = 0; 2434 } 2435 2436 return tmpLatency; 2437 } else 2438 { 2439 return (int32_t) latency; 2440 } 2441 } 2442 2443 int32_t AudioDeviceLinuxPulse::ReadRecordedData( 2444 const void* bufferData, 2445 size_t bufferSize) EXCLUSIVE_LOCKS_REQUIRED(_critSect) 2446 { 2447 size_t size = bufferSize; 2448 uint32_t numRecSamples = _recordBufferSize / (2 * _recChannels); 2449 2450 // Account for the peeked data and the used data. 2451 uint32_t recDelay = (uint32_t) ((LatencyUsecs(_recStream) 2452 / 1000) + 10 * ((size + _recordBufferUsed) / _recordBufferSize)); 2453 2454 _sndCardRecDelay = recDelay; 2455 2456 if (_playStream) 2457 { 2458 // Get the playout delay. 2459 _sndCardPlayDelay = (uint32_t) (LatencyUsecs(_playStream) / 1000); 2460 } 2461 2462 if (_recordBufferUsed > 0) 2463 { 2464 // Have to copy to the buffer until it is full. 2465 size_t copy = _recordBufferSize - _recordBufferUsed; 2466 if (size < copy) 2467 { 2468 copy = size; 2469 } 2470 2471 memcpy(&_recBuffer[_recordBufferUsed], bufferData, copy); 2472 _recordBufferUsed += copy; 2473 bufferData = static_cast<const char *> (bufferData) + copy; 2474 size -= copy; 2475 2476 if (_recordBufferUsed != _recordBufferSize) 2477 { 2478 // Not enough data yet to pass to VoE. 2479 return 0; 2480 } 2481 2482 // Provide data to VoiceEngine. 2483 if (ProcessRecordedData(_recBuffer, numRecSamples, recDelay) == -1) 2484 { 2485 // We have stopped recording. 2486 return -1; 2487 } 2488 2489 _recordBufferUsed = 0; 2490 } 2491 2492 // Now process full 10ms sample sets directly from the input. 2493 while (size >= _recordBufferSize) 2494 { 2495 // Provide data to VoiceEngine. 2496 if (ProcessRecordedData( 2497 static_cast<int8_t *> (const_cast<void *> (bufferData)), 2498 numRecSamples, recDelay) == -1) 2499 { 2500 // We have stopped recording. 2501 return -1; 2502 } 2503 2504 bufferData = static_cast<const char *> (bufferData) + 2505 _recordBufferSize; 2506 size -= _recordBufferSize; 2507 2508 // We have consumed 10ms of data. 2509 recDelay -= 10; 2510 } 2511 2512 // Now save any leftovers for later. 2513 if (size > 0) 2514 { 2515 memcpy(_recBuffer, bufferData, size); 2516 _recordBufferUsed = size; 2517 } 2518 2519 return 0; 2520 } 2521 2522 int32_t AudioDeviceLinuxPulse::ProcessRecordedData( 2523 int8_t *bufferData, 2524 uint32_t bufferSizeInSamples, 2525 uint32_t recDelay) EXCLUSIVE_LOCKS_REQUIRED(_critSect) 2526 { 2527 uint32_t currentMicLevel(0); 2528 uint32_t newMicLevel(0); 2529 2530 _ptrAudioBuffer->SetRecordedBuffer(bufferData, bufferSizeInSamples); 2531 2532 if (AGC()) 2533 { 2534 // Store current mic level in the audio buffer if AGC is enabled 2535 if (MicrophoneVolume(currentMicLevel) == 0) 2536 { 2537 // This call does not affect the actual microphone volume 2538 _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel); 2539 } 2540 } 2541 2542 const uint32_t clockDrift(0); 2543 // TODO(andrew): this is a temporary hack, to avoid non-causal far- and 2544 // near-end signals at the AEC for PulseAudio. I think the system delay is 2545 // being correctly calculated here, but for legacy reasons we add +10 ms 2546 // to the value in the AEC. The real fix will be part of a larger 2547 // investigation into managing system delay in the AEC. 2548 if (recDelay > 10) 2549 recDelay -= 10; 2550 else 2551 recDelay = 0; 2552 _ptrAudioBuffer->SetVQEData(_sndCardPlayDelay, recDelay, clockDrift); 2553 _ptrAudioBuffer->SetTypingStatus(KeyPressed()); 2554 // Deliver recorded samples at specified sample rate, 2555 // mic level etc. to the observer using callback. 2556 UnLock(); 2557 _ptrAudioBuffer->DeliverRecordedData(); 2558 Lock(); 2559 2560 // We have been unlocked - check the flag again. 2561 if (!_recording) 2562 { 2563 return -1; 2564 } 2565 2566 if (AGC()) 2567 { 2568 newMicLevel = _ptrAudioBuffer->NewMicLevel(); 2569 if (newMicLevel != 0) 2570 { 2571 // The VQE will only deliver non-zero microphone levels when a 2572 // change is needed. 2573 // Set this new mic level (received from the observer as return 2574 // value in the callback). 2575 WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, 2576 " AGC change of volume: old=%u => new=%u", 2577 currentMicLevel, newMicLevel); 2578 if (SetMicrophoneVolume(newMicLevel) == -1) 2579 { 2580 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, 2581 _id, 2582 " the required modification of the microphone " 2583 "volume failed"); 2584 } 2585 } 2586 } 2587 2588 return 0; 2589 } 2590 2591 bool AudioDeviceLinuxPulse::PlayThreadFunc(void* pThis) 2592 { 2593 return (static_cast<AudioDeviceLinuxPulse*> (pThis)->PlayThreadProcess()); 2594 } 2595 2596 bool AudioDeviceLinuxPulse::RecThreadFunc(void* pThis) 2597 { 2598 return (static_cast<AudioDeviceLinuxPulse*> (pThis)->RecThreadProcess()); 2599 } 2600 2601 bool AudioDeviceLinuxPulse::PlayThreadProcess() 2602 { 2603 switch (_timeEventPlay.Wait(1000)) 2604 { 2605 case kEventSignaled: 2606 break; 2607 case kEventError: 2608 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 2609 "EventWrapper::Wait() failed"); 2610 return true; 2611 case kEventTimeout: 2612 return true; 2613 } 2614 2615 CriticalSectionScoped lock(&_critSect); 2616 2617 if (_startPlay) 2618 { 2619 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 2620 "_startPlay true, performing initial actions"); 2621 2622 _startPlay = false; 2623 _playDeviceName = NULL; 2624 2625 // Set if not default device 2626 if (_outputDeviceIndex > 0) 2627 { 2628 // Get the playout device name 2629 _playDeviceName = new char[kAdmMaxDeviceNameSize]; 2630 _deviceIndex = _outputDeviceIndex; 2631 PlayoutDevices(); 2632 } 2633 2634 // Start muted only supported on 0.9.11 and up 2635 if (LATE(pa_context_get_protocol_version)(_paContext) 2636 >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) 2637 { 2638 // Get the currently saved speaker mute status 2639 // and set the initial mute status accordingly 2640 bool enabled(false); 2641 _mixerManager.SpeakerMute(enabled); 2642 if (enabled) 2643 { 2644 _playStreamFlags |= PA_STREAM_START_MUTED; 2645 } 2646 } 2647 2648 // Get the currently saved speaker volume 2649 uint32_t volume = 0; 2650 if (update_speaker_volume_at_startup_) 2651 _mixerManager.SpeakerVolume(volume); 2652 2653 PaLock(); 2654 2655 // NULL gives PA the choice of startup volume. 2656 pa_cvolume* ptr_cvolume = NULL; 2657 if (update_speaker_volume_at_startup_) { 2658 pa_cvolume cVolumes; 2659 ptr_cvolume = &cVolumes; 2660 2661 // Set the same volume for all channels 2662 const pa_sample_spec *spec = 2663 LATE(pa_stream_get_sample_spec)(_playStream); 2664 LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume); 2665 update_speaker_volume_at_startup_ = false; 2666 } 2667 2668 // Connect the stream to a sink 2669 if (LATE(pa_stream_connect_playback)( 2670 _playStream, 2671 _playDeviceName, 2672 &_playBufferAttr, 2673 (pa_stream_flags_t) _playStreamFlags, 2674 ptr_cvolume, NULL) != PA_OK) 2675 { 2676 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2677 " failed to connect play stream, err=%d", 2678 LATE(pa_context_errno)(_paContext)); 2679 } 2680 2681 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2682 " play stream connected"); 2683 2684 // Wait for state change 2685 while (LATE(pa_stream_get_state)(_playStream) != PA_STREAM_READY) 2686 { 2687 LATE(pa_threaded_mainloop_wait)(_paMainloop); 2688 } 2689 2690 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2691 " play stream ready"); 2692 2693 // We can now handle write callbacks 2694 EnableWriteCallback(); 2695 2696 PaUnLock(); 2697 2698 // Clear device name 2699 if (_playDeviceName) 2700 { 2701 delete [] _playDeviceName; 2702 _playDeviceName = NULL; 2703 } 2704 2705 _playing = true; 2706 _playStartEvent.Set(); 2707 2708 return true; 2709 } 2710 2711 if (_playing) 2712 { 2713 if (!_recording) 2714 { 2715 // Update the playout delay 2716 _sndCardPlayDelay = (uint32_t) (LatencyUsecs(_playStream) 2717 / 1000); 2718 } 2719 2720 if (_playbackBufferUnused < _playbackBufferSize) 2721 { 2722 2723 size_t write = _playbackBufferSize - _playbackBufferUnused; 2724 if (_tempBufferSpace < write) 2725 { 2726 write = _tempBufferSpace; 2727 } 2728 2729 PaLock(); 2730 if (LATE(pa_stream_write)( 2731 _playStream, 2732 (void *) &_playBuffer[_playbackBufferUnused], 2733 write, NULL, (int64_t) 0, 2734 PA_SEEK_RELATIVE) != PA_OK) 2735 { 2736 _writeErrors++; 2737 if (_writeErrors > 10) 2738 { 2739 if (_playError == 1) 2740 { 2741 WEBRTC_TRACE(kTraceWarning, 2742 kTraceUtility, _id, 2743 " pending playout error exists"); 2744 } 2745 // Triggers callback from module process thread. 2746 _playError = 1; 2747 WEBRTC_TRACE( 2748 kTraceError, 2749 kTraceUtility, 2750 _id, 2751 " kPlayoutError message posted: " 2752 "_writeErrors=%u, error=%d", 2753 _writeErrors, 2754 LATE(pa_context_errno)(_paContext)); 2755 _writeErrors = 0; 2756 } 2757 } 2758 PaUnLock(); 2759 2760 _playbackBufferUnused += write; 2761 _tempBufferSpace -= write; 2762 } 2763 2764 uint32_t numPlaySamples = _playbackBufferSize / (2 * _playChannels); 2765 // Might have been reduced to zero by the above. 2766 if (_tempBufferSpace > 0) 2767 { 2768 // Ask for new PCM data to be played out using the 2769 // AudioDeviceBuffer ensure that this callback is executed 2770 // without taking the audio-thread lock. 2771 UnLock(); 2772 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2773 " requesting data"); 2774 uint32_t nSamples = 2775 _ptrAudioBuffer->RequestPlayoutData(numPlaySamples); 2776 Lock(); 2777 2778 // We have been unlocked - check the flag again. 2779 if (!_playing) 2780 { 2781 return true; 2782 } 2783 2784 nSamples = _ptrAudioBuffer->GetPlayoutData(_playBuffer); 2785 if (nSamples != numPlaySamples) 2786 { 2787 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, 2788 _id, " invalid number of output samples(%d)", 2789 nSamples); 2790 } 2791 2792 size_t write = _playbackBufferSize; 2793 if (_tempBufferSpace < write) 2794 { 2795 write = _tempBufferSpace; 2796 } 2797 2798 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2799 " will write"); 2800 PaLock(); 2801 if (LATE(pa_stream_write)(_playStream, (void *) &_playBuffer[0], 2802 write, NULL, (int64_t) 0, 2803 PA_SEEK_RELATIVE) != PA_OK) 2804 { 2805 _writeErrors++; 2806 if (_writeErrors > 10) 2807 { 2808 if (_playError == 1) 2809 { 2810 WEBRTC_TRACE(kTraceWarning, 2811 kTraceUtility, _id, 2812 " pending playout error exists"); 2813 } 2814 // Triggers callback from module process thread. 2815 _playError = 1; 2816 WEBRTC_TRACE( 2817 kTraceError, 2818 kTraceUtility, 2819 _id, 2820 " kPlayoutError message posted: " 2821 "_writeErrors=%u, error=%d", 2822 _writeErrors, 2823 LATE(pa_context_errno)(_paContext)); 2824 _writeErrors = 0; 2825 } 2826 } 2827 PaUnLock(); 2828 2829 _playbackBufferUnused = write; 2830 } 2831 2832 _tempBufferSpace = 0; 2833 PaLock(); 2834 EnableWriteCallback(); 2835 PaUnLock(); 2836 2837 } // _playing 2838 2839 return true; 2840 } 2841 2842 bool AudioDeviceLinuxPulse::RecThreadProcess() 2843 { 2844 switch (_timeEventRec.Wait(1000)) 2845 { 2846 case kEventSignaled: 2847 break; 2848 case kEventError: 2849 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 2850 "EventWrapper::Wait() failed"); 2851 return true; 2852 case kEventTimeout: 2853 return true; 2854 } 2855 2856 CriticalSectionScoped lock(&_critSect); 2857 2858 if (_startRec) 2859 { 2860 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 2861 "_startRec true, performing initial actions"); 2862 2863 _recDeviceName = NULL; 2864 2865 // Set if not default device 2866 if (_inputDeviceIndex > 0) 2867 { 2868 // Get the recording device name 2869 _recDeviceName = new char[kAdmMaxDeviceNameSize]; 2870 _deviceIndex = _inputDeviceIndex; 2871 RecordingDevices(); 2872 } 2873 2874 PaLock(); 2875 2876 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2877 " connecting stream"); 2878 2879 // Connect the stream to a source 2880 if (LATE(pa_stream_connect_record)(_recStream, 2881 _recDeviceName, 2882 &_recBufferAttr, 2883 (pa_stream_flags_t) _recStreamFlags) != PA_OK) 2884 { 2885 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2886 " failed to connect rec stream, err=%d", 2887 LATE(pa_context_errno)(_paContext)); 2888 } 2889 2890 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2891 " connected"); 2892 2893 // Wait for state change 2894 while (LATE(pa_stream_get_state)(_recStream) != PA_STREAM_READY) 2895 { 2896 LATE(pa_threaded_mainloop_wait)(_paMainloop); 2897 } 2898 2899 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2900 " done"); 2901 2902 // We can now handle read callbacks 2903 EnableReadCallback(); 2904 2905 PaUnLock(); 2906 2907 // Clear device name 2908 if (_recDeviceName) 2909 { 2910 delete [] _recDeviceName; 2911 _recDeviceName = NULL; 2912 } 2913 2914 _startRec = false; 2915 _recording = true; 2916 _recStartEvent.Set(); 2917 2918 return true; 2919 } 2920 2921 if (_recording) 2922 { 2923 // Read data and provide it to VoiceEngine 2924 if (ReadRecordedData(_tempSampleData, _tempSampleDataSize) == -1) 2925 { 2926 return true; 2927 } 2928 2929 _tempSampleData = NULL; 2930 _tempSampleDataSize = 0; 2931 2932 PaLock(); 2933 while (true) 2934 { 2935 // Ack the last thing we read 2936 if (LATE(pa_stream_drop)(_recStream) != 0) 2937 { 2938 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, 2939 _id, " failed to drop, err=%d\n", 2940 LATE(pa_context_errno)(_paContext)); 2941 } 2942 2943 if (LATE(pa_stream_readable_size)(_recStream) <= 0) 2944 { 2945 // Then that was all the data 2946 break; 2947 } 2948 2949 // Else more data. 2950 const void *sampleData; 2951 size_t sampleDataSize; 2952 2953 if (LATE(pa_stream_peek)(_recStream, &sampleData, &sampleDataSize) 2954 != 0) 2955 { 2956 _recError = 1; // triggers callback from module process thread 2957 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, 2958 _id, " RECORD_ERROR message posted, error = %d", 2959 LATE(pa_context_errno)(_paContext)); 2960 break; 2961 } 2962 2963 _sndCardRecDelay = (uint32_t) (LatencyUsecs(_recStream) 2964 / 1000); 2965 2966 // Drop lock for sigslot dispatch, which could take a while. 2967 PaUnLock(); 2968 // Read data and provide it to VoiceEngine 2969 if (ReadRecordedData(sampleData, sampleDataSize) == -1) 2970 { 2971 return true; 2972 } 2973 PaLock(); 2974 2975 // Return to top of loop for the ack and the check for more data. 2976 } 2977 2978 EnableReadCallback(); 2979 PaUnLock(); 2980 2981 } // _recording 2982 2983 return true; 2984 } 2985 2986 bool AudioDeviceLinuxPulse::KeyPressed() const{ 2987 2988 char szKey[32]; 2989 unsigned int i = 0; 2990 char state = 0; 2991 2992 if (!_XDisplay) 2993 return false; 2994 2995 // Check key map status 2996 XQueryKeymap(_XDisplay, szKey); 2997 2998 // A bit change in keymap means a key is pressed 2999 for (i = 0; i < sizeof(szKey); i++) 3000 state |= (szKey[i] ^ _oldKeyState[i]) & szKey[i]; 3001 3002 // Save old state 3003 memcpy((char*)_oldKeyState, (char*)szKey, sizeof(_oldKeyState)); 3004 return (state != 0); 3005 } 3006 } 3007