diff options
| -rw-r--r-- | indra/newview/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/newview/app_settings/logcontrol.xml | 1 | ||||
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 22 | ||||
| -rw-r--r-- | indra/newview/app_settings/settings_per_account.xml | 11 | ||||
| -rw-r--r-- | indra/newview/llcallfloater.cpp | 23 | ||||
| -rw-r--r-- | indra/newview/llcallfloater.h | 18 | ||||
| -rw-r--r-- | indra/newview/llpanelvoiceeffect.cpp | 146 | ||||
| -rw-r--r-- | indra/newview/llpanelvoiceeffect.h | 73 | ||||
| -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 | 12 | ||||
| -rw-r--r-- | indra/newview/llvoiceclient.h | 67 | ||||
| -rw-r--r-- | indra/newview/llvoicevivox.cpp | 442 | ||||
| -rw-r--r-- | indra/newview/llvoicevivox.h | 138 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/floater_voice_controls.xml | 53 | 
17 files changed, 902 insertions, 120 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index f2bed843c9..f2a2a129f8 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -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 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 4e4c0274e7..13bac70f27 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -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> diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index 3ce32a05b0..c94ae1fca1 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -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 diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index dd99c6564c..60a2392d87 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -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(); diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h index 0a8ea7de39..e4341175e2 100644 --- a/indra/newview/llcallfloater.h +++ b/indra/newview/llcallfloater.h @@ -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; } diff --git a/indra/newview/llpanelvoiceeffect.cpp b/indra/newview/llpanelvoiceeffect.cpp new file mode 100644 index 0000000000..4de1fffef9 --- /dev/null +++ b/indra/newview/llpanelvoiceeffect.cpp @@ -0,0 +1,146 @@ +/**  + * @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; +	} +	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); +	} +} diff --git a/indra/newview/llpanelvoiceeffect.h b/indra/newview/llpanelvoiceeffect.h new file mode 100644 index 0000000000..f1a746e288 --- /dev/null +++ b/indra/newview/llpanelvoiceeffect.h @@ -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 diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index feaf7335c0..0cc100e529 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()->getParticipantList(participant_uuids); diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp index 29237946d2..ea7601517d 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 25b46f8e55..34a85710c1 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -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));  	} diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 573fab1f4f..074f9b8bba 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -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; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 91353281a8..52a7de61a0 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -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 diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index e08fed7ae9..44da7bcf93 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -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;  };  /** diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index bcb1a70efb..a2ea21056d 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), @@ -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,15 +1645,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 +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"))  		 { diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 59fec8b954..4d18a897c7 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -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 - - 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..216766c3a6 100644 --- a/indra/newview/skins/default/xui/en/floater_voice_controls.xml +++ b/indra/newview/skins/default/xui/en/floater_voice_controls.xml @@ -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"  | 
