summaryrefslogtreecommitdiff
path: root/indra/newview/llvoiceclient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llvoiceclient.cpp')
-rw-r--r--indra/newview/llvoiceclient.cpp612
1 files changed, 436 insertions, 176 deletions
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index ca028269fe..2e5d4f1776 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -35,8 +35,13 @@
#include <boost/tokenizer.hpp>
+// library includes
+#include "llnotificationsutil.h"
+#include "llsdserialize.h"
#include "llsdutil.h"
+
+// project includes
#include "llvoavatar.h"
#include "llbufferstream.h"
#include "llfile.h"
@@ -55,21 +60,18 @@
#include "llappviewer.h" // for gDisconnected, gDisableVoice
#include "llmutelist.h" // to check for muted avatars
#include "llagent.h"
+#include "llvoavatarself.h"
#include "llcachename.h"
#include "llimview.h" // for LLIMMgr
-#include "llimpanel.h" // for LLVoiceChannel
#include "llparcel.h"
#include "llviewerparcelmgr.h"
-#include "llfirstuse.h"
+//#include "llfirstuse.h"
+#include "llspeakers.h"
+#include "lltrans.h"
#include "llviewerwindow.h"
#include "llviewercamera.h"
#include "llvoavatarself.h"
-
-#include "llfloaterfriends.h" //VIVOX, inorder to refresh communicate panel
-#include "llfloaterchat.h" // for LLFloaterChat::addChat()
-
-// for Talk Button's state updating
-#include "llnearbychatbar.h"
+#include "llvoicechannel.h"
// for base64 decoding
#include "apr_base64.h"
@@ -85,6 +87,12 @@
static bool sConnectingToAgni = false;
F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
+const F32 LLVoiceClient::VOLUME_MIN = 0.f;
+const F32 LLVoiceClient::VOLUME_DEFAULT = 0.5f;
+const F32 LLVoiceClient::VOLUME_MAX = 1.0f;
+
+const F32 VOLUME_SCALE_VIVOX = 0.01f;
+
const F32 SPEAKING_TIMEOUT = 1.f;
const int VOICE_MAJOR_VERSION = 1;
@@ -101,6 +109,13 @@ const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
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.
+const int MAX_NORMAL_JOINING_SPATIAL_NUM = 50;
+
static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str)
{
LLMD5 md5_uuid;
@@ -112,31 +127,15 @@ static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str)
static int scale_mic_volume(float volume)
{
// incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.
- // Map it as follows: 0.0 -> 40, 1.0 -> 44, 2.0 -> 75
-
- volume -= 1.0f; // offset volume to the range [-1.0 ... 1.0], with 0 at the default.
- int scaled_volume = 44; // offset scaled_volume by its default level
- if(volume < 0.0f)
- scaled_volume += ((int)(volume * 4.0f)); // (44 - 40)
- else
- scaled_volume += ((int)(volume * 31.0f)); // (75 - 44)
-
- return scaled_volume;
+ // Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70
+ return 30 + (int)(volume * 20.0f);
}
static int scale_speaker_volume(float volume)
{
// incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
- // Map it as follows: 0.0 -> 0, 0.5 -> 62, 1.0 -> 75
-
- volume -= 0.5f; // offset volume to the range [-0.5 ... 0.5], with 0 at the default.
- int scaled_volume = 62; // offset scaled_volume by its default level
- if(volume < 0.0f)
- scaled_volume += ((int)(volume * 124.0f)); // (62 - 0) * 2
- else
- scaled_volume += ((int)(volume * 26.0f)); // (75 - 62) * 2
-
- return scaled_volume;
+ // Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70
+ return 30 + (int)(volume * 40.0f);
}
class LLViewerVoiceAccountProvisionResponder :
@@ -254,6 +253,7 @@ protected:
std::string nameString;
std::string audioMediaString;
std::string displayNameString;
+ std::string deviceString;
int participantType;
bool isLocallyMuted;
bool isModeratorMuted;
@@ -308,8 +308,14 @@ void LLVivoxProtocolParser::reset()
ignoringTags = false;
accumulateText = false;
energy = 0.f;
+ hasText = false;
+ hasAudio = false;
+ hasVideo = false;
+ terminated = false;
ignoreDepth = 0;
isChannel = false;
+ incoming = false;
+ enabled = false;
isEvent = false;
isLocallyMuted = false;
isModeratorMuted = false;
@@ -485,6 +491,14 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
{
gVoiceClient->clearRenderDevices();
}
+ else if (!stricmp("CaptureDevice", tag))
+ {
+ deviceString.clear();
+ }
+ else if (!stricmp("RenderDevice", tag))
+ {
+ deviceString.clear();
+ }
else if (!stricmp("Buddies", tag))
{
gVoiceClient->deleteAllBuddies();
@@ -508,7 +522,6 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
void LLVivoxProtocolParser::EndTag(const char *tag)
{
const std::string& string = textBuffer;
- bool clearbuffer = true;
responseDepth--;
@@ -580,6 +593,8 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
nameString = string;
else if (!stricmp("DisplayName", tag))
displayNameString = string;
+ else if (!stricmp("Device", tag))
+ deviceString = string;
else if (!stricmp("AccountName", tag))
nameString = string;
else if (!stricmp("ParticipantType", tag))
@@ -596,18 +611,13 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
uriString = string;
else if (!stricmp("Presence", tag))
statusString = string;
- else if (!stricmp("Device", tag))
- {
- // This closing tag shouldn't clear the accumulated text.
- clearbuffer = false;
- }
else if (!stricmp("CaptureDevice", tag))
{
- gVoiceClient->addCaptureDevice(textBuffer);
+ gVoiceClient->addCaptureDevice(deviceString);
}
else if (!stricmp("RenderDevice", tag))
{
- gVoiceClient->addRenderDevice(textBuffer);
+ gVoiceClient->addRenderDevice(deviceString);
}
else if (!stricmp("Buddy", tag))
{
@@ -648,12 +658,8 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
else if (!stricmp("SubscriptionType", tag))
subscriptionType = string;
-
- if(clearbuffer)
- {
- textBuffer.clear();
- accumulateText= false;
- }
+ textBuffer.clear();
+ accumulateText= false;
if (responseDepth == 0)
{
@@ -1106,6 +1112,200 @@ static void killGateway()
#endif
+class LLSpeakerVolumeStorage : public LLSingleton<LLSpeakerVolumeStorage>
+{
+ LOG_CLASS(LLSpeakerVolumeStorage);
+public:
+
+ /**
+ * Stores volume level for specified user.
+ *
+ * @param[in] speaker_id - LLUUID of user to store volume level for.
+ * @param[in] volume - volume level to be stored for user.
+ */
+ void storeSpeakerVolume(const LLUUID& speaker_id, F32 volume);
+
+ /**
+ * Gets stored volume level for specified speaker
+ *
+ * @param[in] speaker_id - LLUUID of user to retrieve volume level for.
+ * @param[out] volume - set to stored volume if found, otherwise unmodified.
+ * @return - true if a stored volume is found.
+ */
+ bool getSpeakerVolume(const LLUUID& speaker_id, F32& volume);
+
+ /**
+ * Removes stored volume level for specified user.
+ *
+ * @param[in] speaker_id - LLUUID of user to remove.
+ */
+ void removeSpeakerVolume(const LLUUID& speaker_id);
+
+private:
+ friend class LLSingleton<LLSpeakerVolumeStorage>;
+ LLSpeakerVolumeStorage();
+ ~LLSpeakerVolumeStorage();
+
+ const static std::string SETTINGS_FILE_NAME;
+
+ void load();
+ void save();
+
+ static F32 transformFromLegacyVolume(F32 volume_in);
+ static F32 transformToLegacyVolume(F32 volume_in);
+
+ typedef std::map<LLUUID, F32> speaker_data_map_t;
+ speaker_data_map_t mSpeakersData;
+};
+
+const std::string LLSpeakerVolumeStorage::SETTINGS_FILE_NAME = "volume_settings.xml";
+
+LLSpeakerVolumeStorage::LLSpeakerVolumeStorage()
+{
+ load();
+}
+
+LLSpeakerVolumeStorage::~LLSpeakerVolumeStorage()
+{
+ save();
+}
+
+void LLSpeakerVolumeStorage::storeSpeakerVolume(const LLUUID& speaker_id, F32 volume)
+{
+ if ((volume >= LLVoiceClient::VOLUME_MIN) && (volume <= LLVoiceClient::VOLUME_MAX))
+ {
+ mSpeakersData[speaker_id] = volume;
+
+ // Enable this when debugging voice slider issues. It's way too spammy even for debug-level logging.
+ // LL_DEBUGS("Voice") << "Stored volume = " << volume << " for " << id << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("Voice") << "Attempted to store out of range volume " << volume << " for " << speaker_id << LL_ENDL;
+ llassert(0);
+ }
+}
+
+bool LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id, F32& volume)
+{
+ speaker_data_map_t::const_iterator it = mSpeakersData.find(speaker_id);
+
+ if (it != mSpeakersData.end())
+ {
+ volume = it->second;
+
+ // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
+ // LL_DEBUGS("Voice") << "Retrieved stored volume = " << volume << " for " << id << LL_ENDL;
+
+ return true;
+ }
+
+ return false;
+}
+
+void LLSpeakerVolumeStorage::removeSpeakerVolume(const LLUUID& speaker_id)
+{
+ mSpeakersData.erase(speaker_id);
+
+ // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
+ // LL_DEBUGS("Voice") << "Removing stored volume for " << id << LL_ENDL;
+}
+
+/* static */ F32 LLSpeakerVolumeStorage::transformFromLegacyVolume(F32 volume_in)
+{
+ // Convert to linear-logarithmic [0.0..1.0] with 0.5 = 0dB
+ // from legacy characteristic composed of two square-curves
+ // that intersect at volume_in = 0.5, volume_out = 0.56
+
+ F32 volume_out = 0.f;
+ volume_in = llclamp(volume_in, 0.f, 1.0f);
+
+ if (volume_in <= 0.5f)
+ {
+ volume_out = volume_in * volume_in * 4.f * 0.56f;
+ }
+ else
+ {
+ volume_out = (1.f - 0.56f) * (4.f * volume_in * volume_in - 1.f) / 3.f + 0.56f;
+ }
+
+ return volume_out;
+}
+
+/* static */ F32 LLSpeakerVolumeStorage::transformToLegacyVolume(F32 volume_in)
+{
+ // Convert from linear-logarithmic [0.0..1.0] with 0.5 = 0dB
+ // to legacy characteristic composed of two square-curves
+ // that intersect at volume_in = 0.56, volume_out = 0.5
+
+ F32 volume_out = 0.f;
+ volume_in = llclamp(volume_in, 0.f, 1.0f);
+
+ if (volume_in <= 0.56f)
+ {
+ volume_out = sqrt(volume_in / (4.f * 0.56f));
+ }
+ else
+ {
+ volume_out = sqrt((3.f * (volume_in - 0.56f) / (1.f - 0.56f) + 1.f) / 4.f);
+ }
+
+ return volume_out;
+}
+
+void LLSpeakerVolumeStorage::load()
+{
+ // load per-resident voice volume information
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME);
+
+ LL_INFOS("Voice") << "Loading stored speaker volumes from: " << filename << LL_ENDL;
+
+ LLSD settings_llsd;
+ llifstream file;
+ file.open(filename);
+ if (file.is_open())
+ {
+ LLSDSerialize::fromXML(settings_llsd, file);
+ }
+
+ for (LLSD::map_const_iterator iter = settings_llsd.beginMap();
+ iter != settings_llsd.endMap(); ++iter)
+ {
+ // Maintain compatibility with 1.23 non-linear saved volume levels
+ F32 volume = transformFromLegacyVolume((F32)iter->second.asReal());
+
+ storeSpeakerVolume(LLUUID(iter->first), volume);
+ }
+}
+
+void LLSpeakerVolumeStorage::save()
+{
+ // If we quit from the login screen we will not have an SL account
+ // name. Don't try to save, otherwise we'll dump a file in
+ // C:\Program Files\SecondLife\ or similar. JC
+ std::string user_dir = gDirUtilp->getLindenUserDir();
+ if (!user_dir.empty())
+ {
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME);
+ LLSD settings_llsd;
+
+ LL_INFOS("Voice") << "Saving stored speaker volumes to: " << filename << LL_ENDL;
+
+ for(speaker_data_map_t::const_iterator iter = mSpeakersData.begin(); iter != mSpeakersData.end(); ++iter)
+ {
+ // Maintain compatibility with 1.23 non-linear saved volume levels
+ F32 volume = transformToLegacyVolume(iter->second);
+
+ settings_llsd[iter->first.asString()] = volume;
+ }
+
+ llofstream file;
+ file.open(filename);
+ LLSDSerialize::toPrettyXML(settings_llsd, file);
+ }
+}
+
+
///////////////////////////////////////////////////////////////////////////////////////////////
LLVoiceClient::LLVoiceClient() :
@@ -1114,6 +1314,7 @@ LLVoiceClient::LLVoiceClient() :
mRelogRequested(false),
mConnected(false),
mPump(NULL),
+ mSpatialJoiningNum(0),
mTuningMode(false),
mTuningEnergy(0.0f),
@@ -1153,7 +1354,6 @@ LLVoiceClient::LLVoiceClient() :
mEarLocation(0),
mSpeakerVolumeDirty(true),
mSpeakerMuteDirty(true),
- mSpeakerVolume(0),
mMicVolume(0),
mMicVolumeDirty(true),
@@ -1164,6 +1364,10 @@ LLVoiceClient::LLVoiceClient() :
{
gVoiceClient = this;
+ mAPIVersion = LLTrans::getString("NotConnected");
+
+ mSpeakerVolume = scale_speaker_volume(0);
+
#if LL_DARWIN || LL_LINUX || LL_SOLARIS
// HACK: THIS DOES NOT BELONG HERE
// When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
@@ -1599,7 +1803,7 @@ void LLVoiceClient::stateMachine()
}
else
{
- LL_WARNS("Voice") << "region doesn't have ParcelVoiceInfoRequest capability. This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL;
+ LL_WARNS_ONCE("Voice") << "region doesn't have ParcelVoiceInfoRequest capability. This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL;
}
}
}
@@ -1868,7 +2072,7 @@ void LLVoiceClient::stateMachine()
}
else
{
- LL_WARNS("Voice") << "region doesn't have ProvisionVoiceAccountRequest capability!" << LL_ENDL;
+ LL_WARNS_ONCE("Voice") << "region doesn't have ProvisionVoiceAccountRequest capability!" << LL_ENDL;
}
}
}
@@ -2113,6 +2317,8 @@ void LLVoiceClient::stateMachine()
//MARK: stateNoChannel
case stateNoChannel:
+
+ mSpatialJoiningNum = 0;
// Do this here as well as inside sendPositionalUpdate().
// Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync.
sendFriendsListUpdates();
@@ -2169,6 +2375,23 @@ void LLVoiceClient::stateMachine()
//MARK: stateJoiningSession
case stateJoiningSession: // waiting for session handle
+
+ // If this is true we have problem with connection to voice server (EXT-4313).
+ // See descriptions of mSpatialJoiningNum and MAX_NORMAL_JOINING_SPATIAL_NUM.
+ if(mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM)
+ {
+ // Notify observers to let them know there is problem with voice
+ notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
+ llwarns << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << llendl;
+ }
+
+ // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for
+ // example for p2p many times while waiting for response, so it can't be used to detect errors
+ if(mAudioSession && mAudioSession->mIsSpatial)
+ {
+ mSpatialJoiningNum++;
+ }
+
// joinedAudioSession() will transition from here to stateSessionJoined.
if(!mVoiceEnabled)
{
@@ -2192,6 +2415,8 @@ void LLVoiceClient::stateMachine()
//MARK: stateSessionJoined
case stateSessionJoined: // session handle received
+
+ mSpatialJoiningNum = 0;
// It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
// before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck.
// For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
@@ -2280,9 +2505,10 @@ void LLVoiceClient::stateMachine()
enforceTether();
}
- // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast)
- // or every 10hz, whichever is sooner.
- if((mAudioSession && mAudioSession->mVolumeDirty) || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
+ // Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often
+ // -- the user can only click so fast) or every 10hz, whichever is sooner.
+ // Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged.
+ if((mAudioSession && mAudioSession->mMuteDirty) || mPTTDirty || mUpdateTimer.hasExpired())
{
mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
sendPositionalUpdate();
@@ -3277,38 +3503,26 @@ void LLVoiceClient::sendPositionalUpdate(void)
stream << "</Request>\n\n\n";
}
- if(mAudioSession && mAudioSession->mVolumeDirty)
+ if(mAudioSession && (mAudioSession->mVolumeDirty || mAudioSession->mMuteDirty))
{
participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
mAudioSession->mVolumeDirty = false;
+ mAudioSession->mMuteDirty = false;
for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
{
participantState *p = iter->second;
-
+
if(p->mVolumeDirty)
{
// Can't set volume/mute for yourself
if(!p->mIsSelf)
{
- int volume = 56; // nominal default value
+ // scale from the range 0.0-1.0 to vivox volume in the range 0-100
+ S32 volume = llround(p->mVolume / VOLUME_SCALE_VIVOX);
+
bool mute = p->mOnMuteList;
-
- if(p->mUserVolume != -1)
- {
- // scale from user volume in the range 0-400 (with 100 as "normal") to vivox volume in the range 0-100 (with 56 as "normal")
- if(p->mUserVolume < 100)
- volume = (p->mUserVolume * 56) / 100;
- else
- volume = (((p->mUserVolume - 100) * (100 - 56)) / 300) + 56;
- }
- else if(p->mVolume != -1)
- {
- // Use the previously reported internal volume (comes in with a ParticipantUpdatedEvent)
- volume = p->mVolume;
- }
-
if(mute)
{
@@ -3316,10 +3530,16 @@ void LLVoiceClient::sendPositionalUpdate(void)
// If we want the user to be muted, set their volume to 0 as well.
// This isn't perfect, but it will at least reduce their volume to a minimum.
volume = 0;
+
+ // Mark the current volume level as set to prevent incoming events
+ // changing it to 0, so that we can return to it when unmuting.
+ p->mVolumeSet = true;
}
-
+
if(volume == 0)
+ {
mute = true;
+ }
LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL;
@@ -3332,12 +3552,17 @@ void LLVoiceClient::sendPositionalUpdate(void)
<< "<Volume>" << volume << "</Volume>"
<< "</Request>\n\n\n";
- // Send a "mute for me" command for the user
- stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantMuteForMe.1\">"
- << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
- << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
- << "<Mute>" << (mute?"1":"0") << "</Mute>"
- << "</Request>\n\n\n";
+ if(!mAudioSession->mIsP2P)
+ {
+ // Send a "mute for me" command for the user
+ // Doesn't work in P2P sessions
+ stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantMuteForMe.1\">"
+ << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
+ << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
+ << "<Mute>" << (mute?"1":"0") << "</Mute>"
+ << "<Scope>Audio</Scope>"
+ << "</Request>\n\n\n";
+ }
}
p->mVolumeDirty = false;
@@ -3413,7 +3638,7 @@ void LLVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
if(mSpeakerMuteDirty)
{
- const char *muteval = ((mSpeakerVolume == 0)?"true":"false");
+ const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0))?"true":"false");
mSpeakerMuteDirty = false;
@@ -3749,6 +3974,7 @@ void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusS
{
// Connector created, move forward.
LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL;
+ mAPIVersion = versionID;
mConnectorHandle = connectorHandle;
if(getState() == stateConnectorStarting)
{
@@ -4273,7 +4499,7 @@ void LLVoiceClient::mediaStreamUpdatedEvent(
if(incoming)
{
// Send the voice chat invite to the GUI layer
- // TODO: Question: Should we correlate with the mute list here?
+ // *TODO: Question: Should we correlate with the mute list here?
session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID);
session->mVoiceInvitePending = true;
if(session->mName.empty())
@@ -4453,7 +4679,41 @@ void LLVoiceClient::participantUpdatedEvent(
{
participant->mPower = 0.0f;
}
- participant->mVolume = volume;
+
+ // Ignore incoming volume level if it has been explicitly set, or there
+ // is a volume or mute change pending.
+ if ( !participant->mVolumeSet && !participant->mVolumeDirty)
+ {
+ participant->mVolume = (F32)volume * VOLUME_SCALE_VIVOX;
+ }
+
+ // *HACK: mantipov: added while working on EXT-3544
+ /*
+ Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE
+ LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER.
+
+ participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted
+ Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug.
+ Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates.
+
+ But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post()
+ voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager
+ and event is not fired.
+
+ So, we have to call LLSpeakerMgr::update() here. In any case it is better than call it
+ in LLCallFloater::draw()
+ */
+ LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel();
+
+ // ignore session ID of local chat
+ if (voice_cnl && voice_cnl->getSessionID().notNull())
+ {
+ LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID());
+ if (speaker_manager)
+ {
+ speaker_manager->update(true);
+ }
+ }
}
else
{
@@ -4691,10 +4951,6 @@ void LLVoiceClient::messageEvent(
LLUUID::null, // default arg
LLVector3::zero, // default arg
true); // prepend name and make it a link to the user's profile
-
- chat.mText = std::string("IM: ") + session->mName + std::string(": ") + message;
- // If the chat should come in quietly (i.e. we're in busy mode), pretend it's from a local agent.
- LLFloaterChat::addChat( chat, TRUE, quiet_chat );
}
}
}
@@ -4814,7 +5070,7 @@ void LLVoiceClient::muteListChanged()
// Check to see if this participant is on the mute list already
if(p->updateMuteState())
- mAudioSession->mVolumeDirty = true;
+ mAudioSession->mMuteDirty = true;
}
}
}
@@ -4837,10 +5093,10 @@ LLVoiceClient::participantState::participantState(const std::string &uri) :
mIsModeratorMuted(false),
mLastSpokeTimestamp(0.f),
mPower(0.f),
- mVolume(-1),
- mOnMuteList(false),
- mUserVolume(-1),
- mVolumeDirty(false),
+ mVolume(VOLUME_DEFAULT),
+ mOnMuteList(false),
+ mVolumeSet(false),
+ mVolumeDirty(false),
mAvatarIDValid(false),
mIsSelf(false)
{
@@ -4889,7 +5145,7 @@ LLVoiceClient::participantState *LLVoiceClient::sessionState::addParticipant(con
result->mAvatarID = id;
if(result->updateMuteState())
- mVolumeDirty = true;
+ mMuteDirty = true;
}
else
{
@@ -4900,7 +5156,13 @@ LLVoiceClient::participantState *LLVoiceClient::sessionState::addParticipant(con
}
mParticipantsByUUID.insert(participantUUIDMap::value_type(&(result->mAvatarID), result));
-
+
+ if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume))
+ {
+ result->mVolumeDirty = true;
+ mVolumeDirty = true;
+ }
+
LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL;
}
@@ -4986,6 +5248,17 @@ LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void)
return result;
}
+void LLVoiceClient::getParticipantsUUIDSet(std::set<LLUUID>& participant_uuids)
+{
+ if (NULL == mAudioSession) return;
+
+ participantUUIDMap::const_iterator it = mAudioSession->mParticipantsByUUID.begin(),
+ it_end = mAudioSession->mParticipantsByUUID.end();
+ for (; it != it_end; ++it)
+ {
+ participant_uuids.insert((*(*it).first));
+ }
+}
LLVoiceClient::participantState *LLVoiceClient::sessionState::findParticipant(const std::string &uri)
{
@@ -5221,24 +5494,25 @@ LLVoiceClient::sessionState* LLVoiceClient::startUserIMSession(const LLUUID &uui
// No session with user, need to start one.
std::string uri = sipURIFromID(uuid);
session = addSession(uri);
+
+ llassert(session);
+ if (!session) return NULL;
+
session->mIsSpatial = false;
session->mReconnect = false;
session->mIsP2P = true;
session->mCallerID = uuid;
}
- if(session)
+ if(session->mHandle.empty())
{
- if(session->mHandle.empty())
- {
- // Session isn't active -- start it up.
- sessionCreateSendMessage(session, false, true);
- }
- else
- {
- // Session is already active -- start up text.
- sessionTextConnectSendMessage(session);
- }
+ // Session isn't active -- start it up.
+ sessionCreateSendMessage(session, false, true);
+ }
+ else
+ {
+ // Session is already active -- start up text.
+ sessionTextConnectSendMessage(session);
}
return session;
@@ -5674,12 +5948,10 @@ void LLVoiceClient::enforceTether(void)
void LLVoiceClient::updatePosition(void)
{
-
if(gVoiceClient)
{
- LLVOAvatar *agent = gAgent.getAvatarObject();
LLViewerRegion *region = gAgent.getRegion();
- if(region && agent)
+ if(region && isAgentAvatarValid())
{
LLMatrix3 rot;
LLVector3d pos;
@@ -5697,9 +5969,9 @@ void LLVoiceClient::updatePosition(void)
rot); // rotation matrix
// Send the current avatar position to the voice code
- rot = agent->getRootJoint()->getWorldRotation().getMatrix3();
+ rot = gAgentAvatarp->getRootJoint()->getWorldRotation().getMatrix3();
- pos = agent->getPositionGlobal();
+ pos = gAgentAvatarp->getPositionGlobal();
// TODO: Can we get the head offset from outside the LLVOAvatar?
// pos += LLVector3d(mHeadOffset);
pos += LLVector3d(0.f, 0.f, 1.f);
@@ -5788,7 +6060,6 @@ bool LLVoiceClient::getMuteMic() const
void LLVoiceClient::setUserPTTState(bool ptt)
{
mUserPTTState = ptt;
- if (LLNearbyChatBar::instanceExists()) LLNearbyChatBar::getInstance()->setPTTState(ptt);
}
bool LLVoiceClient::getUserPTTState()
@@ -5799,7 +6070,6 @@ bool LLVoiceClient::getUserPTTState()
void LLVoiceClient::toggleUserPTTState(void)
{
mUserPTTState = !mUserPTTState;
- if (LLNearbyChatBar::instanceExists()) LLNearbyChatBar::getInstance()->setPTTState(mUserPTTState);
}
void LLVoiceClient::setVoiceEnabled(bool enabled)
@@ -5830,6 +6100,15 @@ bool LLVoiceClient::voiceEnabled()
return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice");
}
+//AD *TODO: investigate possible merge of voiceWorking() and voiceEnabled() into one non-static method
+bool LLVoiceClient::voiceWorking()
+{
+ //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758)
+ // Condition with joining spatial num was added to take into account possible problems with connection to voice
+ // server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info.
+ return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated);
+}
+
void LLVoiceClient::setLipSyncEnabled(BOOL enabled)
{
mLipSyncEnabled = enabled;
@@ -5869,6 +6148,10 @@ void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle)
mPTTIsToggle = PTTIsToggle;
}
+bool LLVoiceClient::getPTTIsToggle()
+{
+ return mPTTIsToggle;
+}
void LLVoiceClient::setPTTKey(std::string &key)
{
@@ -5904,7 +6187,8 @@ void LLVoiceClient::setVoiceVolume(F32 volume)
if(scaled_volume != mSpeakerVolume)
{
- if((scaled_volume == 0) || (mSpeakerVolume == 0))
+ int min_volume = scale_speaker_volume(0);
+ if((scaled_volume == min_volume) || (mSpeakerVolume == min_volume))
{
mSpeakerMuteDirty = true;
}
@@ -5927,8 +6211,6 @@ void LLVoiceClient::setMicGain(F32 volume)
void LLVoiceClient::keyDown(KEY key, MASK mask)
{
-// LL_DEBUGS("Voice") << "key is " << LLKeyboard::stringFromKey(key) << LL_ENDL;
-
if (gKeyboard->getKeyRepeated(key))
{
// ignore auto-repeat keys
@@ -5937,44 +6219,39 @@ void LLVoiceClient::keyDown(KEY key, MASK mask)
if(!mPTTIsMiddleMouse)
{
- if(mPTTIsToggle)
- {
- if(key == mPTTKey)
- {
- toggleUserPTTState();
- }
- }
- else if(mPTTKey != KEY_NONE)
- {
- setUserPTTState(gKeyboard->getKeyDown(mPTTKey));
- }
+ bool down = (mPTTKey != KEY_NONE)
+ && gKeyboard->getKeyDown(mPTTKey);
+ inputUserControlState(down);
}
}
void LLVoiceClient::keyUp(KEY key, MASK mask)
{
if(!mPTTIsMiddleMouse)
{
- if(!mPTTIsToggle && (mPTTKey != KEY_NONE))
+ bool down = (mPTTKey != KEY_NONE)
+ && gKeyboard->getKeyDown(mPTTKey);
+ inputUserControlState(down);
+ }
+}
+void LLVoiceClient::inputUserControlState(bool down)
+{
+ if(mPTTIsToggle)
+ {
+ if(down) // toggle open-mic state on 'down'
{
- setUserPTTState(gKeyboard->getKeyDown(mPTTKey));
+ toggleUserPTTState();
}
}
+ else // set open-mic state as an absolute
+ {
+ setUserPTTState(down);
+ }
}
void LLVoiceClient::middleMouseState(bool down)
{
if(mPTTIsMiddleMouse)
{
- if(mPTTIsToggle)
- {
- if(down)
- {
- toggleUserPTTState();
- }
- }
- else
- {
- setUserPTTState(down);
- }
+ inputUserControlState(down);
}
}
@@ -6078,51 +6355,21 @@ BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)
return result;
}
-// External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
-// internal = 400 * external^2
+// External accessors.
F32 LLVoiceClient::getUserVolume(const LLUUID& id)
{
- F32 result = 0.0f;
+ // Minimum volume will be returned for users with voice disabled
+ F32 result = VOLUME_MIN;
participantState *participant = findParticipantByID(id);
if(participant)
{
- S32 ires = 100; // nominal default volume
-
- if(participant->mIsSelf)
- {
- // Always make it look like the user's own volume is set at the default.
- }
- else if(participant->mUserVolume != -1)
- {
- // Use the internal volume
- ires = participant->mUserVolume;
-
- // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
-// LL_DEBUGS("Voice") << "mapping from mUserVolume " << ires << LL_ENDL;
- }
- else if(participant->mVolume != -1)
- {
- // Map backwards from vivox volume
-
- // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
-// LL_DEBUGS("Voice") << "mapping from mVolume " << participant->mVolume << LL_ENDL;
+ result = participant->mVolume;
- if(participant->mVolume < 56)
- {
- ires = (participant->mVolume * 100) / 56;
- }
- else
- {
- ires = (((participant->mVolume - 56) * 300) / (100 - 56)) + 100;
- }
- }
- result = sqrtf(((F32)ires) / 400.f);
+ // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
+ // LL_DEBUGS("Voice") << "mVolume = " << result << " for " << id << LL_ENDL;
}
- // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
-// LL_DEBUGS("Voice") << "returning " << result << LL_ENDL;
-
return result;
}
@@ -6131,13 +6378,23 @@ void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
if(mAudioSession)
{
participantState *participant = findParticipantByID(id);
- if (participant)
+ if (participant && !participant->mIsSelf)
{
- // volume can amplify by as much as 4x!
- S32 ivol = (S32)(400.f * volume * volume);
- participant->mUserVolume = llclamp(ivol, 0, 400);
- participant->mVolumeDirty = TRUE;
- mAudioSession->mVolumeDirty = TRUE;
+ if (!is_approx_equal(volume, VOLUME_DEFAULT))
+ {
+ // Store this volume setting for future sessions if it has been
+ // changed from the default
+ LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume);
+ }
+ else
+ {
+ // Remove stored volume setting if it is returned to the default
+ LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id);
+ }
+
+ participant->mVolume = llclamp(volume, VOLUME_MIN, VOLUME_MAX);
+ participant->mVolumeDirty = true;
+ mAudioSession->mVolumeDirty = true;
}
}
}
@@ -6263,6 +6520,7 @@ void LLVoiceClient::filePlaybackSetMode(bool vox, float speed)
}
LLVoiceClient::sessionState::sessionState() :
+ mErrorStatusCode(0),
mMediaStreamState(streamStateUnknown),
mTextStreamState(streamStateUnknown),
mCreateInProgress(false),
@@ -6277,6 +6535,7 @@ LLVoiceClient::sessionState::sessionState() :
mVoiceEnabled(false),
mReconnect(false),
mVolumeDirty(false),
+ mMuteDirty(false),
mParticipantsChanged(false)
{
}
@@ -6921,7 +7180,8 @@ void LLVoiceClient::notifyFriendObservers()
void LLVoiceClient::lookupName(const LLUUID &id)
{
- gCacheName->get(id, FALSE, &LLVoiceClient::onAvatarNameLookup);
+ BOOL is_group = FALSE;
+ gCacheName->get(id, is_group, &LLVoiceClient::onAvatarNameLookup);
}
//static
@@ -7059,7 +7319,7 @@ class LLViewerRequiredVoiceVersion : public LLHTTPNode
if (!sAlertedUser)
{
//sAlertedUser = TRUE;
- LLNotifications::instance().add("VoiceVersionMismatch");
+ LLNotificationsUtil::add("VoiceVersionMismatch");
gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener
}
}