 /** 
 * @file audioengine.cpp
 * @brief implementation of LLAudioEngine class abstracting the Open
 * AL audio support
 *
 * $LicenseInfo:firstyear=2000&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 "llstreamingaudio.h"

#include "llerror.h"
#include "llmath.h"

#include "sound_ids.h"  // temporary hack for min/max distances

#include "llfilesystem.h"
#include "lldir.h"
#include "llaudiodecodemgr.h"
#include "llassetstorage.h"


// necessary for grabbing sounds from sim (implemented in viewer)	
extern void request_sound(const LLUUID &sound_guid);

LLAudioEngine* gAudiop = NULL;

// NaCl - Sound explorer
S32 LLAudioSource::sSoundHistoryPruneCounter;
// NaCl End

//
// LLAudioEngine implementation
//


LLAudioEngine::LLAudioEngine()
{
	setDefaults();
}


LLAudioEngine::~LLAudioEngine()
{
}

LLStreamingAudioInterface* LLAudioEngine::getStreamingAudioImpl()
{
	return mStreamingAudioImpl;
}

void LLAudioEngine::setStreamingAudioImpl(LLStreamingAudioInterface *impl)
{
	// <FS> FMOD fixes
	if (mStreamingAudioImpl)
		delete mStreamingAudioImpl;
	// </FS>
	mStreamingAudioImpl = impl;
}

void LLAudioEngine::setDefaults()
{
	mMaxWindGain = 1.f;

	mListenerp = NULL;

	mMuted = false;
	mUserData = NULL;

	mLastStatus = 0;

	mEnableWind = false;

	mChannels.fill(nullptr);
	mBuffers.fill(nullptr);

	mMasterGain = 1.f;
	// Setting mInternalGain to an out of range value fixes the issue reported in STORM-830.
	// There is an edge case in setMasterGain during startup which prevents setInternalGain from 
	// being called if the master volume setting and mInternalGain both equal 0, so using -1 forces
	// the if statement in setMasterGain to execute when the viewer starts up.
	mInternalGain = -1.f;
	mNextWindUpdate = 0.f;

	mStreamingAudioImpl = NULL;

	for (U32 i = 0; i < LLAudioEngine::AUDIO_TYPE_COUNT; i++)
		mSecondaryGain[i] = 1.0f;
}


bool LLAudioEngine::init(void* userdata, const std::string &app_title)
{
	setDefaults();

	mUserData = userdata;
	
	allocateListener();

	LL_INFOS("AudioEngine") << "LLAudioEngine::init() AudioEngine successfully initialized" << LL_ENDL;

	return true;
}


void LLAudioEngine::shutdown()
{
	// <FS> FMOD fixes
	// Clean up streaming audio
	delete mStreamingAudioImpl;
	mStreamingAudioImpl = NULL;
	// </FS>

	// Clean up wind source
	cleanupWind();

	// Clean up audio sources
	source_map::iterator iter_src;
	for (iter_src = mAllSources.begin(); iter_src != mAllSources.end(); iter_src++)
	{
		delete iter_src->second;
	}


	// Clean up audio data
	data_map::iterator iter_data;
	for (iter_data = mAllData.begin(); iter_data != mAllData.end(); iter_data++)
	{
		delete iter_data->second;
	}


	// Clean up channels
	S32 i;
	for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
	{
		delete mChannels[i];
		mChannels[i] = NULL;
	}

	// Clean up buffers
	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
	{
		delete mBuffers[i];
		mBuffers[i] = NULL;
	}
}


// virtual
void LLAudioEngine::startInternetStream(const std::string& url)
{
	if (mStreamingAudioImpl)
		mStreamingAudioImpl->start(url);
}


// virtual
void LLAudioEngine::stopInternetStream()
{
	if (mStreamingAudioImpl)
		mStreamingAudioImpl->stop();
}

// virtual
void LLAudioEngine::pauseInternetStream(S32 pause)
{
	if (mStreamingAudioImpl)
		mStreamingAudioImpl->pause(pause);
}

// virtual
void LLAudioEngine::updateInternetStream()
{
	if (mStreamingAudioImpl)
		mStreamingAudioImpl->update();
}

// virtual
LLAudioEngine::LLAudioPlayState LLAudioEngine::isInternetStreamPlaying()
{
	if (mStreamingAudioImpl)
		return (LLAudioEngine::LLAudioPlayState) mStreamingAudioImpl->isPlaying();

	return LLAudioEngine::AUDIO_STOPPED; // Stopped
}


// virtual
void LLAudioEngine::setInternetStreamGain(F32 vol)
{
	if (mStreamingAudioImpl)
		mStreamingAudioImpl->setGain(vol);
}

// virtual
std::string LLAudioEngine::getInternetStreamURL()
{
	if (mStreamingAudioImpl)
		return mStreamingAudioImpl->getURL();
	else return std::string();
}


void LLAudioEngine::updateChannels()
{
	S32 i;
	for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
	{
		if (mChannels[i])
		{
			mChannels[i]->updateBuffer();
			mChannels[i]->update3DPosition();
			mChannels[i]->updateLoop();
		}
	}
}

void LLAudioEngine::idle()
{
	// "Update" all of our audio sources, clean up dead ones.
	// Primarily does position updating, cleanup of unused audio sources.
	// Also does regeneration of the current priority of each audio source.

	S32 i;
	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
	{
		if (mBuffers[i])
		{
			mBuffers[i]->mInUse = false;
		}
	}

	F32 max_priority = -1.f;
	LLAudioSource *max_sourcep = NULL; // Maximum priority source without a channel
	source_map::iterator iter;
	for (iter = mAllSources.begin(); iter != mAllSources.end();)
	{
		LLAudioSource *sourcep = iter->second;

		// Update this source
		sourcep->update();
		sourcep->updatePriority();

		if (sourcep->isDone())
		{
			// The source is done playing, clean it up.
			delete sourcep;
            iter = mAllSources.erase(iter);
			continue;
		}

		if (sourcep->isMuted())
		{
			++iter;
		  	continue;
		}

		if (!sourcep->getChannel() && sourcep->getCurrentBuffer())
		{
			// We could potentially play this sound if its priority is high enough.
			if (sourcep->getPriority() > max_priority)
			{
				max_priority = sourcep->getPriority();
				max_sourcep = sourcep;
			}
		}

		// Move on to the next source
		iter++;
	}

	// Now, do priority-based organization of audio sources.
	// All channels used, check priorities.
	// Find channel with lowest priority
	if (max_sourcep)
	{
		LLAudioChannel *channelp = getFreeChannel(max_priority);
		if (channelp)
		{
			//LL_INFOS() << "Replacing source in channel due to priority!" << LL_ENDL;
			max_sourcep->setChannel(channelp);
			channelp->setSource(max_sourcep);
			if (max_sourcep->isSyncSlave())
			{
				// A sync slave, it doesn't start playing until it's synced up with the master.
				// Flag this channel as waiting for sync, and return true.
				channelp->setWaiting(true);
			}
			else
			{
				channelp->setWaiting(false);
				channelp->play();
			}
		}
	}

	
	// Do this BEFORE we update the channels
	// Update the channels to sync up with any changes that the source made,
	// such as changing what sound was playing.
	updateChannels();

	// Update queued sounds (switch to next queued data if the current has finished playing)
	for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter)
	{
		// This is lame, instead of this I could actually iterate through all the sources
		// attached to each channel, since only those with active channels
		// can have anything interesting happen with their queue? (Maybe not true)
		LLAudioSource *sourcep = iter->second;
		if (!sourcep->mQueuedDatap || sourcep->isMuted())
		{
			// Muted, or nothing queued, so we don't care.
			continue;
		}

		LLAudioChannel *channelp = sourcep->getChannel();
		if (!channelp)
		{
			// This sound isn't playing, so we just process move the queue
			sourcep->mCurrentDatap = sourcep->mQueuedDatap;
			sourcep->mQueuedDatap = NULL;

			// Reset the timer so the source doesn't die.
			sourcep->mAgeTimer.reset();
			// Make sure we have the buffer set up if we just decoded the data
			if (sourcep->mCurrentDatap)
			{
				updateBufferForData(sourcep->mCurrentDatap);
			}

			// Actually play the associated data.
			sourcep->setupChannel();
			channelp = sourcep->getChannel();
			if (channelp)
			{
				channelp->updateBuffer();
				sourcep->getChannel()->play();
			}
			continue;
		}
		else
		{
			// Check to see if the current sound is done playing, or looped.
			if (!channelp->isPlaying())
			{
				sourcep->mCurrentDatap = sourcep->mQueuedDatap;
				sourcep->mQueuedDatap = NULL;

				// Reset the timer so the source doesn't die.
				sourcep->mAgeTimer.reset();

				// Make sure we have the buffer set up if we just decoded the data
				if (sourcep->mCurrentDatap)
				{
					updateBufferForData(sourcep->mCurrentDatap);
				}

				// Actually play the associated data.
				sourcep->setupChannel();
				channelp->updateBuffer();
				sourcep->getChannel()->play();
			}
			else if (sourcep->isLoop())
			{
				// It's a loop, we need to check and see if we're done with it.
				if (channelp->mLoopedThisFrame)
				{
					sourcep->mCurrentDatap = sourcep->mQueuedDatap;
					sourcep->mQueuedDatap = NULL;

					// Actually, should do a time sync so if we're a loop master/slave
					// we don't drift away.
					sourcep->setupChannel();
					sourcep->getChannel()->play();
				}
			}
		}
	}

	// Lame, update the channels AGAIN.
	// Update the channels to sync up with any changes that the source made,
	// such as changing what sound was playing.
	updateChannels();
	
	// Hack!  For now, just use a global sync master;
	LLAudioSource *sync_masterp = NULL;
	LLAudioChannel *master_channelp = NULL;
	F32 max_sm_priority = -1.f;
	for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter)
	{
		LLAudioSource *sourcep = iter->second;
		if (sourcep->isMuted())
		{
			continue;
		}
		if (sourcep->isSyncMaster())
		{
			if (sourcep->getPriority() > max_sm_priority)
			{
				sync_masterp = sourcep;
				master_channelp = sync_masterp->getChannel();
				max_sm_priority = sourcep->getPriority();
			}
		}
	}

	if (master_channelp && master_channelp->mLoopedThisFrame)
	{
		// Synchronize loop slaves with their masters
		// Update queued sounds (switch to next queued data if the current has finished playing)
		for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter)
		{
			LLAudioSource *sourcep = iter->second;

			if (!sourcep->isSyncSlave())
			{
				// Not a loop slave, we don't need to do anything
				continue;
			}

			LLAudioChannel *channelp = sourcep->getChannel();
			if (!channelp)
			{
				// Not playing, don't need to bother.
				continue;
			}

			if (!channelp->isPlaying())
			{
				// Now we need to check if our loop master has just looped, and
				// start playback if that's the case.
				if (sync_masterp->getChannel())
				{
					channelp->playSynced(master_channelp);
					channelp->setWaiting(false);
				}
			}
		}
	}

	// Sync up everything that the audio engine needs done.
	commitDeferredChanges();
	
	// Flush unused buffers that are stale enough
	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
	{
		if (mBuffers[i])
		{
			if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f)
			{
				//LL_INFOS() << "Flushing unused buffer!" << LL_ENDL;
				mBuffers[i]->mAudioDatap->mBufferp = NULL;
				delete mBuffers[i];
				mBuffers[i] = NULL;
			}
		}
	}


	// Clear all of the looped flags for the channels
	for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
	{
		if (mChannels[i])
		{
			mChannels[i]->mLoopedThisFrame = false;
		}
	}

	// Decode audio files
    LLAudioDecodeMgr::getInstance()->processQueue();
	
	// Call this every frame, just in case we somehow
	// missed picking it up in all the places that can add
	// or request new data.
	startNextTransfer();

	updateInternetStream();
}



bool LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid)
{
	if (!adp)
	{
		return false;
	}

	// Update the audio buffer first - load a sound if we have it.
	// Note that this could potentially cause us to waste time updating buffers
	// for sounds that actually aren't playing, although this should be mitigated
	// by the fact that we limit the number of buffers, and we flush buffers based
	// on priority.
	if (!adp->getBuffer())
	{
		if (adp->hasDecodedData())
		{
			adp->load();
		}
		else if (adp->hasLocalData())
		{
			if (audio_uuid.notNull())
			{
                LLAudioDecodeMgr::getInstance()->addDecodeRequest(audio_uuid);
			}
		}
		else
		{
			return false;
		}
	}
	return true;
}


void LLAudioEngine::enableWind(bool enable)
{
	if (enable && (!mEnableWind))
	{
		mEnableWind = initWind();
	}
	else if (mEnableWind && (!enable))
	{
		mEnableWind = false;
		cleanupWind();
	}
}


LLAudioBuffer * LLAudioEngine::getFreeBuffer()
{
	S32 i;
	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
	{
		if (!mBuffers[i])
		{
			mBuffers[i] = createBuffer();
			return mBuffers[i];
		}
	}


	// Grab the oldest unused buffer
	F32 max_age = -1.f;
	S32 buffer_id = -1;
	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
	{
		if (mBuffers[i])
		{
			if (!mBuffers[i]->mInUse)
			{
				if (mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > max_age)
				{
					max_age = mBuffers[i]->mLastUseTimer.getElapsedTimeF32();
					buffer_id = i;
				}
			}
		}
	}

	if (buffer_id >= 0)
	{
		LL_DEBUGS() << "Taking over unused buffer " << buffer_id << LL_ENDL;
		//LL_INFOS() << "Flushing unused buffer!" << LL_ENDL;
		mBuffers[buffer_id]->mAudioDatap->mBufferp = NULL;
		delete mBuffers[buffer_id];
		mBuffers[buffer_id] = createBuffer();
		return mBuffers[buffer_id];
	}
	return NULL;
}


LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority)
{
	S32 i;
    for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
	{
		if (!mChannels[i])
		{
			// No channel allocated here, use it.
			mChannels[i] = createChannel();
			return mChannels[i];
		}
		else
		{
			// Channel is allocated but not playing right now, use it.
			if (!mChannels[i]->isPlaying() && !mChannels[i]->isWaiting())
			{
				mChannels[i]->cleanup();
				if (mChannels[i]->getSource())
				{
					mChannels[i]->getSource()->setChannel(NULL);
				}
				return mChannels[i];
			}
		}
	}

	// All channels used, check priorities.
	// Find channel with lowest priority and see if we want to replace it.
	F32 min_priority = 10000.f;
	LLAudioChannel *min_channelp = NULL;

    for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
	{
		LLAudioChannel *channelp = mChannels[i];
		LLAudioSource *sourcep = channelp->getSource();
		if (sourcep->getPriority() < min_priority)
		{
			min_channelp = channelp;
			min_priority = sourcep->getPriority();
		}
	}

	if (min_priority > priority || !min_channelp)
	{
		// All playing channels have higher priority, return.
		return NULL;
	}

	// Flush the minimum priority channel, and return it.
	min_channelp->cleanup();
	min_channelp->getSource()->setChannel(NULL);
	return min_channelp;
}


void LLAudioEngine::cleanupBuffer(LLAudioBuffer *bufferp)
{
	S32 i;
	for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++)
	{
		if (mBuffers[i] == bufferp)
		{
			delete mBuffers[i];
			mBuffers[i] = NULL;
		}
	}
}


bool LLAudioEngine::preloadSound(const LLUUID &uuid)
{
	LL_DEBUGS("AudioEngine")<<"( "<<uuid<<" )"<<LL_ENDL;

	// <FS:ND> Protect against corrupted sounds. Just do a quick exit instead of trying to preload over and over again.
	if( gAudiop->isCorruptSound( uuid ) )
		return false;
	// </FS:ND>

	getAudioData(uuid);	// We don't care about the return value, this is just to make sure
									// that we have an entry, which will mean that the audio engine knows about this

    if (LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid))
	{
		// This means that we do have a local copy, and we're working on decoding it.
		return true;
	}

	return false;
}

bool LLAudioEngine::isWindEnabled()
{
	return mEnableWind;
}


void LLAudioEngine::setMuted(bool muted)
{
	if (muted != mMuted)
	{
		mMuted = muted;
		setMasterGain(mMasterGain);
	}
	enableWind(!mMuted);
}

void LLAudioEngine::setMasterGain(const F32 gain)
{
	mMasterGain = gain;
	F32 internal_gain = getMuted() ? 0.f : gain;
	if (internal_gain != mInternalGain)
	{
		mInternalGain = internal_gain;
		setInternalGain(mInternalGain);
	}
}

F32 LLAudioEngine::getMasterGain()
{
	return mMasterGain;
}

void LLAudioEngine::setSecondaryGain(S32 type, F32 gain)
{
	llassert(type < LLAudioEngine::AUDIO_TYPE_COUNT);
	
	mSecondaryGain[type] = gain;
}

F32 LLAudioEngine::getSecondaryGain(S32 type)
{
	return mSecondaryGain[type];
}

F32 LLAudioEngine::getInternetStreamGain()
{
	if (mStreamingAudioImpl)
		return mStreamingAudioImpl->getGain();
	else
		return 1.0f;
}

void LLAudioEngine::setMaxWindGain(F32 gain)
{
	mMaxWindGain = gain;
}


F64 LLAudioEngine::mapWindVecToGain(LLVector3 wind_vec)
{
	F64 gain = 0.0;
	
	gain = wind_vec.magVec();

	if (gain)
	{
		if (gain > 20)
		{
			gain = 20;
		}
		gain = gain/20.0;
	}

	return (gain);
} 


F64 LLAudioEngine::mapWindVecToPitch(LLVector3 wind_vec)
{
	LLVector3 listen_right;
	F64 theta;
  
	// Wind frame is in listener-relative coordinates
	LLVector3 norm_wind = wind_vec;
	norm_wind.normVec();
	listen_right.setVec(1.0,0.0,0.0);

	// measure angle between wind vec and listener right axis (on 0,PI)
	theta = acos(norm_wind * listen_right);

	// put it on 0, 1
	theta /= F_PI;					

	// put it on [0, 0.5, 0]
	if (theta > 0.5) theta = 1.0-theta;
	if (theta < 0) theta = 0;

	return (theta);
}


F64 LLAudioEngine::mapWindVecToPan(LLVector3 wind_vec)
{
	LLVector3 listen_right;
	F64 theta;
  
	// Wind frame is in listener-relative coordinates
	listen_right.setVec(1.0,0.0,0.0);

	LLVector3 norm_wind = wind_vec;
	norm_wind.normVec();

	// measure angle between wind vec and listener right axis (on 0,PI)
	theta = acos(norm_wind * listen_right);

	// put it on 0, 1
	theta /= F_PI;					

	return (theta);
}


void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain,
								 // / <FS:Testy> Optional parameter for setting the audio source UUID
								 // const S32 type, const LLVector3d &pos_global, const LLUUID& source_object)
								 const S32 type, const LLVector3d &pos_global, const LLUUID& source_object, const LLUUID& audio_source_id)
{
	// Create a new source (since this can't be associated with an existing source.
	//LL_INFOS() << "Localized: " << audio_uuid << LL_ENDL;

	// NaCl - Do not load silent sounds.
	if (mMuted || gain <FLT_EPSILON*2)
	{
		return;
	}

	// <FS:Testy> Only generate the id if one wasn't passed to the method
	//LLUUID source_id;
	//source_id.generate();
	LLUUID source_id = audio_source_id;
	if (source_id.isNull())
	{
		source_id.generate();
	}

	LLAudioSource *asp = new LLAudioSource(source_id, owner_id, gain, type, source_object, true);
	addAudioSource(asp);
	if (pos_global.isExactlyZero())
	{
		// For sound preview and UI
		asp->setForcedPriority(true);
	}
	else
	{
		asp->setPositionGlobal(pos_global);
	}
	asp->updatePriority();
	asp->play(audio_uuid);
}

void LLAudioEngine::triggerSound(SoundData& soundData)
{
	triggerSound(soundData.audio_uuid, soundData.owner_id, soundData.gain, soundData.type, soundData.pos_global);
}

void LLAudioEngine::setListenerPos(LLVector3 aVec)
{
	mListenerp->setPosition(aVec);  
}


LLVector3 LLAudioEngine::getListenerPos()
{
	if (mListenerp)
	{
		return(mListenerp->getPosition());  
	}
	else
	{
		return(LLVector3::zero);
	}
}


void LLAudioEngine::setListenerVelocity(LLVector3 aVec)
{
	mListenerp->setVelocity(aVec);  
}


void LLAudioEngine::translateListener(LLVector3 aVec)
{
	mListenerp->translate(aVec);	
}


void LLAudioEngine::orientListener(LLVector3 up, LLVector3 at)
{
	mListenerp->orient(up, at);  
}


void LLAudioEngine::setListener(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at)
{
	mListenerp->set(pos,vel,up,at);  
}


void LLAudioEngine::setDopplerFactor(F32 factor)
{
	if (mListenerp)
	{
		mListenerp->setDopplerFactor(factor);  
	}
}


F32 LLAudioEngine::getDopplerFactor()
{
	if (mListenerp)
	{
		return mListenerp->getDopplerFactor();
	}
	else
	{
		return 0.f;
	}
}


void LLAudioEngine::setRolloffFactor(F32 factor)
{
	if (mListenerp)
	{
		mListenerp->setRolloffFactor(factor);  
	}
}


F32 LLAudioEngine::getRolloffFactor()
{
	if (mListenerp)
	{
		return mListenerp->getRolloffFactor();  
	}
	else
	{
		return 0.f;
	}
}


void LLAudioEngine::commitDeferredChanges()
{
	mListenerp->commitDeferredChanges();  
}


LLAudioSource * LLAudioEngine::findAudioSource(const LLUUID &source_id)
{
	source_map::iterator iter;
	iter = mAllSources.find(source_id);

	if (iter == mAllSources.end())
	{
		return NULL;
	}
	else
	{
		return iter->second;
	}
}


LLAudioData * LLAudioEngine::getAudioData(const LLUUID &audio_uuid)
{
	LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA;
	// <FS:ND> Protect against corrupted sounds. Just do a quick exit instead of trying to decode over and over again.
	if( isCorruptSound( audio_uuid ) )
		return 0;
	// </FS:ND>

	data_map::iterator iter;
	iter = mAllData.find(audio_uuid);
	if (iter == mAllData.end())
	{
		// Create the new audio data
		LLAudioData *adp = new LLAudioData(audio_uuid);
		mAllData[audio_uuid] = adp;
		return adp;
	}
	else
	{
		return iter->second;
	}
}

void LLAudioEngine::addAudioSource(LLAudioSource *asp)
{
	mAllSources[asp->getID()] = asp;
}


void LLAudioEngine::cleanupAudioSource(LLAudioSource *asp)
{
	source_map::iterator iter;
	iter = mAllSources.find(asp->getID());
	if (iter == mAllSources.end())
	{
		LL_WARNS() << "Cleaning up unknown audio source!" << LL_ENDL;
		return;
	}
	else
	{
		LL_DEBUGS("AudioEngine") << "Cleaning up audio sources for "<< asp->getID() <<LL_ENDL;
	delete asp;
	mAllSources.erase(iter);
}
}


bool LLAudioEngine::hasDecodedFile(const LLUUID &uuid)
{
	std::string uuid_str;
	uuid.toString(uuid_str);

	std::string wav_path;
	// <FS:Ansariel> Sound cache
	//wav_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str);
	wav_path = gDirUtilp->getExpandedFilename(LL_PATH_FS_SOUND_CACHE,uuid_str);
	// </FS:Ansariel>
	wav_path += ".dsf";

	if (gDirUtilp->fileExists(wav_path))
	{
		return true;
	}
	else
	{
		return false;
	}
}


bool LLAudioEngine::hasLocalFile(const LLUUID &uuid)
{
	// See if it's in the cache.
	bool have_local = LLFileSystem::getExists(uuid, LLAssetType::AT_SOUND);
	LL_DEBUGS("AudioEngine") << "sound uuid " << uuid << " exists in cache" << LL_ENDL;
	return have_local;
}

void LLAudioEngine::startNextTransfer()
{
	//LL_INFOS() << "LLAudioEngine::startNextTransfer()" << LL_ENDL;
	if (mCurrentTransfer.notNull() || getMuted())
	{
		//LL_INFOS() << "Transfer in progress, aborting" << LL_ENDL;
		return;
	}

	// Get the ID for the next asset that we want to transfer.
	// Pick one in the following order:
	LLUUID asset_id;
	S32 i;
	LLAudioSource *asp = NULL;
	LLAudioData *adp = NULL;
	data_map::iterator data_iter;

	// Check all channels for currently playing sounds.
	F32 max_pri = -1.f;
	for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
	{
		if (!mChannels[i])
		{
			continue;
		}

		asp = mChannels[i]->getSource();
		if (!asp)
		{
			continue;
		}
		if (asp->getPriority() <= max_pri)
		{
			continue;
		}

		if (asp->getPriority() <= max_pri)
		{
			continue;
		}

		adp = asp->getCurrentData();
		if (!adp)
		{
			continue;
		}

        if (!adp->hasLocalData() && !adp->hasDecodeFailed())
		{
			asset_id = adp->getID();
			max_pri = asp->getPriority();
		}
	}

	// Check all channels for currently queued sounds.
	if (asset_id.isNull())
	{
		max_pri = -1.f;
		for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
		{
			if (!mChannels[i])
			{
				continue;
			}

			LLAudioSource *asp;
			asp = mChannels[i]->getSource();
			if (!asp)
			{
				continue;
			}

			if (asp->getPriority() <= max_pri)
			{
				continue;
			}

			adp = asp->getQueuedData();
			if (!adp)
			{
				continue;
			}

            if (!adp->hasLocalData() && !adp->hasDecodeFailed())
			{
				asset_id = adp->getID();
				max_pri = asp->getPriority();
			}
		}
	}

	// Check all live channels for other sounds (preloads).
	if (asset_id.isNull())
	{
		max_pri = -1.f;
		for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++)
		{
			if (!mChannels[i])
			{
				continue;
			}

			LLAudioSource *asp;
			asp = mChannels[i]->getSource();
			if (!asp)
			{
				continue;
			}

			if (asp->getPriority() <= max_pri)
			{
				continue;
			}


			for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++)
			{
				LLAudioData *adp = data_iter->second;
				if (!adp)
				{
					continue;
				}

                if (!adp->hasLocalData() && !adp->hasDecodeFailed())
				{
					asset_id = adp->getID();
					max_pri = asp->getPriority();
				}
			}
		}
	}

	// Check all sources
	if (asset_id.isNull())
	{
		max_pri = -1.f;
		source_map::iterator source_iter;
		for (source_iter = mAllSources.begin(); source_iter != mAllSources.end(); source_iter++)
		{
			asp = source_iter->second;
			if (!asp)
			{
				continue;
			}

			if (asp->getPriority() <= max_pri)
			{
				continue;
			}

			adp = asp->getCurrentData();
            if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed())
			{
				asset_id = adp->getID();
				max_pri = asp->getPriority();
				continue;
			}

			adp = asp->getQueuedData();
            if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed())
			{
				asset_id = adp->getID();
				max_pri = asp->getPriority();
				continue;
			}

			for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++)
			{
				LLAudioData *adp = data_iter->second;
				if (!adp)
				{
					continue;
				}

                if (!adp->hasLocalData() && !adp->hasDecodeFailed())
				{
					asset_id = adp->getID();
					max_pri = asp->getPriority();
					break;
				}
			}
		}
	}

	if (asset_id.notNull())
	{
		//LL_INFOS() << "Getting asset data for: " << asset_id << LL_ENDL;
		mCurrentTransfer = asset_id;
		mCurrentTransferTimer.reset();
		gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND,
									assetCallback, NULL);
	}
	else
	{
		//LL_INFOS() << "No pending transfers?" << LL_ENDL;
	}
}


// static
void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code, LLExtStat ext_status)
{
	if (!gAudiop)
	{
		LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL;
		return;
	}

	if (result_code)
	{
		LL_INFOS() << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << LL_ENDL;
		// Need to mark data as bad to avoid constant rerequests.
		LLAudioData *adp = gAudiop->getAudioData(uuid);
		if (adp)
        {	// Make sure everything is cleared
            adp->setHasDecodeFailed(true);
			adp->setHasLocalData(false);
			adp->setHasDecodedData(false);
			adp->setHasCompletedDecode(true);
		}
	}
	else
	{
		LLAudioData *adp = gAudiop->getAudioData(uuid);
		if (!adp)
        {
			// Should never happen
			LL_WARNS() << "Got asset callback without audio data for " << uuid << LL_ENDL;
			gAudiop->removeAudioData( uuid ); // <FS:ND/> Remove this corrupt asset from the queue, or we're in danger of endless recursion.
        }
		else
		{
			// LL_INFOS() << "Got asset callback with good audio data for " << uuid << ", making decode request" << LL_ENDL;
            adp->setHasDecodeFailed(false);
		    adp->setHasLocalData(true);
            LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid);
		}
	}
	gAudiop->mCurrentTransfer = LLUUID::null;
	gAudiop->startNextTransfer();
}

// <FS:Ansariel> Output device selection
//virtual
LLAudioEngine::output_device_map_t LLAudioEngine::getDevices()
{
	return output_device_map_t();
}

void LLAudioEngine::OnOutputDeviceListChanged(output_device_map_t output_device_map)
{
	mOutputDeviceListChangedCallback(output_device_map);
}
// </FS:Ansariel>

//
// LLAudioSource implementation
//


LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32 gain, const S32 type, const LLUUID& source_id, const bool isTrigger)
:	mID(id),
	mOwnerID(owner_id),
	mPriority(0.f),
	mGain(gain),
	mSourceMuted(false),
	mForcedPriority(false),
	mLoop(false),
	mSyncMaster(false),
	mSyncSlave(false),
	mQueueSounds(false),
	mPlayedOnce(false),
	mCorrupted(false),
	mType(type),
	mChannelp(NULL),
	mCurrentDatap(NULL),
	mQueuedDatap(NULL),
// NaCl - Sound explorer
	mSourceID(source_id),
	mIsTrigger(isTrigger)
{
	mLogID.generate();
}

std::map<LLUUID, LLSoundHistoryItem> gSoundHistory;

// static
void LLAudioSource::logSoundPlay(const LLUUID& id, LLVector3d position, S32 type, const LLUUID& assetid, const LLUUID& ownerid, const LLUUID& sourceid, bool is_trigger, bool is_looped)
{
	// <FS:ND> Corrupt asset, do not bother
	if( gAudiop->isCorruptSound( assetid ) )
		return;
	// </FS:ND>

	// <FS:ND> Do not overflow our log here.
	pruneSoundLog();
	if( gSoundHistory.size() > 2048 )
		return; // Might clear out oldest entries before giving up?
	// </FS:ND>

	LLSoundHistoryItem item;
	item.mID = id;
	item.mPosition = position;
	item.mType = type;
	item.mAssetID = assetid;
	item.mOwnerID = ownerid;
	item.mSourceID = sourceid;
	item.mPlaying = true;
	item.mTimeStarted = LLTimer::getElapsedSeconds();
	item.mTimeStopped = F64_MAX;
	item.mIsTrigger = is_trigger;
	item.mIsLooped = is_looped;
	
	item.mReviewed = false;
	item.mReviewedCollision = false;
	
	gSoundHistory[id] = item;
}

// static
void LLAudioSource::logSoundStop(const LLUUID& id)
{
	if (gSoundHistory.find(id) != gSoundHistory.end())
	{
		gSoundHistory[id].mPlaying = false;
		gSoundHistory[id].mTimeStopped = LLTimer::getElapsedSeconds();
		pruneSoundLog();
	}
}

// static
void LLAudioSource::pruneSoundLog()
{
	if (++sSoundHistoryPruneCounter >= 64)
	{
		sSoundHistoryPruneCounter = 0;
		while (gSoundHistory.size() > 256)
		{
			std::map<LLUUID, LLSoundHistoryItem>::iterator iter = gSoundHistory.begin();
			std::map<LLUUID, LLSoundHistoryItem>::iterator end = gSoundHistory.end();
			U64 lowest_time = (*iter).second.mTimeStopped;
			LLUUID lowest_id = (*iter).first;
			for ( ; iter != end; ++iter)
			{
				if ((*iter).second.mTimeStopped < lowest_time)
				{
					lowest_time = (*iter).second.mTimeStopped;
					lowest_id = (*iter).first;
				}
			}
			gSoundHistory.erase(lowest_id);
		}
	}
}
// NaCl End


LLAudioSource::~LLAudioSource()
{
	if (mChannelp)
	{
		// Stop playback of this sound
		mChannelp->setSource(NULL);
		mChannelp = NULL;
	}
	// NaCl - Sound Explorer
	if (mType != LLAudioEngine::AUDIO_TYPE_UI) // && mSourceID.notNull())
	{
		logSoundStop(mLogID);
	}
	// NaCl End
}


void LLAudioSource::setChannel(LLAudioChannel *channelp)
{
	if (channelp == mChannelp)
	{
		return;
	}

	mChannelp = channelp;
}


void LLAudioSource::update()
{
	if(mCorrupted)
	{
		return ; //no need to update
	}

	if (!getCurrentBuffer())
	{
		LLAudioData *adp = getCurrentData();
		if (adp)
		{
			// Hack - try and load the sound.  Will do this as a callback
			// on decode later.
            if (adp->getBuffer())
			{
				play(adp->getID());
			}
            else if (adp->hasDecodedData() && !adp->hasWAVLoadFailed())
            {
                adp->load();
            }
            else if (adp->hasCompletedDecode() && adp->hasDecodeFailed()) // Only mark corrupted after decode is done
			{
				LL_WARNS() << "Marking LLAudioSource corrupted for " << adp->getID() << LL_ENDL;
				mCorrupted = true ;
				gAudiop->markSoundCorrupt( adp->getID() );
			}
		}
	}
}

void LLAudioSource::updatePriority()
{
	if (isForcedPriority())
	{
		mPriority = 1.f;
	}
	else if (isMuted())
	{
		mPriority = 0.f;
	}
	else
	{
		// Priority is based on distance
		LLVector3 dist_vec;
		dist_vec.setVec(getPositionGlobal());

		if (gAudiop)
		{
			dist_vec -= gAudiop->getListenerPos();
		}

		F32 dist_squared = llmax(1.f, dist_vec.magVecSquared());

		mPriority = mGain / dist_squared;
	}
}

bool LLAudioSource::setupChannel()
{
	LLAudioData *adp = getCurrentData();

	if (!adp->getBuffer())
	{
		// We're not ready to play back the sound yet, so don't try and allocate a channel for it.
		//LL_WARNS() << "Aborting, no buffer" << LL_ENDL;
		return false;
	}

	if (!gAudiop)
	{
		LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL;
		return false;
	}

	if (!mChannelp)
	{
		// Update the priority, in case we need to push out another channel.
		updatePriority();

		setChannel(gAudiop->getFreeChannel(getPriority()));
	}

	if (!mChannelp)
	{
		// Ugh, we don't have any free channels.
		// Now we have to reprioritize.
		// For now, just don't play the sound.
		//LL_WARNS() << "Aborting, no free channels" << LL_ENDL;
		return false;
	}

	mChannelp->setSource(this);
	return true;
}

void LLAudioSource::stop()
{
    play(LLUUID::null);
    if (mCurrentDatap)
    {
        // always reset data if something wants us to stop
        mCurrentDatap = nullptr;
    }
}

bool LLAudioSource::play(const LLUUID &audio_uuid)
{
	// NaCl - Sound Explorer
	if(mType != LLAudioEngine::AUDIO_TYPE_UI) //&& mSourceID.notNull())
	{
		logSoundPlay(mLogID, mPositionGlobal, mType, audio_uuid, mOwnerID, mSourceID, mIsTrigger, mLoop);
	}
	// NaCl End
	// Special abuse of play(); don't play a sound, but kill it.
	if (audio_uuid.isNull())
	{
		if (getChannel())
		{
			// NaCl - Sound Explorer
			if(getChannel()->getSource())
			// NaCl End
			getChannel()->setSource(NULL);
			setChannel(NULL);
			if (!isMuted())
			{
				mCurrentDatap = NULL;
			}
		}
		return false;
	}

	// Reset our age timeout if someone attempts to play the source.
	mAgeTimer.reset();

	if (!gAudiop)
	{
		LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL;
		return false;
	}

	LLAudioData *adp = gAudiop->getAudioData(audio_uuid);
	if( !adp )
		return false;
	addAudioData(adp);

	if (isMuted())
	{
		return false;
	}

	bool has_buffer = gAudiop->updateBufferForData(adp, audio_uuid);
	if (!has_buffer)
	{
		// Don't bother trying to set up a channel or anything, we don't have an audio buffer.
		return false;
	}

	if (!setupChannel())
	{
		return false;
	}

	if (isSyncSlave())
	{
		// A sync slave, it doesn't start playing until it's synced up with the master.
		// Flag this channel as waiting for sync, and return true.
		getChannel()->setWaiting(true);
		return true;
	}

	getChannel()->play();
	return true;
}


bool LLAudioSource::isDone() const
{
	if(mCorrupted)
	{
		return true ;
	}

	const F32 MAX_AGE = 60.f;
	const F32 MAX_UNPLAYED_AGE = 15.f;
	const F32 MAX_MUTED_AGE = 11.f;

	if (isLoop())
	{
		// Looped sources never die on their own.
		return false;
	}

	if (hasPendingPreloads())
	{
		return false;
	}

	if (mQueuedDatap)
	{
		// Don't kill this sound if we've got something queued up to play.
		return false;
	}

	F32 elapsed = mAgeTimer.getElapsedTimeF32();

	// This is a single-play source
	if (!mChannelp)
	{
		if ((elapsed > (mSourceMuted ? MAX_MUTED_AGE : MAX_UNPLAYED_AGE)) || mPlayedOnce)
		{
			// We don't have a channel assigned, and it's been
			// over 15 seconds since we tried to play it.  Don't bother.
			//LL_INFOS() << "No channel assigned, source is done" << LL_ENDL;
			return true;
		}
		else
		{
			return false;
		}
	}

	if (mChannelp->isPlaying())
	{
		if (elapsed > MAX_AGE)
		{
			// Arbitarily cut off non-looped sounds when they're old.
			return true;
		}
		else
		{
			// Sound is still playing and we haven't timed out, don't kill it.
			return false;
		}
	}

	if ((elapsed > MAX_UNPLAYED_AGE) || mPlayedOnce)
	{
		// The sound isn't playing back after 15 seconds or we're already done playing it, kill it.
		return true;
	}

	return false;
}


void LLAudioSource::addAudioData(LLAudioData *adp, const bool set_current)
{
	// Only handle a single piece of audio data associated with a source right now,
	// until I implement prefetch.

	if (!gAudiop)
	{
		LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL;
		return;
	}

	if (set_current)
	{
		if (!mCurrentDatap)
		{
			mCurrentDatap = adp;
			if (mChannelp)
			{
				mChannelp->updateBuffer();
				mChannelp->play();
			}

			// Make sure the audio engine knows that we want to request this sound.
			gAudiop->startNextTransfer();
			return;
		}
		else if (mQueueSounds)
		{
			// If we have current data, and we're queuing, put
			// the object onto the queue.
			if (mQueuedDatap)
			{
				// We only queue one sound at a time, and it's a FIFO.
				// Don't put it onto the queue.
				return;
			}

			if (adp == mCurrentDatap && isLoop())
			{
				// No point in queueing the same sound if
				// we're looping.
				return;
			}
			mQueuedDatap = adp;

			// Make sure the audio engine knows that we want to request this sound.
			gAudiop->startNextTransfer();
		}
		else
		{
			if (mCurrentDatap != adp)
			{
				// Right now, if we're currently playing this sound in a channel, we
				// update the buffer that the channel's associated with
				// and play it.  This may not be the correct behavior.
				mCurrentDatap = adp;
				if (mChannelp)
				{
					mChannelp->updateBuffer();
					mChannelp->play();
				}
				// Make sure the audio engine knows that we want to request this sound.
				gAudiop->startNextTransfer();
			}
		}
	}
	else
	{
		// Add it to the preload list.
		mPreloadMap[adp->getID()] = adp;
		gAudiop->startNextTransfer();
	}
}


bool LLAudioSource::hasPendingPreloads() const
{
	// Check to see if we've got any preloads on deck for this source
	data_map::const_iterator iter;
	for (iter = mPreloadMap.begin(); iter != mPreloadMap.end(); iter++)
	{
		LLAudioData *adp = iter->second;
		// note: a bad UUID will forever be !hasDecodedData()
        // but also hasDecodeFailed(), hence the check for hasDecodeFailed()
		if (!adp)
		{
			continue;
		}
        if (!adp->hasDecodedData() && !adp->hasDecodeFailed())
		{
			// This source is still waiting for a preload
			return true;
		}
	}

	return false;
}


LLAudioData * LLAudioSource::getCurrentData()
{
	return mCurrentDatap;
}

LLAudioData * LLAudioSource::getQueuedData()
{
	return mQueuedDatap;
}

LLAudioBuffer * LLAudioSource::getCurrentBuffer()
{
	if (!mCurrentDatap)
	{
		return NULL;
	}

	return mCurrentDatap->getBuffer();
}




//
// LLAudioChannel implementation
//


LLAudioChannel::LLAudioChannel() :
	mCurrentSourcep(NULL),
	mCurrentBufferp(NULL),
	mLoopedThisFrame(false),
	mWaiting(false),
	mSecondaryGain(1.0f)
{
}


LLAudioChannel::~LLAudioChannel()
{
	// Need to disconnect any sources which are using this channel.
	//LL_INFOS() << "Cleaning up audio channel" << LL_ENDL;
	if (mCurrentSourcep)
	{
		mCurrentSourcep->setChannel(NULL);
	}
	mCurrentBufferp = NULL;
}


void LLAudioChannel::setSource(LLAudioSource *sourcep)
{
	//LL_INFOS() << this << ": setSource(" << sourcep << ")" << LL_ENDL;

	if (!sourcep)
	{
		// Clearing the source for this channel, don't need to do anything.
		//LL_INFOS() << "Clearing source for channel" << LL_ENDL;
		cleanup();
		mCurrentSourcep = NULL;
		mWaiting = false;
	}
	else
	{
		LL_DEBUGS("AudioEngine") << "( id: " << sourcep->getID() << ")" << LL_ENDL;

	if (sourcep == mCurrentSourcep)
	{
		// Don't reallocate the channel, this will make FMOD goofy.
		//LL_INFOS() << "Calling setSource with same source!" << LL_ENDL;
	}

	mCurrentSourcep = sourcep;

	updateBuffer();
	update3DPosition();
}
}


bool LLAudioChannel::updateBuffer()
{
	if (!gAudiop)
	{
		LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL;
		return false;
	}

	if (!mCurrentSourcep)
	{
		// This channel isn't associated with any source, nothing
		// to be updated
		return false;
	}

	// Initialize the channel's gain setting for this sound.
	setSecondaryGain(gAudiop->getSecondaryGain(mCurrentSourcep->getType()));

	LLAudioBuffer *bufferp = mCurrentSourcep->getCurrentBuffer();
	if (bufferp == mCurrentBufferp)
	{
		if (bufferp)
		{
			// The source hasn't changed what buffer it's playing
			bufferp->mLastUseTimer.reset();
			bufferp->mInUse = true;
		}
		return false;
	}

	//
	// The source changed what buffer it's playing.  We need to clean up
	// the existing channel
	//
	cleanup();

	mCurrentBufferp = bufferp;
	if (bufferp)
	{
		bufferp->mLastUseTimer.reset();
		bufferp->mInUse = true;
	}

	if (!mCurrentBufferp)
	{
		// There's no new buffer to be played, so we just abort.
		return false;
	}

	return true;
}




//
// LLAudioData implementation
//


LLAudioData::LLAudioData(const LLUUID &uuid) :
	mID(uuid),
	mBufferp(NULL),
	mHasLocalData(false),
	mHasDecodedData(false),
	mHasCompletedDecode(false),
    mHasDecodeFailed(false),
    mHasWAVLoadFailed(false)
{
	if (uuid.isNull())
	{
		// This is a null sound.
		return;
	}

	if (!gAudiop)
	{
		LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL;
		return;
	}
	
	if (gAudiop->hasDecodedFile(uuid))
	{
		// Already have a decoded version, don't need to decode it.
		setHasLocalData(true);
		setHasDecodedData(true);
		setHasCompletedDecode(true);
	}
	else if (gAssetStorage && gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
	{
		setHasLocalData(true);
	}
}

//return false when the audio file is corrupted.
bool LLAudioData::load()
{
	// For now, just assume we're going to use one buffer per audiodata.
	if (mBufferp)
	{
		// We already have this sound in a buffer, don't do anything.
		LL_INFOS() << "Already have a buffer for this sound, don't bother loading!" << LL_ENDL;
        mHasWAVLoadFailed = false;
		return true;
	}

	if (!gAudiop)
	{
		LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL;
        mHasWAVLoadFailed = true;
		return false;
	}
	
	mBufferp = gAudiop->getFreeBuffer();
	if (!mBufferp)
	{
		// No free buffers, abort.
		LL_INFOS() << "Not able to allocate a new audio buffer, aborting." << LL_ENDL;
        // *TODO: Mark this failure differently so the audio engine could retry loading this buffer in the future
        mHasWAVLoadFailed = true;
		return true;
	}

	std::string uuid_str;
	std::string wav_path;
	mID.toString(uuid_str);
	// <FS:Ansariel> Sound cache
	//wav_path= gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf";
	wav_path= gDirUtilp->getExpandedFilename(LL_PATH_FS_SOUND_CACHE,uuid_str) + ".dsf";
	// </FS:Ansariel>

    mHasWAVLoadFailed = !mBufferp->loadWAV(wav_path);
    if (mHasWAVLoadFailed)
	{
		// Hrm.  Right now, let's unset the buffer, since it's empty.
		gAudiop->cleanupBuffer(mBufferp);
		mBufferp = NULL;

		// <FS:Ansariel> FIRE-480: Opening multiple instances causes sound failures
		if (!gDirUtilp->fileExists(wav_path))
		{
			mHasLocalData = false;
			mHasDecodedData = false;
			mHasCompletedDecode = false;
			mHasDecodeFailed = false;
			mHasWAVLoadFailed = false;
			gAudiop->preloadSound(mID);
		}
		// </FS:Ansariel>

		return false;
	}
	mBufferp->mAudioDatap = this;
	return true;
}

// <FS:ND> Protect against corrupted sounds

const U32 ND_MAX_SOUNDRETRIES = 25;

void LLAudioEngine::markSoundCorrupt( LLUUID const &aId )
{
	std::map<LLUUID,U32>::iterator itr = mCorruptData.find( aId );
	if( mCorruptData.end() == itr )
		mCorruptData[ aId ] = 1;
	else if( itr->second != ND_MAX_SOUNDRETRIES )
		itr->second += 1;
}

bool LLAudioEngine::isCorruptSound( LLUUID const &aId ) const
{
	std::map<LLUUID,U32>::const_iterator itr = mCorruptData.find( aId );
	if( mCorruptData.end() == itr )
		return false;

	return itr->second == ND_MAX_SOUNDRETRIES;
}
// </FS:ND>

// <FS:Ansariel> Asset blacklisting
void LLAudioEngine::removeAudioData(const LLUUID& audio_uuid)
{
	if (audio_uuid.isNull())
	{
		return;
	}

	data_map::iterator iter = mAllData.find(audio_uuid);
	if (iter != mAllData.end())
	{
		uuid_vec_t delete_list;
		source_map::iterator iter2;
		for (iter2 = mAllSources.begin(); iter2 != mAllSources.end(); ++iter2)
		{
			LLAudioSource* sourcep = iter2->second;
			if (sourcep && sourcep->getCurrentData() && sourcep->getCurrentData()->getID() == audio_uuid)
			{
				delete_list.push_back(iter2->first);
			}
		}

		uuid_vec_t::iterator delete_list_end = delete_list.end();
		for (uuid_vec_t::iterator del_it = delete_list.begin(); del_it != delete_list_end; ++del_it)
		{
			LLUUID source_id = *del_it;
			LLAudioSource* sourcep = mAllSources[source_id];
			LLAudioChannel* chan = sourcep->getChannel();
			delete sourcep;
			mAllSources.erase(source_id);
			if (chan)
			{
				chan->cleanup();
			}
		}
		
		LLAudioData* data = iter->second;
		if (data)
		{
			LLAudioBuffer* buf = data->getBuffer();
			if (buf)
			{
				S32 i;
				for (i = 0; i < LL_MAX_AUDIO_BUFFERS; ++i)
				{
					if (mBuffers[i] == buf)
					{
						mBuffers[i] = NULL;
					}
				}
				delete buf;
			}
			delete data;
		}

		mAllData.erase(audio_uuid);
	}
}
// </FS:Ansariel>
