diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index f56be4004a4a30da71e7bc420e2d738965ccd025..69927e5e0e940fdc9329f3135216eee26f46a280 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -386,6 +386,7 @@ set(viewer_SOURCE_FILES
     llspatialpartition.cpp
     llspeakbutton.cpp
     llspeakers.cpp
+    llspeakingindicatormanager.cpp
     llsplitbutton.cpp
     llsprite.cpp
     llstartup.cpp
@@ -890,6 +891,7 @@ set(viewer_HEADER_FILES
     llspatialpartition.h
     llspeakbutton.h
     llspeakers.h
+    llspeakingindicatormanager.h
     llsplitbutton.h
     llsprite.h
     llstartup.h
diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp
index a2e17db3d8ad451f01c145ecc555c7593f70e717..0a0ad81165b947a40e32b51fd797fd0866e8d6b0 100644
--- a/indra/newview/llavatarlistitem.cpp
+++ b/indra/newview/llavatarlistitem.cpp
@@ -127,6 +127,15 @@ BOOL  LLAvatarListItem::postBuild()
 	return TRUE;
 }
 
+S32 LLAvatarListItem::notifyParent(const LLSD& info)
+{
+	if (info.has("visibility_changed"))
+	{
+		updateChildren();
+	}
+	return 0;
+}
+
 void LLAvatarListItem::onMouseEnter(S32 x, S32 y, MASK mask)
 {
 	childSetVisible("hovered_icon", true);
@@ -246,8 +255,10 @@ void LLAvatarListItem::showSpeakingIndicator(bool visible)
 	// Already done? Then do nothing.
 	if (mSpeakingIndicator->getVisible() == (BOOL)visible)
 		return;
-	mSpeakingIndicator->setVisible(visible);
-	updateChildren();
+// Disabled to not contradict with SpeakingIndicatorManager functionality. EXT-3976
+// probably this method should be totally removed.
+//	mSpeakingIndicator->setVisible(visible);
+//	updateChildren();
 }
 
 void LLAvatarListItem::setAvatarIconVisible(bool visible)
diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h
index 868ee546d44be124d6eb2f524c23f58181b9beff..f76ffb391d455a9bd1f3c2bfbabe026976dacfce 100644
--- a/indra/newview/llavatarlistitem.h
+++ b/indra/newview/llavatarlistitem.h
@@ -74,6 +74,11 @@ class LLAvatarListItem : public LLPanel, public LLFriendObserver
 	virtual ~LLAvatarListItem();
 
 	virtual BOOL postBuild();
+
+	/**
+	 * Processes notification from speaker indicator to update children when indicator's visibility is changed.
+	 */
+	virtual S32	notifyParent(const LLSD& info);
 	virtual void onMouseLeave(S32 x, S32 y, MASK mask);
 	virtual void onMouseEnter(S32 x, S32 y, MASK mask);
 	virtual void setValue(const LLSD& value);
diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp
index 63803469dd1519e25dcfa9b686fc1eea81d5801e..89b0105ae9e771c0c6db120e217f5d6067cc316d 100644
--- a/indra/newview/lloutputmonitorctrl.cpp
+++ b/indra/newview/lloutputmonitorctrl.cpp
@@ -77,7 +77,9 @@ LLOutputMonitorCtrl::LLOutputMonitorCtrl(const LLOutputMonitorCtrl::Params& p)
 	mImageLevel3(p.image_level_3),
 	mAutoUpdate(p.auto_update),
 	mSpeakerId(p.speaker_id),
-	mIsAgentControl(false)
+	mIsAgentControl(false),
+	mIsSwitchDirty(false),
+	mShouldSwitchOn(false)
 {
 	//static LLUIColor output_monitor_muted_color = LLUIColorTable::instance().getColor("OutputMonitorMutedColor", LLColor4::orange);
 	//static LLUIColor output_monitor_overdriven_color = LLUIColorTable::instance().getColor("OutputMonitorOverdrivenColor", LLColor4::red);
@@ -108,6 +110,7 @@ LLOutputMonitorCtrl::LLOutputMonitorCtrl(const LLOutputMonitorCtrl::Params& p)
 LLOutputMonitorCtrl::~LLOutputMonitorCtrl()
 {
 	LLMuteList::getInstance()->removeObserver(this);
+	LLSpeakingIndicatorManager::unregisterSpeakingIndicator(mSpeakerId, this);
 }
 
 void LLOutputMonitorCtrl::setPower(F32 val)
@@ -117,6 +120,26 @@ void LLOutputMonitorCtrl::setPower(F32 val)
 
 void LLOutputMonitorCtrl::draw()
 {
+	// see also switchIndicator()
+	if (mIsSwitchDirty)
+	{
+		mIsSwitchDirty = false;
+		if (mShouldSwitchOn)
+		{
+			// just notify parent visibility may have changed
+			notifyParentVisibilityChanged();
+		}
+		else
+		{
+			// make itself invisible and notify parent about this
+			setVisible(FALSE);
+			notifyParentVisibilityChanged();
+
+			// no needs to render for invisible element
+			return;
+		}
+	}
+
 	// Copied from llmediaremotectrl.cpp
 	// *TODO: Give the LLOutputMonitorCtrl an agent-id to monitor, then
 	// call directly into gVoiceClient to ask if that agent-id is muted, is
@@ -229,6 +252,7 @@ void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id)
 	if (speaker_id.isNull() || speaker_id == mSpeakerId) return;
 
 	mSpeakerId = speaker_id;
+	LLSpeakingIndicatorManager::registerSpeakingIndicator(mSpeakerId, this);
 
 	//mute management
 	if (mAutoUpdate)
@@ -251,3 +275,51 @@ void LLOutputMonitorCtrl::onChange()
 	// check only blocking on voice. EXT-3542
 	setIsMuted(LLMuteList::getInstance()->isMuted(mSpeakerId, LLMute::flagVoiceChat));
 }
+
+// virtual
+void LLOutputMonitorCtrl::switchIndicator(bool switch_on)
+{
+	if (switch_on)
+	{
+		setVisible((BOOL)switch_on);
+		if (isInVisibleChain())
+		{
+			notifyParentVisibilityChanged();
+		}
+		else
+		{
+			LL_DEBUGS("SpeakingIndicator") << "Indicator is not in visible chain, parent won't be notified: " << mSpeakerId << LL_ENDL;
+			mIsSwitchDirty = true;
+			mShouldSwitchOn = true;
+		}
+	}
+	else
+	{
+		if (isInVisibleChain())
+		{
+			LL_DEBUGS("SpeakingIndicator") << "Indicator is in visible chain, notifying parent: " << mSpeakerId << LL_ENDL;
+			setVisible((BOOL)switch_on);
+			notifyParentVisibilityChanged();
+		}
+		else
+		{
+			LL_DEBUGS("SpeakingIndicator") << "Indicator is not in visible chain, parent won't be notified: " << mSpeakerId << LL_ENDL;
+			mIsSwitchDirty = true;
+			mShouldSwitchOn = false;
+		}
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////
+// PRIVATE SECTION
+//////////////////////////////////////////////////////////////////////////
+void LLOutputMonitorCtrl::notifyParentVisibilityChanged()
+{
+	LL_DEBUGS("SpeakingIndicator") << "Notify parent that visibility was changed: " << mSpeakerId << " ,new_visibility: " << getVisible() << LL_ENDL;
+
+	LLSD params = LLSD().with("visibility_changed", getVisible());
+
+	notifyParent(params);
+}
+
+// EOF
diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h
index 85ea552a572b049fbf796b0f914fcabb9481f7d5..2bbfa251e9d8e2624b3e1bd1881ce951b0868495 100644
--- a/indra/newview/lloutputmonitorctrl.h
+++ b/indra/newview/lloutputmonitorctrl.h
@@ -36,6 +36,7 @@
 #include "v4color.h"
 #include "llview.h"
 #include "llmutelist.h"
+#include "llspeakingindicatormanager.h"
 
 class LLTextBox;
 class LLUICtrlFactory;
@@ -45,7 +46,7 @@ class LLUICtrlFactory;
 //
 
 class LLOutputMonitorCtrl
-: public LLView, LLMuteListObserver
+: public LLView, public LLSpeakingIndicator, LLMuteListObserver
 {
 public:
 	struct Params : public LLInitParam::Block<Params, LLView::Params>
@@ -90,7 +91,29 @@ class LLOutputMonitorCtrl
 	//called by mute list
 	virtual void onChange();
 
+	/**
+	 * Implementation of LLSpeakingIndicator interface.
+	 * Behavior is implemented via changing visibility.
+	 *
+	 * If instance is in visible chain now (all parents are visible) it changes visibility 
+	 * and notify parent about this.
+	 *
+	 * Otherwise it marks an instance as dirty and stores necessary visibility.
+	 * It will be applied in next draw and parent will be notified.
+	 */
+	virtual void	switchIndicator(bool switch_on);
+
 private:
+
+	/**
+	 * Notifies parent about changed visibility.
+	 *
+	 * Passes LLSD with "visibility_changed" => <current visibility> value.
+	 * For now it is processed by LLAvatarListItem to update (reshape) its children.
+	 * Implemented fo complete EXT-3976
+	 */
+	void			notifyParentVisibilityChanged();
+
 	//static LLColor4	sColorMuted;
 	//static LLColor4	sColorNormal;
 	//static LLColor4	sColorOverdriven;
@@ -117,6 +140,10 @@ class LLOutputMonitorCtrl
 
 	/** uuid of a speaker being monitored */
 	LLUUID			mSpeakerId;
+
+	/** indicates if the instance is dirty and should notify parent */
+	bool			mIsSwitchDirty;
+	bool			mShouldSwitchOn;
 };
 
 #endif
diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1c8fc6ac9dd48c2f2d408ac71410375d345b0759
--- /dev/null
+++ b/indra/newview/llspeakingindicatormanager.cpp
@@ -0,0 +1,289 @@
+/** 
+ * @file llspeakingindicatormanager.cpp
+ * @author Mike Antipov
+ * @brief Implementation of SpeackerIndicatorManager class to process registered LLSpeackerIndicator
+ * depend on avatars are in the same voice channel.
+ *
+ * $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 "llspeakingindicatormanager.h"
+
+
+#include "llagentdata.h"
+#include "llvoicechannel.h"
+#include "llvoiceclient.h"
+
+/**
+ * This class intended to control visibility of avatar speaking indicators depend on whether avatars
+ * are in the same voice channel.
+ *
+ * Speaking indicator should be visible for avatars in the same voice channel. See EXT-3976.
+ *
+ * It stores passed instances of LLOutputMonitorCtrl in a multimap by avatar LLUUID.
+ * It observes changing of voice channel and changing of participant list in voice channel.
+ * When voice channel or voice participant list is changed it updates visibility of an appropriate 
+ * speaking indicator.
+ *
+ * Several indicators can be registered for the same avatar.
+ */
+class SpeakingIndicatorManager : public LLSingleton<SpeakingIndicatorManager>, LLVoiceClientParticipantObserver
+{
+	LOG_CLASS(SpeakingIndicatorManager);
+public:
+
+	/**
+	 * Stores passed speaking indicator to control its visibility.
+	 *
+	 * Registered indicator is set visible if an appropriate avatar is in the same voice channel with Agent.
+	 * It ignores instances of Agent's indicator.
+	 *
+	 * @param speaker_id LLUUID of an avatar whose speaking indicator is registered.
+	 * @param speaking_indicator instance of the speaking indicator to be registered.
+	 */
+	void registerSpeakingIndicator(const LLUUID& speaker_id, LLSpeakingIndicator* const speaking_indicator);
+
+	/**
+	 * Removes passed speaking indicator from observing.
+	 *
+	 * @param speaker_id LLUUID of an avatar whose speaking indicator should be unregistered.
+	 * @param speaking_indicator instance of the speaking indicator to be unregistered.
+	 */
+	void unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator);
+
+private:
+	typedef std::vector<LLUUID> speaker_ids_t;
+	typedef std::multimap<LLUUID, LLSpeakingIndicator*> speaking_indicators_mmap_t;
+	typedef speaking_indicators_mmap_t::value_type speaking_indicator_value_t;
+	typedef speaking_indicators_mmap_t::const_iterator indicator_const_iterator;
+	typedef std::pair<indicator_const_iterator, indicator_const_iterator> indicator_range_t;
+
+	friend class LLSingleton<SpeakingIndicatorManager>;
+	SpeakingIndicatorManager();
+	~SpeakingIndicatorManager();
+
+	/**
+	 * Callback to determine when voice channel is changed.
+	 *
+	 * It switches all registered speaking indicators off.
+	 * To reduce overheads only switched on indicators are processed.
+	 */
+	void sOnCurrentChannelChanged(const LLUUID& session_id);
+
+	/**
+	 * Callback of changing voice participant list (from LLVoiceClientParticipantObserver).
+	 *
+	 * Switches off indicators had been switched on and switches on indicators of current participants list.
+	 * There is only a few indicators in lists should be switched off/on.
+	 * 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();
+
+	/**
+	 * Changes state of indicators specified by LLUUIDs
+	 *
+	 * @param speakers_uuids - avatars' LLUUIDs whose speaking indicators should be switched
+	 * @param switch_on - if TRUE specified indicator will be switched on, off otherwise.
+	 */
+	void switchSpeakerIndicators(const speaker_ids_t& speakers_uuids, BOOL switch_on);
+
+	/**
+	 * Multimap with all registered speaking indicators
+	 */
+	speaking_indicators_mmap_t mSpeakingIndicators;
+
+	/**
+	 * LUUIDs of avatar for which we have speaking indicators switched on.
+	 *
+	 * Is used to switch off all previously ON indicators when voice participant list is changed.
+	 *
+	 * @see onChange()
+	 */
+	speaker_ids_t mSwitchedIndicatorsOn;
+};
+
+//////////////////////////////////////////////////////////////////////////
+// PUBLIC SECTION
+//////////////////////////////////////////////////////////////////////////
+void SpeakingIndicatorManager::registerSpeakingIndicator(const LLUUID& speaker_id, LLSpeakingIndicator* const speaking_indicator)
+{
+	if (speaker_id == gAgentID) return;
+
+	LL_DEBUGS("SpeakingIndicator") << "Registering indicator: " << speaker_id << LL_ENDL;
+	speaking_indicator_value_t value_type(speaker_id, speaking_indicator);
+	mSpeakingIndicators.insert(value_type);
+
+	BOOL is_in_same_voice = FALSE;
+
+	// search passed speaker id among a list of voice participants 
+	LLVoiceClient::participantMap *voice_map = LLVoiceClient::getInstance()->getParticipantList();
+	if (voice_map)
+	{
+		for (LLVoiceClient::participantMap::const_iterator iter = voice_map->begin();
+			iter != voice_map->end(); ++iter)
+		{
+			const LLUUID id = (*iter).second->mAvatarID;
+
+			if (speaker_id == id)
+			{
+				is_in_same_voice = TRUE;
+				break;
+			}
+
+		}
+	}
+
+	speaker_ids_t speakers_uuids;
+	speakers_uuids.push_back(speaker_id);
+	switchSpeakerIndicators(speakers_uuids, is_in_same_voice);
+}
+
+void SpeakingIndicatorManager::unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator)
+{
+	speaking_indicators_mmap_t::iterator it;
+	it = mSpeakingIndicators.find(speaker_id);
+	for (;it != mSpeakingIndicators.end(); ++it)
+	{
+		if (it->second == speaking_indicator)
+		{
+			mSpeakingIndicators.erase(it);
+			break;
+		}
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////
+// PRIVATE SECTION
+//////////////////////////////////////////////////////////////////////////
+SpeakingIndicatorManager::SpeakingIndicatorManager()
+{
+	LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&SpeakingIndicatorManager::sOnCurrentChannelChanged, this, _1));
+	LLVoiceClient::getInstance()->addObserver(this);
+}
+
+SpeakingIndicatorManager::~SpeakingIndicatorManager()
+{
+	// Don't use LLVoiceClient::getInstance() here without check
+	// singleton MAY have already been destroyed.
+	if(LLVoiceClient::instanceExists())
+	{
+		LLVoiceClient::getInstance()->removeObserver(this);
+	}
+}
+
+void SpeakingIndicatorManager::sOnCurrentChannelChanged(const LLUUID& /*session_id*/)
+{
+	switchSpeakerIndicators(mSwitchedIndicatorsOn, FALSE);
+	mSwitchedIndicatorsOn.clear();
+}
+
+void SpeakingIndicatorManager::onChange()
+{
+	LL_DEBUGS("SpeakingIndicator") << "Voice participant list was changed, updating indicators" << LL_ENDL;
+
+	speaker_ids_t speakers_uuids;
+	// Get a list of participants from VoiceClient
+	LLVoiceClient::participantMap *voice_map = gVoiceClient->getParticipantList();
+	if (voice_map)
+	{
+		for (LLVoiceClient::participantMap::const_iterator iter = voice_map->begin();
+			iter != voice_map->end(); ++iter)
+		{
+			LLUUID id = (*iter).second->mAvatarID;
+
+			if (id == gAgentID) continue;
+
+			speakers_uuids.push_back(id);
+		}
+	}
+
+	LL_DEBUGS("SpeakingIndicator") << "Switching all OFF, count: " << mSwitchedIndicatorsOn.size() << LL_ENDL;
+
+	// switch all indicators off
+	switchSpeakerIndicators(mSwitchedIndicatorsOn, FALSE);
+	mSwitchedIndicatorsOn.clear();
+
+	LL_DEBUGS("SpeakingIndicator") << "Switching all ON, count: " << speakers_uuids.size() << LL_ENDL;
+	// then switch current voice participants indicators on
+	switchSpeakerIndicators(speakers_uuids, TRUE);
+}
+
+void SpeakingIndicatorManager::switchSpeakerIndicators(const speaker_ids_t& speakers_uuids, BOOL switch_on)
+{
+	speaker_ids_t::const_iterator it_uuid = speakers_uuids.begin(); 
+	for (; it_uuid != speakers_uuids.end(); ++it_uuid)
+	{
+		LL_DEBUGS("SpeakingIndicator") << "Looking for indicator: " << *it_uuid << LL_ENDL;
+		indicator_range_t it_range = mSpeakingIndicators.equal_range(*it_uuid);
+		indicator_const_iterator it_indicator = it_range.first;
+		bool was_found = false;
+		for (; it_indicator != it_range.second; ++it_indicator)
+		{
+			was_found = true;
+			LLSpeakingIndicator* indicator = (*it_indicator).second;
+			indicator->switchIndicator(switch_on);
+		}
+
+		if (was_found)
+		{
+			LL_DEBUGS("SpeakingIndicator") << mSpeakingIndicators.count(*it_uuid) << " indicators where found" << LL_ENDL;
+
+			if (switch_on)
+			{
+				// TODO: probably std::set will be better
+				//		if (mSwitchedIndicatorsOn.find(*it_uuid) == mSwitchedIndicatorsOn.end())
+				{
+					mSwitchedIndicatorsOn.push_back(*it_uuid);
+				}
+			}
+		}
+		else
+		{
+			LL_WARNS("SpeakingIndicator") << "indicator was not found among registered: " << *it_uuid << LL_ENDL;
+		}
+	}
+}
+
+/************************************************************************/
+/*         LLSpeakingIndicatorManager namespace implementation          */
+/************************************************************************/
+
+void LLSpeakingIndicatorManager::registerSpeakingIndicator(const LLUUID& speaker_id, LLSpeakingIndicator* const speaking_indicator)
+{
+	SpeakingIndicatorManager::instance().registerSpeakingIndicator(speaker_id, speaking_indicator);
+}
+
+void LLSpeakingIndicatorManager::unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator)
+{
+	SpeakingIndicatorManager::instance().unregisterSpeakingIndicator(speaker_id, speaking_indicator);
+}
+
+// EOF
+
diff --git a/indra/newview/llspeakingindicatormanager.h b/indra/newview/llspeakingindicatormanager.h
new file mode 100644
index 0000000000000000000000000000000000000000..f32c70bd73a6843cc0f76f1efd9fce77cf90e20b
--- /dev/null
+++ b/indra/newview/llspeakingindicatormanager.h
@@ -0,0 +1,67 @@
+/** 
+ * @file llspeakingindicatormanager.h
+ * @author Mike Antipov
+ * @brief Interfeace of LLSpeackerIndicator class to be processed depend on avatars are in the same voice channel.
+ * Also register/unregister methods for this class are declared
+ *
+ * $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_LLSPEAKINGINDICATORMANAGER_H
+#define LL_LLSPEAKINGINDICATORMANAGER_H
+
+class LLSpeakingIndicator
+{
+public:
+	virtual void switchIndicator(bool switch_on) = 0;
+};
+
+// See EXT-3976.
+namespace LLSpeakingIndicatorManager
+{
+	/**
+	 * Stores passed speaking indicator to control its visibility.
+	 *
+	 * Registered indicator is set visible if an appropriate avatar is in the same voice channel with Agent.
+	 * It ignores instances of Agent's indicator.
+	 *
+	 * @param speaker_id LLUUID of an avatar whose speaker indicator is registered.
+	 * @param speaking_indicator instance of the speaker indicator to be registered.
+	 */
+	void registerSpeakingIndicator(const LLUUID& speaker_id, LLSpeakingIndicator* const speaking_indicator);
+
+	/**
+	 * Removes passed speaking indicator from observing.
+	 *
+	 * @param speaker_id LLUUID of an avatar whose speaker indicator should be unregistered.
+	 * @param speaking_indicator instance of the speaker indicator to be unregistered.
+	 */
+	void unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator);
+}
+
+#endif // LL_LLSPEAKINGINDICATORMANAGER_H