From f3120efae8a737336d8e313ca5cb5bb519f9b358 Mon Sep 17 00:00:00 2001
From: Bjoseph Wombat <bjoseph@vivox.com>
Date: Sun, 22 Mar 2015 13:04:09 -0400
Subject: [PATCH] Mic setting changes some device list is up to date, mic loop
 test works, removed obsolete code and fine tuned voice state machine to avoid
 frequent neccessary code paths.

---
 indra/newview/llpanelvoicedevicesettings.cpp |  30 +-
 indra/newview/llpanelvoicedevicesettings.h   |   2 +
 indra/newview/llvoiceclient.cpp              |  12 +
 indra/newview/llvoiceclient.h                |   2 +
 indra/newview/llvoicevivox.cpp               | 343 +++++++------------
 indra/newview/llvoicevivox.h                 |  13 +-
 6 files changed, 159 insertions(+), 243 deletions(-)

diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp
index 3946d6a63b..07f8045546 100755
--- a/indra/newview/llpanelvoicedevicesettings.cpp
+++ b/indra/newview/llpanelvoicedevicesettings.cpp
@@ -51,7 +51,7 @@ LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings()
 	mCtrlOutputDevices = NULL;
 	mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
 	mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
-	mDevicesUpdated = FALSE;
+	mDevicesUpdated = FALSE;  //obsolete
 	mUseTuningMode = true;
 
 	// grab "live" mic volume level
@@ -80,6 +80,10 @@ BOOL LLPanelVoiceDeviceSettings::postBuild()
 	mLocalizedDeviceNames[DEFAULT_DEVICE]				= getString("default_text");
 	mLocalizedDeviceNames["No Device"]					= getString("name_no_device");
 	mLocalizedDeviceNames["Default System Device"]		= getString("name_default_system_device");
+
+	mCtrlOutputDevices->setMouseDownCallback(boost::bind(&LLPanelVoiceDeviceSettings::onOutputDevicesClicked, this));
+	mCtrlInputDevices->setMouseDownCallback(boost::bind(&LLPanelVoiceDeviceSettings::onInputDevicesClicked, this));
+	
 	
 	return TRUE;
 }
@@ -226,9 +230,8 @@ void LLPanelVoiceDeviceSettings::refresh()
 			mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM);
 			mCtrlOutputDevices->setValue(mOutputDevice);
 		}
-		mDevicesUpdated = FALSE;
 	}
-	else if (!mDevicesUpdated)
+	else if (LLVoiceClient::getInstance()->deviceSettingsUpdated())
 	{
 		LLVoiceDeviceList::const_iterator iter;
 		
@@ -272,7 +275,6 @@ void LLPanelVoiceDeviceSettings::refresh()
 				mOutputDevice = DEFAULT_DEVICE;
 			}
 		}
-		mDevicesUpdated = TRUE;
 	}	
 }
 
@@ -281,7 +283,6 @@ void LLPanelVoiceDeviceSettings::initialize()
 	mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
 	mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
 	mMicVolume = gSavedSettings.getF32("AudioLevelMic");
-	mDevicesUpdated = FALSE;
 
 	// ask for new device enumeration
 	LLVoiceClient::getInstance()->refreshDeviceLists();
@@ -314,8 +315,8 @@ void LLPanelVoiceDeviceSettings::onCommitInputDevice()
 {
 	if(LLVoiceClient::getInstance())
 	{
-		LLVoiceClient::getInstance()->setCaptureDevice(
-			mCtrlInputDevices->getValue().asString());
+		mInputDevice = mCtrlInputDevices->getValue().asString();
+		LLVoiceClient::getInstance()->setRenderDevice(mInputDevice);
 	}
 }
 
@@ -323,7 +324,18 @@ void LLPanelVoiceDeviceSettings::onCommitOutputDevice()
 {
 	if(LLVoiceClient::getInstance())
 	{
-		LLVoiceClient::getInstance()->setRenderDevice(
-			mCtrlInputDevices->getValue().asString());
+		
+		mOutputDevice = mCtrlOutputDevices->getValue().asString(); 
+		LLVoiceClient::getInstance()->setRenderDevice(mOutputDevice);
 	}
 }
+
+void LLPanelVoiceDeviceSettings::onOutputDevicesClicked()
+{
+	LLVoiceClient::getInstance()->refreshDeviceLists(false);  // fill in the pop up menus again if needed.
+}
+
+void LLPanelVoiceDeviceSettings::onInputDevicesClicked()
+{
+	LLVoiceClient::getInstance()->refreshDeviceLists(false);  // fill in the pop up menus again if needed.
+}
diff --git a/indra/newview/llpanelvoicedevicesettings.h b/indra/newview/llpanelvoicedevicesettings.h
index 83464f476a..355bc02b05 100755
--- a/indra/newview/llpanelvoicedevicesettings.h
+++ b/indra/newview/llpanelvoicedevicesettings.h
@@ -53,6 +53,8 @@ protected:
 
 	void onCommitInputDevice();
 	void onCommitOutputDevice();
+	void onOutputDevicesClicked();
+	void onInputDevicesClicked();
 
 	F32 mMicVolume;
 	std::string mInputDevice;
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 6df1bda135..e09ca1f72a 100755
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -266,6 +266,18 @@ bool LLVoiceClient::deviceSettingsAvailable()
 	}
 }
 
+bool LLVoiceClient::deviceSettingsUpdated()
+{
+	if (mVoiceModule)
+	{
+		return mVoiceModule->deviceSettingsUpdated();
+	}
+	else
+	{
+		return false;
+	}
+}
+
 void LLVoiceClient::refreshDeviceLists(bool clearCurrentList)
 {
 	if (mVoiceModule) mVoiceModule->refreshDeviceLists(clearCurrentList);
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index 4c6ff5ef8f..51961468ca 100755
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -128,6 +128,7 @@ public:
 	// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
 	// i.e. when the daemon is running and connected, and the device lists are populated.
 	virtual bool deviceSettingsAvailable()=0;
+	virtual bool deviceSettingsUpdated() = 0;
 	
 	// Requery the vivox daemon for the current list of input/output devices.
 	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
@@ -335,6 +336,7 @@ public:
 	// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
 	// i.e. when the daemon is running and connected, and the device lists are populated.
 	bool deviceSettingsAvailable();
+	bool deviceSettingsUpdated();	// returns true when the device list has been updated recently.
 		
 	// Requery the vivox daemon for the current list of input/output devices.
 	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 99190e9919..6ac8d84771 100755
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -91,10 +91,9 @@ const F32 LOGIN_RETRY_SECONDS = 10.0f;
 const int MAX_LOGIN_RETRIES = 12;
 
 // Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine()
-// which is treated as normal. If this number is exceeded we suspect there is a problem with connection
-// to voice server (EXT-4313). When voice works correctly, there is from 1 to 15 times. 50 was chosen 
-// to make sure we don't make mistake when slight connection problems happen- situation when connection to server is 
-// blocked is VERY rare and it's better to sacrifice response time in this situation for the sake of stability.
+// which is treated as normal. The is the number of frames to wait for a channel join before giving up.  This was changed 
+// from the original count of 50 for two reason.  Modern PCs have higher frame rates and sometimes the SLVoice process 
+// backs up processing join requests.  There is a log statement that records when channel joins take longer than 100 frames.
 const int MAX_NORMAL_JOINING_SPATIAL_NUM = 1500;
 
 // How often to check for expired voice fonts in seconds
@@ -277,9 +276,10 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
 	mTuningEnergy(0.0f),
 	mTuningMicVolume(0),
 	mTuningMicVolumeDirty(true),
-	mTuningSpeakerVolume(0),
+	mTuningSpeakerVolume(50),     // Set to 50 so the user can hear himself when he sets his mic volume
 	mTuningSpeakerVolumeDirty(true),
 	mTuningExitState(stateDisabled),
+	mDevicesListUpdated(false),
 
 	mAreaVoiceDisabled(false),
 	mAudioSession(NULL),
@@ -718,8 +718,9 @@ void LLVivoxVoiceClient::stateMachine()
 		setVoiceEnabled(false);
 	}
 	
-	if(mVoiceEnabled || (!mIsInitialized &&!mTerminateDaemon) )
+	if ((getState() == stateRunning) && inSpatialChannel() && mUpdateTimer.hasExpired() && !mTerminateDaemon)
 	{
+		// poll the avatar position so its available in various states when a 3d position is sent.
 		updatePosition();
 	}
 	else if(mTuningMode)
@@ -728,7 +729,8 @@ void LLVivoxVoiceClient::stateMachine()
 	}
 	else
 	{
-		if((getState() != stateDisabled) && (getState() != stateDisableCleanup))
+		if (!gSavedSettings.getBOOL("EnableVoiceChat"))
+		//if((getState() != stateDisabled) && (getState() != stateDisableCleanup))
 		{
 			// User turned off voice support.  Send the cleanup messages, close the socket, and reset.
 			if(!mConnected || mTerminateDaemon)
@@ -746,6 +748,10 @@ void LLVivoxVoiceClient::stateMachine()
 		}
 	}
 	
+	
+	// send any requests to adjust mic and speaker settings if they have changed
+	sendLocalAudioUpdates(); 
+
 
 	switch(getState())
 	{
@@ -973,6 +979,19 @@ void LLVivoxVoiceClient::stateMachine()
 				else
 				{
 					// loop mic back to render device.
+					//setMuteMic(0);						// make sure the mic is not muted
+					std::ostringstream stream;
+
+					stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
+						<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+						<< "<Value>false</Value>"
+						<< "</Request>\n\n\n";
+
+					// Dirty the mute mic state so that it will get reset when we finishing previewing
+					mMuteMicDirty = true;
+					mTuningSpeakerVolumeDirty = true;
+
+					writeString(stream.str());
 					tuningCaptureStartSendMessage(1);  // 1-loop, zero, don't loop
 
 					setState(stateMicTuningRunning);
@@ -1243,16 +1262,8 @@ void LLVivoxVoiceClient::stateMachine()
 			}
 			
 			// Set the initial state of mic mute, local speaker volume, etc.
-			{
-				std::ostringstream stream;
-				
-				buildLocalAudioUpdates(stream);
+			sendLocalAudioUpdates();
 				
-				if(!stream.str().empty())
-				{
-					writeString(stream.str());
-				}
-			}
 		break;
 
 		//MARK: stateVoiceFontsWait
@@ -1525,9 +1536,9 @@ void LLVivoxVoiceClient::stateMachine()
 			// Must do this first, since it uses mAudioSession.
 			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
 			
-			if(mAudioSession)
+			if (mAudioSession)
 			{
-                leaveAudioSession();  
+				leaveAudioSession();
 				sessionState *oldSession = mAudioSession;
 
 				mAudioSession = NULL;
@@ -1977,21 +1988,6 @@ void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session
 	
 }
 
-/*  obsolete 
-void LLVivoxVoiceClient::sessionTextDisconnectSendMessage(sessionState *session)
-{
-	std::ostringstream stream;
-	
-	LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;	
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">"
-		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
-		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
-	<< "</Request>\n\n\n";
-	
-	writeString(stream.str());
-}
-*/
 
 void LLVivoxVoiceClient::getCaptureDevicesSendMessage()
 {
@@ -2050,6 +2046,10 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name)
 		}
 	}
 }
+void LLVivoxVoiceClient::setDevicesListUpdated(bool state)
+{
+	mDevicesListUpdated = state;
+}
 
 void LLVivoxVoiceClient::clearRenderDevices()
 {	
@@ -2210,6 +2210,16 @@ bool LLVivoxVoiceClient::deviceSettingsAvailable()
 	
 	return result;
 }
+bool LLVivoxVoiceClient::deviceSettingsUpdated()
+{
+	if (mDevicesListUpdated)
+	{
+		// a hot swap event or a polling of the audio devices has been parsed since the last redraw of the input and output device panel.
+		mDevicesListUpdated = !mDevicesListUpdated; // toggle the setting
+		return true;
+	}
+	return false;		
+}
 
 void LLVivoxVoiceClient::refreshDeviceLists(bool clearCurrentList)
 {
@@ -2373,7 +2383,6 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
 {	
 	std::ostringstream stream;
 	
-	if (getState() != stateRunning) return;   // don't send position updates if we are transitioning between out of running.
 
 	if(mSpatialCoordsDirty)
 	{
@@ -2582,7 +2591,7 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
 		}
 	}
 			
-	buildLocalAudioUpdates(stream);
+	//sendLocalAudioUpdates();  obsolete, used to send volume setting on position updates
 	
 	if(!stream.str().empty())
 	{
@@ -2622,68 +2631,73 @@ void LLVivoxVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
 	}
 }
 
-void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
+void LLVivoxVoiceClient::sendLocalAudioUpdates()
 {
-	buildSetCaptureDevice(stream);
+	// Check all of the dirty states and then send messages to those needing to be changed.
+	// Tuningmode hands its own mute settings.
 
-	buildSetRenderDevice(stream);
+	std::ostringstream stream;
 
-	if(mMuteMicDirty)
+	if (mMuteMicDirty && !mTuningMode)
 	{
 		mMuteMicDirty = false;
 
 		// Send a local mute command.
-		
-		LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic?"true":"false") << LL_ENDL;
+
+		LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic ? "true" : "false") << LL_ENDL;
 
 		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
 			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
-			<< "<Value>" << (mMuteMic?"true":"false") << "</Value>"
+			<< "<Value>" << (mMuteMic ? "true" : "false") << "</Value>"
 			<< "</Request>\n\n\n";
-		
+
 	}
 
-	if(mSpeakerMuteDirty)
+	if (mSpeakerMuteDirty && !mTuningMode)
 	{
-	  const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0))?"true":"false");
+		const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0)) ? "true" : "false");
 
 		mSpeakerMuteDirty = false;
 
-		LL_INFOS("Voice") << "Setting speaker mute to " << muteval  << LL_ENDL;
-		
+		LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL;
+
 		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">"
 			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
 			<< "<Value>" << muteval << "</Value>"
-			<< "</Request>\n\n\n";	
-		
+			<< "</Request>\n\n\n";
+
 	}
-	
-	if(mSpeakerVolumeDirty)
+
+	if (mSpeakerVolumeDirty)
 	{
 		mSpeakerVolumeDirty = false;
 
-		LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume  << LL_ENDL;
+		LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL;
 
 		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
 			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
 			<< "<Value>" << mSpeakerVolume << "</Value>"
 			<< "</Request>\n\n\n";
-			
+
 	}
-	
-	if(mMicVolumeDirty)
+
+	if (mMicVolumeDirty)
 	{
 		mMicVolumeDirty = false;
 
-		LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume  << LL_ENDL;
+		LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL;
 
 		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">"
 			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
 			<< "<Value>" << mMicVolume << "</Value>"
-			<< "</Request>\n\n\n";				
+			<< "</Request>\n\n\n";
 	}
 
-	
+
+	if (!stream.str().empty())
+	{
+		writeString(stream.str());
+	}
 }
 
 /////////////////////////////
@@ -2830,7 +2844,8 @@ void LLVivoxVoiceClient::sessionConnectResponse(std::string &requestId, int stat
 	sessionState *session = findSession(requestId);
 	// 1026 is session already has media,  somehow mediaconnect was called twice on the same session.
 	// set the session info to reflect that the user is already connected.
-	if (statusCode == 1026){
+	if (statusCode == 1026)
+	{
 		session->mVoiceEnabled = true;
 		session->mMediaConnectInProgress = false;
 		session->mMediaStreamState = streamStateConnected;
@@ -2846,7 +2861,9 @@ void LLVivoxVoiceClient::sessionConnectResponse(std::string &requestId, int stat
 			session->mErrorStatusCode = statusCode;
 			session->mErrorStatusString = statusString;
 			if (session == mAudioSession)
+			{
 				setState(stateJoinSessionFailed);
+			}
 		}
 	}
 	else
@@ -2985,7 +3002,6 @@ void LLVivoxVoiceClient::joinedAudioSession(sessionState *session)
 	{
 		setState(stateSessionJoined);
 		
-		// SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now.
 		// Add the current user as a participant here.
 		participantState *participant = session->addParticipant(sipURIFromName(mAccountName));
 		if(participant)
@@ -3299,55 +3315,6 @@ void LLVivoxVoiceClient::mediaStreamUpdatedEvent(
 	}
 }
 
-/* Obsolete 
-void LLVivoxVoiceClient::textStreamUpdatedEvent(
-	std::string &sessionHandle, 
-	std::string &sessionGroupHandle, 
-	bool enabled,
-	int state, 
-	bool incoming)
-{
-	sessionState *session = findSession(sessionHandle);
-	
-	if(session)
-	{
-		// Save the state for later use
-		session->mTextStreamState = state;
-		
-		// We know about this session
-		switch(state)
-		{
-			case 0:	// We see this when the text stream closes
-				LL_DEBUGS("Voice") << "stream closed" << LL_ENDL;
-			break;
-			
-			case 1:	// We see this on an incoming call from the Connector
-				// Try to send any text messages queued for this session.
-				sendQueuedTextMessages(session);
-
-				// Send the text chat invite to the GUI layer
-				// TODO: Question: Should we correlate with the mute list here?
-				session->mTextInvitePending = true;
-				if(session->mName.empty())
-				{
-					lookupName(session->mCallerID);
-				}
-				else
-				{
-					// Act like we just finished resolving the name
-					avatarNameResolved(session->mCallerID, session->mName);
-				}
-			break;
-
-			default:
-				LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
-			break;
-			
-		}
-	}
-}
- obsolete */
-
 void LLVivoxVoiceClient::participantAddedEvent(
 		std::string &sessionHandle, 
 		std::string &sessionGroupHandle, 
@@ -4224,62 +4191,6 @@ LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const L
 	return session;
 }
 
-/* obsolete 
-BOOL LLVivoxVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
-{
-	bool result = false;
-
-	// Attempt to locate the indicated session
-	sessionState *session = startUserIMSession(participant_id);
-	if(session)
-	{
-		// found the session, attempt to send the message
-		session->mTextMsgQueue.push(message);
-		
-		// Try to send queued messages (will do nothing if the session is not open yet)
-		sendQueuedTextMessages(session);
-
-		// The message is queued, so we succeed.
-		result = true;
-	}	
-	else
-	{
-		LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL;
-	}
-	
-	return result;
-}
-*/
-/* obsolete
-void LLVivoxVoiceClient::sendQueuedTextMessages(sessionState *session)
-{
-	if(session->mTextStreamState == 1)
-	{
-		if(!session->mTextMsgQueue.empty())
-		{
-			std::ostringstream stream;
-			
-			while(!session->mTextMsgQueue.empty())
-			{
-				std::string message = session->mTextMsgQueue.front();
-				session->mTextMsgQueue.pop();
-				stream
-				<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SendMessage.1\">"
-					<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
-					<< "<MessageHeader>text/HTML</MessageHeader>"
-					<< "<MessageBody>" << message << "</MessageBody>"
-				<< "</Request>"
-				<< "\n\n\n";
-			}		
-			writeString(stream.str());
-		}
-	}
-	else
-	{
-		// Session isn't connected yet, defer until later.
-	}
-}
- obsolete */
 
 void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid)
 {
@@ -4641,11 +4552,6 @@ void LLVivoxVoiceClient::enforceTether(void)
 
 void LLVivoxVoiceClient::updatePosition(void)
 {
-	
-    // Throttle the position updates to one every 1/10 of a second if we are in an audio session at all
-    if (mAudioSession == NULL) {
-        return;
-    }
 
 	LLViewerRegion *region = gAgent.getRegion();
 	if(region && isAgentAvatarValid())
@@ -5109,7 +5015,7 @@ void LLVivoxVoiceClient::filePlaybackSetMode(bool vox, float speed)
 LLVivoxVoiceClient::sessionState::sessionState() :
         mErrorStatusCode(0),
 	mMediaStreamState(streamStateUnknown),
-	mTextStreamState(streamStateUnknown),
+	//mTextStreamState(streamStateUnknown),
 	mCreateInProgress(false),
 	mMediaConnectInProgress(false),
 	mVoiceInvitePending(false),
@@ -6705,6 +6611,10 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
 			uriString = string;
 		else if (!stricmp("Presence", tag))
 			statusString = string;
+		else if (!stricmp("CaptureDevices", tag))
+			LLVivoxVoiceClient::getInstance()->setDevicesListUpdated(true);
+		else if (!stricmp("RenderDevices", tag))
+			LLVivoxVoiceClient::getInstance()->setDevicesListUpdated(true);
 		else if (!stricmp("CaptureDevice", tag))
 		{
 			LLVivoxVoiceClient::getInstance()->addCaptureDevice(deviceString);
@@ -6833,7 +6743,13 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
 	if (isEvent)
 	{
 		const char *eventTypeCstr = eventTypeString.c_str();
-		if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
+		if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
+		{
+			// These happen so often that logging them is pretty useless.
+			squelchDebugOutput = true;
+			LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
+		}
+		else if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
 		{
 			LLVivoxVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state);
 		}
@@ -6857,7 +6773,7 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
 		}
 		else if (!stricmp(eventTypeCstr, "SessionGroupUpdatedEvent"))
 		{
-			//TODO, we don't process this event, but we should not WARN that we have received it.
+			//nothng useful to process for this event, but we should not WARN that we have received it.
 		}
 		else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent"))
 		{
@@ -6887,13 +6803,6 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
 			*/
 			LLVivoxVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType);
 		}
-		/* obsolete, let else statement complain if a text message arrives 
-		else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent"))
-		{
-
-			LLVivoxVoiceClient::getInstance()->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming);
-
-		} */
 		else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent"))
 		{
 			/* 
@@ -6920,52 +6829,20 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
 			 */
 			LLVivoxVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString);
 		}
-		else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
-		{
-			/*
-			 <Event type="ParticipantUpdatedEvent">
-			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
-			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
-			 <ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri>
-			 <IsModeratorMuted>false</IsModeratorMuted>
-			 <IsSpeaking>true</IsSpeaking>
-			 <Volume>44</Volume>
-			 <Energy>0.0879437</Energy>
-			 </Event>
-			 */
-			
-			// These happen so often that logging them is pretty useless.
-			squelchDebugOutput = true;
-			
-			LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
-		}
 		else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent"))
 		{
 			// These are really spammy in tuning mode
 			squelchDebugOutput = true;
-
 			LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy);
 		}
-		else if (!stricmp(eventTypeCstr, "BuddyChangedEvent"))
-		{
-			/*
-			 <Event type="BuddyChangedEvent">
-			 <AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle>
-			 <BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI>
-			 <DisplayName>Monroe Tester</DisplayName>
-			 <BuddyData />
-			 <GroupID>0</GroupID>
-			 <ChangeType>Set</ChangeType>
-			 </Event>
-			 */		
-			// TODO: Question: Do we need to process this at all?
-		}
 		else if (!stricmp(eventTypeCstr, "MessageEvent"))  
 		{
+			//TODO:  This probably is not received any more, it was used to support SLim clients
 			LLVivoxVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString);
 		}
 		else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))  
 		{
+			//TODO:  This probably is not received any more, it was used to support SLim clients
 			LLVivoxVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType);
 		}
 		else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))
@@ -6985,19 +6862,29 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
 			 */
 			// We don't need to process this, but we also shouldn't warn on it, since that confuses people.
 		}
-		
-		else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent"))  
+		else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent"))
 		{
-			/*
-			 <Event type="SessionGroupRemovedEvent">
-			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
-			 </Event>
-			 */
 			// We don't need to process this, but we also shouldn't warn on it, since that confuses people.
 		}
-		else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent"))  
+		else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent"))
 		{	// Yet another ignored event
 		}
+		else if (!stricmp(eventTypeCstr, "AudioDeviceHotSwapEvent"))
+		{
+			/*
+			<Event type = "AudioDeviceHotSwapEvent">
+			<EventType>RenderDeviceChanged< / EventType>
+			<RelevantDevice>
+			<Device>Speakers(Turtle Beach P11 Headset)< / Device>
+			<DisplayName>Speakers(Turtle Beach P11 Headset)< / DisplayName>
+			<Type>SpecificDevice< / Type>
+			< / RelevantDevice>
+			< / Event>
+			*/
+			// an audio device was removed or added, fetch and update the local list of audio devices.
+			LLVivoxVoiceClient::getInstance()->getCaptureDevicesSendMessage();
+			LLVivoxVoiceClient::getInstance()->getRenderDevicesSendMessage();
+		}
 		else
 		{
 			LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL;
@@ -7006,7 +6893,12 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
 	else
 	{
 		const char *actionCstr = actionString.c_str();
-		if (!stricmp(actionCstr, "Connector.Create.1"))
+		if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
+		{
+			// We don't need to process these, but they're so spammy we don't want to log them.
+			squelchDebugOutput = true;
+		}
+		else if (!stricmp(actionCstr, "Connector.Create.1"))
 		{
 			LLVivoxVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID);
 		}
@@ -7034,11 +6926,6 @@ void LLVivoxProtocolParser::processResponse(std::string tag)
 		{
 			LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString);			
 		}
-		else if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
-		{
-			// We don't need to process these, but they're so spammy we don't want to log them.
-			squelchDebugOutput = true;
-		}
 		else if (!stricmp(actionCstr, "Account.GetSessionFonts.1"))
 		{
 			LLVivoxVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString);
diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h
index 541cccd30f..1b8a45744a 100755
--- a/indra/newview/llvoicevivox.h
+++ b/indra/newview/llvoicevivox.h
@@ -91,6 +91,7 @@ public:
 	// This returns true when it's safe to bring up the "device settings" dialog in the prefs.
 	// i.e. when the daemon is running and connected, and the device lists are populated.
 	virtual bool deviceSettingsAvailable();
+	virtual bool deviceSettingsUpdated();  //return if the list has been updated and never fetched,  only to be called from the voicepanel.
 	
 	// Requery the vivox daemon for the current list of input/output devices.
 	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed
@@ -327,7 +328,6 @@ protected:
 		LLUUID		mCallerID;
 		int			mErrorStatusCode;
 		int			mMediaStreamState;
-		int			mTextStreamState;    // obsolete
 		bool		mCreateInProgress;	// True if a Session.Create has been sent for this session and no response has been received yet.
 		bool		mMediaConnectInProgress;	// True if a Session.MediaConnect has been sent for this session and no response has been received yet.
 		bool		mVoiceInvitePending;	// True if a voice invite is pending for this session (usually waiting on a name lookup)
@@ -459,14 +459,15 @@ protected:
 	void clearCaptureDevices();
 	void addCaptureDevice(const std::string& name);
 	void clearRenderDevices();
+	void setDevicesListUpdated(bool state);
 	void addRenderDevice(const std::string& name);	
 	void buildSetAudioDevices(std::ostringstream &stream);
 	
 	void getCaptureDevicesSendMessage();
 	void getRenderDevicesSendMessage();
 	
-	// local audio updates
-	void buildLocalAudioUpdates(std::ostringstream &stream);		
+	// local audio updates, mic mute, speaker mute, mic volume and speaker volumes
+	void sendLocalAudioUpdates();
 
 
 	/////////////////////////////
@@ -482,7 +483,6 @@ protected:
 	void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state);
 	void mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType);
 	void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming);
-	//obsolete void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming);
 	void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString);
 	void sessionGroupAddedEvent(std::string &sessionGroupHandle);
 	void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle);
@@ -678,7 +678,9 @@ private:
 	bool mTuningMicVolumeDirty;
 	int mTuningSpeakerVolume;
 	bool mTuningSpeakerVolumeDirty;
-	state mTuningExitState;					// state to return to when we leave tuning mode.
+	state mTuningExitState;			    // state to return to when we leave tuning mode.
+	bool mDevicesListUpdated;			// set to true when the device list has been updated
+										// and false when the panelvoicedevicesettings has queried for an update status.
 	
 	std::string mSpatialSessionURI;
 	std::string mSpatialSessionCredentials;
@@ -762,7 +764,6 @@ private:
 	// start a text IM session with the specified user
 	// This will be asynchronous, the session may be established at a future time.
 	sessionState* startUserIMSession(const LLUUID& uuid);
-	// obsolete void sendQueuedTextMessages(sessionState *session);
 	
 	void enforceTether(void);
 	
-- 
GitLab