diff options
Diffstat (limited to 'indra/newview/llvoicevivox.cpp')
-rw-r--r-- | indra/newview/llvoicevivox.cpp | 779 |
1 files changed, 734 insertions, 45 deletions
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index bcb1a70efb..7daf865151 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -67,15 +67,11 @@ #include "llviewernetwork.h" #include "llnotificationsutil.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 const F32 VOLUME_SCALE_VIVOX = 0.01f; @@ -101,14 +97,6 @@ const int MAX_LOGIN_RETRIES = 12; 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. @@ -325,6 +313,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mBuddyListMapPopulated(false), mBlockRulesListReceived(false), mAutoAcceptRulesListReceived(false), + mCaptureDeviceDirty(false), mRenderDeviceDirty(false), mSpatialCoordsDirty(false), @@ -348,10 +337,13 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mVoiceEnabled(false), mWriteInProgress(false), - mLipSyncEnabled(false) - + mLipSyncEnabled(false), + mVoiceFontsReceived(false), + mVoiceFontsNew(false), + mCaptureBufferRecording(false), + mCaptureBufferPlaying(false) { mSpeakerVolume = scale_speaker_volume(0); @@ -654,6 +646,11 @@ std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState) CASE(stateMicTuningStart); CASE(stateMicTuningRunning); CASE(stateMicTuningStop); + CASE(stateCaptureBufferPaused); + CASE(stateCaptureBufferRecStart); + CASE(stateCaptureBufferRecording); + CASE(stateCaptureBufferPlayStart); + CASE(stateCaptureBufferPlaying); CASE(stateConnectorStart); CASE(stateConnectorStarting); CASE(stateConnectorStarted); @@ -662,6 +659,8 @@ std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState) CASE(stateNeedsLogin); CASE(stateLoggingIn); CASE(stateLoggedIn); + CASE(stateVoiceFontsWait); + CASE(stateVoiceFontsReceived); CASE(stateCreatingSessionGroup); CASE(stateNoChannel); CASE(stateJoiningSession); @@ -775,8 +774,10 @@ void LLVivoxVoiceClient::stateMachine() // Clean up and reset everything. closeSocket(); deleteAllSessions(); - deleteAllBuddies(); - + deleteAllBuddies(); + deleteVoiceFonts(); + deleteVoiceFontTemplates(); + mConnectorHandle.clear(); mAccountHandle.clear(); mAccountPassword.clear(); @@ -1126,8 +1127,62 @@ void LLVivoxVoiceClient::stateMachine() } break; - - //MARK: stateConnectorStart + + //MARK: stateCaptureBufferPaused + case stateCaptureBufferPaused: + if (mCaptureBufferRecording) + { + setState(stateCaptureBufferRecStart); + // Update UI, should really be separated from the VoiceFont callback + notifyVoiceFontObservers(); + } + else if (mCaptureBufferPlaying) + { + setState(stateCaptureBufferPlayStart); + notifyVoiceFontObservers(); + } + else if (mCaptureBufferClear) + { + mCaptureBufferClear = false; + setState(stateNoChannel); + } + break; + + //MARK: stateCaptureBufferRecStart + case stateCaptureBufferRecStart: + captureBufferRecordStartSendMessage(); + setState(stateCaptureBufferRecording); + break; + + //MARK: stateCaptureBufferRecording + case stateCaptureBufferRecording: + if (!mCaptureBufferRecording || mCaptureBufferPlaying || mCaptureBufferClear) + { + mCaptureBufferRecording = false; + captureBufferRecordStopSendMessage(); + setState(stateCaptureBufferPaused); + notifyVoiceFontObservers(); + } + break; + + //MARK: stateCaptureBufferPlayStart + case stateCaptureBufferPlayStart: + captureBufferPlayStartSendMessage(mPreviewVoiceFontID); + setState(stateCaptureBufferPlaying); + break; + + //MARK: stateCaptureBufferPlaying + case stateCaptureBufferPlaying: + if (!mCaptureBufferPlaying || mCaptureBufferRecording || mCaptureBufferClear) + { + mCaptureBufferPlaying = false; + captureBufferPlayStopSendMessage(); + setState(stateCaptureBufferPaused); + notifyVoiceFontObservers(); + } + break; + + //MARK: stateConnectorStart case stateConnectorStart: if(!mVoiceEnabled) { @@ -1222,6 +1277,18 @@ void LLVivoxVoiceClient::stateMachine() notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + // *FIX: Remove VoiceFontsAvailable temporary setting (only used to test UI behaviour with no fonts) + if (LLVoiceClient::instance().getVoiceEffectEnabled() && gSavedSettings.getBOOL("VoiceFontsAvailable")) + { + // request the set of available voice fonts + setState(stateVoiceFontsWait); + refreshVoiceEffectLists(true); + } + else + { + setState(stateVoiceFontsReceived); + } + // request the current set of block rules (we'll need them when updating the friends list) accountListBlockRulesSendMessage(); @@ -1253,12 +1320,21 @@ void LLVivoxVoiceClient::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); @@ -1306,6 +1382,11 @@ void LLVivoxVoiceClient::stateMachine() mTuningExitState = stateNoChannel; setState(stateMicTuningStart); } + else if(mCaptureBufferRecording) + { + mTuningExitState = stateNoChannel; + setState(stateCaptureBufferRecStart); + } else if(sessionNeedsRelog(mNextAudioSession)) { requestRelog(); @@ -1316,6 +1397,7 @@ void LLVivoxVoiceClient::stateMachine() sessionState *oldSession = mAudioSession; mAudioSession = mNextAudioSession; + mAudioSessionChanged = true; if(!mAudioSession->mReconnect) { mNextAudioSession = NULL; @@ -1547,6 +1629,8 @@ void LLVivoxVoiceClient::stateMachine() mAccountHandle.clear(); deleteAllSessions(); deleteAllBuddies(); + deleteVoiceFonts(); + deleteVoiceFontTemplates(); if(mVoiceEnabled && !mRelogRequested) { @@ -1627,15 +1711,15 @@ void LLVivoxVoiceClient::stateMachine() } - if(mAudioSession && mAudioSession->mParticipantsChanged) + if (mAudioSessionChanged) { - mAudioSession->mParticipantsChanged = false; - mAudioSessionChanged = true; + mAudioSessionChanged = false; + notifyParticipantObservers(); + notifyVoiceFontObservers(); } - - if(mAudioSessionChanged) + else if (mAudioSession && mAudioSession->mParticipantsChanged) { - mAudioSessionChanged = false; + mAudioSession->mParticipantsChanged = false; notifyParticipantObservers(); } } @@ -1751,8 +1835,11 @@ void LLVivoxVoiceClient::sessionGroupCreateSendMessage() void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText) { - LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; - + LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + session->mCreateInProgress = true; if(startAudio) { @@ -1776,10 +1863,11 @@ void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool st << "<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()); @@ -1787,8 +1875,11 @@ void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool st void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText) { - LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; - + LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + session->mCreateInProgress = true; if(startAudio) { @@ -1814,6 +1905,7 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session << "<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" @@ -1824,7 +1916,10 @@ void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session) { - LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL; + LL_DEBUGS("Voice") << "Connecting audio to session handle: " << session->mHandle << LL_ENDL; + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; session->mMediaConnectInProgress = true; @@ -1834,6 +1929,7 @@ void LLVivoxVoiceClient::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"; @@ -3156,7 +3252,7 @@ void LLVivoxVoiceClient::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. @@ -3434,6 +3530,22 @@ void LLVivoxVoiceClient::accountLoginStateChangeEvent( } } +void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType) +{ + if (mediaCompletionType == "AuxBufferAudioCapture") + { + mCaptureBufferRecording = false; + } + else if (mediaCompletionType == "AuxBufferAudioRender") + { + mCaptureBufferPlaying = false; + } + else + { + LL_DEBUGS("Voice") << "Unknown MediaCompletionType: " << mediaCompletionType << LL_ENDL; + } +} + void LLVivoxVoiceClient::mediaStreamUpdatedEvent( std::string &sessionHandle, std::string &sessionGroupHandle, @@ -4134,8 +4246,8 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti else { // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. - // This tells code in LLVivoxVoiceClient 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); } } @@ -4630,7 +4742,7 @@ BOOL LLVivoxVoiceClient::isOnlineSIP(const LLUUID &id) return result; } -bool LLVivoxVoiceClient::isVoiceWorking() +bool LLVivoxVoiceClient::isVoiceWorking() const { //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 @@ -5650,7 +5762,12 @@ LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::stri result = new sessionState(); result->mSIPURI = uri; result->mHandle = handle; - + + if (LLVoiceClient::instance().getVoiceEffectEnabled()) + { + result->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); + } + mSessions.insert(result); if(!result->mHandle.empty()) @@ -6075,8 +6192,8 @@ void LLVivoxVoiceClient::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); } } @@ -6239,6 +6356,499 @@ void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string } } +bool LLVivoxVoiceClient::setVoiceEffect(const LLUUID& id) +{ + if (!mAudioSession) + { + return false; + } + + if (!id.isNull()) + { + if (mVoiceFontMap.empty()) + { + LL_DEBUGS("Voice") << "Voice fonts not available." << LL_ENDL; + return false; + } + else if (mVoiceFontMap.find(id) == mVoiceFontMap.end()) + { + LL_DEBUGS("Voice") << "Invalid voice font " << id << LL_ENDL; + return false; + } + } + + // *TODO: Check for expired fonts? + mAudioSession->mVoiceFontID = id; + + // *TODO: Separate voice font defaults for spatial chat and IM? + gSavedPerAccountSettings.setString("VoiceEffectDefault", id.asString()); + + sessionSetVoiceFontSendMessage(mAudioSession); + notifyVoiceFontObservers(); + + return true; +} + +const LLUUID LLVivoxVoiceClient::getVoiceEffect() +{ + return mAudioSession ? mAudioSession->mVoiceFontID : LLUUID::null; +} + +LLSD LLVivoxVoiceClient::getVoiceEffectProperties(const LLUUID& id) +{ + LLSD sd; + + voice_font_map_t::iterator iter = mVoiceFontMap.find(id); + if (iter != mVoiceFontMap.end()) + { + sd["template_only"] = false; + } + else + { + // Voice effect is not in the voice font map, see if there is a template + iter = mVoiceFontTemplateMap.find(id); + if (iter == mVoiceFontTemplateMap.end()) + { + LL_WARNS("Voice") << "Voice effect " << id << "not found." << LL_ENDL; + return sd; + } + sd["template_only"] = true; + } + + voiceFontEntry *font = iter->second; + sd["name"] = font->mName; + sd["expired"] = font->mHasExpired; + sd["expiry_date"] = font->mExpirationDate; + sd["is_new"] = font->mIsNew; + + return sd; +} + +LLVivoxVoiceClient::voiceFontEntry::voiceFontEntry(LLUUID& id) : + mID(id), + mFontIndex(0), + mHasExpired(false), + mFontType(VOICE_FONT_TYPE_NONE), + mFontStatus(VOICE_FONT_STATUS_NONE), + mIsNew(false) +{ +} + +LLVivoxVoiceClient::voiceFontEntry::~voiceFontEntry() +{ +} + +void LLVivoxVoiceClient::refreshVoiceEffectLists(bool clear_lists) +{ + if (clear_lists) + { + mVoiceFontsReceived = false; + deleteVoiceFonts(); + deleteVoiceFontTemplates(); + } + + accountGetSessionFontsSendMessage(); + accountGetTemplateFontsSendMessage(); +} + +const voice_effect_list_t& LLVivoxVoiceClient::getVoiceEffectList() const +{ + return mVoiceFontList; +} + +const voice_effect_list_t& LLVivoxVoiceClient::getVoiceEffectTemplateList() const +{ + return mVoiceFontTemplateList; +} + +void LLVivoxVoiceClient::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, + const bool template_font) +{ + // 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; + + voice_font_map_t& font_map = template_font ? mVoiceFontTemplateMap : mVoiceFontMap; + voice_effect_list_t& font_list = template_font ? mVoiceFontTemplateList : mVoiceFontList; + + // Check whether we've seen this font before and create an entry for it if not. + voice_font_map_t::iterator iter = font_map.find(font_id); + bool new_font = (iter == font_map.end()); + if (new_font) + { + font = new voiceFontEntry(font_id); + } + else + { + font = iter->second; + } + + if (font) + { + font->mFontIndex = font_index; + // Use the description for the human readable name if available, as the + // "name" may 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; + + // Only flag it as a new font if we have already seen the font list. + if (!template_font && mVoiceFontsReceived && new_font) + { + font->mIsNew = true; + mVoiceFontsNew = true; + } + + LL_DEBUGS("Voice") << (template_font?"Template: ":"") << 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 (new_font) + { + font_map.insert(voice_font_map_t::value_type(font->mID, font)); + font_list.insert(voice_effect_list_t::value_type(font->mName, font->mID)); + } + } +} + +void LLVivoxVoiceClient::deleteVoiceFonts() +{ + mVoiceFontList.clear(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) + { + delete iter->second; + } + mVoiceFontMap.clear(); +} + +void LLVivoxVoiceClient::deleteVoiceFontTemplates() +{ + mVoiceFontTemplateList.clear(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontTemplateMap.begin(); iter != mVoiceFontTemplateMap.end(); ++iter) + { + delete iter->second; + } + mVoiceFontTemplateMap.clear(); +} + +S32 LLVivoxVoiceClient::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; +} + +S32 LLVivoxVoiceClient::getVoiceFontTemplateIndex(const LLUUID& id) const +{ + S32 result = 0; + if (!id.isNull()) + { + voice_font_map_t::const_iterator it = mVoiceFontTemplateMap.find(id); + if (it != mVoiceFontTemplateMap.end()) + { + result = it->second->mFontIndex; + } + else + { + LL_DEBUGS("Voice") << "Selected voice font template " << id << " is not available." << LL_ENDL; + } + } + return result; +} + +void LLVivoxVoiceClient::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 LLVivoxVoiceClient::accountGetTemplateFontsSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Requesting voice font template list." << LL_ENDL; + + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.GetTemplateFonts.1\">" + << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "</Request>" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::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 LLVivoxVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString) +{ + // Voice font list entries were updated via addVoiceFont() during parsing. + if(getState() == stateVoiceFontsWait) + { + setState(stateVoiceFontsReceived); + } + mVoiceFontsReceived = true; + + notifyVoiceFontObservers(mVoiceFontsNew); + mVoiceFontsNew = false; +} + +void LLVivoxVoiceClient::accountGetTemplateFontsResponse(int statusCode, const std::string &statusString) +{ + // Voice font list entries were updated via addVoiceFont() during parsing. + notifyVoiceFontObservers(); +} +void LLVivoxVoiceClient::addObserver(LLVoiceEffectObserver* observer) +{ + mVoiceFontObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLVoiceEffectObserver* observer) +{ + mVoiceFontObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyVoiceFontObservers(bool new_fonts) +{ + for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin(); + it != mVoiceFontObservers.end(); + ) + { + LLVoiceEffectObserver* observer = *it; + observer->onVoiceEffectChanged(new_fonts); + // In case onVoiceEffectChanged() deleted an entry. + it = mVoiceFontObservers.upper_bound(observer); + } +} + +void LLVivoxVoiceClient::recordPreviewBuffer(bool enable) +{ + if (enable) + { + mCaptureBufferRecording = true; + LL_DEBUGS("Voice") << "Starting recording" << LL_ENDL; + if(getState() >= stateNoChannel) + { + LL_DEBUGS("Voice") << "no channel" << LL_ENDL; + sessionTerminate(); + } + } + else + { + mCaptureBufferRecording = false; + } +} + +void LLVivoxVoiceClient::playPreviewBuffer(bool enable, const LLUUID& effect_id) +{ + if (enable && !isPreviewReady()) + { + LL_DEBUGS("Voice") << "No preview buffer to play" << LL_ENDL; + return; + } + + mCaptureBufferPlaying = enable; + if (mCaptureBufferPlaying) + { + mPreviewVoiceFontID = effect_id; + } +} + +void LLVivoxVoiceClient::clearPreviewBuffer() +{ + mCaptureBufferClear = true; +} + +bool LLVivoxVoiceClient::isPreviewRecording() +{ + return mCaptureBufferRecording; +} + +bool LLVivoxVoiceClient::isPreviewReady() +{ + state preview_state = getState(); + switch (preview_state) + { + case stateCaptureBufferPaused: + case stateCaptureBufferRecording: + case stateCaptureBufferPlaying: + return true; + break; + default: + return false; + break; + } +} + +bool LLVivoxVoiceClient::isPreviewPlaying() +{ + return mCaptureBufferPlaying; +} + +void LLVivoxVoiceClient::captureBufferRecordStartSendMessage() +{ if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Starting audio capture to buffer." << LL_ENDL; + + // Start capture + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.StartBufferCapture.1\">" + << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "</Request>" + << "\n\n\n"; + + // Unmute the mic + stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" + << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" + << "<Value>false</Value>" + << "</Request>\n\n\n"; + + // Dirty the PTT state so that it will get reset when we finishing previewing + mPTTDirty = true; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::captureBufferRecordStopSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Stopping audio capture to buffer." << LL_ENDL; + + // Mute the mic. PTT state was dirtied at recording start, so will be reset when finished previewing. + stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" + << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" + << "<Value>true</Value>" + << "</Request>\n\n\n"; + + // Stop capture + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">" + << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "</Request>" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_font_id) +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Starting audio buffer playback." << LL_ENDL; + + S32 font_index = getVoiceFontTemplateIndex(voice_font_id); + LL_DEBUGS("Voice") << "With voice font: " << voice_font_id << " (" << font_index << ")" << LL_ENDL; + + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.PlayAudioBuffer.1\">" + << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "<TemplateFontID>" << font_index << "</TemplateFontID>" + << "<FontDelta />" + << "</Request>" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::captureBufferPlayStopSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Stopping audio buffer playback." << LL_ENDL; + + stream + << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">" + << "<AccountHandle>" << mAccountHandle << "</AccountHandle>" + << "</Request>" + << "\n\n\n"; + + writeString(stream.str()); + } +} LLVivoxProtocolParser::LLVivoxProtocolParser() { @@ -6457,7 +7067,30 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) { LLVivoxVoiceClient::getInstance()->deleteAllAutoAcceptRules(); } - + else if (!stricmp("SessionFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDateString.clear(); + hasExpired = false; + fontType = 0; + fontStatus = 0; + } + else if (!stricmp("TemplateFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDateString.clear(); + hasExpired = false; + fontType = 0; + fontStatus = 0; + } + else if (!stricmp("MediaCompletionType", tag)) + { + mediaCompletionType.clear(); + } } } responseDepth++; @@ -6603,8 +7236,43 @@ void LLVivoxProtocolParser::EndTag(const char *tag) subscriptionHandle = string; else if (!stricmp("SubscriptionType", tag)) subscriptionType = string; - - + else if (!stricmp("SessionFont", tag)) + { + LLVivoxVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDateString, hasExpired, fontType, fontStatus, false); + } + else if (!stricmp("TemplateFont", tag)) + { + LLVivoxVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDateString, hasExpired, fontType, fontStatus, true); + } + 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("MediaCompletionType", tag)) + { + mediaCompletionType = string;; + } + textBuffer.clear(); accumulateText= false; @@ -6686,7 +7354,17 @@ void LLVivoxProtocolParser::processResponse(std::string tag) </Event> */ LLVivoxVoiceClient::getInstance()->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); - } + } + else if (!stricmp(eventTypeCstr, "MediaCompletionEvent")) + { + /* + <Event type="MediaCompletionEvent"> + <SessionGroupHandle /> + <MediaCompletionType>AuxBufferAudioCapture</MediaCompletionType> + </Event> + */ + LLVivoxVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType); + } else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent")) { /* @@ -6747,6 +7425,9 @@ void LLVivoxProtocolParser::processResponse(std::string tag) } else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) { + // These are really spamming in tuning mode + squelchDebugOutput = true; + LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); } else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent")) @@ -6861,6 +7542,14 @@ void LLVivoxProtocolParser::processResponse(std::string tag) // 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); + } + else if (!stricmp(actionCstr, "Account.GetTemplateFonts.1")) + { + LLVivoxVoiceClient::getInstance()->accountGetTemplateFontsResponse(statusCode, statusString); + } /* else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) { |