From ba3e89b5c731ed6dc36d4650928be8081b3289f5 Mon Sep 17 00:00:00 2001
From: callum_linden <none@none>
Date: Fri, 18 Jan 2013 16:43:19 -0800
Subject: first push - patch from CmdCupCake plus force FMODEX on

---
 indra/llaudio/CMakeLists.txt              |  26 +-
 indra/llaudio/llaudioengine.h             |   1 -
 indra/llaudio/llaudioengine_fmodex.cpp    | 746 ++++++++++++++++++++++++++++++
 indra/llaudio/llaudioengine_fmodex.h      | 129 ++++++
 indra/llaudio/lllistener_fmodex.cpp       | 135 ++++++
 indra/llaudio/lllistener_fmodex.h         |  65 +++
 indra/llaudio/llstreamingaudio.h          |   2 +
 indra/llaudio/llstreamingaudio_fmodex.cpp | 382 +++++++++++++++
 indra/llaudio/llstreamingaudio_fmodex.h   |  75 +++
 indra/llaudio/llwindgen.h                 |  34 +-
 10 files changed, 1574 insertions(+), 21 deletions(-)
 create mode 100644 indra/llaudio/llaudioengine_fmodex.cpp
 create mode 100644 indra/llaudio/llaudioengine_fmodex.h
 create mode 100644 indra/llaudio/lllistener_fmodex.cpp
 create mode 100644 indra/llaudio/lllistener_fmodex.h
 create mode 100644 indra/llaudio/llstreamingaudio_fmodex.cpp
 create mode 100644 indra/llaudio/llstreamingaudio_fmodex.h

(limited to 'indra/llaudio')

diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt
index 632e5d46e3..e404cfc10e 100644
--- a/indra/llaudio/CMakeLists.txt
+++ b/indra/llaudio/CMakeLists.txt
@@ -5,7 +5,13 @@ project(llaudio)
 include(00-Common)
 include(Audio)
 include(LLAudio)
-include(FMOD)
+if (FMODEX)
+  include(FMODEX)
+  set(FMOD OFF)
+endif (FMODEX)
+if (NOT FMODEX)
+  include(FMOD)
+endif (NOT FMODEX)
 include(OPENAL)
 include(LLCommon)
 include(LLMath)
@@ -24,7 +30,6 @@ include_directories(
     ${VORBIS_INCLUDE_DIRS}
     ${OPENAL_LIB_INCLUDE_DIRS}
     ${FREEAULT_LIB_INCLUDE_DIRS}
-    ${FMOD_INCLUDE_DIR}
     )
 
 set(llaudio_SOURCE_FILES
@@ -44,6 +49,23 @@ set(llaudio_HEADER_FILES
     llwindgen.h
     )
 
+if (FMODEX)
+    include_directories(
+        ${FMODEX_INCLUDE_DIR}
+        )
+    list(APPEND llaudio_SOURCE_FILES
+         llaudioengine_fmodex.cpp
+         lllistener_fmodex.cpp
+         llstreamingaudio_fmodex.cpp
+         )
+
+    list(APPEND llaudio_HEADER_FILES
+         llaudioengine_fmodex.h
+         lllistener_fmodex.h
+         llstreamingaudio_fmodex.h
+         )
+endif (FMODEX)
+
 if (FMOD)
     include_directories(
         ${FMOD_INCLUDE_DIR}
diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h
index df1e4dc305..dbaba0fb91 100644
--- a/indra/llaudio/llaudioengine.h
+++ b/indra/llaudio/llaudioengine.h
@@ -42,7 +42,6 @@
 #include "lllistener.h"
 
 const F32 LL_WIND_UPDATE_INTERVAL = 0.1f;
-const F32 LL_ROLLOFF_MULTIPLIER_UNDER_WATER = 5.f;			//  How much sounds are weaker under water
 const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f;
 
 const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f;
diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp
new file mode 100644
index 0000000000..c3c30d87fe
--- /dev/null
+++ b/indra/llaudio/llaudioengine_fmodex.cpp
@@ -0,0 +1,746 @@
+/** 
+ * @file audioengine_fmodex.cpp
+ * @brief Implementation of LLAudioEngine class abstracting the audio 
+ * support as a FMODEX implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llstreamingaudio.h"
+#include "llstreamingaudio_fmodex.h"
+
+#include "llaudioengine_fmodex.h"
+#include "lllistener_fmodex.h"
+
+#include "llerror.h"
+#include "llmath.h"
+#include "llrand.h"
+
+#include "fmod.hpp"
+#include "fmod_errors.h"
+#include "lldir.h"
+#include "llapr.h"
+
+#include "sound_ids.h"
+
+FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels);
+
+FMOD::ChannelGroup *LLAudioEngine_FMODEX::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0};
+
+LLAudioEngine_FMODEX::LLAudioEngine_FMODEX(bool enable_profiler)
+{
+	mInited = false;
+	mWindGen = NULL;
+	mWindDSP = NULL;
+	mSystem = NULL;
+	mEnableProfiler = enable_profiler;
+}
+
+
+LLAudioEngine_FMODEX::~LLAudioEngine_FMODEX()
+{
+}
+
+
+inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string)
+{
+	if(result == FMOD_OK)
+		return false;
+	llwarns << string << " Error: " << FMOD_ErrorString(result) << llendl;
+	return true;
+}
+
+void* F_STDCALL decode_alloc(unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr)
+{
+	if(type & FMOD_MEMORY_STREAM_DECODE)
+	{
+		llinfos << "Decode buffer size: " << size << llendl;
+	}
+	else if(type & FMOD_MEMORY_STREAM_FILE)
+	{
+		llinfos << "Strean buffer size: " << size << llendl;
+	}
+	return new char[size];
+}
+void* F_STDCALL decode_realloc(void *ptr, unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr)
+{
+	memset(ptr,0,size);
+	return ptr;
+}
+void F_STDCALL decode_dealloc(void *ptr, FMOD_MEMORY_TYPE type, const char *sourcestr)
+{
+	delete[] (char*)ptr;
+}
+
+bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata)
+{
+	U32 version;
+	FMOD_RESULT result;
+
+	LL_DEBUGS("AppInit") << "LLAudioEngine_FMODEX::init() initializing FMOD" << LL_ENDL;
+
+	//result = FMOD::Memory_Initialize(NULL, 0, &decode_alloc, &decode_realloc, &decode_dealloc, FMOD_MEMORY_STREAM_DECODE | FMOD_MEMORY_STREAM_FILE);
+	//if(Check_FMOD_Error(result, "FMOD::Memory_Initialize"))
+	//	return false;
+
+	result = FMOD::System_Create(&mSystem);
+	if(Check_FMOD_Error(result, "FMOD::System_Create"))
+		return false;
+
+	//will call LLAudioEngine_FMODEX::allocateListener, which needs a valid mSystem pointer.
+	LLAudioEngine::init(num_channels, userdata);	
+	
+	result = mSystem->getVersion(&version);
+	Check_FMOD_Error(result, "FMOD::System::getVersion");
+
+	if (version < FMOD_VERSION)
+	{
+		LL_WARNS("AppInit") << "Error : You are using the wrong FMOD Ex version (" << version
+			<< ")!  You should be using FMOD Ex" << FMOD_VERSION << LL_ENDL;
+	}
+
+	result = mSystem->setSoftwareFormat(44100, FMOD_SOUND_FORMAT_PCM16, 0, 0, FMOD_DSP_RESAMPLER_LINEAR);
+	Check_FMOD_Error(result,"FMOD::System::setSoftwareFormat");
+
+	// In this case, all sounds, PLUS wind and stream will be software.
+	result = mSystem->setSoftwareChannels(num_channels + 2);
+	Check_FMOD_Error(result,"FMOD::System::setSoftwareChannels");
+
+	U32 fmod_flags = FMOD_INIT_NORMAL;
+	if(mEnableProfiler)
+	{
+		fmod_flags |= FMOD_INIT_ENABLE_PROFILE;
+		mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]);
+		mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]);
+		mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]);
+		mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]);
+	}
+
+#if LL_LINUX
+	bool audio_ok = false;
+
+	if (!audio_ok)
+	{
+		if (NULL == getenv("LL_BAD_FMOD_PULSEAUDIO")) /*Flawfinder: ignore*/
+		{
+			LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL;
+			if(mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK &&
+				(result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK)
+			{
+				LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY"	<< LL_ENDL;
+				audio_ok = true;
+			}
+			else 
+			{
+				Check_FMOD_Error(result, "PulseAudio audio output FAILED to initialize");
+			}
+		} 
+		else 
+		{
+			LL_DEBUGS("AppInit") << "PulseAudio audio output SKIPPED" << LL_ENDL;
+		}	
+	}
+	if (!audio_ok)
+	{
+		if (NULL == getenv("LL_BAD_FMOD_ALSA"))		/*Flawfinder: ignore*/
+		{
+			LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
+			if(mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK &&
+			    (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK)
+			{
+				LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
+				audio_ok = true;
+			} 
+			else 
+			{
+				Check_FMOD_Error(result, "ALSA audio output FAILED to initialize");
+			}
+		} 
+		else 
+		{
+			LL_DEBUGS("AppInit") << "ALSA audio output SKIPPED" << LL_ENDL;
+		}
+	}
+	if (!audio_ok)
+	{
+		if (NULL == getenv("LL_BAD_FMOD_OSS")) 	 /*Flawfinder: ignore*/
+		{
+			LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL;
+			if(mSystem->setOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK &&
+			    (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK)
+			{
+				LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL;
+				audio_ok = true;
+			}
+			else
+			{
+				Check_FMOD_Error(result, "OSS audio output FAILED to initialize");
+			}
+		}
+		else 
+		{
+			LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL;
+		}
+	}
+	if (!audio_ok)
+	{
+		LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL;
+		return false;
+	}
+
+	// We're interested in logging which output method we
+	// ended up with, for QA purposes.
+	FMOD_OUTPUTTYPE output_type;
+	mSystem->getOutput(&output_type);
+	switch (output_type)
+	{
+		case FMOD_OUTPUTTYPE_NOSOUND: 
+			LL_INFOS("AppInit") << "Audio output: NoSound" << LL_ENDL; break;
+		case FMOD_OUTPUTTYPE_PULSEAUDIO:	
+			LL_INFOS("AppInit") << "Audio output: PulseAudio" << LL_ENDL; break;
+		case FMOD_OUTPUTTYPE_ALSA: 
+			LL_INFOS("AppInit") << "Audio output: ALSA" << LL_ENDL; break;
+		case FMOD_OUTPUTTYPE_OSS:	
+			LL_INFOS("AppInit") << "Audio output: OSS" << LL_ENDL; break;
+		default:
+			LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break;
+	};
+#else // LL_LINUX
+
+	// initialize the FMOD engine
+	result = mSystem->init( num_channels + 2, fmod_flags, 0);
+	if (result == FMOD_ERR_OUTPUT_CREATEBUFFER)
+	{
+		/*
+		Ok, the speaker mode selected isn't supported by this soundcard. Switch it
+		back to stereo...
+		*/
+		result = mSystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
+		Check_FMOD_Error(result,"Error falling back to stereo mode");
+		/*
+		... and re-init.
+		*/
+		result = mSystem->init( num_channels + 2, fmod_flags, 0);
+	}
+	if(Check_FMOD_Error(result, "Error initializing FMOD Ex"))
+		return false;
+#endif
+
+	// set up our favourite FMOD-native streaming audio implementation if none has already been added
+	if (!getStreamingAudioImpl()) // no existing implementation added
+		setStreamingAudioImpl(new LLStreamingAudio_FMODEX(mSystem));
+
+	LL_INFOS("AppInit") << "LLAudioEngine_FMODEX::init() FMOD Ex initialized correctly" << LL_ENDL;
+
+	int r_numbuffers, r_samplerate, r_channels, r_bits;
+	unsigned int r_bufferlength;
+	char r_name[256];
+	mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers);
+	mSystem->getSoftwareFormat(&r_samplerate, NULL, &r_channels, NULL, NULL, &r_bits);
+	mSystem->getDriverInfo(0, r_name, 255, 0);
+	r_name[255] = '\0';
+	int latency = 1000.0 * r_bufferlength * r_numbuffers /r_samplerate;
+
+	LL_INFOS("AppInit") << "FMOD device: "<< r_name << "\n"
+		<< "FMOD Ex parameters: " << r_samplerate << " Hz * " << r_channels << " * " <<r_bits <<" bit\n"
+		<< "\tbuffer " << r_bufferlength << " * " << r_numbuffers << " (" << latency <<"ms)" << LL_ENDL;
+
+	mInited = true;
+
+	return true;
+}
+
+
+std::string LLAudioEngine_FMODEX::getDriverName(bool verbose)
+{
+	llassert_always(mSystem);
+	if (verbose)
+	{
+		U32 version;
+		if(!Check_FMOD_Error(mSystem->getVersion(&version), "FMOD::System::getVersion"))
+		{
+			return llformat("FMOD Ex %1x.%02x.%02x", version >> 16, version >> 8 & 0x000000FF, version & 0x000000FF);
+		}
+	}
+	return "FMODEx";
+}
+
+
+void LLAudioEngine_FMODEX::allocateListener(void)
+{	
+	mListenerp = (LLListener *) new LLListener_FMODEX(mSystem);
+	if (!mListenerp)
+	{
+		llwarns << "Listener creation failed" << llendl;
+	}
+}
+
+
+void LLAudioEngine_FMODEX::shutdown()
+{
+	stopInternetStream();
+
+	llinfos << "About to LLAudioEngine::shutdown()" << llendl;
+	LLAudioEngine::shutdown();
+	
+	llinfos << "LLAudioEngine_FMODEX::shutdown() closing FMOD Ex" << llendl;
+	mSystem->close();
+	mSystem->release();
+	llinfos << "LLAudioEngine_FMODEX::shutdown() done closing FMOD Ex" << llendl;
+
+	delete mListenerp;
+	mListenerp = NULL;
+}
+
+
+LLAudioBuffer * LLAudioEngine_FMODEX::createBuffer()
+{
+	return new LLAudioBufferFMODEX(mSystem);
+}
+
+
+LLAudioChannel * LLAudioEngine_FMODEX::createChannel()
+{
+	return new LLAudioChannelFMODEX(mSystem);
+}
+
+bool LLAudioEngine_FMODEX::initWind()
+{
+	mNextWindUpdate = 0.0;
+
+	if (!mWindDSP)
+	{
+		FMOD_DSP_DESCRIPTION dspdesc;
+		memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION));	//Set everything to zero
+		strncpy(dspdesc.name,"Wind Unit", sizeof(dspdesc.name));	//Set name to "Wind Unit"
+		dspdesc.channels=2;
+		dspdesc.read = &windCallback; //Assign callback.
+		if(Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP"))
+			return false;
+
+		if(mWindGen)
+			delete mWindGen;
+	
+		float frequency = 44100;
+		mWindDSP->getDefaults(&frequency,0,0,0);
+		mWindGen = new LLWindGen<MIXBUFFERFORMAT>((U32)frequency);
+		mWindDSP->setUserData((void*)mWindGen);
+	}
+
+	if (mWindDSP)
+	{
+		mSystem->playDSP(FMOD_CHANNEL_FREE, mWindDSP, false, 0);
+		return true;
+	}
+	return false;
+}
+
+
+void LLAudioEngine_FMODEX::cleanupWind()
+{
+	if (mWindDSP)
+	{
+		mWindDSP->remove();
+		mWindDSP->release();
+		mWindDSP = NULL;
+	}
+
+	delete mWindGen;
+	mWindGen = NULL;
+}
+
+
+//-----------------------------------------------------------------------
+void LLAudioEngine_FMODEX::updateWind(LLVector3 wind_vec, F32 camera_height_above_water)
+{
+	LLVector3 wind_pos;
+	F64 pitch;
+	F64 center_freq;
+
+	if (!mEnableWind)
+	{
+		return;
+	}
+	
+	if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
+	{
+		
+		// wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
+		// need to convert this to the conventional orientation DS3D and OpenAL use
+		// where +X = right, +Y = up, +Z = backwards
+
+		wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
+
+		// cerr << "Wind update" << endl;
+
+		pitch = 1.0 + mapWindVecToPitch(wind_vec);
+		center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
+		
+		mWindGen->mTargetFreq = (F32)center_freq;
+		mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
+		mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
+  	}
+}
+
+//-----------------------------------------------------------------------
+void LLAudioEngine_FMODEX::setInternalGain(F32 gain)
+{
+	if (!mInited)
+	{
+		return;
+	}
+
+	gain = llclamp( gain, 0.0f, 1.0f );
+
+	FMOD::ChannelGroup *master_group;
+	mSystem->getMasterChannelGroup(&master_group);
+
+	master_group->setVolume(gain);
+
+	LLStreamingAudioInterface *saimpl = getStreamingAudioImpl();
+	if ( saimpl )
+	{
+		// fmod likes its streaming audio channel gain re-asserted after
+		// master volume change.
+		saimpl->setGain(saimpl->getGain());
+	}
+}
+
+//
+// LLAudioChannelFMODEX implementation
+//
+
+LLAudioChannelFMODEX::LLAudioChannelFMODEX(FMOD::System *system) : LLAudioChannel(), mSystemp(system), mChannelp(NULL), mLastSamplePos(0)
+{
+}
+
+
+LLAudioChannelFMODEX::~LLAudioChannelFMODEX()
+{
+	cleanup();
+}
+
+bool LLAudioChannelFMODEX::updateBuffer()
+{
+	if (LLAudioChannel::updateBuffer())
+	{
+		// Base class update returned true, which means that we need to actually
+		// set up the channel for a different buffer.
+
+		LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)mCurrentSourcep->getCurrentBuffer();
+
+		// Grab the FMOD sample associated with the buffer
+		FMOD::Sound *soundp = bufferp->getSound();
+		if (!soundp)
+		{
+			// This is bad, there should ALWAYS be a sound associated with a legit
+			// buffer.
+			llerrs << "No FMOD sound!" << llendl;
+			return false;
+		}
+
+
+		// Actually play the sound.  Start it off paused so we can do all the necessary
+		// setup.
+		if(!mChannelp)
+		{
+			FMOD_RESULT result = getSystem()->playSound(FMOD_CHANNEL_FREE, soundp, true, &mChannelp);
+			Check_FMOD_Error(result, "FMOD::System::playSound");
+		}
+
+		//llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl;
+	}
+
+	// If we have a source for the channel, we need to update its gain.
+	if (mCurrentSourcep)
+	{
+		// SJB: warnings can spam and hurt framerate, disabling
+		FMOD_RESULT result;
+
+		result = mChannelp->setVolume(getSecondaryGain() * mCurrentSourcep->getGain());
+		//Check_FMOD_Error(result, "FMOD::Channel::setVolume");
+
+		result = mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);
+		/*if(Check_FMOD_Error(result, "FMOD::Channel::setMode"))
+		{
+			S32 index;
+			mChannelp->getIndex(&index);
+ 			llwarns << "Channel " << index << "Source ID: " << mCurrentSourcep->getID()
+ 					<< " at " << mCurrentSourcep->getPositionGlobal() << llendl;		
+		}*/
+	}
+
+	return true;
+}
+
+
+void LLAudioChannelFMODEX::update3DPosition()
+{
+	if (!mChannelp)
+	{
+		// We're not actually a live channel (i.e., we're not playing back anything)
+		return;
+	}
+
+	LLAudioBufferFMODEX  *bufferp = (LLAudioBufferFMODEX  *)mCurrentBufferp;
+	if (!bufferp)
+	{
+		// We don't have a buffer associated with us (should really have been picked up
+		// by the above if.
+		return;
+	}
+
+	if (mCurrentSourcep->isAmbient())
+	{
+		// Ambient sound, don't need to do any positional updates.
+		set3DMode(false);
+	}
+	else
+	{
+		// Localized sound.  Update the position and velocity of the sound.
+		set3DMode(true);
+
+		LLVector3 float_pos;
+		float_pos.setVec(mCurrentSourcep->getPositionGlobal());
+		FMOD_RESULT result = mChannelp->set3DAttributes((FMOD_VECTOR*)float_pos.mV, (FMOD_VECTOR*)mCurrentSourcep->getVelocity().mV);
+		Check_FMOD_Error(result, "FMOD::Channel::set3DAttributes");
+	}
+}
+
+
+void LLAudioChannelFMODEX::updateLoop()
+{
+	if (!mChannelp)
+	{
+		// May want to clear up the loop/sample counters.
+		return;
+	}
+
+	//
+	// Hack:  We keep track of whether we looped or not by seeing when the
+	// sample position looks like it's going backwards.  Not reliable; may
+	// yield false negatives.
+	//
+	U32 cur_pos;
+	mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES);
+
+	if (cur_pos < (U32)mLastSamplePos)
+	{
+		mLoopedThisFrame = true;
+	}
+	mLastSamplePos = cur_pos;
+}
+
+
+void LLAudioChannelFMODEX::cleanup()
+{
+	if (!mChannelp)
+	{
+		//llinfos << "Aborting cleanup with no channel handle." << llendl;
+		return;
+	}
+
+	//llinfos << "Cleaning up channel: " << mChannelID << llendl;
+	Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop");
+
+	mCurrentBufferp = NULL;
+	mChannelp = NULL;
+}
+
+
+void LLAudioChannelFMODEX::play()
+{
+	if (!mChannelp)
+	{
+		llwarns << "Playing without a channel handle, aborting" << llendl;
+		return;
+	}
+
+	Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::pause");
+
+	getSource()->setPlayedOnce(true);
+
+	if(LLAudioEngine_FMODEX::mChannelGroups[getSource()->getType()])
+		mChannelp->setChannelGroup(LLAudioEngine_FMODEX::mChannelGroups[getSource()->getType()]);
+}
+
+
+void LLAudioChannelFMODEX::playSynced(LLAudioChannel *channelp)
+{
+	LLAudioChannelFMODEX *fmod_channelp = (LLAudioChannelFMODEX*)channelp;
+	if (!(fmod_channelp->mChannelp && mChannelp))
+	{
+		// Don't have channels allocated to both the master and the slave
+		return;
+	}
+
+	U32 cur_pos;
+	if(Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES), "Unable to retrieve current position"))
+		return;
+
+	cur_pos %= mCurrentBufferp->getLength();
+	
+	// Try to match the position of our sync master
+	Check_FMOD_Error(mChannelp->setPosition(cur_pos,FMOD_TIMEUNIT_PCMBYTES),"Unable to set current position");
+
+	// Start us playing
+	play();
+}
+
+
+bool LLAudioChannelFMODEX::isPlaying()
+{
+	if (!mChannelp)
+	{
+		return false;
+	}
+
+	bool paused, playing;
+	mChannelp->getPaused(&paused);
+	mChannelp->isPlaying(&playing);
+	return !paused && playing;
+}
+
+
+//
+// LLAudioChannelFMODEX implementation
+//
+
+
+LLAudioBufferFMODEX::LLAudioBufferFMODEX(FMOD::System *system) : mSystemp(system), mSoundp(NULL)
+{
+}
+
+
+LLAudioBufferFMODEX::~LLAudioBufferFMODEX()
+{
+	if(mSoundp)
+	{
+		mSoundp->release();
+		mSoundp = NULL;
+	}
+}
+
+
+bool LLAudioBufferFMODEX::loadWAV(const std::string& filename)
+{
+	// Try to open a wav file from disk.  This will eventually go away, as we don't
+	// really want to block doing this.
+	if (filename.empty())
+	{
+		// invalid filename, abort.
+		return false;
+	}
+
+	if (!LLAPRFile::isExist(filename, NULL, LL_APR_RPB))
+	{
+		// File not found, abort.
+		return false;
+	}
+	
+	if (mSoundp)
+	{
+		// If there's already something loaded in this buffer, clean it up.
+		mSoundp->release();
+		mSoundp = NULL;
+	}
+
+	FMOD_MODE base_mode = FMOD_LOOP_NORMAL | FMOD_SOFTWARE;
+	FMOD_CREATESOUNDEXINFO exinfo;
+	memset(&exinfo,0,sizeof(exinfo));
+	exinfo.cbsize = sizeof(exinfo);
+	exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV;	//Hint to speed up loading.
+	// Load up the wav file into an fmod sample
+#if LL_WINDOWS
+	FMOD_RESULT result = getSystem()->createSound((const char*)utf8str_to_utf16str(filename).c_str(), base_mode | FMOD_UNICODE, &exinfo, &mSoundp);
+#else
+	FMOD_RESULT result = getSystem()->createSound(filename.c_str(), base_mode, &exinfo, &mSoundp);
+#endif
+
+	if (result != FMOD_OK)
+	{
+		// We failed to load the file for some reason.
+		llwarns << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << llendl;
+
+		//
+		// If we EVER want to load wav files provided by end users, we need
+		// to rethink this!
+		//
+		// file is probably corrupt - remove it.
+		LLFile::remove(filename);
+		return false;
+	}
+
+	// Everything went well, return true
+	return true;
+}
+
+
+U32 LLAudioBufferFMODEX::getLength()
+{
+	if (!mSoundp)
+	{
+		return 0;
+	}
+
+	U32 length;
+	mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES);
+	return length;
+}
+
+
+void LLAudioChannelFMODEX::set3DMode(bool use3d)
+{
+	FMOD_MODE current_mode;
+	if(mChannelp->getMode(&current_mode) != FMOD_OK)
+		return;
+	FMOD_MODE new_mode = current_mode;	
+	new_mode &= ~(use3d ? FMOD_2D : FMOD_3D);
+	new_mode |= use3d ? FMOD_3D : FMOD_2D;
+
+	if(current_mode != new_mode)
+	{
+		mChannelp->setMode(new_mode);
+	}
+}
+
+
+FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *originalbuffer, float *newbuffer, unsigned int length, int inchannels, int outchannels)
+{
+	// originalbuffer = fmod's original mixbuffer.
+	// newbuffer = the buffer passed from the previous DSP unit.
+	// length = length in samples at this mix time.
+	// userdata = user parameter passed through in FSOUND_DSP_Create.
+	
+	LLWindGen<LLAudioEngine_FMODEX::MIXBUFFERFORMAT> *windgen;
+	FMOD::DSP *thisdsp = (FMOD::DSP *)dsp_state->instance;
+
+	thisdsp->getUserData((void **)&windgen);
+	S32 channels, configwidth, configheight;
+	thisdsp->getInfo(0, 0, &channels, &configwidth, &configheight);
+	
+	windgen->windGenerate((LLAudioEngine_FMODEX::MIXBUFFERFORMAT *)newbuffer, length);
+
+	return FMOD_OK;
+}
diff --git a/indra/llaudio/llaudioengine_fmodex.h b/indra/llaudio/llaudioengine_fmodex.h
new file mode 100644
index 0000000000..cf3d967d4f
--- /dev/null
+++ b/indra/llaudio/llaudioengine_fmodex.h
@@ -0,0 +1,129 @@
+/** 
+ * @file audioengine_fmodex.h
+ * @brief Definition of LLAudioEngine class abstracting the audio 
+ * support as a FMODEX implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_AUDIOENGINE_FMODEX_H
+#define LL_AUDIOENGINE_FMODEX_H
+
+#include "llaudioengine.h"
+#include "lllistener_fmod.h"
+#include "llwindgen.h"
+
+//Stubs
+class LLAudioStreamManagerFMODEX;
+namespace FMOD
+{
+	class System;
+	class Channel;
+	class ChannelGroup;
+	class Sound;
+	class DSP;
+}
+
+//Interfaces
+class LLAudioEngine_FMODEX : public LLAudioEngine 
+{
+public:
+	LLAudioEngine_FMODEX(bool enable_profiler);
+	virtual ~LLAudioEngine_FMODEX();
+
+	// initialization/startup/shutdown
+	virtual bool init(const S32 num_channels, void *user_data);
+	virtual std::string getDriverName(bool verbose);
+	virtual void allocateListener();
+
+	virtual void shutdown();
+
+	/*virtual*/ bool initWind();
+	/*virtual*/ void cleanupWind();
+
+	/*virtual*/void updateWind(LLVector3 direction, F32 camera_height_above_water);
+
+	typedef F32 MIXBUFFERFORMAT;
+
+	FMOD::System *getSystem()				const {return mSystem;}
+protected:
+	/*virtual*/ LLAudioBuffer *createBuffer(); // Get a free buffer, or flush an existing one if you have to.
+	/*virtual*/ LLAudioChannel *createChannel(); // Create a new audio channel.
+
+	/*virtual*/ void setInternalGain(F32 gain);
+
+	bool mInited;
+
+	LLWindGen<MIXBUFFERFORMAT> *mWindGen;
+
+	FMOD::DSP *mWindDSP;
+	FMOD::System *mSystem;
+	bool mEnableProfiler;
+
+public:
+	static FMOD::ChannelGroup *mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT];
+};
+
+
+class LLAudioChannelFMODEX : public LLAudioChannel
+{
+public:
+	LLAudioChannelFMODEX(FMOD::System *audioengine);
+	virtual ~LLAudioChannelFMODEX();
+
+protected:
+	/*virtual*/ void play();
+	/*virtual*/ void playSynced(LLAudioChannel *channelp);
+	/*virtual*/ void cleanup();
+	/*virtual*/ bool isPlaying();
+
+	/*virtual*/ bool updateBuffer();
+	/*virtual*/ void update3DPosition();
+	/*virtual*/ void updateLoop();
+
+	void set3DMode(bool use3d);
+protected:
+	FMOD::System *getSystem()	const {return mSystemp;}
+	FMOD::System *mSystemp;
+	FMOD::Channel *mChannelp;
+	S32 mLastSamplePos;
+};
+
+
+class LLAudioBufferFMODEX : public LLAudioBuffer
+{
+public:
+	LLAudioBufferFMODEX(FMOD::System *audioengine);
+	virtual ~LLAudioBufferFMODEX();
+
+	/*virtual*/ bool loadWAV(const std::string& filename);
+	/*virtual*/ U32 getLength();
+	friend class LLAudioChannelFMODEX;
+protected:
+	FMOD::System *getSystem()	const {return mSystemp;}
+	FMOD::System *mSystemp;
+	FMOD::Sound *getSound()		const{ return mSoundp; }
+	FMOD::Sound *mSoundp;
+};
+
+
+#endif // LL_AUDIOENGINE_FMODEX_H
diff --git a/indra/llaudio/lllistener_fmodex.cpp b/indra/llaudio/lllistener_fmodex.cpp
new file mode 100644
index 0000000000..4bc745b89a
--- /dev/null
+++ b/indra/llaudio/lllistener_fmodex.cpp
@@ -0,0 +1,135 @@
+/** 
+ * @file listener_fmodex.cpp
+ * @brief Implementation of LISTENER class abstracting the audio
+ * support as a FMODEX implementation (windows only)
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llaudioengine.h"
+#include "lllistener_fmodex.h"
+#include "fmod.hpp"
+
+//-----------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------
+LLListener_FMODEX::LLListener_FMODEX(FMOD::System *system)
+{
+	mSystem = system;
+	init();
+}
+
+//-----------------------------------------------------------------------
+LLListener_FMODEX::~LLListener_FMODEX()
+{
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODEX::init(void)
+{
+	// do inherited
+	LLListener::init();
+	mDopplerFactor = 1.0f;
+	mRolloffFactor = 1.0f;
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODEX::translate(LLVector3 offset)
+{
+	LLListener::translate(offset);
+
+	mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODEX::setPosition(LLVector3 pos)
+{
+	LLListener::setPosition(pos);
+
+	mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODEX::setVelocity(LLVector3 vel)
+{
+	LLListener::setVelocity(vel);
+
+	mSystem->set3DListenerAttributes(0, NULL, (FMOD_VECTOR*)mVelocity.mV, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODEX::orient(LLVector3 up, LLVector3 at)
+{
+	LLListener::orient(up, at);
+
+	// Welcome to the transition between right and left
+	// (coordinate systems, that is)
+	// Leaving the at vector alone results in a L/R reversal
+	// since DX is left-handed and we (LL, OpenGL, OpenAL) are right-handed
+	at = -at;
+
+	mSystem->set3DListenerAttributes(0, NULL, NULL, (FMOD_VECTOR*)at.mV, (FMOD_VECTOR*)up.mV);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMODEX::commitDeferredChanges()
+{
+	mSystem->update();
+}
+
+
+void LLListener_FMODEX::setRolloffFactor(F32 factor)
+{
+	//An internal FMODEx optimization skips 3D updates if there have not been changes to the 3D sound environment.
+	//Sadly, a change in rolloff is not accounted for, thus we must touch the listener properties as well.
+	//In short: Changing the position ticks a dirtyflag inside fmodex, which makes it not skip 3D processing next update call.
+	if(mRolloffFactor != factor)
+	{
+		LLVector3 pos = mVelocity - LLVector3(0.f,0.f,.1f);
+		mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)pos.mV, NULL, NULL, NULL);
+		mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mVelocity.mV, NULL, NULL, NULL);
+	}
+	mRolloffFactor = factor;
+	mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
+}
+
+
+F32 LLListener_FMODEX::getRolloffFactor()
+{
+	return mRolloffFactor;
+}
+
+
+void LLListener_FMODEX::setDopplerFactor(F32 factor)
+{
+	mDopplerFactor = factor;
+	mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
+}
+
+
+F32 LLListener_FMODEX::getDopplerFactor()
+{
+	return mDopplerFactor;
+}
+
+
diff --git a/indra/llaudio/lllistener_fmodex.h b/indra/llaudio/lllistener_fmodex.h
new file mode 100644
index 0000000000..073b65d53a
--- /dev/null
+++ b/indra/llaudio/lllistener_fmodex.h
@@ -0,0 +1,65 @@
+/** 
+ * @file listener_fmodex.h
+ * @brief Description of LISTENER class abstracting the audio support
+ * as an FMOD 3D implementation (windows and Linux)
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LISTENER_FMODEX_H
+#define LL_LISTENER_FMODEX_H
+
+#include "lllistener.h"
+
+//Stubs
+namespace FMOD
+{
+	class System;
+}
+
+//Interfaces
+class LLListener_FMODEX : public LLListener
+{
+ public:  
+	LLListener_FMODEX(FMOD::System *system);
+	virtual ~LLListener_FMODEX();
+	virtual void init();  
+
+	virtual void translate(LLVector3 offset);
+	virtual void setPosition(LLVector3 pos);
+	virtual void setVelocity(LLVector3 vel);
+	virtual void orient(LLVector3 up, LLVector3 at);
+	virtual void commitDeferredChanges();
+
+	virtual void setDopplerFactor(F32 factor);
+	virtual F32 getDopplerFactor();
+	virtual void setRolloffFactor(F32 factor);
+	virtual F32 getRolloffFactor();
+ protected:
+	 FMOD::System *mSystem;
+	 F32 mDopplerFactor;
+	 F32 mRolloffFactor;
+};
+
+#endif
+
+
diff --git a/indra/llaudio/llstreamingaudio.h b/indra/llaudio/llstreamingaudio.h
index 20104af744..93479f9d59 100644
--- a/indra/llaudio/llstreamingaudio.h
+++ b/indra/llaudio/llstreamingaudio.h
@@ -45,6 +45,8 @@ class LLStreamingAudioInterface
 	virtual void setGain(F32 vol) = 0;
 	virtual F32 getGain() = 0;
 	virtual std::string getURL() = 0;
+	virtual bool supportsAdjustableBufferSizes(){return false;}
+	virtual void setBufferSizes(U32 streambuffertime, U32 decodebuffertime){};
 };
 
 #endif // LL_STREAMINGAUDIO_H
diff --git a/indra/llaudio/llstreamingaudio_fmodex.cpp b/indra/llaudio/llstreamingaudio_fmodex.cpp
new file mode 100644
index 0000000000..463d816331
--- /dev/null
+++ b/indra/llaudio/llstreamingaudio_fmodex.cpp
@@ -0,0 +1,382 @@
+/** 
+ * @file streamingaudio_fmodex.cpp
+ * @brief LLStreamingAudio_FMODEX implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llmath.h"
+
+#include "fmod.hpp"
+#include "fmod_errors.h"
+
+#include "llstreamingaudio_fmodex.h"
+
+
+class LLAudioStreamManagerFMODEX
+{
+public:
+	LLAudioStreamManagerFMODEX(FMOD::System *system, const std::string& url);
+	FMOD::Channel* startStream();
+	bool stopStream(); // Returns true if the stream was successfully stopped.
+	bool ready();
+
+	const std::string& getURL() 	{ return mInternetStreamURL; }
+
+	FMOD_OPENSTATE getOpenState(unsigned int* percentbuffered=NULL, bool* starving=NULL, bool* diskbusy=NULL);
+protected:
+	FMOD::System* mSystem;
+	FMOD::Channel* mStreamChannel;
+	FMOD::Sound* mInternetStream;
+	bool mReady;
+
+	std::string mInternetStreamURL;
+};
+
+
+
+//---------------------------------------------------------------------------
+// Internet Streaming
+//---------------------------------------------------------------------------
+LLStreamingAudio_FMODEX::LLStreamingAudio_FMODEX(FMOD::System *system) :
+	mSystem(system),
+	mCurrentInternetStreamp(NULL),
+	mFMODInternetStreamChannelp(NULL),
+	mGain(1.0f)
+{
+	// Number of milliseconds of audio to buffer for the audio card.
+	// Must be larger than the usual Second Life frame stutter time.
+	const U32 buffer_seconds = 10;		//sec
+	const U32 estimated_bitrate = 128;	//kbit/sec
+	mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES);
+
+	// Here's where we set the size of the network buffer and some buffering 
+	// parameters.  In this case we want a network buffer of 16k, we want it 
+	// to prebuffer 40% of that when we first connect, and we want it 
+	// to rebuffer 80% of that whenever we encounter a buffer underrun.
+
+	// Leave the net buffer properties at the default.
+	//FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80);
+}
+
+
+LLStreamingAudio_FMODEX::~LLStreamingAudio_FMODEX()
+{
+	// nothing interesting/safe to do.
+}
+
+
+void LLStreamingAudio_FMODEX::start(const std::string& url)
+{
+	//if (!mInited)
+	//{
+	//	llwarns << "startInternetStream before audio initialized" << llendl;
+	//	return;
+	//}
+
+	// "stop" stream but don't clear url, etc. in case url == mInternetStreamURL
+	stop();
+
+	if (!url.empty())
+	{
+		llinfos << "Starting internet stream: " << url << llendl;
+		mCurrentInternetStreamp = new LLAudioStreamManagerFMODEX(mSystem,url);
+		mURL = url;
+	}
+	else
+	{
+		llinfos << "Set internet stream to null" << llendl;
+		mURL.clear();
+	}
+}
+
+
+void LLStreamingAudio_FMODEX::update()
+{
+	// Kill dead internet streams, if possible
+	std::list<LLAudioStreamManagerFMODEX *>::iterator iter;
+	for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
+	{
+		LLAudioStreamManagerFMODEX *streamp = *iter;
+		if (streamp->stopStream())
+		{
+			llinfos << "Closed dead stream" << llendl;
+			delete streamp;
+			mDeadStreams.erase(iter++);
+		}
+		else
+		{
+			iter++;
+		}
+	}
+
+	// Don't do anything if there are no streams playing
+	if (!mCurrentInternetStreamp)
+	{
+		return;
+	}
+
+	unsigned int progress;
+	bool starving;
+	bool diskbusy;
+	FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState(&progress, &starving, &diskbusy);
+
+	if (open_state == FMOD_OPENSTATE_READY)
+	{
+		// Stream is live
+
+		// start the stream if it's ready
+		if (!mFMODInternetStreamChannelp &&
+			(mFMODInternetStreamChannelp = mCurrentInternetStreamp->startStream()))
+		{
+			// Reset volume to previously set volume
+			setGain(getGain());
+			mFMODInternetStreamChannelp->setPaused(false);
+			mLastStarved.stop();
+		}
+	}
+	else if(open_state == FMOD_OPENSTATE_ERROR)
+	{
+		stop();
+		return;
+	}
+
+	if(mFMODInternetStreamChannelp)
+	{
+		FMOD::Sound *sound = NULL;
+
+		if(mFMODInternetStreamChannelp->getCurrentSound(&sound) == FMOD_OK && sound)
+		{
+			if(starving)
+			{
+				if(!mLastStarved.getStarted())
+				{
+					llinfos << "Stream starvation detected! Muting stream audio until it clears." << llendl;
+					llinfos << "  (diskbusy="<<diskbusy<<")" << llendl;
+					llinfos << "  (progress="<<progress<<")" << llendl;
+					mFMODInternetStreamChannelp->setMute(true);
+				}
+				mLastStarved.start();
+			}
+			else if(mLastStarved.getStarted() && mLastStarved.getElapsedTimeF32() > 1.f)
+			{
+				mLastStarved.stop();
+				mFMODInternetStreamChannelp->setMute(false);
+			}
+		}
+	}
+}
+
+void LLStreamingAudio_FMODEX::stop()
+{
+	mLastStarved.stop();
+
+	if (mFMODInternetStreamChannelp)
+	{
+		mFMODInternetStreamChannelp->setPaused(true);
+		mFMODInternetStreamChannelp->setPriority(0);
+		mFMODInternetStreamChannelp = NULL;
+	}
+
+	if (mCurrentInternetStreamp)
+	{
+		llinfos << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << llendl;
+		if (mCurrentInternetStreamp->stopStream())
+		{
+			delete mCurrentInternetStreamp;
+		}
+		else
+		{
+			llwarns << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << llendl;
+			mDeadStreams.push_back(mCurrentInternetStreamp);
+		}
+		mCurrentInternetStreamp = NULL;
+		//mURL.clear();
+	}
+}
+
+void LLStreamingAudio_FMODEX::pause(int pauseopt)
+{
+	if (pauseopt < 0)
+	{
+		pauseopt = mCurrentInternetStreamp ? 1 : 0;
+	}
+
+	if (pauseopt)
+	{
+		if (mCurrentInternetStreamp)
+		{
+			stop();
+		}
+	}
+	else
+	{
+		start(getURL());
+	}
+}
+
+
+// A stream is "playing" if it has been requested to start.  That
+// doesn't necessarily mean audio is coming out of the speakers.
+int LLStreamingAudio_FMODEX::isPlaying()
+{
+	if (mCurrentInternetStreamp)
+	{
+		return 1; // Active and playing
+	}
+	else if (!mURL.empty())
+	{
+		return 2; // "Paused"
+	}
+	else
+	{
+		return 0;
+	}
+}
+
+
+F32 LLStreamingAudio_FMODEX::getGain()
+{
+	return mGain;
+}
+
+
+std::string LLStreamingAudio_FMODEX::getURL()
+{
+	return mURL;
+}
+
+
+void LLStreamingAudio_FMODEX::setGain(F32 vol)
+{
+	mGain = vol;
+
+	if (mFMODInternetStreamChannelp)
+	{
+		vol = llclamp(vol * vol, 0.f, 1.f);	//should vol be squared here?
+
+		mFMODInternetStreamChannelp->setVolume(vol);
+	}
+}
+
+///////////////////////////////////////////////////////
+// manager of possibly-multiple internet audio streams
+
+LLAudioStreamManagerFMODEX::LLAudioStreamManagerFMODEX(FMOD::System *system, const std::string& url) :
+	mSystem(system),
+	mStreamChannel(NULL),
+	mInternetStream(NULL),
+	mReady(false)
+{
+	mInternetStreamURL = url;
+
+	/*FMOD_CREATESOUNDEXINFO exinfo;
+	memset(&exinfo,0,sizeof(exinfo));
+	exinfo.cbsize = sizeof(exinfo);
+	exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_OGGVORBIS;	//Hint to speed up loading.*/
+
+	FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_MPEGSEARCH | FMOD_IGNORETAGS, 0, &mInternetStream);
+
+	if (result!= FMOD_OK)
+	{
+		llwarns << "Couldn't open fmod stream, error "
+			<< FMOD_ErrorString(result)
+			<< llendl;
+		mReady = false;
+		return;
+	}
+
+	mReady = true;
+}
+
+FMOD::Channel *LLAudioStreamManagerFMODEX::startStream()
+{
+	// We need a live and opened stream before we try and play it.
+	if (!mInternetStream || getOpenState() != FMOD_OPENSTATE_READY)
+	{
+		llwarns << "No internet stream to start playing!" << llendl;
+		return NULL;
+	}
+
+	if(mStreamChannel)
+		return mStreamChannel;	//Already have a channel for this stream.
+
+	mSystem->playSound(FMOD_CHANNEL_FREE, mInternetStream, true, &mStreamChannel);
+	return mStreamChannel;
+}
+
+bool LLAudioStreamManagerFMODEX::stopStream()
+{
+	if (mInternetStream)
+	{
+
+
+		bool close = true;
+		switch (getOpenState())
+		{
+		case FMOD_OPENSTATE_CONNECTING:
+			close = false;
+			break;
+		/*case FSOUND_STREAM_NET_NOTCONNECTED:
+		case FSOUND_STREAM_NET_BUFFERING:
+		case FSOUND_STREAM_NET_READY:
+		case FSOUND_STREAM_NET_ERROR:*/
+		default:
+			close = true;
+		}
+
+		if (close)
+		{
+			mInternetStream->release();
+			mStreamChannel = NULL;
+			mInternetStream = NULL;
+			return true;
+		}
+		else
+		{
+			return false;
+		}
+	}
+	else
+	{
+		return true;
+	}
+}
+
+FMOD_OPENSTATE LLAudioStreamManagerFMODEX::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy)
+{
+	FMOD_OPENSTATE state;
+	mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy);
+	return state;
+}
+
+void LLStreamingAudio_FMODEX::setBufferSizes(U32 streambuffertime, U32 decodebuffertime)
+{
+	mSystem->setStreamBufferSize(streambuffertime/1000*128*128, FMOD_TIMEUNIT_RAWBYTES);
+	FMOD_ADVANCEDSETTINGS settings;
+	memset(&settings,0,sizeof(settings));
+	settings.cbsize=sizeof(settings);
+	settings.defaultDecodeBufferSize = decodebuffertime;//ms
+	mSystem->setAdvancedSettings(&settings);
+}
diff --git a/indra/llaudio/llstreamingaudio_fmodex.h b/indra/llaudio/llstreamingaudio_fmodex.h
new file mode 100644
index 0000000000..3751dd60ad
--- /dev/null
+++ b/indra/llaudio/llstreamingaudio_fmodex.h
@@ -0,0 +1,75 @@
+/** 
+ * @file streamingaudio_fmodex.h
+ * @brief Definition of LLStreamingAudio_FMODEX implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_STREAMINGAUDIO_FMOD_H
+#define LL_STREAMINGAUDIO_FMOD_H
+
+#include "stdtypes.h" // from llcommon
+
+#include "llstreamingaudio.h"
+#include "lltimer.h"
+
+//Stubs
+class LLAudioStreamManagerFMODEX;
+namespace FMOD
+{
+	class System;
+	class Channel;
+}
+
+//Interfaces
+class LLStreamingAudio_FMODEX : public LLStreamingAudioInterface
+{
+ public:
+	LLStreamingAudio_FMODEX(FMOD::System *system);
+	/*virtual*/ ~LLStreamingAudio_FMODEX();
+
+	/*virtual*/ void start(const std::string& url);
+	/*virtual*/ void stop();
+	/*virtual*/ void pause(int pause);
+	/*virtual*/ void update();
+	/*virtual*/ int isPlaying();
+	/*virtual*/ void setGain(F32 vol);
+	/*virtual*/ F32 getGain();
+	/*virtual*/ std::string getURL();
+
+	/*virtual*/ bool supportsAdjustableBufferSizes(){return true;}
+	/*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime);
+private:
+	FMOD::System *mSystem;
+
+	LLAudioStreamManagerFMODEX *mCurrentInternetStreamp;
+	FMOD::Channel *mFMODInternetStreamChannelp;
+	std::list<LLAudioStreamManagerFMODEX *> mDeadStreams;
+
+	std::string mURL;
+	F32 mGain;
+
+	LLTimer mLastStarved;
+};
+
+
+#endif // LL_STREAMINGAUDIO_FMOD_H
diff --git a/indra/llaudio/llwindgen.h b/indra/llaudio/llwindgen.h
index b9cecb60a1..719b0ecbf2 100644
--- a/indra/llaudio/llwindgen.h
+++ b/indra/llaudio/llwindgen.h
@@ -27,6 +27,7 @@
 #define WINDGEN_H
 
 #include "llcommon.h"
+#include "llrand.h"
 
 template <class MIXBUFFERFORMAT_T>
 class LLWindGen
@@ -54,7 +55,9 @@ public:
 	}
 
 	const U32 getInputSamplingRate() { return mInputSamplingRate; }
-	
+	const F32 getNextSample();
+	const F32 getClampedSample(bool clamp, F32 sample);
+
 	// newbuffer = the buffer passed from the previous DSP unit.
 	// numsamples = length in samples-per-channel at this mix time.
 	// NOTE: generates L/R interleaved stereo
@@ -89,7 +92,7 @@ public:
 			
 			// Start with white noise
 			// This expression is fragile, rearrange it and it will break!
-			next_sample = (F32)rand() * (1.0f / (F32)(RAND_MAX / (U16_MAX / 8))) + (F32)(S16_MIN / 8);
+			next_sample = getNextSample();
 			
 			// Apply a pinking filter
 			// Magic numbers taken from PKE method at http://www.firstpr.com.au/dsp/pink-noise/
@@ -126,23 +129,13 @@ public:
 			for (U8 i=mSubSamples; i && numsamples; --i, --numsamples) 
 			{
 				mLastSample = mLastSample + delta;
-				S32	sample_right = (S32)(mLastSample * mCurrentPanGainR);
-				S32	sample_left = (S32)mLastSample - sample_right;
+				MIXBUFFERFORMAT_T	sample_right = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample * mCurrentPanGainR);
+				MIXBUFFERFORMAT_T	sample_left = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample - (F32)sample_right);
 				
-				if (!clip)
-				{
-					*cursamplep = (MIXBUFFERFORMAT_T)sample_left;
-					++cursamplep;
-					*cursamplep = (MIXBUFFERFORMAT_T)sample_right;
-					++cursamplep;
-				}
-				else
-				{
-					*cursamplep = (MIXBUFFERFORMAT_T)llclamp(sample_left, (S32)S16_MIN, (S32)S16_MAX);
-					++cursamplep;
-					*cursamplep = (MIXBUFFERFORMAT_T)llclamp(sample_right, (S32)S16_MIN, (S32)S16_MAX);
-					++cursamplep;
-				}
+				*cursamplep = sample_left;
+				++cursamplep;
+				*cursamplep = sample_right;
+				++cursamplep;
 			}
 		}
 		
@@ -173,4 +166,9 @@ private:
 	F32 mLastSample;
 };
 
+template<class T> inline const F32 LLWindGen<T>::getNextSample() { return (F32)rand() * (1.0f / (F32)(RAND_MAX / (U16_MAX / 8))) + (F32)(S16_MIN / 8); }
+template<> inline const F32 LLWindGen<F32>::getNextSample() { return ll_frand()-.5f; }
+template<class T> inline const F32 LLWindGen<T>::getClampedSample(bool clamp, F32 sample) { return clamp ? (F32)llclamp((S32)sample,(S32)S16_MIN,(S32)S16_MAX) : sample; }
+template<> inline const F32 LLWindGen<F32>::getClampedSample(bool clamp, F32 sample) { return sample; }
+
 #endif
-- 
cgit v1.2.3