diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index f2bed843c98e0a4b64d8f8f0f064d8fbab8c9d08..f2a2a129f851d0c18461930b4bc935698401bc57 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 d7bb64ce8a7787b39abc4bf24ebfc954571cdfdb..d3fb958638434e558102a5307666b20b079f8d18 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 4e4c0274e7752a1eab4f5bea850ca5d08de1f2e9..13bac70f2724c235cf99e0f07f8ae736f2d99deb 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 3ce32a05b0de4dd9e3094c516a679145b351968a..c94ae1fca1e183cae8d2117f3ebc3a38cbc137d6 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 dd99c6564c15aacc9ee44d6fdb4b6517708a94be..60a2392d87d6d33be554bf3ff59c084ef8ff8ec2 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 0a8ea7de39995e879e2246090aac810bc5f1f2a1..e4341175e28eded36ae6c6d4df0e7f1d7a0644cc 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 @@ class LLCallFloater : public LLTransientDockableFloater, LLVoiceClientParticipan
 	 *
 	 * 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 @@ class LLCallFloater : public LLTransientDockableFloater, LLVoiceClientParticipan
 	 *
 	 * @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 0000000000000000000000000000000000000000..4de1fffef9178caec8446f4e67d9ec3ba10be5c9
--- /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 0000000000000000000000000000000000000000..f1a746e288e6018354e51c48bc812c262e666640
--- /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 feaf7335c04085a9e6acb46fc119cee9baa57f69..0cc100e52980e1298eb3c345d7f3074440c24324 100644
--- a/indra/newview/llparticipantlist.cpp
+++ b/indra/newview/llparticipantlist.cpp
@@ -92,7 +92,7 @@ class LLAvalineUpdater : public LLVoiceClientParticipantObserver
 		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 29237946d2b7dc63525f1a2dd2664b7d26764e3c..ea7601517d95f3d25c7442e6e3ce0c87c1fcb544 100644
--- a/indra/newview/llspeakingindicatormanager.cpp
+++ b/indra/newview/llspeakingindicatormanager.cpp
@@ -107,7 +107,7 @@ class SpeakingIndicatorManager : public LLSingleton<SpeakingIndicatorManager>, L
 	 * 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 25b46f8e550a49c14aab55d2678ff21523ace30e..34a85710c1046870e3ab4e406cb89eb6fa7af17b 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 573fab1f4f645bb51fb93a5a85de36f1fcf7ef82..074f9b8bba58259f10771bc15466695f5e796c19 100644
--- a/indra/newview/llvoicechannel.h
+++ b/indra/newview/llvoicechannel.h
@@ -113,7 +113,7 @@ class LLVoiceChannel : public LLVoiceClientStatusObserver
 	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 91353281a86719ba44e1eacb84f8e8bd7d897f81..52a7de61a06e95b8d26963ac7e3b77f4c9d50a29 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 e08fed7ae9d53dc5c9fc78bc01feb4b37229eeee..44da7bcf93eb6893b5b51fd8a57c3b9229375188 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 @@ class LLVoiceModuleInterface
 	
 	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 @@ class LLVoiceModuleInterface
 	//////////////////////////
 	/// @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 @@ class LLVoiceModuleInterface
 };
 
 
+//////////////////////////////////
+/// @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 @@ class LLVoiceClient: public LLSingleton<LLVoiceClient>
 
 	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 @@ class LLVoiceClient: public LLSingleton<LLVoiceClient>
 	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 bcb1a70efbe0176bc03a2d833212c8decd3c9e71..a2ea21056d3dd09282621e47b3cb58d8eab958c1 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 59fec8b954942e6d57dd028f0918d0fb7ac4e6f1..4d18a897c7de0f32c16700e6da1ed4dcc9558404 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 @@ class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual 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 @@ class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual 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 @@ class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual public
 		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 @@ class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual public
 		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 @@ class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual public
 		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 @@ class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual public
 	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 @@ class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual public
 	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 @@ class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual public
 	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 @@ class LLVivoxProtocolParser : public LLIOPipe
 	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 @@ class LLVivoxProtocolParser : public LLIOPipe
 
 #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 5b77f11d710584e98153d8fb98be9ffbcf2a0000..216766c3a6c5745c1c0ac34bc58c5377adc283c5 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"