From ba35fdd97c0eedb698a026f889092caa1c41b61b Mon Sep 17 00:00:00 2001
From: Richard Linden <none@none>
Date: Thu, 15 Apr 2010 17:22:35 -0700
Subject: EXT-6888 - Implement per-media instance volume control

---
 indra/media_plugins/webkit/CMakeLists.txt          |   2 +
 .../webkit/windows_volume_catcher.cpp              | 291 +++++++++++++++++++++
 indra/newview/llpanelprimmediacontrols.cpp         |   2 +-
 3 files changed, 294 insertions(+), 1 deletion(-)
 create mode 100644 indra/media_plugins/webkit/windows_volume_catcher.cpp

diff --git a/indra/media_plugins/webkit/CMakeLists.txt b/indra/media_plugins/webkit/CMakeLists.txt
index 4f183cddeb..40722622a4 100644
--- a/indra/media_plugins/webkit/CMakeLists.txt
+++ b/indra/media_plugins/webkit/CMakeLists.txt
@@ -59,6 +59,8 @@ elseif (DARWIN)
        ${CORESERVICES_LIBRARY}     # for Component Manager calls
        ${AUDIOUNIT_LIBRARY}        # for AudioUnit calls
        )
+elseif (WINDOWS)
+  list(APPEND media_plugin_webkit_SOURCE_FILES windows_volume_catcher.cpp)
 else (LINUX AND PULSEAUDIO)
   # All other platforms use the dummy volume catcher for now.
   list(APPEND media_plugin_webkit_SOURCE_FILES dummy_volume_catcher.cpp)
diff --git a/indra/media_plugins/webkit/windows_volume_catcher.cpp b/indra/media_plugins/webkit/windows_volume_catcher.cpp
new file mode 100644
index 0000000000..b01986db56
--- /dev/null
+++ b/indra/media_plugins/webkit/windows_volume_catcher.cpp
@@ -0,0 +1,291 @@
+/** 
+ * @file windows_volume_catcher.cpp
+ * @brief A Windows implementation of volume level control of all audio channels opened by a process.
+ *
+ * @cond
+ * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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$
+ * @endcond
+ */
+
+#include "volume_catcher.h"
+#include <windows.h>
+
+class Mixer
+{
+public:
+	static Mixer* create(U32 index);
+	~Mixer();
+
+	void setMute(bool mute);
+	void setVolume(F32 volume_left, F32 volume_right);
+
+private:
+	Mixer(HMIXER handle, U32 mute_control_id, U32 volume_control_id, U32 min_volume, U32 max_volume);
+
+	HMIXER	mHandle;
+	U32		mMuteControlID;
+	U32		mVolumeControlID;
+	U32		mMinVolume;
+	U32		mMaxVolume;
+};
+
+Mixer* Mixer::create(U32 index)
+{
+	HMIXER mixer_handle;
+	MMRESULT result = mixerOpen( &mixer_handle,
+							index,	
+							0,	// HWND to call when state changes - not used
+							0,	// user data for callback - not used
+							MIXER_OBJECTF_MIXER );
+
+	if (result == MMSYSERR_NOERROR)
+	{
+		MIXERLINE mixer_line;
+		mixer_line.cbStruct = sizeof( MIXERLINE );
+		// try speakers first
+		mixer_line.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
+
+		MMRESULT result = mixerGetLineInfo( reinterpret_cast< HMIXEROBJ >( mixer_handle ),
+								&mixer_line,
+								MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE );
+		if (result != MMSYSERR_NOERROR)
+		{
+			// failed - try headphones next
+			mixer_line.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_HEADPHONES;
+			result = mixerGetLineInfo( reinterpret_cast< HMIXEROBJ >( mixer_handle ),
+									&mixer_line,
+									MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE );
+		}
+
+		if (result == MMSYSERR_NOERROR)
+		{
+			// get control id
+			MIXERCONTROL mixer_control;
+			MIXERLINECONTROLS mixer_line_controls;
+			mixer_line_controls.cbStruct = sizeof( MIXERLINECONTROLS );
+			mixer_line_controls.dwLineID = mixer_line.dwLineID;
+			mixer_line_controls.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
+			mixer_line_controls.cControls = 1;
+			mixer_line_controls.cbmxctrl = sizeof( MIXERCONTROL );
+			mixer_line_controls.pamxctrl = &mixer_control;
+
+			result = mixerGetLineControls( reinterpret_cast< HMIXEROBJ >( mixer_handle ),
+				&mixer_line_controls,
+				MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE );
+			if (result == MMSYSERR_NOERROR )
+			{
+				// We have a mute mixer.  Remember the mute control id
+				U32 mute_control_id = mixer_control.dwControlID;
+
+				// now query for volume controls
+				mixer_line_controls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
+				result = mixerGetLineControls( reinterpret_cast< HMIXEROBJ >( mixer_handle ),
+					&mixer_line_controls,
+					MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE );
+
+				if (result == MMSYSERR_NOERROR)
+				{
+					// we have both mute and volume controls for this mixer, so remember it
+					return new Mixer(mixer_handle, 
+								mute_control_id, 
+								mixer_control.dwControlID, 
+								mixer_control.Bounds.dwMinimum, 
+								mixer_control.Bounds.dwMaximum);
+				}
+			}
+		}
+	}
+	return NULL;
+}
+
+Mixer::Mixer(HMIXER handle, U32 mute_control_id, U32 volume_control_id, U32 min_volume, U32 max_volume)
+:	mHandle(handle),
+	mMuteControlID(mute_control_id),
+	mVolumeControlID(volume_control_id),
+	mMinVolume(min_volume),
+	mMaxVolume(max_volume)
+{}
+
+Mixer::~Mixer()
+{
+}
+
+void Mixer::setMute(bool mute)
+{
+	MIXERCONTROLDETAILS_BOOLEAN mixer_control_details_bool = { mute };
+	MIXERCONTROLDETAILS mixer_control_details;
+	mixer_control_details.cbStruct = sizeof( MIXERCONTROLDETAILS );
+	mixer_control_details.dwControlID = mMuteControlID;
+	mixer_control_details.cChannels = 1;
+	mixer_control_details.cMultipleItems = 0;
+	mixer_control_details.cbDetails = sizeof( MIXERCONTROLDETAILS_BOOLEAN );
+	mixer_control_details.paDetails = &mixer_control_details_bool;
+
+	mixerSetControlDetails( reinterpret_cast< HMIXEROBJ >( mHandle ),
+								 &mixer_control_details,
+								 MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE );
+}
+
+void Mixer::setVolume(F32 volume_left, F32 volume_right)
+{
+	U32 volume_left_mixer = (U32)
+							((F32)mMinVolume 
+								+ (volume_left * ((F32)mMaxVolume - (F32)mMinVolume)));
+	U32 volume_right_mixer = (U32)
+							((F32)mMinVolume 
+								+ (volume_right * ((F32)mMaxVolume - (F32)mMinVolume)));
+	MIXERCONTROLDETAILS_UNSIGNED mixer_control_details_unsigned[ 2 ] = { volume_left_mixer, volume_right_mixer };
+	MIXERCONTROLDETAILS mixer_control_details;
+	mixer_control_details.cbStruct = sizeof( MIXERCONTROLDETAILS );
+	mixer_control_details.dwControlID = mVolumeControlID;
+	mixer_control_details.cChannels = 2;
+	mixer_control_details.cMultipleItems = 0;
+	mixer_control_details.cbDetails = sizeof( MIXERCONTROLDETAILS_UNSIGNED );
+	mixer_control_details.paDetails = &mixer_control_details_unsigned;
+
+	mixerSetControlDetails( reinterpret_cast< HMIXEROBJ >( mHandle ),
+								 &mixer_control_details,
+								 MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE );
+}
+
+
+class VolumeCatcherImpl
+{
+public:
+
+	void setVolume(F32 volume);
+	void setPan(F32 pan);
+	
+	static VolumeCatcherImpl *getInstance();
+private:
+	// This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
+	VolumeCatcherImpl();
+	~VolumeCatcherImpl();
+
+	static VolumeCatcherImpl *sInstance;
+	
+	F32 	mVolume;
+	F32 	mPan;
+	typedef std::vector<Mixer*> mixer_vector_t;
+	mixer_vector_t	mMixers;
+};
+
+VolumeCatcherImpl *VolumeCatcherImpl::sInstance = NULL;
+
+VolumeCatcherImpl *VolumeCatcherImpl::getInstance()
+{
+	if(!sInstance)
+	{
+		sInstance = new VolumeCatcherImpl;
+	}
+	
+	return sInstance;
+}
+
+VolumeCatcherImpl::VolumeCatcherImpl()
+:	mVolume(1.0f),
+	mPan(0.f) // 0 is centered
+{
+	U32 num_mixers = mixerGetNumDevs();
+	for (U32 mixer_index = 0; mixer_index < num_mixers; ++mixer_index)
+	{
+		Mixer* mixerp = Mixer::create(mixer_index);
+		if (mixerp)
+		{
+			mMixers.push_back(mixerp);
+		}
+	}
+}
+
+VolumeCatcherImpl::~VolumeCatcherImpl()
+{
+	for(mixer_vector_t::iterator it = mMixers.begin(), end_it = mMixers.end();
+		it != end_it;
+		++it)
+	{
+		delete *it;
+		*it = NULL;
+	}
+}
+
+
+void VolumeCatcherImpl::setVolume(F32 volume)
+{
+	F32 left_volume = volume * min(1.f, 1.f - mPan);
+	F32 right_volume = volume * max(0.f, 1.f + mPan);
+	
+	for(mixer_vector_t::iterator it = mMixers.begin(), end_it = mMixers.end();
+		it != end_it;
+		++it)
+	{
+		(*it)->setVolume(left_volume, right_volume);
+		
+		if (volume == 0.f && mVolume != 0.f)
+		{
+			(*it)->setMute(true);
+		}
+		else if (mVolume == 0.f && volume != 0.f)
+		{
+			(*it)->setMute(false);
+		}
+
+	}
+	mVolume = volume;
+}
+
+void VolumeCatcherImpl::setPan(F32 pan)
+{
+	mPan = pan;
+}
+
+/////////////////////////////////////////////////////
+
+VolumeCatcher::VolumeCatcher()
+{
+	pimpl = VolumeCatcherImpl::getInstance();
+}
+
+VolumeCatcher::~VolumeCatcher()
+{
+	// Let the instance persist until exit.
+}
+
+void VolumeCatcher::setVolume(F32 volume)
+{
+	pimpl->setVolume(volume);
+}
+
+void VolumeCatcher::setPan(F32 pan)
+{
+	pimpl->setPan(pan);
+}
+
+void VolumeCatcher::pump()
+{
+	// No periodic tasks are necessary for this implementation.
+}
+
diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp
index 83244edb8e..f9ca05f178 100644
--- a/indra/newview/llpanelprimmediacontrols.cpp
+++ b/indra/newview/llpanelprimmediacontrols.cpp
@@ -66,7 +66,7 @@
 #include "llwindow.h"
 #include "llfloatertools.h"  // to enable hide if build tools are up
 
-#if defined(LL_DARWIN) || (defined(LL_WINDOW) && (! defined(LL_RELEASE_FOR_DOWNLOAD)) )
+#if defined(LL_DARWIN) || (LL_WINDOWS && !LL_RELEASE_FOR_DOWNLOAD )
 #define PER_MEDIA_VOLUME
 #endif
 
-- 
cgit v1.2.3