diff options
-rw-r--r-- | indra/newview/app_settings/logcontrol.xml | 1 | ||||
-rw-r--r-- | indra/newview/app_settings/settings.xml | 11 | ||||
-rw-r--r-- | indra/newview/llcallfloater.cpp | 57 | ||||
-rw-r--r-- | indra/newview/llcallfloater.h | 29 | ||||
-rw-r--r-- | indra/newview/llparticipantlist.cpp | 2 | ||||
-rw-r--r-- | indra/newview/llspeakingindicatormanager.cpp | 4 | ||||
-rw-r--r-- | indra/newview/llvoicechannel.cpp | 6 | ||||
-rw-r--r-- | indra/newview/llvoicechannel.h | 2 | ||||
-rw-r--r-- | indra/newview/llvoiceclient.cpp | 360 | ||||
-rw-r--r-- | indra/newview/llvoiceclient.h | 99 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_voice_controls.xml | 42 |
11 files changed, 549 insertions, 64 deletions
diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index d7bb64ce8a..d3fb958638 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -40,6 +40,7 @@ </array> <key>tags</key> <array> + <string>Voice</string> </array> </map> </array> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index a6dbe00759..e763f9cebc 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10405,6 +10405,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>VoiceFontDefault</key> + <map> + <key>Comment</key> + <string>Selected voice font</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>00000000-0000-0000-0000-000000000000</string> + </map> <key>AutoDisengageMic</key> <map> <key>Comment</key> diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index 5a96613870..703c7dd435 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -52,6 +52,7 @@ #include "lltransientfloatermgr.h" #include "llviewerwindow.h" #include "llvoicechannel.h" +#include "llvoiceclient.h" // for Voice font list types #include "llviewerparcelmgr.h" static void get_voice_participants_uuids(uuid_vec_t& speakers_uuids); @@ -95,7 +96,7 @@ static void* create_non_avatar_caller(void*) return new LLNonAvatarCaller; } -LLVoiceChannel* LLCallFloater::sCurrentVoiceCanel = NULL; +LLVoiceChannel* LLCallFloater::sCurrentVoiceChannel = NULL; LLCallFloater::LLCallFloater(const LLSD& key) : LLTransientDockableFloater(NULL, false, key) @@ -105,6 +106,7 @@ LLCallFloater::LLCallFloater(const LLSD& key) , mNonAvatarCaller(NULL) , mVoiceType(VC_LOCAL_CHAT) , mAgentPanel(NULL) +, mVoiceFont(NULL) , mSpeakingIndicator(NULL) , mIsModeratorMutedVoice(false) , mInitParticipantsVoiceState(false) @@ -113,7 +115,8 @@ LLCallFloater::LLCallFloater(const LLSD& key) mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLCallFloater::removeVoiceLeftParticipant, this, _1), voice_left_remove_delay); mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL); - LLVoiceClient::getInstance()->addObserver(this); + LLVoiceClient::instance().addObserver(dynamic_cast<LLVoiceClientParticipantObserver*>(this)); + LLVoiceClient::instance().addObserver(dynamic_cast<LLVoiceClientFontsObserver*>(this)); LLTransientFloaterMgr::getInstance()->addControlView(this); // force docked state since this floater doesn't save it between recreations @@ -133,7 +136,8 @@ LLCallFloater::~LLCallFloater() if(LLVoiceClient::instanceExists()) { - LLVoiceClient::instance().removeObserver(this); + LLVoiceClient::instance().removeObserver(dynamic_cast<LLVoiceClientParticipantObserver*>(this)); + LLVoiceClient::instance().removeObserver(dynamic_cast<LLVoiceClientFontsObserver*>(this)); } LLTransientFloaterMgr::getInstance()->removeControlView(this); } @@ -147,6 +151,9 @@ BOOL LLCallFloater::postBuild() childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this)); + mVoiceFont = getChild<LLComboBox>("voice_font"); + childSetCommitCallback("voice_font", commitVoiceFont, this); // *FIX: childSetCommitCallback deprecated + mNonAvatarCaller = getChild<LLNonAvatarCaller>("non_avatar_caller"); mNonAvatarCaller->setVisible(FALSE); @@ -158,7 +165,6 @@ BOOL LLCallFloater::postBuild() initAgentData(); - connectToChannel(LLVoiceChannel::getCurrentVoiceChannel()); setIsChrome(true); @@ -204,7 +210,7 @@ void LLCallFloater::draw() } // virtual -void LLCallFloater::onChange() +void LLCallFloater::onParticipantsChanged() { if (NULL == mParticipants) return; @@ -219,6 +225,31 @@ void LLCallFloater::onChange() } } +// virtual +void LLCallFloater::onVoiceFontsChanged() +{ + if (mVoiceFont) + { + mVoiceFont->removeall(); + mVoiceFont->add(getString("no_voice_font"), LLUUID::null); + + if (LLVoiceClient::instance().hasVoiceFonts()) + { + const LLVoiceClient::voice_font_list_t font_list = LLVoiceClient::instance().getVoiceFontList(); + for (LLVoiceClient::voice_font_list_t::const_iterator it = font_list.begin(); it != font_list.end(); ++it) + { + mVoiceFont->add(*(it->first), *(it->second), ADD_BOTTOM); + } + mVoiceFont->setEnabled(true); + mVoiceFont->setValue(LLVoiceClient::instance().getVoiceFont()); + } + else + { + mVoiceFont->setEnabled(false); + } + } +} + ////////////////////////////////////////////////////////////////////////// /// PRIVATE SECTION ////////////////////////////////////////////////////////////////////////// @@ -232,6 +263,12 @@ void LLCallFloater::leaveCall() } } +/* static */ +void LLCallFloater::commitVoiceFont(LLUICtrl* ctrl, void* userdata) +{ + LLVoiceClient::getInstance()->setVoiceFont(ctrl->getValue()); +} + void LLCallFloater::updateSession() { LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel(); @@ -296,7 +333,7 @@ void LLCallFloater::updateSession() } updateTitle(); - + //hide "Leave Call" button for nearby chat bool is_local_chat = mVoiceType == VC_LOCAL_CHAT; childSetVisible("leave_call_btn_panel", !is_local_chat); @@ -370,7 +407,7 @@ void LLCallFloater::sOnCurrentChannelChanged(const LLUUID& /*session_id*/) // *NOTE: if signal was sent for voice channel with LLVoiceChannel::STATE_NO_CHANNEL_INFO // it sill be sent for the same channel again (when state is changed). // So, lets ignore this call. - if (channel == sCurrentVoiceCanel) return; + if (channel == sCurrentVoiceChannel) return; LLCallFloater* call_floater = LLFloaterReg::getTypedInstance<LLCallFloater>("voice_controls"); @@ -718,9 +755,9 @@ void LLCallFloater::connectToChannel(LLVoiceChannel* channel) { mVoiceChannelStateChangeConnection.disconnect(); - sCurrentVoiceCanel = channel; + sCurrentVoiceChannel = channel; - mVoiceChannelStateChangeConnection = sCurrentVoiceCanel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2)); + mVoiceChannelStateChangeConnection = sCurrentVoiceChannel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2)); updateState(channel->getState()); } @@ -740,7 +777,7 @@ void LLCallFloater::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old void LLCallFloater::updateState(const LLVoiceChannel::EState& new_state) { - LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceCanel->getSessionName() << LL_ENDL; + LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceChannel->getSessionName() << LL_ENDL; if (LLVoiceChannel::STATE_CONNECTED == new_state) { updateSession(); diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 0a8ea7de39..160eff312c 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -40,6 +40,7 @@ class LLAvatarList; class LLAvatarListItem; +class LLComboBox; class LLNonAvatarCaller; class LLOutputMonitorCtrl; class LLParticipantList; @@ -47,17 +48,19 @@ class LLSpeakerMgr; class LLSpeakersDelayActionsStorage; /** - * The Voice Control Panel is an ambient window summoned by clicking the flyout chevron on the Speak button. - * It can be torn-off and freely positioned onscreen. + * The Voice Control Panel is an ambient window summoned by clicking the flyout chevron + * on the Speak button. It can be torn-off and freely positioned onscreen. * - * When the Resident is engaged in Nearby Voice Chat, the Voice Control Panel provides control over - * the Resident's own microphone input volume, the audible volume of each of the other participants, - * the Resident's own Voice Morphing settings (if she has subscribed to enable the feature), and Voice Recording. + * When the Resident is engaged in Voice Chat, the Voice Control Panel provides control + * over the audible volume of each of the other participants, the Resident's own Voice + * Morphing settings (if she has subscribed to enable the feature), and Voice Recording. * - * When the Resident is engaged in any chat except Nearby Chat, the Voice Control Panel also provides an - * 'Leave Call' button to allow the Resident to leave that voice channel. + * When the Resident is engaged in any chat except Nearby Chat, the Voice Control Panel + * also provides a 'Leave Call' button to allow the Resident to leave that voice channel. */ -class LLCallFloater : public LLTransientDockableFloater, LLVoiceClientParticipantObserver +class LLCallFloater : public LLTransientDockableFloater, + LLVoiceClientParticipantObserver, + LLVoiceClientFontsObserver { public: @@ -75,7 +78,10 @@ public: * * Refreshes list to display participants not in voice as disabled. */ - /*virtual*/ void onChange(); + /*virtual*/ void onParticipantsChanged(); + + /// Called by LLVoiceClient::notifyVoiceFontObservers when voice font list is changed. + /*virtual*/ void onVoiceFontsChanged(); static void sOnCurrentChannelChanged(const LLUUID& session_id); @@ -101,6 +107,8 @@ private: void leaveCall(); + static void commitVoiceFont(LLUICtrl*,void* userdata); + /** * Updates mSpeakerManager and list according to current Voice Channel * @@ -230,6 +238,7 @@ private: LLNonAvatarCaller* mNonAvatarCaller; EVoiceControls mVoiceType; LLPanel* mAgentPanel; + LLComboBox* mVoiceFont; LLOutputMonitorCtrl* mSpeakingIndicator; bool mIsModeratorMutedVoice; @@ -259,7 +268,7 @@ private: * * @see sOnCurrentChannelChanged() */ - static LLVoiceChannel* sCurrentVoiceCanel; + static LLVoiceChannel* sCurrentVoiceChannel; /* virtual */ LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index c3748ca81d..d0c882e16e 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -92,7 +92,7 @@ public: mAvalineCallers.insert(avaline_caller_id); } - void onChange() + void onParticipantsChanged() { uuid_set_t participant_uuids; LLVoiceClient::getInstance()->getParticipantsUUIDSet(participant_uuids); diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp index cc06179481..70028dc21b 100644 --- a/indra/newview/llspeakingindicatormanager.cpp +++ b/indra/newview/llspeakingindicatormanager.cpp @@ -107,7 +107,7 @@ private: * So, method does not calculate difference between these list it only switches off already * switched on indicators and switches on indicators of voice channel participants */ - void onChange(); + void onParticipantsChanged(); /** * Changes state of indicators specified by LLUUIDs @@ -205,7 +205,7 @@ void SpeakingIndicatorManager::sOnCurrentChannelChanged(const LLUUID& /*session_ mSwitchedIndicatorsOn.clear(); } -void SpeakingIndicatorManager::onChange() +void SpeakingIndicatorManager::onParticipantsChanged() { LL_DEBUGS("SpeakingIndicator") << "Voice participant list was changed, updating indicators" << LL_ENDL; diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 7bb1006e93..9ab0bf9552 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -887,9 +887,9 @@ void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::s else { LL_WARNS("Voice") << "incoming SIP URL is not provided. Channel may not work properly." << LL_ENDL; - // In case of incoming AvaLine call generated URI will be differ from original one. - // This is because Avatar-2-Avatar URI is based on avatar UUID but Avaline is not. - // See LLVoiceClient::sessionAddedEvent() -> setUUIDFromStringHash() + // In the case of an incoming AvaLine call, the generated URI will be different from the + // original one. This is because the P2P URI is based on avatar UUID but Avaline is not. + // See LLVoiceClient::sessionAddedEvent() setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); } diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 941cccacc3..5236b11853 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -112,7 +112,7 @@ protected: void doSetState(const EState& state); void setURI(std::string uri); - // there can be two directions ICOMING and OUTGOING + // there can be two directions INCOMING and OUTGOING EDirection mCallDirection; std::string mURI; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 298ce3fcec..d131285512 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -73,15 +73,11 @@ #include "llvoavatarself.h" #include "llvoicechannel.h" +#include "stringize.h" + // for base64 decoding #include "apr_base64.h" -// for SHA1 hash -#include "apr_sha1.h" - -// for MD5 hash -#include "llmd5.h" - #define USE_SESSION_GROUPS 0 static bool sConnectingToAgni = false; @@ -116,14 +112,6 @@ const int MAX_LOGIN_RETRIES = 12; // 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; - md5_uuid.update((const unsigned char*)str.data(), str.size()); - md5_uuid.finalize(); - md5_uuid.raw_digest(uuid.mData); -} - static int scale_mic_volume(float volume) { // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default. @@ -254,6 +242,12 @@ protected: std::string audioMediaString; std::string displayNameString; std::string deviceString; + S32 id; + std::string descriptionString; + std::string expirationDateString; + bool hasExpired; + S32 fontType; + S32 fontStatus; int participantType; bool isLocallyMuted; bool isModeratorMuted; @@ -511,7 +505,20 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) { gVoiceClient->deleteAllAutoAcceptRules(); } - + else if (!stricmp("SessionFonts", tag)) + { + LLVoiceClient::getInstance()->deleteAllVoiceFonts(); + } + else if (!stricmp("SessionFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDateString.clear(); + hasExpired = false; + fontType = 0; + fontStatus = 0; + } } } responseDepth++; @@ -639,6 +646,34 @@ void LLVivoxProtocolParser::EndTag(const char *tag) autoAcceptMask = string; else if (!stricmp("AutoAddAsBuddy", tag)) autoAddAsBuddy = string; + else if (!stricmp("SessionFont", tag)) + { + LLVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDateString, hasExpired, fontType, fontStatus); + } + else if (!stricmp("ID", tag)) + { + id = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("Description", tag)) + { + descriptionString = string; + } + else if (!stricmp("ExpirationDate", tag)) + { + expirationDateString = string; + } + else if (!stricmp("Expired", tag)) + { + hasExpired = !stricmp(string.c_str(), "1"); + } + else if (!stricmp("Type", tag)) + { + fontType = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("Status", tag)) + { + fontStatus = strtol(string.c_str(), NULL, 10); + } else if (!stricmp("MessageHeader", tag)) messageHeader = string; else if (!stricmp("MessageBody", tag)) @@ -909,6 +944,10 @@ void LLVivoxProtocolParser::processResponse(std::string tag) { gVoiceClient->accountListAutoAcceptRulesResponse(statusCode, statusString); } + else if (!stricmp(actionCstr, "Account.GetSessionFonts.1")) + { + LLVoiceClient::getInstance()->accountGetSessionFontsResponse(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. @@ -1337,6 +1376,7 @@ LLVoiceClient::LLVoiceClient() : mBuddyListMapPopulated(false), mBlockRulesListReceived(false), mAutoAcceptRulesListReceived(false), + mCaptureDeviceDirty(false), mRenderDeviceDirty(false), mSpatialCoordsDirty(false), @@ -1675,6 +1715,8 @@ std::string LLVoiceClient::state2string(LLVoiceClient::state inState) CASE(stateNeedsLogin); CASE(stateLoggingIn); CASE(stateLoggedIn); + CASE(stateVoiceFontsWait); + CASE(stateVoiceFontsReceived); CASE(stateCreatingSessionGroup); CASE(stateNoChannel); CASE(stateJoiningSession); @@ -1816,7 +1858,8 @@ void LLVoiceClient::stateMachine() // Clean up and reset everything. closeSocket(); deleteAllSessions(); - deleteAllBuddies(); + deleteAllBuddies(); + deleteAllVoiceFonts(); mConnectorHandle.clear(); mAccountHandle.clear(); @@ -2250,6 +2293,10 @@ void LLVoiceClient::stateMachine() notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + // request the set of available voice fonts + setState(stateVoiceFontsWait); + accountGetSessionFontsSendMessage(); + // request the current set of block rules (we'll need them when updating the friends list) accountListBlockRulesSendMessage(); @@ -2281,12 +2328,21 @@ void LLVoiceClient::stateMachine() writeString(stream.str()); } } + break; + //MARK: stateVoiceFontsWait + case stateVoiceFontsWait: // Await voice font list + // accountGetSessionFontsResponse() will transition from here to + // stateVoiceFontsReceived, to ensure we have the voice font list + // before attempting to create a session. + break; + + //MARK: stateVoiceFontsReceived + case stateVoiceFontsReceived: // Voice font list received #if USE_SESSION_GROUPS // create the main session group - sessionGroupCreateSendMessage(); - setState(stateCreatingSessionGroup); + sessionGroupCreateSendMessage(); #else // Not using session groups -- skip the stateCreatingSessionGroup state. setState(stateNoChannel); @@ -2574,6 +2630,7 @@ void LLVoiceClient::stateMachine() mAccountHandle.clear(); deleteAllSessions(); deleteAllBuddies(); + deleteAllVoiceFonts(); if(mVoiceEnabled && !mRelogRequested) { @@ -2757,6 +2814,24 @@ void LLVoiceClient::accountListAutoAcceptRulesSendMessage() } } +void LLVoiceClient::accountGetSessionFontsSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Requesting voice font list." << LL_ENDL; + + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.GetSessionFonts.1\">" + << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "</Request>" + << "\n\n\n"; + + writeString(stream.str()); + } +} + void LLVoiceClient::sessionGroupCreateSendMessage() { if(!mAccountHandle.empty()) @@ -2779,7 +2854,10 @@ void LLVoiceClient::sessionGroupCreateSendMessage() void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText) { LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; - + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + session->mCreateInProgress = true; if(startAudio) { @@ -2803,10 +2881,11 @@ void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAu << "<Password>" << LLURI::escape(session->mHash, allowed_chars) << "</Password>" << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"; } - + stream << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>" << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>" + << "<VoiceFontID>" << font_index << "</VoiceFontID>" << "<Name>" << mChannelName << "</Name>" << "</Request>\n\n\n"; writeString(stream.str()); @@ -2815,7 +2894,10 @@ void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAu void LLVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText) { LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; - + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + session->mCreateInProgress = true; if(startAudio) { @@ -2841,6 +2923,7 @@ void LLVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, boo << "<Name>" << mChannelName << "</Name>" << "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>" << "<ConnectText>" << (startText?"true":"false") << "</ConnectText>" + << "<VoiceFontID>" << font_index << "</VoiceFontID>" << "<Password>" << password << "</Password>" << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>" << "</Request>\n\n\n" @@ -2853,6 +2936,9 @@ void LLVoiceClient::sessionMediaConnectSendMessage(sessionState *session) { LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL; + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + session->mMediaConnectInProgress = true; std::ostringstream stream; @@ -2861,6 +2947,7 @@ void LLVoiceClient::sessionMediaConnectSendMessage(sessionState *session) << "<Request requestId=\"" << session->mHandle << "\" action=\"Session.MediaConnect.1\">" << "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" << "<SessionHandle>" << session->mHandle << "</SessionHandle>" + << "<VoiceFontID>" << font_index << "</VoiceFontID>" << "<Media>Audio</Media>" << "</Request>\n\n\n"; @@ -2882,6 +2969,22 @@ void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session) writeString(stream.str()); } +void LLVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session) +{ + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << "), session handle: " << session->mHandle << LL_ENDL; + + std::ostringstream stream; + + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetVoiceFont.1\">" + << "<SessionHandle>" << session->mHandle << "</SessionHandle>" + << "<SessionFontID>" << font_index << "</SessionFontID>" + << "</Request>\n\n\n"; + + writeString(stream.str()); +} + void LLVoiceClient::sessionTerminate() { mSessionTerminateRequested = true; @@ -4171,7 +4274,7 @@ void LLVoiceClient::sessionAddedEvent( else { LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL; - setUUIDFromStringHash(session->mCallerID, session->mSIPURI); + session->mCallerID.generate(session->mSIPURI); session->mSynthesizedCallerID = true; // Can't look up the name in this case -- we have to extract it from the URI. @@ -5150,8 +5253,8 @@ LLVoiceClient::participantState *LLVoiceClient::sessionState::addParticipant(con else { // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. - // This tells code in LLVoiceClient that the ID will not be in the name cache. - setUUIDFromStringHash(result->mAvatarID, uri); + // This indicates that the ID will not be in the name cache. + result->mAvatarID.generate(uri); } } @@ -6538,6 +6641,7 @@ LLVoiceClient::sessionState::sessionState() : mMuteDirty(false), mParticipantsChanged(false) { + mVoiceFontID = LLUUID(gSavedSettings.getString("VoiceFontDefault")); } LLVoiceClient::sessionState::~sessionState() @@ -7050,6 +7154,177 @@ void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const s } } +LLVoiceClient::voiceFontEntry::voiceFontEntry(LLUUID& id) : + mID(id), + mFontIndex(0), + mHasExpired(false), + mFontType(VOICE_FONT_TYPE_NONE), + mFontStatus(VOICE_FONT_STATUS_NONE) +{ +} + +LLVoiceClient::voiceFontEntry::~voiceFontEntry() +{ +} + +void LLVoiceClient::deleteAllVoiceFonts() +{ + mVoiceFontList.clear(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) + { + delete iter->second; + } + mVoiceFontMap.clear(); +} + +void LLVoiceClient::addVoiceFont(const S32 font_index, + const std::string &name, + const std::string &description, + const std::string &expiration_date, + const bool has_expired, + const S32 font_type, + const S32 font_status) +{ + // Vivox SessionFontIDs are not guaranteed to remain the same between + // sessions or grids so use a UUID for the name. + + // If received name is not a UUID, fudge one by hashing the name and type + LLUUID font_id; + if (LLUUID::validate(name)) + { + font_id = LLUUID(name); + } + else + { + font_id.generate(STRINGIZE(font_type << ":" << name)); + } + + voiceFontEntry *font = NULL; + + // Hopefully won't happen, but behave gracefully if there is a duplicate + // by Replacing the previous one unless this one has expired. + // *TODO: Should maybe check for the later expiry date if neither has + // expired, and favour user fonts over root fonts? But as we shouldn't + // have duplicates anyway, it's probably not worth the effort. + voice_font_map_t::iterator iter = mVoiceFontMap.find(&font_id); + bool duplicate = (iter != mVoiceFontMap.end()); + if (duplicate) + { + LL_DEBUGS("Voice") << "Voice font " << font_index << " duplicates " << iter->second->mFontIndex << "!" << LL_ENDL; + + if (!has_expired) + { + font = iter->second; + } + } + else + { + font = new voiceFontEntry(font_id); + } + + if (font) + { + font->mFontIndex = font_index; + // Use the description for the human readable name if available, as the + // "name" will probably be a UUID. + font->mName = description.empty() ? name : description; + font->mExpirationDate = expiration_date; + font->mHasExpired = has_expired; + font->mFontType = font_type; + font->mFontStatus = font_status; + + LL_DEBUGS("Voice") << "Adding voice font : " << font_id << " (" << font_index << ") : " << name << (has_expired?" (Expired)":"") << LL_ENDL; + + if (font_type < VOICE_FONT_TYPE_NONE || font_type >= VOICE_FONT_TYPE_UNKNOWN) + { + LL_DEBUGS("Voice") << "Unknown voice font type: " << font_type << LL_ENDL; + } + if (font_status < VOICE_FONT_STATUS_NONE || font_status >= VOICE_FONT_STATUS_UNKNOWN) + { + LL_DEBUGS("Voice") << "Unknown voice font status: " << font_status << LL_ENDL; + } + + if (!duplicate) + { + mVoiceFontMap.insert(voice_font_map_t::value_type(&(font->mID), font)); + mVoiceFontList.insert(voice_font_list_t::value_type(&(font->mName), &(font->mID))); + } + } +} + +bool LLVoiceClient::setVoiceFont(const LLUUID& id) +{ + if (!mAudioSession || !mAudioSession->mVoiceEnabled || !hasVoiceFonts()) + { + LL_DEBUGS("Voice") << "Voice fonts not available." << LL_ENDL; + return false; + } + + if (id.isNull() || (mVoiceFontMap.find(&id) != mVoiceFontMap.end())) + { + // *TODO: Check for expired fonts? + mAudioSession->mVoiceFontID = id; + + // *TODO: Separate voice font defaults for spatial chat and IM? + gSavedSettings.setString("VoiceFontDefault", id.asString()); + } + else + { + LL_DEBUGS("Voice") << "Invalid voice font " << id << LL_ENDL; + return false; + } + + sessionSetVoiceFontSendMessage(mAudioSession); + notifyVoiceFontObservers(); + return true; +} + +const LLUUID LLVoiceClient::getVoiceFont() +{ + if (mAudioSession) + { + return getVoiceFont(mAudioSession->mHandle); + } + else + { + return LLUUID::null; + } +} + +const LLUUID LLVoiceClient::getVoiceFont(const std::string &session_handle) +{ + LLUUID result; + if (hasVoiceFonts()) + { + sessionState *session = findSession(session_handle); + if (session) + { + result = mAudioSession->mVoiceFontID; + } + } + return result; +} + +S32 LLVoiceClient::getVoiceFontIndex(const LLUUID& id) const +{ + S32 result = 0; + if (!id.isNull()) + { + voice_font_map_t::const_iterator it = mVoiceFontMap.find(&id); + if (it != mVoiceFontMap.end()) + { + result = it->second->mFontIndex; + } + else + { + LL_DEBUGS("Voice") << "Selected voice font " << id << " is not available." << LL_ENDL; + } + } + return result; +} + void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString) { // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done. @@ -7062,6 +7337,16 @@ void LLVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std mAutoAcceptRulesListReceived = true; } +void LLVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString) +{ + // Voice font list entries were updated via addVoiceFont() during parsing. + if(getState() == stateVoiceFontsWait) + { + setState(stateVoiceFontsReceived); + } + notifyVoiceFontObservers(); +} + void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) { mParticipantObservers.insert(observer); @@ -7079,12 +7364,35 @@ void LLVoiceClient::notifyParticipantObservers() ) { LLVoiceClientParticipantObserver* observer = *it; - observer->onChange(); - // In case onChange() deleted an entry. + observer->onParticipantsChanged(); + // In case onParticipantsChanged() deleted an entry. it = mParticipantObservers.upper_bound(observer); } } +void LLVoiceClient::addObserver(LLVoiceClientFontsObserver* observer) +{ + mVoiceFontObservers.insert(observer); +} + +void LLVoiceClient::removeObserver(LLVoiceClientFontsObserver* observer) +{ + mVoiceFontObservers.erase(observer); +} + +void LLVoiceClient::notifyVoiceFontObservers() +{ + for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin(); + it != mVoiceFontObservers.end(); + ) + { + LLVoiceClientFontsObserver* observer = *it; + observer->onVoiceFontsChanged(); + // In case onVoiceFontsChanged() deleted an entry. + it = mVoiceFontObservers.upper_bound(observer); + } +} + void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) { mStatusObservers.insert(observer); diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index a29c386182..a1083652aa 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -49,7 +49,14 @@ class LLVoiceClientParticipantObserver { public: virtual ~LLVoiceClientParticipantObserver() { } - virtual void onChange() = 0; + virtual void onParticipantsChanged() = 0; +}; + +class LLVoiceClientFontsObserver +{ +public: + virtual ~LLVoiceClientFontsObserver() { } + virtual void onVoiceFontsChanged() = 0; }; class LLVoiceClientStatusObserver @@ -358,6 +365,8 @@ static void updatePosition(void); bool mParticipantsChanged; participantMap mParticipantsByURI; participantUUIDMap mParticipantsByUUID; + + LLUUID mVoiceFontID; }; participantState *findParticipantByID(const LLUUID& id); @@ -429,8 +438,28 @@ static void updatePosition(void); void deleteAllAutoAcceptRules(void); void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy); void accountListBlockRulesResponse(int statusCode, const std::string &statusString); - void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString); - + void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString); + + ///////////////////////////// + // Voice Fonts + bool hasVoiceFonts() const { return !mVoiceFontMap.empty(); }; + bool setVoiceFont(const LLUUID& id); + const LLUUID getVoiceFont(); + const LLUUID getVoiceFont(const std::string &session_handle); + + typedef std::multimap<const std::string*, const LLUUID*, stringMapComparitor> voice_font_list_t; + + const voice_font_list_t &getVoiceFontList() const { return mVoiceFontList; }; + + void addVoiceFont(const S32 id, + const std::string &name, + const std::string &description, + const std::string &expiration_date, + const bool has_expired, + const S32 font_type, + const S32 font_status); + void accountGetSessionFontsResponse(int statusCode, const std::string &statusString); + ///////////////////////////// // session control messages void connectorCreate(); @@ -452,12 +481,15 @@ static void updatePosition(void); void accountListBlockRulesSendMessage(); void accountListAutoAcceptRulesSendMessage(); - + + void accountGetSessionFontsSendMessage(); + void sessionGroupCreateSendMessage(); void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false); void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false); void sessionMediaConnectSendMessage(sessionState *session); // just joins the audio session void sessionTextConnectSendMessage(sessionState *session); // just joins the text session + void sessionSetVoiceFontSendMessage(sessionState *session); void sessionTerminateSendMessage(sessionState *session); void sessionGroupTerminateSendMessage(sessionState *session); void sessionMediaDisconnectSendMessage(sessionState *session); @@ -475,6 +507,9 @@ static void updatePosition(void); void addObserver(LLVoiceClientParticipantObserver* observer); void removeObserver(LLVoiceClientParticipantObserver* observer); + void addObserver(LLVoiceClientFontsObserver* observer); + void removeObserver(LLVoiceClientFontsObserver* observer); + void addObserver(LLVoiceClientStatusObserver* observer); void removeObserver(LLVoiceClientStatusObserver* observer); @@ -489,7 +524,7 @@ static void updatePosition(void); deviceList *getCaptureDevices(); deviceList *getRenderDevices(); - + void setNonSpatialChannel( const std::string &uri, const std::string &credentials); @@ -562,6 +597,8 @@ static void updatePosition(void); stateNeedsLogin, // send login request stateLoggingIn, // waiting for account handle stateLoggedIn, // account handle received + stateVoiceFontsWait, // Awaiting the list of voice fonts + stateVoiceFontsReceived, // List of voice fonts received stateCreatingSessionGroup, // Creating the main session group stateNoChannel, // stateJoiningSession, // waiting for session handle @@ -662,7 +699,48 @@ static void updatePosition(void); bool mBlockRulesListReceived; bool mAutoAcceptRulesListReceived; buddyListMap mBuddyListMap; - + + // Voice Fonts + + S32 getVoiceFontIndex(const LLUUID& id) const; + void deleteAllVoiceFonts(); + + typedef enum e_voice_font_type + { + VOICE_FONT_TYPE_NONE = 0, + VOICE_FONT_TYPE_ROOT = 1, + VOICE_FONT_TYPE_USER = 2, + VOICE_FONT_TYPE_UNKNOWN + } EVoiceFontType; + + typedef enum e_voice_font_status + { + VOICE_FONT_STATUS_NONE = 0, + VOICE_FONT_STATUS_FREE = 1, + VOICE_FONT_STATUS_NOT_FREE = 2, + VOICE_FONT_STATUS_UNKNOWN + } EVoiceFontStatus; + + struct voiceFontEntry + { + voiceFontEntry(LLUUID& id); + ~voiceFontEntry(); + + LLUUID mID; + S32 mFontIndex; + std::string mName; + std::string mExpirationDate; + bool mHasExpired; + S32 mFontType; + S32 mFontStatus; + }; + typedef std::map<const LLUUID*, voiceFontEntry*, uuidMapComparitor> voice_font_map_t; + + voice_font_map_t mVoiceFontMap; + voice_font_list_t mVoiceFontList; + + // Audio devices + deviceList mCaptureDevices; deviceList mRenderDevices; @@ -674,8 +752,8 @@ static void updatePosition(void); // This should be called when the code detects we have changed parcels. // It initiates the call to the server that gets the parcel channel. void parcelChanged(); - - void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); + + void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); void joinSession(sessionState *session); static std::string nameFromAvatar(LLVOAvatar *avatar); @@ -764,6 +842,11 @@ static std::string nameFromsipURI(const std::string &uri); void notifyParticipantObservers(); + typedef std::set<LLVoiceClientFontsObserver*> voice_font_observer_set_t; + voice_font_observer_set_t mVoiceFontObservers; + + void notifyVoiceFontObservers(); + typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t; status_observer_set_t mStatusObservers; diff --git a/indra/newview/skins/default/xui/en/floater_voice_controls.xml b/indra/newview/skins/default/xui/en/floater_voice_controls.xml index 5b77f11d71..885c8304eb 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_controls.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_controls.xml @@ -35,6 +35,9 @@ name="no_one_near"> No one near has voice enabled </string> + <floater.string name="no_voice_font"> + No Voice Effect + </floater.string> <layout_stack clip="false" follows="all" @@ -86,23 +89,56 @@ visible="true" width="20" /> </layout_panel> - <layout_panel + <layout_stack + clip="false" + auto_resize="false" + follows="left|right" + height="23" + layout="topleft" + left="10" + mouse_opaque="false" + name="voice_font_and_leave_call_stack" + orientation="horizontal" + width="263"> + <layout_panel + auto_resize="true" + user_resize="false" + follows="top|left" + height="26" + visible="true" + layout="topleft" + name="voice_font_panel" + width="150"> + <combo_box + follows="left|top|right" + height="23" + name="voice_font" + top_pad="0" + width="150"> + <combo_box.item + label="No Voice Effect" + name="no_voice_font" + value="0" /> + </combo_box> + </layout_panel> + <layout_panel auto_resize="false" user_resize="false" - follows="top|left" + follows="top|right" height="26" visible="true" layout="topleft" name="leave_call_btn_panel" width="100"> <button - follows="right|top" + follows="right|top" height="23" top_pad="0" label="Leave Call" name="leave_call_btn" width="100" /> </layout_panel> + </layout_stack> <layout_panel follows="all" layout="topleft" |