Automated merge with ssh://hg.lindenlab.com/dessie/viewer-public

meow-7.2.2
Aimee Linden 2010-05-13 15:17:20 +01:00
commit 5ee2fc381d
18 changed files with 939 additions and 121 deletions

View File

@ -352,6 +352,7 @@ set(viewer_SOURCE_FILES
llpanelprofileview.cpp
llpanelteleporthistory.cpp
llpaneltiptoast.cpp
llpanelvoiceeffect.cpp
llpanelvolume.cpp
llpanelvolumepulldown.cpp
llparcelselection.cpp
@ -863,6 +864,7 @@ set(viewer_HEADER_FILES
llpanelprofileview.h
llpanelteleporthistory.h
llpaneltiptoast.h
llpanelvoiceeffect.h
llpanelvolume.h
llpanelvolumepulldown.h
llparcelselection.h

View File

@ -40,6 +40,7 @@
</array>
<key>tags</key>
<array>
<string>Voice</string>
</array>
</map>
</array>

View File

@ -10595,6 +10595,28 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>VoiceFontsAvailable</key>
<map>
<key>Comment</key>
<string>Temporary debug setting to test UI with no voice effects available.</string>
<key>Persist</key>
<integer>0</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>VoiceEffectEnabled</key>
<map>
<key>Comment</key>
<string>Whether or not to use Voice Effects and show the UI.</string>
<key>Persist</key>
<integer>0</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>AutoDisengageMic</key>
<map>
<key>Comment</key>

View File

@ -99,6 +99,17 @@
<key>Value</key>
<integer>1</integer>
</map>
<key>VoiceEffectDefault</key>
<map>
<key>Comment</key>
<string>Selected voice effect</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
<string>00000000-0000-0000-0000-000000000000</string>
</map>
<!-- Settings below are for back compatibility only.
They are not used in current viewer anymore. But they can't be removed to avoid

View File

@ -95,7 +95,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)
@ -113,7 +113,7 @@ 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(this);
LLTransientFloaterMgr::getInstance()->addControlView(this);
// force docked state since this floater doesn't save it between recreations
@ -158,7 +158,6 @@ BOOL LLCallFloater::postBuild()
initAgentData();
connectToChannel(LLVoiceChannel::getCurrentVoiceChannel());
setIsChrome(true);
@ -204,7 +203,7 @@ void LLCallFloater::draw()
}
// virtual
void LLCallFloater::onChange()
void LLCallFloater::onParticipantsChanged()
{
if (NULL == mParticipants) return;
updateParticipantsVoiceState();
@ -287,22 +286,22 @@ void LLCallFloater::updateSession()
if (NULL == mSpeakerManager)
{
// by default let show nearby chat participants
// By default show nearby chat participants
mSpeakerManager = LLLocalSpeakerMgr::getInstance();
LL_DEBUGS("Voice") << "Set DEFAULT speaker manager" << LL_ENDL;
mVoiceType = VC_LOCAL_CHAT;
}
updateTitle();
//hide "Leave Call" button for nearby chat
// Hide "Leave Call" button for nearby chat
bool is_local_chat = mVoiceType == VC_LOCAL_CHAT;
childSetVisible("leave_call_btn_panel", !is_local_chat);
refreshParticipantList();
updateAgentModeratorState();
//show floater for voice calls & only in CONNECTED to voice channel state
// Show floater for voice calls & only in CONNECTED to voice channel state
if (!is_local_chat &&
voice_channel &&
LLVoiceChannel::STATE_CONNECTED == voice_channel->getState())
@ -368,7 +367,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");
@ -715,9 +714,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());
}
@ -737,7 +736,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();

View File

@ -47,15 +47,15 @@ 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
{
@ -75,7 +75,7 @@ public:
*
* Refreshes list to display participants not in voice as disabled.
*/
/*virtual*/ void onChange();
/*virtual*/ void onParticipantsChanged();
static void sOnCurrentChannelChanged(const LLUUID& session_id);
@ -259,7 +259,7 @@ private:
*
* @see sOnCurrentChannelChanged()
*/
static LLVoiceChannel* sCurrentVoiceCanel;
static LLVoiceChannel* sCurrentVoiceChannel;
/* virtual */
LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; }

View File

@ -0,0 +1,147 @@
/**
* @file llpanelvoiceeffect.cpp
* @author Aimee Walton
* @brief Panel to select Voice Effects.
*
* $LicenseInfo:firstyear=2010&license=viewergpl$
*
* Copyright (c) 2010, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llpanelvoiceeffect.h"
#include "llcombobox.h"
#include "llpanel.h"
#include "llvoicechannel.h"
#include "llvoiceclient.h"
static LLRegisterPanelClassWrapper<LLPanelVoiceEffect> t_panel_voice_effect("panel_voice_effect");
LLPanelVoiceEffect::LLPanelVoiceEffect()
: mVoiceEffectCombo(NULL)
{
mCommitCallbackRegistrar.add("Voice.CommitVoiceEffect", boost::bind(&LLPanelVoiceEffect::onCommitVoiceEffect, this));
}
LLPanelVoiceEffect::~LLPanelVoiceEffect()
{
if(LLVoiceClient::instanceExists())
{
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
if (effect_interface)
{
effect_interface->removeObserver(this);
}
}
}
// virtual
BOOL LLPanelVoiceEffect::postBuild()
{
mVoiceEffectCombo = getChild<LLComboBox>("voice_effect");
update();
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
if (effect_interface)
{
effect_interface->addObserver(this);
}
return TRUE;
}
//////////////////////////////////////////////////////////////////////////
/// PRIVATE SECTION
//////////////////////////////////////////////////////////////////////////
void LLPanelVoiceEffect::onCommitVoiceEffect()
{
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
if (!effect_interface)
{
mVoiceEffectCombo->setEnabled(false);
return;
}
LLSD value = mVoiceEffectCombo->getValue();
if (value.asInteger() == GET_VOICE_EFFECTS)
{
LL_DEBUGS("Voice") << "GET VOICE FONTS!" << LL_ENDL;
LLWeb::loadURL(getString("get_voice_effects_url"));
}
else if (value.asInteger() == PREVIEW_VOICE_EFFECTS)
{
LL_DEBUGS("Voice") << "PREVIEW VOICE FONTS!" << LL_ENDL;
}
else
{
effect_interface->setVoiceEffect(value.asUUID());
}
mVoiceEffectCombo->setValue(effect_interface->getVoiceEffect());
}
// virtual
void LLPanelVoiceEffect::onVoiceEffectChanged()
{
update();
}
void LLPanelVoiceEffect::update()
{
if (mVoiceEffectCombo)
{
LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface();
if (!effect_interface || !LLVoiceClient::instance().isVoiceWorking())
{
mVoiceEffectCombo->setEnabled(false);
return;
}
mVoiceEffectCombo->removeall();
mVoiceEffectCombo->add(getString("no_voice_effect"), NO_VOICE_EFFECT);
mVoiceEffectCombo->addSeparator();
const voice_effect_list_t& effect_list = effect_interface->getVoiceEffectList();
if (!effect_list.empty())
{
for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it)
{
mVoiceEffectCombo->add(it->first, it->second, ADD_BOTTOM);
}
mVoiceEffectCombo->addSeparator();
}
mVoiceEffectCombo->add(getString("get_voice_effects"), GET_VOICE_EFFECTS);
mVoiceEffectCombo->add(getString("preview_voice_effects"), PREVIEW_VOICE_EFFECTS);
mVoiceEffectCombo->setValue(effect_interface->getVoiceEffect());
mVoiceEffectCombo->setEnabled(true);
}
}

View File

@ -0,0 +1,73 @@
/**
* @file llpanelvoiceeffect.h
* @author Aimee Walton
* @brief Panel to select Voice Effects.
*
* $LicenseInfo:firstyear=2010&license=viewergpl$
*
* Copyright (c) 2010, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_PANELVOICEEFFECT_H
#define LL_PANELVOICEEFFECT_H
#include "llpanel.h"
#include "llvoiceclient.h"
class LLComboBox;
class LLPanelVoiceEffect
: public LLPanel
, public LLVoiceEffectObserver
{
public:
LOG_CLASS(LLPanelVoiceEffect);
LLPanelVoiceEffect();
virtual ~LLPanelVoiceEffect();
virtual BOOL postBuild();
private:
void onCommitVoiceEffect();
void update();
/// Called by voice effect provider when voice effect list is changed.
virtual void onVoiceEffectChanged();
// Fixed entries in the voice effect list
typedef enum e_voice_effect_combo_items
{
NO_VOICE_EFFECT = 0,
GET_VOICE_EFFECTS = 1,
PREVIEW_VOICE_EFFECTS = 2
} EVoiceEffectComboItems;
LLComboBox* mVoiceEffectCombo;
};
#endif //LL_PANELVOICEEFFECT_H

View File

@ -92,7 +92,7 @@ public:
mAvalineCallers.insert(avaline_caller_id);
}
void onChange()
void onParticipantsChanged()
{
uuid_set_t participant_uuids;
LLVoiceClient::getInstance()->getParticipantList(participant_uuids);

View File

@ -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;

View File

@ -891,9 +891,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));
}

View File

@ -113,7 +113,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;

View File

@ -80,9 +80,11 @@ std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserv
///////////////////////////////////////////////////////////////////////////////////////////////
LLVoiceClient::LLVoiceClient()
LLVoiceClient::LLVoiceClient() :
mVoiceModule(NULL),
mVoiceEffectEnabled(LLCachedControl<bool>(gSavedSettings, "VoiceEffectEnabled")),
mVoiceEffectDefault(LLCachedControl<std::string>(gSavedPerAccountSettings, "VoiceEffectDefault"))
{
mVoiceModule = NULL;
}
//---------------------------------------------------
@ -565,7 +567,7 @@ std::string LLVoiceClient::getDisplayName(const LLUUID& id)
}
}
bool LLVoiceClient::isVoiceWorking()
bool LLVoiceClient::isVoiceWorking() const
{
if (mVoiceModule)
{
@ -708,6 +710,10 @@ std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
}
}
LLVoiceEffectInterface* LLVoiceClient::getVoiceEffectInterface() const
{
return getVoiceEffectEnabled() ? dynamic_cast<LLVoiceEffectInterface*>(mVoiceModule) : NULL;
}
///////////////////
// version checking

View File

@ -42,6 +42,7 @@ class LLVOAvatar;
#include "llviewerregion.h"
#include "llcallingcard.h" // for LLFriendObserver
#include "llsecapi.h"
#include "llcontrol.h"
// devices
@ -52,7 +53,7 @@ class LLVoiceClientParticipantObserver
{
public:
virtual ~LLVoiceClientParticipantObserver() { }
virtual void onChange() = 0;
virtual void onParticipantsChanged() = 0;
};
@ -109,7 +110,7 @@ public:
virtual void updateSettings()=0; // call after loading settings and whenever they change
virtual bool isVoiceWorking()=0; // connected to a voice server and voice channel
virtual bool isVoiceWorking() const = 0; // connected to a voice server and voice channel
virtual const LLVoiceVersionInfo& getVersion()=0;
@ -217,8 +218,6 @@ public:
//////////////////////////
/// @name nearby speaker accessors
//@{
virtual BOOL getVoiceEnabled(const LLUUID& id)=0; // true if we've received data for this avatar
virtual std::string getDisplayName(const LLUUID& id)=0;
virtual BOOL isOnlineSIP(const LLUUID &id)=0;
@ -261,6 +260,49 @@ public:
};
//////////////////////////////////
/// @class LLVoiceEffectObserver
class LLVoiceEffectObserver
{
public:
virtual ~LLVoiceEffectObserver() { }
virtual void onVoiceEffectChanged() = 0;
};
typedef std::multimap<const std::string, const LLUUID, LLDictionaryLess> voice_effect_list_t;
//////////////////////////////////
/// @class LLVoiceEffectInterface
/// @brief Voice effect module interface
///
/// Voice effect modules should provide an implementation for this interface.
/////////////////////////////////
class LLVoiceEffectInterface
{
public:
LLVoiceEffectInterface() {}
virtual ~LLVoiceEffectInterface() {}
//////////////////////////
/// @name Accessors
//@{
virtual bool setVoiceEffect(const LLUUID& id) = 0;
virtual const LLUUID getVoiceEffect() = 0;
virtual const voice_effect_list_t &getVoiceEffectList() const = 0;
virtual const voice_effect_list_t &getVoiceEffectTemplateList() const = 0;
//@}
//////////////////////////////
/// @name Status notification
//@{
virtual void addObserver(LLVoiceEffectObserver* observer) = 0;
virtual void removeObserver(LLVoiceEffectObserver* observer) = 0;
//@}
};
class LLVoiceClient: public LLSingleton<LLVoiceClient>
{
LOG_CLASS(LLVoiceClient);
@ -281,7 +323,7 @@ public:
void updateSettings(); // call after loading settings and whenever they change
bool isVoiceWorking(); // connected to a voice server and voice channel
bool isVoiceWorking() const; // connected to a voice server and voice channel
// tuning
void tuningStart();
@ -403,10 +445,23 @@ public:
void removeObserver(LLVoiceClientParticipantObserver* observer);
std::string sipURIFromID(const LLUUID &id);
//////////////////////////
/// @name Voice effects
//@{
bool getVoiceEffectEnabled() const { return mVoiceEffectEnabled; };
LLUUID getVoiceEffectDefault() const { return LLUUID(mVoiceEffectDefault); };
// Returns NULL if voice effects are not supported, or not enabled.
LLVoiceEffectInterface* getVoiceEffectInterface() const;
//@}
protected:
LLVoiceModuleInterface* mVoiceModule;
LLPumpIO *m_servicePump;
LLCachedControl<bool> mVoiceEffectEnabled;
LLCachedControl<std::string> mVoiceEffectDefault;
};
/**

View File

@ -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),
@ -662,6 +651,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 +766,10 @@ void LLVivoxVoiceClient::stateMachine()
// Clean up and reset everything.
closeSocket();
deleteAllSessions();
deleteAllBuddies();
deleteAllBuddies();
deleteVoiceFonts();
deleteVoiceFontTemplates();
mConnectorHandle.clear();
mAccountHandle.clear();
mAccountPassword.clear();
@ -1222,6 +1215,19 @@ 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);
accountGetSessionFontsSendMessage();
}
else
{
setState(stateVoiceFontsReceived);
}
accountGetTemplateFontsSendMessage(); // *TODO: Maybe better to do this only when opening preview rather than on login
// request the current set of block rules (we'll need them when updating the friends list)
accountListBlockRulesSendMessage();
@ -1253,12 +1259,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);
@ -1316,6 +1331,7 @@ void LLVivoxVoiceClient::stateMachine()
sessionState *oldSession = mAudioSession;
mAudioSession = mNextAudioSession;
mAudioSessionChanged = true;
if(!mAudioSession->mReconnect)
{
mNextAudioSession = NULL;
@ -1547,6 +1563,8 @@ void LLVivoxVoiceClient::stateMachine()
mAccountHandle.clear();
deleteAllSessions();
deleteAllBuddies();
deleteVoiceFonts();
deleteVoiceFontTemplates();
if(mVoiceEnabled && !mRelogRequested)
{
@ -1627,16 +1645,16 @@ void LLVivoxVoiceClient::stateMachine()
}
if(mAudioSession && mAudioSession->mParticipantsChanged)
{
mAudioSession->mParticipantsChanged = false;
mAudioSessionChanged = true;
}
if(mAudioSessionChanged)
if (mAudioSessionChanged)
{
mAudioSessionChanged = false;
notifyParticipantObservers();
notifyVoiceFontObservers();
}
else if (mAudioSession && mAudioSession->mParticipantsChanged)
{
mAudioSession->mParticipantsChanged = false;
notifyParticipantObservers();
}
}
@ -1751,8 +1769,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 +1797,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 +1809,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 +1839,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 +1850,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 +1863,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 +3186,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.
@ -4134,8 +4164,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 +4660,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 +5680,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 +6110,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 +6274,269 @@ 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;
}
LLVivoxVoiceClient::voiceFontEntry::voiceFontEntry(LLUUID& id) :
mID(id),
mFontIndex(0),
mHasExpired(false),
mFontType(VOICE_FONT_TYPE_NONE),
mFontStatus(VOICE_FONT_STATUS_NONE)
{
}
LLVivoxVoiceClient::voiceFontEntry::~voiceFontEntry()
{
}
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;
// 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 = font_map.find(font_id);
bool duplicate = (iter != font_map.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") << (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 (!duplicate)
{
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;
}
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);
}
notifyVoiceFontObservers();
}
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()
{
for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin();
it != mVoiceFontObservers.end();
)
{
LLVoiceEffectObserver* observer = *it;
observer->onVoiceEffectChanged();
// In case onVoiceEffectChanged() deleted an entry.
it = mVoiceFontObservers.upper_bound(observer);
}
}
LLVivoxProtocolParser::LLVivoxProtocolParser()
{
@ -6457,7 +6755,34 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
{
LLVivoxVoiceClient::getInstance()->deleteAllAutoAcceptRules();
}
else if (!stricmp("SessionFonts", tag))
{
LLVivoxVoiceClient::getInstance()->deleteVoiceFonts();
}
else if (!stricmp("SessionFont", tag))
{
id = 0;
nameString.clear();
descriptionString.clear();
expirationDateString.clear();
hasExpired = false;
fontType = 0;
fontStatus = 0;
}
else if (!stricmp("TemplateFonts", tag))
{
LLVivoxVoiceClient::getInstance()->deleteVoiceFontTemplates();
}
else if (!stricmp("TemplateFont", tag))
{
id = 0;
nameString.clear();
descriptionString.clear();
expirationDateString.clear();
hasExpired = false;
fontType = 0;
fontStatus = 0;
}
}
}
responseDepth++;
@ -6603,7 +6928,38 @@ 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);
}
textBuffer.clear();
accumulateText= false;
@ -6861,6 +7217,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"))
{

View File

@ -57,15 +57,9 @@ class LLVivoxVoiceClientMuteListObserver;
class LLVivoxVoiceClientFriendsObserver;
class LLVivoxVoiceClientParticipantObserver
{
public:
virtual ~LLVivoxVoiceClientParticipantObserver() { }
virtual void onChange() = 0;
};
class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual public LLVoiceModuleInterface
class LLVivoxVoiceClient : public LLSingleton<LLVivoxVoiceClient>,
virtual public LLVoiceModuleInterface,
virtual public LLVoiceEffectInterface
{
LOG_CLASS(LLVivoxVoiceClient);
public:
@ -84,7 +78,7 @@ public:
virtual void updateSettings(); // call after loading settings and whenever they change
// Returns true if vivox has successfully logged in and is not in error state
virtual bool isVoiceWorking();
virtual bool isVoiceWorking() const;
/////////////////////
/// @name Tuning
@ -232,15 +226,35 @@ public:
virtual void removeObserver(LLFriendObserver* observer);
virtual void addObserver(LLVoiceClientParticipantObserver* observer);
virtual void removeObserver(LLVoiceClientParticipantObserver* observer);
//@}
virtual std::string sipURIFromID(const LLUUID &id);
//@}
/// @name LLVoiceEffectInterface virtual implementations
/// @see LLVoiceEffectInterface
//@{
//////////////////////////
/// @name Accessors
//@{
virtual bool setVoiceEffect(const LLUUID& id);
virtual const LLUUID getVoiceEffect();
virtual const voice_effect_list_t &getVoiceEffectList() const { return mVoiceFontList; };
virtual const voice_effect_list_t &getVoiceEffectTemplateList() const { return mVoiceFontTemplateList; };
//@}
//////////////////////////////
/// @name Status notification
//@{
virtual void addObserver(LLVoiceEffectObserver* observer);
virtual void removeObserver(LLVoiceEffectObserver* observer);
//@}
//@}
protected:
//////////////////////
// Vivox Specific definitions
@ -278,14 +292,13 @@ protected:
bool mIsSpeaking;
bool mIsModeratorMuted;
bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted)
bool mVolumeSet; // true if incoming volume messages should not change the volume
bool mVolumeSet; // true if incoming volume messages should not change the volume
bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed)
bool mAvatarIDValid;
bool mIsSelf;
};
typedef std::map<const std::string, participantState*> participantMap;
typedef std::map<const LLUUID, participantState*> participantUUIDMap;
struct sessionState
@ -332,14 +345,17 @@ protected:
bool mIncoming;
bool mVoiceEnabled;
bool mReconnect; // Whether we should try to reconnect to this session if it's dropped
// Set to true when the mute state of someone in the participant list changes.
// Set to true when the volume/mute state of someone in the participant list changes.
// The code will have to walk the list to find the changed participant(s).
bool mVolumeDirty;
bool mMuteDirty;
bool mMuteDirty;
bool mParticipantsChanged;
participantMap mParticipantsByURI;
participantUUIDMap mParticipantsByUUID;
LLUUID mVoiceFontID;
};
// internal state for a simple state machine. This is used to deal with the asynchronous nature of some of the messages.
@ -364,6 +380,8 @@ protected:
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
@ -591,8 +609,8 @@ protected:
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);
/////////////////////////////
// session control messages
@ -621,7 +639,21 @@ protected:
void lookupName(const LLUUID &id);
static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group);
void avatarNameResolved(const LLUUID &id, const std::string &name);
/////////////////////////////
// Voice fonts
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,
const bool template_font = false);
void accountGetSessionFontsResponse(int statusCode, const std::string &statusString);
void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString);
private:
LLVoiceVersionInfo mVoiceVersion;
@ -804,6 +836,59 @@ private:
typedef std::set<LLFriendObserver*> friend_observer_set_t;
friend_observer_set_t mFriendObservers;
void notifyFriendObservers();
// Voice Fonts
void deleteVoiceFonts();
void deleteVoiceFontTemplates();
S32 getVoiceFontIndex(const LLUUID& id) const;
void accountGetSessionFontsSendMessage();
void accountGetTemplateFontsSendMessage();
void sessionSetVoiceFontSendMessage(sessionState *session);
void notifyVoiceFontObservers();
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;
};
voice_effect_list_t mVoiceFontList;
voice_effect_list_t mVoiceFontTemplateList;
typedef std::map<const LLUUID, voiceFontEntry*> voice_font_map_t;
voice_font_map_t mVoiceFontMap;
voice_font_map_t mVoiceFontTemplateMap;
typedef std::set<LLVoiceEffectObserver*> voice_font_observer_set_t;
voice_font_observer_set_t mVoiceFontObservers;
};
/**
@ -890,7 +975,12 @@ protected:
int numberOfAliases;
std::string subscriptionHandle;
std::string subscriptionType;
S32 id;
std::string descriptionString;
std::string expirationDateString;
bool hasExpired;
S32 fontType;
S32 fontStatus;
// Members for processing text between tags
std::string textBuffer;
@ -913,5 +1003,3 @@ protected:
#endif //LL_VIVOX_VOICE_CLIENT_H

View File

@ -3,7 +3,7 @@
can_resize="true"
can_minimize="true"
can_close="false"
height="202"
height="205"
layout="topleft"
min_height="124"
min_width="190"
@ -50,7 +50,7 @@
user_resize="false"
auto_resize="false"
layout="topleft"
height="26"
height="20"
name="my_panel">
<avatar_icon
enabled="false"
@ -86,23 +86,38 @@
visible="true"
width="20" />
</layout_panel>
<layout_panel
auto_resize="false"
user_resize="false"
follows="top|left"
height="26"
visible="true"
layout="topleft"
name="leave_call_btn_panel"
width="100">
<button
follows="right|top"
height="23"
top_pad="0"
label="Leave Call"
name="leave_call_btn"
width="100" />
</layout_panel>
<layout_stack
clip="true"
auto_resize="false"
follows="left|top|right"
height="26"
layout="topleft"
mouse_opaque="false"
name="voice_effect_and_leave_call_stack"
orientation="horizontal"
width="262">
<panel
class="panel_voice_effect"
name="panel_voice_effect"
visiblity_control="VoiceEffectEnabled"
filename="panel_voice_effect.xml" />
<layout_panel
auto_resize="false"
user_resize="false"
follows="top|right"
height="23"
visible="true"
layout="topleft"
name="leave_call_btn_panel"
width="100">
<button
follows="right|top"
height="23"
label="Leave Call"
name="leave_call_btn"
width="100" />
</layout_panel>
</layout_stack>
<layout_panel
follows="all"
layout="topleft"

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
follows="all"
height="26"
layout="topleft"
name="panel_voice_effect"
width="200">
<string name="no_voice_effect">
No Voice Effect
</string>
<string name="get_voice_effects_url">
https://secondlife.com/my/account/voice.php
</string>
<string name="get_voice_effects">
Add voice effects ▶
</string>
<string name="preview_voice_effects">
Preview ▶
</string>
<combo_box
enabled="false"
follows="left|top|right"
height="23"
name="voice_effect"
top_pad="0"
width="200">
<combo_box.item
label="No Voice Effect"
name="no_voice_effect"
top_pad="0"
value="0" />
<combo_box.commit_callback
function="Voice.CommitVoiceEffect" />
</combo_box>
</panel>