diff --git a/indra/media_plugins/webkit/CMakeLists.txt b/indra/media_plugins/webkit/CMakeLists.txt
index 9f66a77c6426b7b555a58d65b85e798d4fab7e2e..c3a3f8e2b22ad96e579c26a59f24511058746c65 100644
--- a/indra/media_plugins/webkit/CMakeLists.txt
+++ b/indra/media_plugins/webkit/CMakeLists.txt
@@ -45,12 +45,16 @@ set(media_plugin_webkit_LINK_LIBRARIES
   ${PULSEAUDIO_LIBRARIES}
 )
 
-if (LINUX)
+# Select which VolumeCatcher implementation to use
+if (LINUX AND PULSEAUDIO)
   list(APPEND media_plugin_webkit_SOURCE_FILES linux_volume_catcher.cpp)
   list(APPEND media_plugin_webkit_LINK_LIBRARIES
        ${UI_LIBRARIES}     # for glib/GTK
        )
-endif (LINUX)
+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)
+endif (LINUX AND PULSEAUDIO)
 
 add_library(media_plugin_webkit
     SHARED
diff --git a/indra/media_plugins/webkit/dummy_volume_catcher.cpp b/indra/media_plugins/webkit/dummy_volume_catcher.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..45b2c62eba6e226ea06d83a830df963fcf91c35c
--- /dev/null
+++ b/indra/media_plugins/webkit/dummy_volume_catcher.cpp
@@ -0,0 +1,63 @@
+/** 
+ * @file dummy_volume_catcher.cpp
+ * @brief A null implementation of the "VolumeCatcher" class for platforms where it's not implemented yet.
+ *
+ * @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"
+
+
+class VolumeCatcherImpl
+{
+};
+
+/////////////////////////////////////////////////////
+
+VolumeCatcher::VolumeCatcher()
+{
+	pimpl = NULL;
+}
+
+VolumeCatcher::~VolumeCatcher()
+{
+}
+
+void VolumeCatcher::setVolume(F32 volume)
+{
+}
+
+void VolumeCatcher::setPan(F32 pan)
+{
+}
+
+void VolumeCatcher::pump()
+{
+}
+
diff --git a/indra/media_plugins/webkit/linux_volume_catcher.cpp b/indra/media_plugins/webkit/linux_volume_catcher.cpp
index 52ab766f7f1a2016c45773487fcd89469c731c57..2e7fda865e78f7d26e387b71dac309a9f33c37c5 100644
--- a/indra/media_plugins/webkit/linux_volume_catcher.cpp
+++ b/indra/media_plugins/webkit/linux_volume_catcher.cpp
@@ -42,11 +42,9 @@
 
 #include "linden_common.h"
 
-#include "linux_volume_catcher.h"
+#include "volume_catcher.h"
 
 
-#if LL_PULSEAUDIO_ENABLED
-
 extern "C" {
 #include <glib.h>
 
@@ -161,11 +159,11 @@ extern "C" {
 }
 
 
-class LinuxVolumeCatcherImpl
+class VolumeCatcherImpl
 {
 public:
-	LinuxVolumeCatcherImpl();
-	~LinuxVolumeCatcherImpl();
+	VolumeCatcherImpl();
+	~VolumeCatcherImpl();
 
 	void setVolume(F32 volume);
 	void pump(void);
@@ -189,7 +187,7 @@ class LinuxVolumeCatcherImpl
 	bool mGotSyms;
 };
 
-LinuxVolumeCatcherImpl::LinuxVolumeCatcherImpl()
+VolumeCatcherImpl::VolumeCatcherImpl()
 	: mDesiredVolume(0.0f),
 	  mMainloop(NULL),
 	  mPAContext(NULL),
@@ -199,17 +197,17 @@ LinuxVolumeCatcherImpl::LinuxVolumeCatcherImpl()
 	init();
 }
 
-LinuxVolumeCatcherImpl::~LinuxVolumeCatcherImpl()
+VolumeCatcherImpl::~VolumeCatcherImpl()
 {
 	cleanup();
 }
 
-bool LinuxVolumeCatcherImpl::loadsyms(std::string pulse_dso_name)
+bool VolumeCatcherImpl::loadsyms(std::string pulse_dso_name)
 {
 	return grab_pa_syms(pulse_dso_name);
 }
 
-void LinuxVolumeCatcherImpl::init()
+void VolumeCatcherImpl::init()
 {
 	// try to be as defensive as possible because PA's interface is a
 	// bit fragile and (for our purposes) we'd rather simply not function
@@ -262,7 +260,7 @@ void LinuxVolumeCatcherImpl::init()
 	}
 }
 
-void LinuxVolumeCatcherImpl::cleanup()
+void VolumeCatcherImpl::cleanup()
 {
 	mConnected = false;
 
@@ -280,7 +278,7 @@ void LinuxVolumeCatcherImpl::cleanup()
 	mMainloop = NULL;
 }
 
-void LinuxVolumeCatcherImpl::setVolume(F32 volume)
+void VolumeCatcherImpl::setVolume(F32 volume)
 {
 	mDesiredVolume = volume;
 	
@@ -294,13 +292,13 @@ void LinuxVolumeCatcherImpl::setVolume(F32 volume)
 	pump();
 }
 
-void LinuxVolumeCatcherImpl::pump()
+void VolumeCatcherImpl::pump()
 {
 	gboolean may_block = FALSE;
 	g_main_context_iteration(g_main_context_default(), may_block);
 }
 
-void LinuxVolumeCatcherImpl::connected_okay()
+void VolumeCatcherImpl::connected_okay()
 {
 	pa_operation *op;
 
@@ -324,7 +322,7 @@ void LinuxVolumeCatcherImpl::connected_okay()
 	}
 }
 
-void LinuxVolumeCatcherImpl::update_all_volumes(F32 volume)
+void VolumeCatcherImpl::update_all_volumes(F32 volume)
 {
 	for (std::set<U32>::iterator it = mSinkInputIndices.begin();
 	     it != mSinkInputIndices.end(); ++it)
@@ -333,7 +331,7 @@ void LinuxVolumeCatcherImpl::update_all_volumes(F32 volume)
 	}
 }
 
-void LinuxVolumeCatcherImpl::update_index_volume(U32 index, F32 volume)
+void VolumeCatcherImpl::update_index_volume(U32 index, F32 volume)
 {
 	static pa_cvolume cvol;
 	llpa_cvolume_set(&cvol, mSinkInputNumChannels[index],
@@ -355,7 +353,7 @@ void LinuxVolumeCatcherImpl::update_index_volume(U32 index, F32 volume)
 
 void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info *sii, int eol, void *userdata)
 {
-	LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)userdata);
+	VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata);
 	llassert(impl);
 
 	if (0 == eol)
@@ -386,7 +384,7 @@ void callback_discovered_sinkinput(pa_context *context, const pa_sink_input_info
 
 void callback_subscription_alert(pa_context *context, pa_subscription_event_type_t t, uint32_t index, void *userdata)
 {
-	LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)userdata);
+	VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata);
 	llassert(impl);
 
 	switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
@@ -420,7 +418,7 @@ void callback_subscription_alert(pa_context *context, pa_subscription_event_type
 
 void callback_context_state(pa_context *context, void *userdata)
 {
-	LinuxVolumeCatcherImpl *impl = dynamic_cast<LinuxVolumeCatcherImpl*>((LinuxVolumeCatcherImpl*)userdata);
+	VolumeCatcherImpl *impl = dynamic_cast<VolumeCatcherImpl*>((VolumeCatcherImpl*)userdata);
 	llassert(impl);
 	
 	switch (llpa_context_get_state(context))
@@ -441,48 +439,30 @@ void callback_context_state(pa_context *context, void *userdata)
 
 /////////////////////////////////////////////////////
 
-LinuxVolumeCatcher::LinuxVolumeCatcher()
+VolumeCatcher::VolumeCatcher()
 {
-	pimpl = new LinuxVolumeCatcherImpl();
+	pimpl = new VolumeCatcherImpl();
 }
 
-LinuxVolumeCatcher::~LinuxVolumeCatcher()
+VolumeCatcher::~VolumeCatcher()
 {
 	delete pimpl;
 	pimpl = NULL;
 }
 
-void LinuxVolumeCatcher::setVolume(F32 volume)
+void VolumeCatcher::setVolume(F32 volume)
 {
 	llassert(pimpl);
 	pimpl->setVolume(volume);
 }
 
-void LinuxVolumeCatcher::pump()
-{
-	llassert(pimpl);
-	pimpl->pump();
-}
-
-#else // !LL_PULSEAUDIO_ENABLED
-
-// stub.
-
-LinuxVolumeCatcher::LinuxVolumeCatcher()
-{
-	pimpl = NULL;
-}
-
-LinuxVolumeCatcher::~LinuxVolumeCatcher()
+void VolumeCatcher::setPan(F32 pan)
 {
+	// TODO: implement this (if possible)
 }
 
-void LinuxVolumeCatcher::setVolume(F32 volume)
-{
-}
-
-void LinuxVolumeCatcher::pump()
+void VolumeCatcher::pump()
 {
+	llassert(pimpl);
+	pimpl->pump();
 }
-
-#endif // LL_PULSEAUDIO_ENABLED
diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp
index 436e077e9b65c695035a334bf3c104859485d8de..47766a24cbd0f97ae071de530309a53bed7561b7 100644
--- a/indra/media_plugins/webkit/media_plugin_webkit.cpp
+++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp
@@ -50,9 +50,7 @@
 # define LL_QTWEBKIT_USES_PIXMAPS 0
 #endif // LL_LINUX
 
-#if LL_LINUX
-# include "linux_volume_catcher.h"
-#endif // LL_LINUX
+# include "volume_catcher.h"
 
 #if LL_WINDOWS
 # include <direct.h>
@@ -119,9 +117,7 @@ class MediaPluginWebKit :
 	F32 mBackgroundG;
 	F32 mBackgroundB;
 	
-#if LL_LINUX
-	LinuxVolumeCatcher mLinuxVolumeCatcher;
-#endif // LL_LINUX
+	VolumeCatcher mVolumeCatcher;
 
 	void setInitState(int state)
 	{
@@ -135,9 +131,7 @@ class MediaPluginWebKit :
 	{
 		LLQtWebKit::getInstance()->pump( milliseconds );
 		
-#if LL_LINUX
-		mLinuxVolumeCatcher.pump();
-#endif // LL_LINUX
+		mVolumeCatcher.pump();
 
 		checkEditState();
 		
@@ -1139,9 +1133,7 @@ void MediaPluginWebKit::receiveMessage(const char *message_string)
 
 void MediaPluginWebKit::setVolume(F32 volume)
 {
-#if LL_LINUX
-	mLinuxVolumeCatcher.setVolume(volume);
-#endif // LL_LINUX
+	mVolumeCatcher.setVolume(volume);
 }
 
 int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data)
diff --git a/indra/media_plugins/webkit/volume_catcher.h b/indra/media_plugins/webkit/volume_catcher.h
new file mode 100644
index 0000000000000000000000000000000000000000..77b10cfed0a490bcfd3a08ddf279c0f13ab9721d
--- /dev/null
+++ b/indra/media_plugins/webkit/volume_catcher.h
@@ -0,0 +1,59 @@
+/** 
+ * @file volume_catcher.h
+ * @brief Interface to a class with platform-specific implementations that allows control of the audio volume of all sources in the current 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
+ */
+
+#ifndef VOLUME_CATCHER_H
+#define VOLUME_CATCHER_H
+
+#include "linden_common.h"
+
+class VolumeCatcherImpl;
+
+class VolumeCatcher
+{
+ public:
+	VolumeCatcher();
+	~VolumeCatcher();
+
+	void setVolume(F32 volume); // 0.0 - 1.0
+	
+	// Set the left-right pan of audio sources
+	// where -1.0 = left, 0 = center, and 1.0 = right
+	void setPan(F32 pan); 
+
+	void pump(); // call this at least a few times a second if you can - it affects how quickly we can 'catch' a new audio source and adjust its volume
+	
+ private:
+	VolumeCatcherImpl *pimpl;
+};
+
+#endif // VOLUME_CATCHER_H