From c826aea079c59950a4064a94825534884fed8bf8 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 1 Apr 2024 21:39:17 -0700 Subject: Fix "default" audio device handling. Windows and Mac/Linux behave slightly differently with respect to Default devices, in that mac/linux (I think) simply assumes the device at index 0 is the default one, and windows has a separate API for enabling the default device. --- indra/llwebrtc/llwebrtc.cpp | 230 ++++++++++++++++++++++++---------------- indra/llwebrtc/llwebrtc_impl.h | 6 +- indra/newview/llvoicewebrtc.cpp | 66 ++++++------ 3 files changed, 173 insertions(+), 129 deletions(-) (limited to 'indra') diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 074b037529..75b2332bed 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -40,6 +40,11 @@ namespace llwebrtc { +static int16_t PLAYOUT_DEVICE_DEFAULT = -1; +static int16_t PLAYOUT_DEVICE_BAD = -2; +static int16_t RECORD_DEVICE_DEFAULT = -1; +static int16_t RECORD_DEVICE_BAD = -2; + LLAudioDeviceObserver::LLAudioDeviceObserver() : mSumVector {0}, mMicrophoneEnergy(0.0) {} float LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; } @@ -189,10 +194,16 @@ void LLWebRTCImpl::init() std::unique_ptr(mTuningAudioDeviceObserver)); mTuningDeviceModule->Init(); - mTuningDeviceModule->SetStereoRecording(true); - mTuningDeviceModule->SetStereoPlayout(true); + mTuningDeviceModule->SetPlayoutDevice(mPlayoutDevice); + mTuningDeviceModule->SetRecordingDevice(mRecordingDevice); mTuningDeviceModule->EnableBuiltInAEC(false); mTuningDeviceModule->SetAudioDeviceSink(this); + mTuningDeviceModule->InitMicrophone(); + mTuningDeviceModule->InitSpeaker(); + mTuningDeviceModule->InitRecording(); + mTuningDeviceModule->InitPlayout(); + mTuningDeviceModule->SetStereoRecording(true); + mTuningDeviceModule->SetStereoPlayout(true); updateDevices(); }); @@ -207,13 +218,13 @@ void LLWebRTCImpl::init() mPeerDeviceModule->Init(); mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice); mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); - mPeerDeviceModule->SetStereoRecording(true); - mPeerDeviceModule->SetStereoPlayout(true); mPeerDeviceModule->EnableBuiltInAEC(false); mPeerDeviceModule->InitMicrophone(); mPeerDeviceModule->InitSpeaker(); mPeerDeviceModule->InitRecording(); mPeerDeviceModule->InitPlayout(); + mPeerDeviceModule->SetStereoRecording(true); + mPeerDeviceModule->SetStereoPlayout(true); }); // The custom processor allows us to retrieve audio data (and levels) @@ -375,145 +386,169 @@ void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer) } } -static int16_t ll_get_device_module_capture_device(rtc::scoped_refptr device_module, const std::string &id) -{ - int16_t recordingDevice = 0; - int16_t captureDeviceCount = device_module->RecordingDevices(); - for (int16_t i = 0; i < captureDeviceCount; i++) - { - char name[webrtc::kAdmMaxDeviceNameSize]; - char guid[webrtc::kAdmMaxGuidSize]; - device_module->RecordingDeviceName(i, name, guid); - if (id == guid || id == "Default") // first one in list is default - { - RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; - recordingDevice = i; - break; - } - } - return recordingDevice; -} - void ll_set_device_module_capture_device(rtc::scoped_refptr device_module, int16_t device) { device_module->StopRecording(); - device_module->SetRecordingDevice(device); +#if LL_WINDOWS + if (device < 0) + { + device_module->SetRecordingDevice(webrtc::AudioDeviceModule::kDefaultDevice); + } + else + { + device_module->SetRecordingDevice(device); + } +#else + // passed in default is -1, but the device list + // has it at 0 + device_module->SetPlayoutDevice(device + 1); +#endif device_module->InitMicrophone(); - device_module->SetStereoRecording(false); device_module->InitRecording(); + device_module->SetStereoRecording(false); device_module->StartRecording(); } void LLWebRTCImpl::setCaptureDevice(const std::string &id) { - - mWorkerThread->PostTask( - [this, id]() + int16_t recordingDevice = RECORD_DEVICE_DEFAULT; + if (id != "Default") + { + for (int16_t i = 0; i < mRecordingDeviceList.size(); i++) { - int16_t recordingDevice = ll_get_device_module_capture_device(mTuningDeviceModule, id); - if (recordingDevice != mRecordingDevice) + if (mRecordingDeviceList[i].mID == id) { - mRecordingDevice = recordingDevice; - if (mTuningMode) - { - ll_set_device_module_capture_device(mTuningDeviceModule, recordingDevice); - } - else - { - ll_set_device_module_capture_device(mPeerDeviceModule, recordingDevice); - } + recordingDevice = i; + break; } - }); -} - -static int16_t ll_get_device_module_render_device( - rtc::scoped_refptr device_module, - const std::string &id) -{ - int16_t playoutDevice = 0; - int16_t playoutDeviceCount = device_module->PlayoutDevices(); - for (int16_t i = 0; i < playoutDeviceCount; i++) - { - char name[webrtc::kAdmMaxDeviceNameSize]; - char guid[webrtc::kAdmMaxGuidSize]; - device_module->PlayoutDeviceName(i, name, guid); - if (id == guid || id == "Default") // first one in list is default - { - RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; - playoutDevice = i; - break; } } - return playoutDevice; + if (recordingDevice == mRecordingDevice) + { + return; + } + mRecordingDevice = recordingDevice; + if (mTuningMode) + { + mWorkerThread->PostTask([this, recordingDevice]() { ll_set_device_module_capture_device(mTuningDeviceModule, recordingDevice); }); + } + else + { + mWorkerThread->PostTask([this, recordingDevice]() { ll_set_device_module_capture_device(mPeerDeviceModule, recordingDevice); }); + } } void ll_set_device_module_render_device(rtc::scoped_refptr device_module, int16_t device) { device_module->StopPlayout(); - device_module->SetPlayoutDevice(device); +#if LL_WINDOWS + if (device < 0) + { + device_module->SetPlayoutDevice(webrtc::AudioDeviceModule::kDefaultDevice); + } + else + { + device_module->SetPlayoutDevice(device); + } +#else + device_module->SetPlayoutDevice(device + 1); +#endif device_module->InitSpeaker(); - device_module->SetStereoPlayout(false); device_module->InitPlayout(); - device_module->StartPlayout(); + device_module->SetStereoPlayout(true); } void LLWebRTCImpl::setRenderDevice(const std::string &id) { - mWorkerThread->PostTask( - [this, id]() - { - int16_t playoutDevice = ll_get_device_module_render_device(mTuningDeviceModule, id); - if (playoutDevice != mPlayoutDevice) + int16_t playoutDevice = PLAYOUT_DEVICE_DEFAULT; + if (id != "Default") + { + for (int16_t i = 0; i < mPlayoutDeviceList.size(); i++) + { + if (mPlayoutDeviceList[i].mID == id) { - mPlayoutDevice = playoutDevice; - if (mTuningMode) - { - ll_set_device_module_render_device(mTuningDeviceModule, playoutDevice); - } - else - { - ll_set_device_module_render_device(mPeerDeviceModule, playoutDevice); - } + playoutDevice = i; + break; } - }); + } + } + if (playoutDevice == mPlayoutDevice) + { + return; + } + mPlayoutDevice = playoutDevice; + + if (mTuningMode) + { + mWorkerThread->PostTask( + [this, playoutDevice]() + { + ll_set_device_module_render_device(mTuningDeviceModule, playoutDevice); + }); + } + else + { + mWorkerThread->PostTask( + [this, playoutDevice]() + { + ll_set_device_module_render_device(mPeerDeviceModule, playoutDevice); + mPeerDeviceModule->StartPlayout(); + }); + } } // updateDevices needs to happen on the worker thread. void LLWebRTCImpl::updateDevices() { - int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); - int16_t currentRenderDeviceIndex = mTuningDeviceModule->GetPlayoutDevice(); - - LLWebRTCVoiceDeviceList renderDeviceList; - for (int16_t index = 0; index < renderDeviceCount; index++) + int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); + + mPlayoutDeviceList.clear(); +#if LL_WINDOWS + int16_t index = 0; +#else + // index zero is always "Default" for darwin/linux, + // which is a special case, so skip it. + int16_t index = 1; +#endif + for (; index < renderDeviceCount; index++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; mTuningDeviceModule->PlayoutDeviceName(index, name, guid); - renderDeviceList.emplace_back(name, guid); + mPlayoutDeviceList.emplace_back(name, guid); } - int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); - int16_t currentCaptureDeviceIndex = mTuningDeviceModule->GetRecordingDevice(); - - LLWebRTCVoiceDeviceList captureDeviceList; - for (int16_t index = 0; index < captureDeviceCount; index++) + int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); + + mRecordingDeviceList.clear(); +#if LL_WINDOWS + index = 0; +#else + // index zero is always "Default" for darwin/linux, + // which is a special case, so skip it. + index = 1; +#endif + for (; index < captureDeviceCount; index++) { char name[webrtc::kAdmMaxDeviceNameSize]; char guid[webrtc::kAdmMaxGuidSize]; mTuningDeviceModule->RecordingDeviceName(index, name, guid); - captureDeviceList.emplace_back(name, guid); + mRecordingDeviceList.emplace_back(name, guid); } + for (auto &observer : mVoiceDevicesObserverList) { - observer->OnDevicesChanged(renderDeviceList, - captureDeviceList); + observer->OnDevicesChanged(mPlayoutDeviceList, mRecordingDeviceList); } } void LLWebRTCImpl::OnDevicesUpdated() { + // reset these to a bad value so an update is forced + mRecordingDevice = RECORD_DEVICE_BAD; + mPlayoutDevice = PLAYOUT_DEVICE_BAD; + updateDevices(); } @@ -529,17 +564,24 @@ void LLWebRTCImpl::setTuningMode(bool enable) mPeerDeviceModule->StopPlayout(); ll_set_device_module_render_device(mTuningDeviceModule, mPlayoutDevice); ll_set_device_module_capture_device(mTuningDeviceModule, mRecordingDevice); + mTuningDeviceModule->InitPlayout(); + mTuningDeviceModule->InitRecording(); mTuningDeviceModule->StartRecording(); - mTuningDeviceModule->StartPlayout(); + // TODO: Starting Playout on the TDM appears to create an audio artifact (click) + // in this case, so disabling it for now. We may have to do something different + // if we enable 'echo playback' via the TDM when tuning. + //mTuningDeviceModule->StartPlayout(); } else { mTuningDeviceModule->StopRecording(); - mTuningDeviceModule->StopPlayout(); + //mTuningDeviceModule->StopPlayout(); ll_set_device_module_render_device(mPeerDeviceModule, mPlayoutDevice); ll_set_device_module_capture_device(mPeerDeviceModule, mRecordingDevice); - mPeerDeviceModule->StartRecording(); + mPeerDeviceModule->InitPlayout(); + mPeerDeviceModule->InitRecording(); mPeerDeviceModule->StartPlayout(); + mPeerDeviceModule->StartRecording(); } } ); diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 328e962c50..78ae6b4444 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -244,8 +244,12 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS // accessors in native webrtc for devices aren't apparently implemented yet. bool mTuningMode; - int32_t mPlayoutDevice; int32_t mRecordingDevice; + LLWebRTCVoiceDeviceList mRecordingDeviceList; + + int32_t mPlayoutDevice; + LLWebRTCVoiceDeviceList mPlayoutDeviceList; + bool mMute; LLAudioDeviceObserver * mTuningAudioDeviceObserver; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index f3d3460022..3cc4aa113c 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -294,19 +294,31 @@ void LLWebRTCVoiceClient::updateSettings() LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE setVoiceEnabled(LLVoiceClient::getInstance()->voiceEnabled()); - setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); + static LLCachedControl sVoiceEarLocation(gSavedSettings, "VoiceEarLocation"); + setEarLocation(sVoiceEarLocation); + + static LLCachedControl sInputDevice(gSavedSettings, "VoiceInputAudioDevice"); + setCaptureDevice(sInputDevice); + + static LLCachedControl sOutputDevice(gSavedSettings, "VoiceOutputAudioDevice"); + setRenderDevice(sOutputDevice); + + static LLCachedControl sMicLevel(gSavedSettings, "AudioLevelMic"); + setMicGain(sMicLevel); - std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); - setCaptureDevice(inputDevice); - std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); - setRenderDevice(outputDevice); - F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); - setMicGain(mic_level); - llwebrtc::LLWebRTCDeviceInterface::AudioConfig config; - config.mEchoCancellation = gSavedSettings.getBOOL("VoiceEchoCancellation"); - config.mAGC = gSavedSettings.getBOOL("VoiceAutomaticGainControl"); - config.mNoiseSuppressionLevel = (llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel)gSavedSettings.getU32("VoiceNoiseSuppressionLevel"); + + static LLCachedControl sEchoCancellation(gSavedSettings, "VoiceEchoCancellation", true); + config.mEchoCancellation = sEchoCancellation; + + static LLCachedControl sAGC(gSavedSettings, "VoiceAutomaticGainControl", true); + config.mAGC = sAGC; + + static LLCachedControl sNoiseSuppressionLevel(gSavedSettings, + "VoiceNoiseSuppressionLevel", + llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel::NOISE_SUPPRESSION_LEVEL_VERY_HIGH); + config.mNoiseSuppressionLevel = (llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel) (U32)sNoiseSuppressionLevel; + mWebRTCDeviceInterface->setAudioConfig(config); } @@ -607,6 +619,7 @@ void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceList& render_devices, const llwebrtc::LLWebRTCVoiceDeviceList& capture_devices) { + LL::WorkQueue::postMaybe(mMainQueue, [=] { @@ -618,43 +631,25 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi const llwebrtc::LLWebRTCVoiceDeviceList &capture_devices) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE - std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); LL_DEBUGS("Voice") << "Setting devices to-input: '" << inputDevice << "' output: '" << outputDevice << "'" << LL_ENDL; clearRenderDevices(); - bool renderDeviceSet = false; for (auto &device : render_devices) { addRenderDevice(LLVoiceDevice(device.mDisplayName, device.mID)); - LL_DEBUGS("Voice") << "Checking render device" << "'" << device.mID << "'" << LL_ENDL; - if (outputDevice == device.mID) - { - renderDeviceSet = true; - } - } - if (!renderDeviceSet) - { - setRenderDevice("Default"); } + setRenderDevice(outputDevice); clearCaptureDevices(); - bool captureDeviceSet = false; for (auto &device : capture_devices) { LL_DEBUGS("Voice") << "Checking capture device:'" << device.mID << "'" << LL_ENDL; addCaptureDevice(LLVoiceDevice(device.mDisplayName, device.mID)); - if (inputDevice == device.mID) - { - captureDeviceSet = true; - } - } - if (!captureDeviceSet) - { - setCaptureDevice("Default"); } + setCaptureDevice(inputDevice); setDevicesListUpdated(true); } @@ -1559,10 +1554,13 @@ F32 LLWebRTCVoiceClient::getUserVolume(const LLUUID& id) // Minimum volume will be returned for users with voice disabled F32 result = LLVoiceClient::VOLUME_MIN; - participantStatePtr_t participant(mSession->findParticipantByID(id)); - if(participant) + if (mSession) { - result = participant->mVolume; + participantStatePtr_t participant(mSession->findParticipantByID(id)); + if (participant) + { + result = participant->mVolume; + } } return result; -- cgit v1.2.3