summaryrefslogtreecommitdiff
path: root/indra/llaudio
diff options
context:
space:
mode:
authorMonroe Williams <monroe@lindenlab.com>2009-08-27 19:00:18 +0000
committerMonroe Williams <monroe@lindenlab.com>2009-08-27 19:00:18 +0000
commit745845f79987e4b4ab7f5728746a0eda8898930f (patch)
treef10efd4a638a6a7eda92a960cdb97e5256ff736a /indra/llaudio
parent71344b233d5ae3d5262a492b636af04544952611 (diff)
svn merge -r 129841:129910 svn+ssh://svn.lindenlab.com/svn/linden/branches/moss/pluginapi_05-merge@129910
svn merge -r 129913:131718 svn+ssh://svn.lindenlab.com/svn/linden/branches/pluginapi/pluginapi_05 Some branch shenannigans in the pluginapi_05 branch caused this to become a two-part merge.
Diffstat (limited to 'indra/llaudio')
-rw-r--r--indra/llaudio/CMakeLists.txt42
-rw-r--r--indra/llaudio/llaudiodecodemgr.cpp4
-rw-r--r--indra/llaudio/llaudioengine.cpp1751
-rw-r--r--indra/llaudio/llaudioengine.h452
-rw-r--r--indra/llaudio/llaudioengine_fmod.cpp766
-rw-r--r--indra/llaudio/llaudioengine_fmod.h129
-rw-r--r--indra/llaudio/llaudioengine_openal.cpp546
-rw-r--r--indra/llaudio/llaudioengine_openal.h114
-rw-r--r--indra/llaudio/lllistener.cpp142
-rw-r--r--indra/llaudio/lllistener.h78
-rw-r--r--indra/llaudio/lllistener_ds3d.h74
-rw-r--r--indra/llaudio/lllistener_fmod.cpp131
-rw-r--r--indra/llaudio/lllistener_fmod.h64
-rw-r--r--indra/llaudio/lllistener_openal.cpp116
-rw-r--r--indra/llaudio/lllistener_openal.h64
-rw-r--r--indra/llaudio/llstreamingaudio.h56
-rw-r--r--indra/llaudio/llstreamingaudio_fmod.cpp362
-rw-r--r--indra/llaudio/llstreamingaudio_fmod.h68
-rw-r--r--indra/llaudio/llvorbisencode.cpp505
-rw-r--r--indra/llaudio/llvorbisencode.h53
-rw-r--r--indra/llaudio/llwindgen.h136
21 files changed, 5631 insertions, 22 deletions
diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt
index 235248ee73..80245fd569 100644
--- a/indra/llaudio/CMakeLists.txt
+++ b/indra/llaudio/CMakeLists.txt
@@ -4,15 +4,16 @@ project(llaudio)
include(00-Common)
include(Audio)
+include(LLAudio)
include(FMOD)
include(OPENAL)
include(LLCommon)
include(LLMath)
include(LLMessage)
include(LLVFS)
-include(LLMedia)
include_directories(
+ ${LLAUDIO_INCLUDE_DIRS}
${FMOD_INCLUDE_DIR}
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
@@ -24,42 +25,43 @@ include_directories(
${VORBIS_INCLUDE_DIRS}
${OPENAL_LIB_INCLUDE_DIRS}
${FREEAULT_LIB_INCLUDE_DIRS}
- ${LLMEDIA_INCLUDE_DIRS}
)
set(llaudio_SOURCE_FILES
- audioengine.cpp
- listener.cpp
+ llaudioengine.cpp
+ lllistener.cpp
llaudiodecodemgr.cpp
- vorbisdecode.cpp
- vorbisencode.cpp
+ llvorbisdecode.cpp
+ llvorbisencode.cpp
)
set(llaudio_HEADER_FILES
CMakeLists.txt
- audioengine.h
- listener.h
+ llaudioengine.h
+ lllistener.h
llaudiodecodemgr.h
- vorbisdecode.h
- vorbisencode.h
- windgen.h
+ llvorbisdecode.h
+ llvorbisencode.h
+ llwindgen.h
)
if (FMOD)
list(APPEND llaudio_SOURCE_FILES
- audioengine_fmod.cpp
- listener_fmod.cpp
+ llaudioengine_fmod.cpp
+ lllistener_fmod.cpp
+ llstreamingaudio_fmod.cpp
)
list(APPEND llaudio_HEADER_FILES
- audioengine_fmod.h
- listener_fmod.h
+ llaudioengine_fmod.h
+ lllistener_fmod.h
+ llstreamingaudio_fmod.h
)
if (LINUX)
if (${CXX_VERSION} MATCHES "4.[23]")
- set_source_files_properties(audioengine_fmod.cpp
+ set_source_files_properties(llaudioengine_fmod.cpp
COMPILE_FLAGS -Wno-error=write-strings)
endif (${CXX_VERSION} MATCHES "4.[23]")
endif (LINUX)
@@ -67,13 +69,13 @@ endif (FMOD)
if (OPENAL)
list(APPEND llaudio_SOURCE_FILES
- audioengine_openal.cpp
- listener_openal.cpp
+ llaudioengine_openal.cpp
+ lllistener_openal.cpp
)
list(APPEND llaudio_HEADER_FILES
- audioengine_openal.h
- listener_openal.h
+ llaudioengine_openal.h
+ lllistener_openal.h
)
endif (OPENAL)
diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp
index 6a494d1983..099c4eba40 100644
--- a/indra/llaudio/llaudiodecodemgr.cpp
+++ b/indra/llaudio/llaudiodecodemgr.cpp
@@ -33,8 +33,8 @@
#include "llaudiodecodemgr.h"
-#include "vorbisdecode.h"
-#include "audioengine.h"
+#include "llvorbisdecode.h"
+#include "llaudioengine.h"
#include "lllfsthread.h"
#include "llvfile.h"
#include "llstring.h"
diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp
new file mode 100644
index 0000000000..a28c94d00d
--- /dev/null
+++ b/indra/llaudio/llaudioengine.cpp
@@ -0,0 +1,1751 @@
+ /**
+ * @file audioengine.cpp
+ * @brief implementation of LLAudioEngine class abstracting the Open
+ * AL audio support
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "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 "llvfs.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;
+
+
+//
+// LLAudioEngine implementation
+//
+
+
+LLAudioEngine::LLAudioEngine()
+{
+ setDefaults();
+}
+
+
+LLAudioEngine::~LLAudioEngine()
+{
+}
+
+LLStreamingAudioInterface* LLAudioEngine::getStreamingAudioImpl()
+{
+ return mStreamingAudioImpl;
+}
+
+void LLAudioEngine::setStreamingAudioImpl(LLStreamingAudioInterface *impl)
+{
+ mStreamingAudioImpl = impl;
+}
+
+void LLAudioEngine::setDefaults()
+{
+ mMaxWindGain = 1.f;
+
+ mListenerp = NULL;
+
+ mMuted = false;
+ mUserData = NULL;
+
+ mLastStatus = 0;
+
+ mNumChannels = 0;
+ mEnableWind = false;
+
+ S32 i;
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ mChannels[i] = NULL;
+ }
+ for (i = 0; i < MAX_BUFFERS; i++)
+ {
+ mBuffers[i] = NULL;
+ }
+
+ mMasterGain = 1.f;
+ mNextWindUpdate = 0.f;
+
+ mStreamingAudioImpl = NULL;
+
+ for (U32 i = 0; i < LLAudioEngine::AUDIO_TYPE_COUNT; i++)
+ mSecondaryGain[i] = 1.0f;
+}
+
+
+bool LLAudioEngine::init(const S32 num_channels, void* userdata)
+{
+ setDefaults();
+
+ mNumChannels = num_channels;
+ mUserData = userdata;
+
+ allocateListener();
+
+ // Initialize the decode manager
+ gAudioDecodeMgrp = new LLAudioDecodeMgr;
+
+ llinfos << "LLAudioEngine::init() AudioEngine successfully initialized" << llendl;
+
+ return true;
+}
+
+
+void LLAudioEngine::shutdown()
+{
+ // Clean up decode manager
+ delete gAudioDecodeMgrp;
+ gAudioDecodeMgrp = NULL;
+
+ // 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 < MAX_CHANNELS; i++)
+ {
+ delete mChannels[i];
+ mChannels[i] = NULL;
+ }
+
+ // Clean up buffers
+ for (i = 0; i < MAX_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(int pause)
+{
+ if (mStreamingAudioImpl)
+ mStreamingAudioImpl->pause(pause);
+}
+
+// virtual
+void LLAudioEngine::updateInternetStream()
+{
+ if (mStreamingAudioImpl)
+ mStreamingAudioImpl->update();
+}
+
+// virtual
+int LLAudioEngine::isInternetStreamPlaying()
+{
+ if (mStreamingAudioImpl)
+ return mStreamingAudioImpl->isPlaying();
+
+ return 0; // 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 < MAX_CHANNELS; i++)
+ {
+ if (mChannels[i])
+ {
+ mChannels[i]->updateBuffer();
+ mChannels[i]->update3DPosition();
+ mChannels[i]->updateLoop();
+ }
+ }
+}
+
+static const F32 default_max_decode_time = .002f; // 2 ms
+void LLAudioEngine::idle(F32 max_decode_time)
+{
+ if (max_decode_time <= 0.f)
+ {
+ max_decode_time = default_max_decode_time;
+ }
+
+ // "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.
+
+ if (getMuted())
+ {
+ setInternalGain(0.f);
+ }
+ else
+ {
+ setInternalGain(getMasterGain());
+ }
+
+ S32 i;
+ for (i = 0; i < MAX_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;
+ mAllSources.erase(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)
+ {
+ //llinfos << "Replacing source in channel due to priority!" << llendl;
+ 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)
+ {
+ // 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->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 < MAX_BUFFERS; i++)
+ {
+ if (mBuffers[i])
+ {
+ if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f)
+ {
+ //llinfos << "Flushing unused buffer!" << llendl;
+ mBuffers[i]->mAudioDatap->mBufferp = NULL;
+ delete mBuffers[i];
+ mBuffers[i] = NULL;
+ }
+ }
+ }
+
+
+ // Clear all of the looped flags for the channels
+ for (i = 0; i < MAX_CHANNELS; i++)
+ {
+ if (mChannels[i])
+ {
+ mChannels[i]->mLoopedThisFrame = false;
+ }
+ }
+
+ // Decode audio files
+ gAudioDecodeMgrp->processQueue(max_decode_time);
+
+ // 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())
+ {
+ gAudioDecodeMgrp->addDecodeRequest(audio_uuid);
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+void LLAudioEngine::enableWind(bool enable)
+{
+ if (enable && (!mEnableWind))
+ {
+ initWind();
+ mEnableWind = enable;
+ }
+ else if (mEnableWind && (!enable))
+ {
+ mEnableWind = enable;
+ cleanupWind();
+ }
+}
+
+
+LLAudioBuffer * LLAudioEngine::getFreeBuffer()
+{
+ S32 i;
+ for (i = 0; i < MAX_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 < MAX_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)
+ {
+ llinfos << "Taking over unused buffer " << buffer_id << llendl;
+ //llinfos << "Flushing unused buffer!" << llendl;
+ 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 < mNumChannels; 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 < mNumChannels; 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 < MAX_BUFFERS; i++)
+ {
+ if (mBuffers[i] == bufferp)
+ {
+ delete mBuffers[i];
+ mBuffers[i] = NULL;
+ }
+ }
+}
+
+
+bool LLAudioEngine::preloadSound(const LLUUID &uuid)
+{
+ gAudiop->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 (gAudioDecodeMgrp->addDecodeRequest(uuid))
+ {
+ // This means that we do have a local copy, and we're working on decoding it.
+ return true;
+ }
+
+ // At some point we need to have the audio/asset system check the static VFS
+ // before it goes off and fetches stuff from the server.
+ //llwarns << "Used internal preload for non-local sound" << llendl;
+ return false;
+}
+
+
+bool LLAudioEngine::isWindEnabled()
+{
+ return mEnableWind;
+}
+
+
+void LLAudioEngine::setMuted(bool muted)
+{
+ mMuted = muted;
+ enableWind(!mMuted);
+}
+
+
+void LLAudioEngine::setMasterGain(const F32 gain)
+{
+ mMasterGain = gain;
+ setInternalGain(gain);
+}
+
+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,
+ const S32 type, const LLVector3d &pos_global)
+{
+ // Create a new source (since this can't be associated with an existing source.
+ //llinfos << "Localized: " << audio_uuid << llendl;
+
+ if (mMuted)
+ {
+ return;
+ }
+
+ LLUUID source_id;
+ source_id.generate();
+
+ LLAudioSource *asp = new LLAudioSource(source_id, owner_id, gain, type);
+ gAudiop->addAudioSource(asp);
+ if (pos_global.isExactlyZero())
+ {
+ asp->setAmbient(true);
+ }
+ else
+ {
+ asp->setPositionGlobal(pos_global);
+ }
+ asp->updatePriority();
+ asp->play(audio_uuid);
+}
+
+
+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)
+{
+ 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())
+ {
+ llwarns << "Cleaning up unknown audio source!" << llendl;
+ return;
+ }
+ delete asp;
+ mAllSources.erase(iter);
+}
+
+
+bool LLAudioEngine::hasDecodedFile(const LLUUID &uuid)
+{
+ std::string uuid_str;
+ uuid.toString(uuid_str);
+
+ std::string wav_path;
+ wav_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str);
+ 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 VFS.
+ return gVFS->getExists(uuid, LLAssetType::AT_SOUND);
+}
+
+
+void LLAudioEngine::startNextTransfer()
+{
+ //llinfos << "LLAudioEngine::startNextTransfer()" << llendl;
+ if (mCurrentTransfer.notNull() || getMuted())
+ {
+ //llinfos << "Transfer in progress, aborting" << llendl;
+ 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 < MAX_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->hasValidData())
+ {
+ 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 < MAX_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->hasValidData())
+ {
+ 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 < MAX_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->hasValidData())
+ {
+ 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->hasValidData())
+ {
+ asset_id = adp->getID();
+ max_pri = asp->getPriority();
+ continue;
+ }
+
+ adp = asp->getQueuedData();
+ if (adp && !adp->hasLocalData() && adp->hasValidData())
+ {
+ 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->hasValidData())
+ {
+ asset_id = adp->getID();
+ max_pri = asp->getPriority();
+ break;
+ }
+ }
+ }
+ }
+
+ if (asset_id.notNull())
+ {
+ llinfos << "Getting asset data for: " << asset_id << llendl;
+ gAudiop->mCurrentTransfer = asset_id;
+ gAudiop->mCurrentTransferTimer.reset();
+ gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND,
+ assetCallback, NULL);
+ }
+ else
+ {
+ //llinfos << "No pending transfers?" << llendl;
+ }
+}
+
+
+// static
+void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code, LLExtStat ext_status)
+{
+ if (result_code)
+ {
+ llinfos << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << llendl;
+ // Need to mark data as bad to avoid constant rerequests.
+ LLAudioData *adp = gAudiop->getAudioData(uuid);
+ if (adp)
+ {
+ adp->setHasValidData(false);
+ adp->setHasLocalData(false);
+ adp->setHasDecodedData(false);
+ }
+ }
+ else
+ {
+ LLAudioData *adp = gAudiop->getAudioData(uuid);
+ if (!adp)
+ {
+ // Should never happen
+ llwarns << "Got asset callback without audio data for " << uuid << llendl;
+ }
+ else
+ {
+ adp->setHasValidData(true);
+ adp->setHasLocalData(true);
+ gAudioDecodeMgrp->addDecodeRequest(uuid);
+ }
+ }
+ gAudiop->mCurrentTransfer = LLUUID::null;
+ gAudiop->startNextTransfer();
+}
+
+
+//
+// LLAudioSource implementation
+//
+
+
+LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32 gain, const S32 type)
+: mID(id),
+ mOwnerID(owner_id),
+ mPriority(0.f),
+ mGain(gain),
+ mType(type),
+ mAmbient(false),
+ mLoop(false),
+ mSyncMaster(false),
+ mSyncSlave(false),
+ mQueueSounds(false),
+ mPlayedOnce(false),
+ mChannelp(NULL),
+ mCurrentDatap(NULL),
+ mQueuedDatap(NULL)
+{
+}
+
+
+LLAudioSource::~LLAudioSource()
+{
+ if (mChannelp)
+ {
+ // Stop playback of this sound
+ mChannelp->setSource(NULL);
+ mChannelp = NULL;
+ }
+}
+
+
+void LLAudioSource::setChannel(LLAudioChannel *channelp)
+{
+ if (channelp == mChannelp)
+ {
+ return;
+ }
+
+ mChannelp = channelp;
+}
+
+
+void LLAudioSource::update()
+{
+ if (!getCurrentBuffer())
+ {
+ if (getCurrentData())
+ {
+ // Hack - try and load the sound. Will do this as a callback
+ // on decode later.
+ if (getCurrentData()->load())
+ {
+ play(getCurrentData()->getID());
+ }
+ }
+ }
+}
+
+void LLAudioSource::updatePriority()
+{
+ if (isAmbient())
+ {
+ mPriority = 1.f;
+ }
+ else
+ {
+ // Priority is based on distance
+ LLVector3 dist_vec;
+ dist_vec.setVec(getPositionGlobal());
+ 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.
+ //llwarns << "Aborting, no buffer" << llendl;
+ 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.
+ //llwarns << "Aborting, no free channels" << llendl;
+ return false;
+ }
+
+ mChannelp->setSource(this);
+ return true;
+}
+
+
+bool LLAudioSource::play(const LLUUID &audio_uuid)
+{
+ if (audio_uuid.isNull())
+ {
+ if (getChannel())
+ {
+ getChannel()->setSource(NULL);
+ setChannel(NULL);
+ addAudioData(NULL, true);
+ }
+ }
+ // Reset our age timeout if someone attempts to play the source.
+ mAgeTimer.reset();
+
+ LLAudioData *adp = gAudiop->getAudioData(audio_uuid);
+
+ bool has_buffer = gAudiop->updateBufferForData(adp, audio_uuid);
+
+
+ addAudioData(adp);
+
+ 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 F32 MAX_AGE = 60.f;
+ const F32 MAX_UNPLAYED_AGE = 15.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 > MAX_UNPLAYED_AGE) || mPlayedOnce)
+ {
+ // We don't have a channel assigned, and it's been
+ // over 5 seconds since we tried to play it. Don't bother.
+ //llinfos << "No channel assigned, source is done" << llendl;
+ 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 5 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 (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 !hasValidData(), hence the check for hasValidData()
+ if (!adp->hasDecodedData() && adp->hasValidData())
+ {
+ // 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.
+ //llinfos << "Cleaning up audio channel" << llendl;
+ if (mCurrentSourcep)
+ {
+ mCurrentSourcep->setChannel(NULL);
+ }
+ mCurrentBufferp = NULL;
+}
+
+
+void LLAudioChannel::setSource(LLAudioSource *sourcep)
+{
+ //llinfos << this << ": setSource(" << sourcep << ")" << llendl;
+
+ if (!sourcep)
+ {
+ // Clearing the source for this channel, don't need to do anything.
+ //llinfos << "Clearing source for channel" << llendl;
+ cleanup();
+ mCurrentSourcep = NULL;
+ mWaiting = false;
+ return;
+ }
+
+ if (sourcep == mCurrentSourcep)
+ {
+ // Don't reallocate the channel, this will make FMOD goofy.
+ //llinfos << "Calling setSource with same source!" << llendl;
+ }
+
+ mCurrentSourcep = sourcep;
+
+
+ updateBuffer();
+ update3DPosition();
+}
+
+
+bool LLAudioChannel::updateBuffer()
+{
+ 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.
+ if(gAudiop)
+ {
+ 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),
+ mHasValidData(true)
+{
+ if (uuid.isNull())
+ {
+ // This is a null sound.
+ return;
+ }
+
+ if (gAudiop && gAudiop->hasDecodedFile(uuid))
+ {
+ // Already have a decoded version, don't need to decode it.
+ mHasLocalData = true;
+ mHasDecodedData = true;
+ }
+ else if (gAssetStorage && gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
+ {
+ mHasLocalData = true;
+ }
+}
+
+
+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.
+ llinfos << "Already have a buffer for this sound, don't bother loading!" << llendl;
+ return true;
+ }
+
+ mBufferp = gAudiop->getFreeBuffer();
+ if (!mBufferp)
+ {
+ // No free buffers, abort.
+ llinfos << "Not able to allocate a new audio buffer, aborting." << llendl;
+ return false;
+ }
+
+ std::string uuid_str;
+ std::string wav_path;
+ mID.toString(uuid_str);
+ wav_path= gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf";
+
+ if (!mBufferp->loadWAV(wav_path))
+ {
+ // Hrm. Right now, let's unset the buffer, since it's empty.
+ gAudiop->cleanupBuffer(mBufferp);
+ mBufferp = NULL;
+
+ return false;
+ }
+ mBufferp->mAudioDatap = this;
+ return true;
+}
+
+
diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h
new file mode 100644
index 0000000000..457fd93abe
--- /dev/null
+++ b/indra/llaudio/llaudioengine.h
@@ -0,0 +1,452 @@
+/**
+ * @file audioengine.h
+ * @brief Definition of LLAudioEngine base class abstracting the audio support
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+
+#ifndef LL_AUDIOENGINE_H
+#define LL_AUDIOENGINE_H
+
+#include <list>
+#include <map>
+
+#include "v3math.h"
+#include "v3dmath.h"
+#include "lltimer.h"
+#include "lluuid.h"
+#include "llframetimer.h"
+#include "llassettype.h"
+
+#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;
+const F32 DEFAULT_MIN_DISTANCE = 2.0f;
+
+#define MAX_CHANNELS 30
+#define MAX_BUFFERS 40 // Some extra for preloading, maybe?
+
+// This define is intended to allow us to switch from os based wav
+// file loading to vfs based wav file loading. The problem is that I
+// am unconvinced that the LLWaveFile works for loading sounds from
+// memory. So, until that is fixed up, changed, whatever, this remains
+// undefined.
+//#define USE_WAV_VFILE
+
+class LLVFS;
+
+class LLAudioSource;
+class LLAudioData;
+class LLAudioChannel;
+class LLAudioChannelOpenAL;
+class LLAudioBuffer;
+class LLStreamingAudioInterface;
+
+
+//
+// LLAudioEngine definition
+//
+
+class LLAudioEngine
+{
+ friend class LLAudioChannelOpenAL; // bleh. channel needs some listener methods.
+
+public:
+ enum LLAudioType
+ {
+ AUDIO_TYPE_NONE = 0,
+ AUDIO_TYPE_SFX = 1,
+ AUDIO_TYPE_UI = 2,
+ AUDIO_TYPE_AMBIENT = 3,
+ AUDIO_TYPE_COUNT = 4 // last
+ };
+
+ LLAudioEngine();
+ virtual ~LLAudioEngine();
+
+ // initialization/startup/shutdown
+ virtual bool init(const S32 num_channels, void *userdata);
+ virtual std::string getDriverName(bool verbose) = 0;
+ virtual void shutdown();
+
+ // Used by the mechanics of the engine
+ //virtual void processQueue(const LLUUID &sound_guid);
+ virtual void setListener(LLVector3 pos,LLVector3 vel,LLVector3 up,LLVector3 at);
+ virtual void updateWind(LLVector3 direction, F32 camera_height_above_water) = 0;
+ virtual void idle(F32 max_decode_time = 0.f);
+ virtual void updateChannels();
+
+ //
+ // "End user" functionality
+ //
+ virtual bool isWindEnabled();
+ virtual void enableWind(bool state_b);
+
+ // Use these for temporarily muting the audio system.
+ // Does not change buffers, initialization, etc. but
+ // stops playing new sounds.
+ virtual void setMuted(bool muted);
+ virtual bool getMuted() const { return mMuted; }
+#ifdef USE_PLUGIN_MEDIA
+ LLPluginClassMedia* initializeMedia(const std::string& media_type);
+#endif
+ F32 getMasterGain();
+ void setMasterGain(F32 gain);
+
+ F32 getSecondaryGain(S32 type);
+ void setSecondaryGain(S32 type, F32 gain);
+
+ F32 getInternetStreamGain();
+
+ virtual void setDopplerFactor(F32 factor);
+ virtual F32 getDopplerFactor();
+ virtual void setRolloffFactor(F32 factor);
+ virtual F32 getRolloffFactor();
+ virtual void setMaxWindGain(F32 gain);
+
+
+ // Methods actually related to setting up and removing sounds
+ // Owner ID is the owner of the object making the request
+ void triggerSound(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain,
+ const S32 type = LLAudioEngine::AUDIO_TYPE_NONE,
+ const LLVector3d &pos_global = LLVector3d::zero);
+ bool preloadSound(const LLUUID &id);
+
+ void addAudioSource(LLAudioSource *asp);
+ void cleanupAudioSource(LLAudioSource *asp);
+
+ LLAudioSource *findAudioSource(const LLUUID &source_id);
+ LLAudioData *getAudioData(const LLUUID &audio_uuid);
+
+ // Internet stream implementation manipulation
+ LLStreamingAudioInterface *getStreamingAudioImpl();
+ void setStreamingAudioImpl(LLStreamingAudioInterface *impl);
+ // Internet stream methods - these will call down into the *mStreamingAudioImpl if it exists
+ void startInternetStream(const std::string& url);
+ void stopInternetStream();
+ void pauseInternetStream(int pause);
+ void updateInternetStream(); // expected to be called often
+ int isInternetStreamPlaying();
+ // use a value from 0.0 to 1.0, inclusive
+ void setInternetStreamGain(F32 vol);
+ std::string getInternetStreamURL();
+
+ // For debugging usage
+ virtual LLVector3 getListenerPos();
+
+ LLAudioBuffer *getFreeBuffer(); // Get a free buffer, or flush an existing one if you have to.
+ LLAudioChannel *getFreeChannel(const F32 priority); // Get a free channel or flush an existing one if your priority is higher
+ void cleanupBuffer(LLAudioBuffer *bufferp);
+
+ bool hasDecodedFile(const LLUUID &uuid);
+ bool hasLocalFile(const LLUUID &uuid);
+
+ bool updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid = LLUUID::null);
+
+
+ // Asset callback when we're retrieved a sound from the asset server.
+ void startNextTransfer();
+ static void assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code, LLExtStat ext_status);
+
+ friend class LLPipeline; // For debugging
+public:
+ F32 mMaxWindGain; // Hack. Public to set before fade in?
+
+protected:
+ virtual LLAudioBuffer *createBuffer() = 0;
+ virtual LLAudioChannel *createChannel() = 0;
+
+ virtual void initWind() = 0;
+ virtual void cleanupWind() = 0;
+ virtual void setInternalGain(F32 gain) = 0;
+
+ void commitDeferredChanges();
+
+ virtual void allocateListener() = 0;
+
+
+ // listener methods
+ virtual void setListenerPos(LLVector3 vec);
+ virtual void setListenerVelocity(LLVector3 vec);
+ virtual void orientListener(LLVector3 up, LLVector3 at);
+ virtual void translateListener(LLVector3 vec);
+
+
+ F64 mapWindVecToGain(LLVector3 wind_vec);
+ F64 mapWindVecToPitch(LLVector3 wind_vec);
+ F64 mapWindVecToPan(LLVector3 wind_vec);
+
+protected:
+ LLListener *mListenerp;
+
+ bool mMuted;
+ void* mUserData;
+
+ S32 mLastStatus;
+
+ S32 mNumChannels;
+ bool mEnableWind;
+
+ LLUUID mCurrentTransfer; // Audio file currently being transferred by the system
+ LLFrameTimer mCurrentTransferTimer;
+
+ // A list of all audio sources that are known to the viewer at this time.
+ // This is most likely a superset of the ones that we actually have audio
+ // data for, or are playing back.
+ typedef std::map<LLUUID, LLAudioSource *> source_map;
+ typedef std::map<LLUUID, LLAudioData *> data_map;
+
+ source_map mAllSources;
+ data_map mAllData;
+
+ LLAudioChannel *mChannels[MAX_CHANNELS];
+
+ // Buffers needs to change into a different data structure, as the number of buffers
+ // that we have active should be limited by RAM usage, not count.
+ LLAudioBuffer *mBuffers[MAX_BUFFERS];
+
+ F32 mMasterGain;
+ F32 mSecondaryGain[AUDIO_TYPE_COUNT];
+
+ F32 mNextWindUpdate;
+
+ LLFrameTimer mWindUpdateTimer;
+
+private:
+ void setDefaults();
+ LLStreamingAudioInterface *mStreamingAudioImpl;
+};
+
+
+
+
+//
+// Standard audio source. Can be derived from for special sources, such as those attached to objects.
+//
+
+
+class LLAudioSource
+{
+public:
+ // owner_id is the id of the agent responsible for making this sound
+ // play, for example, the owner of the object currently playing it
+ LLAudioSource(const LLUUID &id, const LLUUID& owner_id, const F32 gain, const S32 type = LLAudioEngine::AUDIO_TYPE_NONE);
+ virtual ~LLAudioSource();
+
+ virtual void update(); // Update this audio source
+ void updatePriority();
+
+ void preload(const LLUUID &audio_id); // Only used for preloading UI sounds, now.
+
+ void addAudioData(LLAudioData *adp, bool set_current = TRUE);
+
+ void setAmbient(const bool ambient) { mAmbient = ambient; }
+ bool isAmbient() const { return mAmbient; }
+
+ void setLoop(const bool loop) { mLoop = loop; }
+ bool isLoop() const { return mLoop; }
+
+ void setSyncMaster(const bool master) { mSyncMaster = master; }
+ bool isSyncMaster() const { return mSyncMaster; }
+
+ void setSyncSlave(const bool slave) { mSyncSlave = slave; }
+ bool isSyncSlave() const { return mSyncSlave; }
+
+ void setQueueSounds(const bool queue) { mQueueSounds = queue; }
+ bool isQueueSounds() const { return mQueueSounds; }
+
+ void setPlayedOnce(const bool played_once) { mPlayedOnce = played_once; }
+
+ void setType(S32 type) { mType = type; }
+ S32 getType() { return mType; }
+
+ void setPositionGlobal(const LLVector3d &position_global) { mPositionGlobal = position_global; }
+ LLVector3d getPositionGlobal() const { return mPositionGlobal; }
+ LLVector3 getVelocity() const { return mVelocity; }
+ F32 getPriority() const { return mPriority; }
+
+ // Gain should always be clamped between 0 and 1.
+ F32 getGain() const { return mGain; }
+ virtual void setGain(const F32 gain) { mGain = llclamp(gain, 0.f, 1.f); }
+
+ const LLUUID &getID() const { return mID; }
+ bool isDone();
+
+ LLAudioData *getCurrentData();
+ LLAudioData *getQueuedData();
+ LLAudioBuffer *getCurrentBuffer();
+
+ bool setupChannel();
+ bool play(const LLUUID &audio_id); // Start the audio source playing
+
+ bool hasPendingPreloads() const; // Has preloads that haven't been done yet
+
+ friend class LLAudioEngine;
+ friend class LLAudioChannel;
+protected:
+ void setChannel(LLAudioChannel *channelp);
+ LLAudioChannel *getChannel() const { return mChannelp; }
+
+protected:
+ LLUUID mID; // The ID of the source is that of the object if it's attached to an object.
+ LLUUID mOwnerID; // owner of the object playing the sound
+ F32 mPriority;
+ F32 mGain;
+ bool mAmbient;
+ bool mLoop;
+ bool mSyncMaster;
+ bool mSyncSlave;
+ bool mQueueSounds;
+ bool mPlayedOnce;
+ S32 mType;
+ LLVector3d mPositionGlobal;
+ LLVector3 mVelocity;
+
+ //LLAudioSource *mSyncMasterp; // If we're a slave, the source that we're synced to.
+ LLAudioChannel *mChannelp; // If we're currently playing back, this is the channel that we're assigned to.
+ LLAudioData *mCurrentDatap;
+ LLAudioData *mQueuedDatap;
+
+ typedef std::map<LLUUID, LLAudioData *> data_map;
+ data_map mPreloadMap;
+
+ LLFrameTimer mAgeTimer;
+};
+
+
+
+
+//
+// Generic metadata about a particular piece of audio data.
+// The actual data is handled by the derived LLAudioBuffer classes which are
+// derived for each audio engine.
+//
+
+
+class LLAudioData
+{
+public:
+ LLAudioData(const LLUUID &uuid);
+ bool load();
+
+ LLUUID getID() const { return mID; }
+ LLAudioBuffer *getBuffer() const { return mBufferp; }
+
+ bool hasLocalData() const { return mHasLocalData; }
+ bool hasDecodedData() const { return mHasDecodedData; }
+ bool hasValidData() const { return mHasValidData; }
+
+ void setHasLocalData(const bool hld) { mHasLocalData = hld; }
+ void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; }
+ void setHasValidData(const bool hvd) { mHasValidData = hvd; }
+
+ friend class LLAudioEngine; // Severe laziness, bad.
+
+protected:
+ LLUUID mID;
+ LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here.
+ bool mHasLocalData;
+ bool mHasDecodedData;
+ bool mHasValidData;
+};
+
+
+//
+// Base class for an audio channel, i.e. a channel which is capable of playing back a sound.
+// Management of channels is done generically, methods for actually manipulating the channel
+// are derived for each audio engine.
+//
+
+
+class LLAudioChannel
+{
+public:
+ LLAudioChannel();
+ virtual ~LLAudioChannel();
+
+ virtual void setSource(LLAudioSource *sourcep);
+ LLAudioSource *getSource() const { return mCurrentSourcep; }
+
+ void setSecondaryGain(F32 gain) { mSecondaryGain = gain; }
+ F32 getSecondaryGain() { return mSecondaryGain; }
+
+ friend class LLAudioEngine;
+ friend class LLAudioSource;
+protected:
+ virtual void play() = 0;
+ virtual void playSynced(LLAudioChannel *channelp) = 0;
+ virtual void cleanup() = 0;
+ virtual bool isPlaying() = 0;
+ void setWaiting(const bool waiting) { mWaiting = waiting; }
+ bool isWaiting() const { return mWaiting; }
+
+ virtual bool updateBuffer(); // Check to see if the buffer associated with the source changed, and update if necessary.
+ virtual void update3DPosition() = 0;
+ virtual void updateLoop() = 0; // Update your loop/completion status, for use by queueing/syncing.
+protected:
+ LLAudioSource *mCurrentSourcep;
+ LLAudioBuffer *mCurrentBufferp;
+ bool mLoopedThisFrame;
+ bool mWaiting; // Waiting for sync.
+ F32 mSecondaryGain;
+};
+
+
+
+
+// Basically an interface class to the engine-specific implementation
+// of audio data that's ready for playback.
+// Will likely get more complex as we decide to do stuff like real streaming audio.
+
+
+class LLAudioBuffer
+{
+public:
+ virtual ~LLAudioBuffer() {};
+ virtual bool loadWAV(const std::string& filename) = 0;
+ virtual U32 getLength() = 0;
+
+ friend class LLAudioEngine;
+ friend class LLAudioChannel;
+ friend class LLAudioData;
+protected:
+ bool mInUse;
+ LLAudioData *mAudioDatap;
+ LLFrameTimer mLastUseTimer;
+};
+
+
+
+extern LLAudioEngine* gAudiop;
+
+#endif
diff --git a/indra/llaudio/llaudioengine_fmod.cpp b/indra/llaudio/llaudioengine_fmod.cpp
new file mode 100644
index 0000000000..7b12b62d53
--- /dev/null
+++ b/indra/llaudio/llaudioengine_fmod.cpp
@@ -0,0 +1,766 @@
+/**
+ * @file audioengine_fmod.cpp
+ * @brief Implementation of LLAudioEngine class abstracting the audio support as a FMOD 3D implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llstreamingaudio.h"
+#include "llstreamingaudio_fmod.h"
+
+#include "llaudioengine_fmod.h"
+#include "lllistener_fmod.h"
+
+#include "llerror.h"
+#include "llmath.h"
+#include "llrand.h"
+
+#include "fmod.h"
+#include "fmod_errors.h"
+#include "lldir.h"
+#include "llapr.h"
+
+#include "sound_ids.h"
+
+
+extern "C" {
+ void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata);
+}
+
+FSOUND_DSPUNIT *gWindDSP = NULL;
+
+
+LLAudioEngine_FMOD::LLAudioEngine_FMOD()
+{
+ mInited = false;
+ mWindGen = NULL;
+}
+
+
+LLAudioEngine_FMOD::~LLAudioEngine_FMOD()
+{
+}
+
+
+bool LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
+{
+ LLAudioEngine::init(num_channels, userdata);
+
+ // Reserve one extra channel for the http stream.
+ if (!FSOUND_SetMinHardwareChannels(num_channels + 1))
+ {
+ LL_WARNS("AppInit") << "FMOD::init[0](), error: " << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
+ }
+
+ LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() initializing FMOD" << LL_ENDL;
+
+ F32 version = FSOUND_GetVersion();
+ if (version < FMOD_VERSION)
+ {
+ LL_WARNS("AppInit") << "Error : You are using the wrong FMOD version (" << version
+ << ")! You should be using FMOD " << FMOD_VERSION << LL_ENDL;
+ //return false;
+ }
+
+ U32 fmod_flags = 0x0;
+
+#if LL_WINDOWS
+ // Windows needs to know which window is frontmost.
+ // This must be called before FSOUND_Init() per the FMOD docs.
+ // This could be used to let FMOD handle muting when we lose focus,
+ // but we don't actually want to do that because we want to distinguish
+ // between minimized and not-focused states.
+ if (!FSOUND_SetHWND(userdata))
+ {
+ LL_WARNS("AppInit") << "Error setting FMOD window: "
+ << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
+ return false;
+ }
+ // Play audio when we don't have focus.
+ // (For example, IM client on top of us.)
+ // This means we also try to play audio when minimized,
+ // so we manually handle muting in that case. JC
+ fmod_flags |= FSOUND_INIT_GLOBALFOCUS;
+#endif
+
+#if LL_LINUX
+ // initialize the FMOD engine
+
+ // This is a hack to use only FMOD's basic FPU mixer
+ // when the LL_VALGRIND environmental variable is set,
+ // otherwise valgrind will fall over on FMOD's MMX detection
+ if (getenv("LL_VALGRIND")) /*Flawfinder: ignore*/
+ {
+ LL_INFOS("AppInit") << "Pacifying valgrind in FMOD init." << LL_ENDL;
+ FSOUND_SetMixer(FSOUND_MIXER_QUALITY_FPU);
+ }
+
+ // If we don't set an output method, Linux FMOD always
+ // decides on OSS and fails otherwise. So we'll manually
+ // try ESD, then OSS, then ALSA.
+ // Why this order? See SL-13250, but in short, OSS emulated
+ // on top of ALSA is ironically more reliable than raw ALSA.
+ // Ack, and ESD has more reliable failure modes - but has worse
+ // latency - than all of them, so wins for now.
+ bool audio_ok = false;
+
+ if (!audio_ok)
+ if (NULL == getenv("LL_BAD_FMOD_ESD")) /*Flawfinder: ignore*/
+ {
+ LL_DEBUGS("AppInit") << "Trying ESD audio output..." << LL_ENDL;
+ if(FSOUND_SetOutput(FSOUND_OUTPUT_ESD) &&
+ FSOUND_Init(44100, num_channels, fmod_flags))
+ {
+ LL_DEBUGS("AppInit") << "ESD audio output initialized OKAY"
+ << LL_ENDL;
+ audio_ok = true;
+ } else {
+ LL_WARNS("AppInit") << "ESD audio output FAILED to initialize: "
+ << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
+ }
+ } else {
+ LL_DEBUGS("AppInit") << "ESD 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(FSOUND_SetOutput(FSOUND_OUTPUT_OSS) &&
+ FSOUND_Init(44100, num_channels, fmod_flags))
+ {
+ LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL;
+ audio_ok = true;
+ } else {
+ LL_WARNS("AppInit") << "OSS audio output FAILED to initialize: "
+ << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
+ }
+ } else {
+ LL_DEBUGS("AppInit") << "OSS 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(FSOUND_SetOutput(FSOUND_OUTPUT_ALSA) &&
+ FSOUND_Init(44100, num_channels, fmod_flags))
+ {
+ LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
+ audio_ok = true;
+ } else {
+ LL_WARNS("AppInit") << "ALSA audio output FAILED to initialize: "
+ << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
+ }
+ } else {
+ LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL;
+ }
+
+ if (!audio_ok)
+ {
+ LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL;
+ return false;
+ }
+
+ // On Linux, FMOD causes a SIGPIPE for some netstream error
+ // conditions (an FMOD bug); ignore SIGPIPE so it doesn't crash us.
+ // NOW FIXED in FMOD 3.x since 2006-10-01.
+ //signal(SIGPIPE, SIG_IGN);
+
+ // We're interested in logging which output method we
+ // ended up with, for QA purposes.
+ switch (FSOUND_GetOutput())
+ {
+ case FSOUND_OUTPUT_NOSOUND: LL_DEBUGS("AppInit") << "Audio output: NoSound" << LL_ENDL; break;
+ case FSOUND_OUTPUT_OSS: LL_DEBUGS("AppInit") << "Audio output: OSS" << LL_ENDL; break;
+ case FSOUND_OUTPUT_ESD: LL_DEBUGS("AppInit") << "Audio output: ESD" << LL_ENDL; break;
+ case FSOUND_OUTPUT_ALSA: LL_DEBUGS("AppInit") << "Audio output: ALSA" << LL_ENDL; break;
+ default: LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break;
+ };
+
+#else // LL_LINUX
+
+ // initialize the FMOD engine
+ if (!FSOUND_Init(44100, num_channels, fmod_flags))
+ {
+ LL_WARNS("AppInit") << "Error initializing FMOD: "
+ << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
+ 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_FMOD());
+
+ LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() FMOD initialized correctly" << LL_ENDL;
+
+ mInited = true;
+
+ return true;
+}
+
+
+std::string LLAudioEngine_FMOD::getDriverName(bool verbose)
+{
+ if (verbose)
+ {
+ F32 version = FSOUND_GetVersion();
+ return llformat("FMOD version %f", version);
+ }
+ else
+ {
+ return "FMOD";
+ }
+}
+
+
+void LLAudioEngine_FMOD::allocateListener(void)
+{
+ mListenerp = (LLListener *) new LLListener_FMOD();
+ if (!mListenerp)
+ {
+ llwarns << "Listener creation failed" << llendl;
+ }
+}
+
+
+void LLAudioEngine_FMOD::shutdown()
+{
+ if (gWindDSP)
+ {
+ FSOUND_DSP_SetActive(gWindDSP,false);
+ FSOUND_DSP_Free(gWindDSP);
+ }
+
+ stopInternetStream();
+
+ LLAudioEngine::shutdown();
+
+ llinfos << "LLAudioEngine_FMOD::shutdown() closing FMOD" << llendl;
+ FSOUND_Close();
+ llinfos << "LLAudioEngine_FMOD::shutdown() done closing FMOD" << llendl;
+
+ delete mListenerp;
+ mListenerp = NULL;
+}
+
+
+LLAudioBuffer * LLAudioEngine_FMOD::createBuffer()
+{
+ return new LLAudioBufferFMOD();
+}
+
+
+LLAudioChannel * LLAudioEngine_FMOD::createChannel()
+{
+ return new LLAudioChannelFMOD();
+}
+
+
+void LLAudioEngine_FMOD::initWind()
+{
+ mWindGen = new LLWindGen<MIXBUFFERFORMAT>;
+
+ if (!gWindDSP)
+ {
+ gWindDSP = FSOUND_DSP_Create(&windCallback, FSOUND_DSP_DEFAULTPRIORITY_CLEARUNIT + 20, mWindGen);
+ }
+ if (gWindDSP)
+ {
+ FSOUND_DSP_SetActive(gWindDSP, true);
+ }
+ mNextWindUpdate = 0.0;
+}
+
+
+void LLAudioEngine_FMOD::cleanupWind()
+{
+ if (gWindDSP)
+ {
+ FSOUND_DSP_SetActive(gWindDSP, false);
+ FSOUND_DSP_Free(gWindDSP);
+ gWindDSP = NULL;
+ }
+
+ delete mWindGen;
+ mWindGen = NULL;
+}
+
+
+//-----------------------------------------------------------------------
+void LLAudioEngine_FMOD::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_FMOD::setSourceMinDistance(U16 source_num, F64 distance)
+{
+ if (!mInited)
+ {
+ return;
+ }
+ if (mBuffer[source_num])
+ {
+ mMinDistance[source_num] = (F32) distance;
+ if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num]))
+ {
+ llwarns << "FMOD::setSourceMinDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------
+void LLAudioEngine_FMOD::setSourceMaxDistance(U16 source_num, F64 distance)
+{
+ if (!mInited)
+ {
+ return;
+ }
+ if (mBuffer[source_num])
+ {
+ mMaxDistance[source_num] = (F32) distance;
+ if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num]))
+ {
+ llwarns << "FMOD::setSourceMaxDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------
+void LLAudioEngine_FMOD::get3DParams(S32 source_num, S32 *volume, S32 *freq, S32 *inside, S32 *outside, LLVector3 *orient, S32 *out_volume, F32 *min_dist, F32 *max_dist)
+{
+ *volume = 0;
+ *freq = 0;
+ *inside = 0;
+ *outside = 0;
+ *orient = LLVector3::zero;
+ *out_volume = 0;
+ *min_dist = 0.f;
+ *max_dist = 0.f;
+}
+
+*/
+
+
+//-----------------------------------------------------------------------
+void LLAudioEngine_FMOD::setInternalGain(F32 gain)
+{
+ if (!mInited)
+ {
+ return;
+ }
+
+ gain = llclamp( gain, 0.0f, 1.0f );
+ FSOUND_SetSFXMasterVolume( llround( 255.0f * gain ) );
+
+ LLStreamingAudioInterface *saimpl = getStreamingAudioImpl();
+ if ( saimpl )
+ {
+ // fmod likes its streaming audio channel gain re-asserted after
+ // master volume change.
+ saimpl->setGain(saimpl->getGain());
+ }
+}
+
+//
+// LLAudioChannelFMOD implementation
+//
+
+LLAudioChannelFMOD::LLAudioChannelFMOD() : LLAudioChannel(), mChannelID(0), mLastSamplePos(0)
+{
+}
+
+
+LLAudioChannelFMOD::~LLAudioChannelFMOD()
+{
+ cleanup();
+}
+
+
+bool LLAudioChannelFMOD::updateBuffer()
+{
+ if (LLAudioChannel::updateBuffer())
+ {
+ // Base class update returned true, which means that we need to actually
+ // set up the channel for a different buffer.
+
+ LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentSourcep->getCurrentBuffer();
+
+ // Grab the FMOD sample associated with the buffer
+ FSOUND_SAMPLE *samplep = bufferp->getSample();
+ if (!samplep)
+ {
+ // This is bad, there should ALWAYS be a sample associated with a legit
+ // buffer.
+ llerrs << "No FMOD sample!" << llendl;
+ return false;
+ }
+
+
+ // Actually play the sound. Start it off paused so we can do all the necessary
+ // setup.
+ mChannelID = FSOUND_PlaySoundEx(FSOUND_FREE, samplep, FSOUND_DSP_GetSFXUnit(), true);
+
+ //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
+ if (!FSOUND_SetVolume(mChannelID, llround(getSecondaryGain() * mCurrentSourcep->getGain() * 255.0f)))
+ {
+// llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
+ }
+
+ if (!FSOUND_SetLoopMode(mChannelID, mCurrentSourcep->isLoop() ? FSOUND_LOOP_NORMAL : FSOUND_LOOP_OFF))
+ {
+// llwarns << "Channel " << mChannelID << "Source ID: " << mCurrentSourcep->getID()
+// << " at " << mCurrentSourcep->getPositionGlobal() << llendl;
+// llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
+ }
+ }
+
+ return true;
+}
+
+
+void LLAudioChannelFMOD::update3DPosition()
+{
+ if (!mChannelID)
+ {
+ // We're not actually a live channel (i.e., we're not playing back anything)
+ return;
+ }
+
+ LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)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.
+ bufferp->set3DMode(false);
+ }
+ else
+ {
+ // Localized sound. Update the position and velocity of the sound.
+ bufferp->set3DMode(true);
+
+ LLVector3 float_pos;
+ float_pos.setVec(mCurrentSourcep->getPositionGlobal());
+ if (!FSOUND_3D_SetAttributes(mChannelID, float_pos.mV, mCurrentSourcep->getVelocity().mV))
+ {
+ LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::update3DPosition error: " << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
+ }
+ }
+}
+
+
+void LLAudioChannelFMOD::updateLoop()
+{
+ if (!mChannelID)
+ {
+ // 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 = FSOUND_GetCurrentPosition(mChannelID);
+ if (cur_pos < (U32)mLastSamplePos)
+ {
+ mLoopedThisFrame = true;
+ }
+ mLastSamplePos = cur_pos;
+}
+
+
+void LLAudioChannelFMOD::cleanup()
+{
+ if (!mChannelID)
+ {
+ //llinfos << "Aborting cleanup with no channelID." << llendl;
+ return;
+ }
+
+ //llinfos << "Cleaning up channel: " << mChannelID << llendl;
+ if (!FSOUND_StopSound(mChannelID))
+ {
+ LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::cleanup error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
+ }
+
+ mCurrentBufferp = NULL;
+ mChannelID = 0;
+}
+
+
+void LLAudioChannelFMOD::play()
+{
+ if (!mChannelID)
+ {
+ llwarns << "Playing without a channelID, aborting" << llendl;
+ return;
+ }
+
+ if (!FSOUND_SetPaused(mChannelID, false))
+ {
+ llwarns << "LLAudioChannelFMOD::play error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
+ }
+ getSource()->setPlayedOnce(true);
+}
+
+
+void LLAudioChannelFMOD::playSynced(LLAudioChannel *channelp)
+{
+ LLAudioChannelFMOD *fmod_channelp = (LLAudioChannelFMOD*)channelp;
+ if (!(fmod_channelp->mChannelID && mChannelID))
+ {
+ // Don't have channels allocated to both the master and the slave
+ return;
+ }
+
+ U32 position = FSOUND_GetCurrentPosition(fmod_channelp->mChannelID) % mCurrentBufferp->getLength();
+ // Try to match the position of our sync master
+ if (!FSOUND_SetCurrentPosition(mChannelID, position))
+ {
+ llwarns << "LLAudioChannelFMOD::playSynced unable to set current position" << llendl;
+ }
+
+ // Start us playing
+ play();
+}
+
+
+bool LLAudioChannelFMOD::isPlaying()
+{
+ if (!mChannelID)
+ {
+ return false;
+ }
+
+ return FSOUND_IsPlaying(mChannelID) && (!FSOUND_GetPaused(mChannelID));
+}
+
+
+
+//
+// LLAudioBufferFMOD implementation
+//
+
+
+LLAudioBufferFMOD::LLAudioBufferFMOD()
+{
+ mSamplep = NULL;
+}
+
+
+LLAudioBufferFMOD::~LLAudioBufferFMOD()
+{
+ if (mSamplep)
+ {
+ // Clean up the associated FMOD sample if it exists.
+ FSOUND_Sample_Free(mSamplep);
+ mSamplep = NULL;
+ }
+}
+
+
+bool LLAudioBufferFMOD::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 (mSamplep)
+ {
+ // If there's already something loaded in this buffer, clean it up.
+ FSOUND_Sample_Free(mSamplep);
+ mSamplep = NULL;
+ }
+
+ // Load up the wav file into an fmod sample
+#if LL_WINDOWS
+ // MikeS. - Loading the sound file manually and then handing it over to FMOD,
+ // since FMOD uses posix IO internally,
+ // which doesn't work with unicode file paths.
+ LLFILE* sound_file = LLFile::fopen(filename,"rb"); /* Flawfinder: ignore */
+ if (sound_file)
+ {
+ fseek(sound_file,0,SEEK_END);
+ U32 file_length = ftell(sound_file); //Find the length of the file by seeking to the end and getting the offset
+ size_t read_count;
+ fseek(sound_file,0,SEEK_SET); //Seek back to the beginning
+ char* buffer = new char[file_length];
+ llassert(buffer);
+ read_count = fread((void*)buffer,file_length,1,sound_file);//Load it..
+ if(ferror(sound_file)==0 && (read_count == 1)){//No read error, and we got 1 chunk of our size...
+ unsigned int mode_flags = FSOUND_LOOP_NORMAL | FSOUND_LOADMEMORY;
+ //FSOUND_16BITS | FSOUND_MONO | FSOUND_LOADMEMORY | FSOUND_LOOP_NORMAL;
+ mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, buffer, mode_flags , 0, file_length);
+ }
+ delete[] buffer;
+ fclose(sound_file);
+ }
+#else
+ mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, filename.c_str(), FSOUND_LOOP_NORMAL, 0, 0);
+#endif
+
+ if (!mSamplep)
+ {
+ // We failed to load the file for some reason.
+ llwarns << "Could not load data '" << filename << "': "
+ << FMOD_ErrorString(FSOUND_GetError()) << 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 LLAudioBufferFMOD::getLength()
+{
+ if (!mSamplep)
+ {
+ return 0;
+ }
+
+ return FSOUND_Sample_GetLength(mSamplep);
+}
+
+
+void LLAudioBufferFMOD::set3DMode(bool use3d)
+{
+ U16 current_mode = FSOUND_Sample_GetMode(mSamplep);
+
+ if (use3d)
+ {
+ if (!FSOUND_Sample_SetMode(mSamplep, (current_mode & (~FSOUND_2D))))
+ {
+ llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
+ }
+ }
+ else
+ {
+ if (!FSOUND_Sample_SetMode(mSamplep, current_mode | FSOUND_2D))
+ {
+ llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
+ }
+ }
+}
+
+
+void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata)
+{
+ // originalbuffer = fmod's original mixbuffer.
+ // newbuffer = the buffer passed from the previous DSP unit.
+ // length = length in samples at this mix time.
+ // param = user parameter passed through in FSOUND_DSP_Create.
+ //
+ // modify the buffer in some fashion
+
+ LLWindGen<LLAudioEngine_FMOD::MIXBUFFERFORMAT> *windgen =
+ (LLWindGen<LLAudioEngine_FMOD::MIXBUFFERFORMAT> *)userdata;
+ U8 stride;
+
+#if LL_DARWIN
+ stride = sizeof(LLAudioEngine_FMOD::MIXBUFFERFORMAT);
+#else
+ int mixertype = FSOUND_GetMixer();
+ if (mixertype == FSOUND_MIXER_BLENDMODE ||
+ mixertype == FSOUND_MIXER_QUALITY_FPU)
+ {
+ stride = 4;
+ }
+ else
+ {
+ stride = 2;
+ }
+#endif
+
+ newbuffer = windgen->windGenerate((LLAudioEngine_FMOD::MIXBUFFERFORMAT *)newbuffer, length, stride);
+
+ return newbuffer;
+}
diff --git a/indra/llaudio/llaudioengine_fmod.h b/indra/llaudio/llaudioengine_fmod.h
new file mode 100644
index 0000000000..3968657cba
--- /dev/null
+++ b/indra/llaudio/llaudioengine_fmod.h
@@ -0,0 +1,129 @@
+/**
+ * @file audioengine_fmod.h
+ * @brief Definition of LLAudioEngine class abstracting the audio
+ * support as a FMOD 3D implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_AUDIOENGINE_FMOD_H
+#define LL_AUDIOENGINE_FMOD_H
+
+#include "llaudioengine.h"
+#include "lllistener_fmod.h"
+#include "llwindgen.h"
+
+#include "fmod.h"
+
+class LLAudioStreamManagerFMOD;
+
+class LLAudioEngine_FMOD : public LLAudioEngine
+{
+public:
+ LLAudioEngine_FMOD();
+ virtual ~LLAudioEngine_FMOD();
+
+ // 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*/ void initWind();
+ /*virtual*/ void cleanupWind();
+
+ /*virtual*/void updateWind(LLVector3 direction, F32 camera_height_above_water);
+
+#if LL_DARWIN
+ typedef S32 MIXBUFFERFORMAT;
+#else
+ typedef S16 MIXBUFFERFORMAT;
+#endif
+
+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);
+protected:
+ static signed char F_CALLBACKAPI callbackMetaData(char* name, char* value, void* userdata);
+
+ //F32 mMinDistance[MAX_BUFFERS];
+ //F32 mMaxDistance[MAX_BUFFERS];
+
+ bool mInited;
+
+ // On Windows, userdata is the HWND of the application window.
+ void* mUserData;
+
+ LLWindGen<MIXBUFFERFORMAT> *mWindGen;
+};
+
+
+class LLAudioChannelFMOD : public LLAudioChannel
+{
+public:
+ LLAudioChannelFMOD();
+ virtual ~LLAudioChannelFMOD();
+
+protected:
+ /*virtual*/ void play();
+ /*virtual*/ void playSynced(LLAudioChannel *channelp);
+ /*virtual*/ void cleanup();
+ /*virtual*/ bool isPlaying();
+
+ /*virtual*/ bool updateBuffer();
+ /*virtual*/ void update3DPosition();
+ /*virtual*/ void updateLoop();
+
+protected:
+ int mChannelID;
+ S32 mLastSamplePos;
+};
+
+
+class LLAudioBufferFMOD : public LLAudioBuffer
+{
+public:
+ LLAudioBufferFMOD();
+ virtual ~LLAudioBufferFMOD();
+
+ /*virtual*/ bool loadWAV(const std::string& filename);
+ /*virtual*/ U32 getLength();
+ friend class LLAudioChannelFMOD;
+
+ void set3DMode(bool use3d);
+protected:
+ FSOUND_SAMPLE *getSample() { return mSamplep; }
+protected:
+ FSOUND_SAMPLE *mSamplep;
+};
+
+
+#endif // LL_AUDIOENGINE_FMOD_H
diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp
new file mode 100644
index 0000000000..a5982ccbd6
--- /dev/null
+++ b/indra/llaudio/llaudioengine_openal.cpp
@@ -0,0 +1,546 @@
+/**
+ * @file audioengine_openal.cpp
+ * @brief implementation of audio engine using OpenAL
+ * support as a OpenAL 3D implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "lldir.h"
+
+#include "llaudioengine_openal.h"
+#include "lllistener_openal.h"
+
+
+LLAudioEngine_OpenAL::LLAudioEngine_OpenAL()
+ :
+ mWindGen(NULL),
+ mWindBuf(NULL),
+ mWindBufFreq(0),
+ mWindBufSamples(0),
+ mWindBufBytes(0),
+ mWindSource(AL_NONE),
+ mNumEmptyWindALBuffers(MAX_NUM_WIND_BUFFERS)
+{
+}
+
+// virtual
+LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL()
+{
+}
+
+// virtual
+bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata)
+{
+ mWindGen = NULL;
+ LLAudioEngine::init(num_channels, userdata);
+
+ if(!alutInit(NULL, NULL))
+ {
+ llwarns << "LLAudioEngine_OpenAL::init() ALUT initialization failed: " << alutGetErrorString (alutGetError ()) << llendl;
+ return false;
+ }
+
+ llinfos << "LLAudioEngine_OpenAL::init() OpenAL successfully initialized" << llendl;
+
+ llinfos << "OpenAL version: "
+ << ll_safe_string(alGetString(AL_VERSION)) << llendl;
+ llinfos << "OpenAL vendor: "
+ << ll_safe_string(alGetString(AL_VENDOR)) << llendl;
+ llinfos << "OpenAL renderer: "
+ << ll_safe_string(alGetString(AL_RENDERER)) << llendl;
+
+ ALint major = alutGetMajorVersion ();
+ ALint minor = alutGetMinorVersion ();
+ llinfos << "ALUT version: " << major << "." << minor << llendl;
+
+ ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
+
+ alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &major);
+ alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &minor);
+ llinfos << "ALC version: " << major << "." << minor << llendl;
+
+ llinfos << "ALC default device: "
+ << ll_safe_string(alcGetString(device,
+ ALC_DEFAULT_DEVICE_SPECIFIER))
+ << llendl;
+
+ return true;
+}
+
+// virtual
+std::string LLAudioEngine_OpenAL::getDriverName(bool verbose)
+{
+ ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
+ std::ostringstream version;
+
+ version <<
+ "OpenAL";
+
+ if (verbose)
+ {
+ version <<
+ ", version " <<
+ ll_safe_string(alGetString(AL_VERSION)) <<
+ " / " <<
+ ll_safe_string(alGetString(AL_VENDOR)) <<
+ " / " <<
+ ll_safe_string(alGetString(AL_RENDERER));
+
+ if (device)
+ version <<
+ ": " <<
+ ll_safe_string(alcGetString(device,
+ ALC_DEFAULT_DEVICE_SPECIFIER));
+ }
+
+ return version.str();
+}
+
+// virtual
+void LLAudioEngine_OpenAL::allocateListener()
+{
+ mListenerp = (LLListener *) new LLListener_OpenAL();
+ if(!mListenerp)
+ {
+ llwarns << "LLAudioEngine_OpenAL::allocateListener() Listener creation failed" << llendl;
+ }
+}
+
+// virtual
+void LLAudioEngine_OpenAL::shutdown()
+{
+ llinfos << "About to LLAudioEngine::shutdown()" << llendl;
+ LLAudioEngine::shutdown();
+
+ llinfos << "About to alutExit()" << llendl;
+ if(!alutExit())
+ {
+ llwarns << "Nuts." << llendl;
+ llwarns << "LLAudioEngine_OpenAL::shutdown() ALUT shutdown failed: " << alutGetErrorString (alutGetError ()) << llendl;
+ }
+
+ llinfos << "LLAudioEngine_OpenAL::shutdown() OpenAL successfully shut down" << llendl;
+
+ delete mListenerp;
+ mListenerp = NULL;
+}
+
+LLAudioBuffer *LLAudioEngine_OpenAL::createBuffer()
+{
+ return new LLAudioBufferOpenAL();
+}
+
+LLAudioChannel *LLAudioEngine_OpenAL::createChannel()
+{
+ return new LLAudioChannelOpenAL();
+}
+
+void LLAudioEngine_OpenAL::setInternalGain(F32 gain)
+{
+ //llinfos << "LLAudioEngine_OpenAL::setInternalGain() Gain: " << gain << llendl;
+ alListenerf(AL_GAIN, gain);
+}
+
+LLAudioChannelOpenAL::LLAudioChannelOpenAL()
+ :
+ mALSource(AL_NONE),
+ mLastSamplePos(0)
+{
+ alGenSources(1, &mALSource);
+}
+
+LLAudioChannelOpenAL::~LLAudioChannelOpenAL()
+{
+ cleanup();
+ alDeleteSources(1, &mALSource);
+}
+
+void LLAudioChannelOpenAL::cleanup()
+{
+ alSourceStop(mALSource);
+ mCurrentBufferp = NULL;
+}
+
+void LLAudioChannelOpenAL::play()
+{
+ if (mALSource == AL_NONE)
+ {
+ llwarns << "Playing without a mALSource, aborting" << llendl;
+ return;
+ }
+
+ if(!isPlaying())
+ {
+ alSourcePlay(mALSource);
+ getSource()->setPlayedOnce(true);
+ }
+}
+
+void LLAudioChannelOpenAL::playSynced(LLAudioChannel *channelp)
+{
+ if (channelp)
+ {
+ LLAudioChannelOpenAL *masterchannelp =
+ (LLAudioChannelOpenAL*)channelp;
+ if (mALSource != AL_NONE &&
+ masterchannelp->mALSource != AL_NONE)
+ {
+ // we have channels allocated to master and slave
+ ALfloat master_offset;
+ alGetSourcef(masterchannelp->mALSource, AL_SEC_OFFSET,
+ &master_offset);
+
+ llinfos << "Syncing with master at " << master_offset
+ << "sec" << llendl;
+ // *TODO: detect when this fails, maybe use AL_SAMPLE_
+ alSourcef(mALSource, AL_SEC_OFFSET, master_offset);
+ }
+ }
+ play();
+}
+
+bool LLAudioChannelOpenAL::isPlaying()
+{
+ if (mALSource != AL_NONE)
+ {
+ ALint state;
+ alGetSourcei(mALSource, AL_SOURCE_STATE, &state);
+ if(state == AL_PLAYING)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool LLAudioChannelOpenAL::updateBuffer()
+{
+ if (LLAudioChannel::updateBuffer())
+ {
+ // Base class update returned true, which means that we need to actually
+ // set up the source for a different buffer.
+ LLAudioBufferOpenAL *bufferp = (LLAudioBufferOpenAL *)mCurrentSourcep->getCurrentBuffer();
+ ALuint buffer = bufferp->getBuffer();
+ alSourcei(mALSource, AL_BUFFER, buffer);
+ mLastSamplePos = 0;
+ }
+
+ if (mCurrentSourcep)
+ {
+ alSourcef(mALSource, AL_GAIN,
+ mCurrentSourcep->getGain() * getSecondaryGain());
+ alSourcei(mALSource, AL_LOOPING,
+ mCurrentSourcep->isLoop() ? AL_TRUE : AL_FALSE);
+ alSourcef(mALSource, AL_ROLLOFF_FACTOR,
+ gAudiop->mListenerp->getRolloffFactor());
+ }
+
+ return true;
+}
+
+
+void LLAudioChannelOpenAL::updateLoop()
+{
+ if (mALSource == AL_NONE)
+ {
+ 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.
+ //
+ ALint cur_pos;
+ alGetSourcei(mALSource, AL_SAMPLE_OFFSET, &cur_pos);
+ if (cur_pos < mLastSamplePos)
+ {
+ mLoopedThisFrame = true;
+ }
+ mLastSamplePos = cur_pos;
+}
+
+
+void LLAudioChannelOpenAL::update3DPosition()
+{
+ if(!mCurrentSourcep)
+ {
+ return;
+ }
+ if (mCurrentSourcep->isAmbient())
+ {
+ alSource3f(mALSource, AL_POSITION, 0.0, 0.0, 0.0);
+ alSource3f(mALSource, AL_VELOCITY, 0.0, 0.0, 0.0);
+ alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_TRUE);
+ } else {
+ LLVector3 float_pos;
+ float_pos.setVec(mCurrentSourcep->getPositionGlobal());
+ alSourcefv(mALSource, AL_POSITION, float_pos.mV);
+ alSourcefv(mALSource, AL_VELOCITY, mCurrentSourcep->getVelocity().mV);
+ alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_FALSE);
+ }
+
+ alSourcef(mALSource, AL_GAIN, mCurrentSourcep->getGain() * getSecondaryGain());
+}
+
+LLAudioBufferOpenAL::LLAudioBufferOpenAL()
+{
+ mALBuffer = AL_NONE;
+}
+
+LLAudioBufferOpenAL::~LLAudioBufferOpenAL()
+{
+ cleanup();
+}
+
+void LLAudioBufferOpenAL::cleanup()
+{
+ if(mALBuffer != AL_NONE)
+ {
+ alDeleteBuffers(1, &mALBuffer);
+ mALBuffer = AL_NONE;
+ }
+}
+
+bool LLAudioBufferOpenAL::loadWAV(const std::string& filename)
+{
+ cleanup();
+ mALBuffer = alutCreateBufferFromFile(filename.c_str());
+ if(mALBuffer == AL_NONE)
+ {
+ ALenum error = alutGetError();
+ if (gDirUtilp->fileExists(filename))
+ {
+ llwarns <<
+ "LLAudioBufferOpenAL::loadWAV() Error loading "
+ << filename
+ << " " << alutGetErrorString(error) << llendl;
+ }
+ else
+ {
+ // It's common for the file to not actually exist.
+ lldebugs <<
+ "LLAudioBufferOpenAL::loadWAV() Error loading "
+ << filename
+ << " " << alutGetErrorString(error) << llendl;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+U32 LLAudioBufferOpenAL::getLength()
+{
+ if(mALBuffer == AL_NONE)
+ {
+ return 0;
+ }
+ ALint length;
+ alGetBufferi(mALBuffer, AL_SIZE, &length);
+ return length / 2; // convert size in bytes to size in (16-bit) samples
+}
+
+// ------------
+
+void LLAudioEngine_OpenAL::initWind()
+{
+ ALenum error;
+ llinfos << "LLAudioEngine_OpenAL::initWind() start" << llendl;
+
+ mNumEmptyWindALBuffers = MAX_NUM_WIND_BUFFERS;
+
+ alGetError(); /* clear error */
+
+ alGenSources(1,&mWindSource);
+
+ if((error=alGetError()) != AL_NO_ERROR)
+ {
+ llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind sources: "<<error<<llendl;
+ }
+
+ mWindGen = new LLWindGen<WIND_SAMPLE_T>;
+
+ mWindBufFreq = mWindGen->getInputSamplingRate();
+ mWindBufSamples = llceil(mWindBufFreq * WIND_BUFFER_SIZE_SEC);
+ mWindBufBytes = mWindBufSamples * 2 /*stereo*/ * sizeof(WIND_SAMPLE_T);
+
+ mWindBuf = new WIND_SAMPLE_T [mWindBufSamples * 2 /*stereo*/];
+
+ if(mWindBuf==NULL)
+ {
+ llerrs << "LLAudioEngine_OpenAL::initWind() Error creating wind memory buffer" << llendl;
+ mEnableWind=false;
+ }
+
+ llinfos << "LLAudioEngine_OpenAL::initWind() done" << llendl;
+}
+
+void LLAudioEngine_OpenAL::cleanupWind()
+{
+ llinfos << "LLAudioEngine_OpenAL::cleanupWind()" << llendl;
+
+ if (mWindSource != AL_NONE)
+ {
+ // detach and delete all outstanding buffers on the wind source
+ alSourceStop(mWindSource);
+ ALint processed;
+ alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed);
+ while (processed--)
+ {
+ ALuint buffer = AL_NONE;
+ alSourceUnqueueBuffers(mWindSource, 1, &buffer);
+ alDeleteBuffers(1, &buffer);
+ }
+
+ // delete the wind source itself
+ alDeleteSources(1, &mWindSource);
+
+ mWindSource = AL_NONE;
+ }
+
+ delete[] mWindBuf;
+ mWindBuf = NULL;
+
+ delete mWindGen;
+ mWindGen = NULL;
+}
+
+void LLAudioEngine_OpenAL::updateWind(LLVector3 wind_vec, F32 camera_altitude)
+{
+ LLVector3 wind_pos;
+ F64 pitch;
+ F64 center_freq;
+ ALenum error;
+
+ if (!mEnableWind)
+ return;
+
+ if(!mWindBuf)
+ 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]);
+
+ 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);
+
+ alSourcei(mWindSource, AL_LOOPING, AL_FALSE);
+ alSource3f(mWindSource, AL_POSITION, 0.0, 0.0, 0.0);
+ alSource3f(mWindSource, AL_VELOCITY, 0.0, 0.0, 0.0);
+ alSourcef(mWindSource, AL_ROLLOFF_FACTOR, 0.0);
+ alSourcei(mWindSource, AL_SOURCE_RELATIVE, AL_TRUE);
+ }
+
+ // ok lets make a wind buffer now
+
+ ALint processed, queued, unprocessed;
+ alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed);
+ alGetSourcei(mWindSource, AL_BUFFERS_QUEUED, &queued);
+ unprocessed = queued - processed;
+
+ // ensure that there are always at least 3x as many filled buffers
+ // queued as we managed to empty since last time.
+ mNumEmptyWindALBuffers = llmin(mNumEmptyWindALBuffers + processed * 3 - unprocessed, MAX_NUM_WIND_BUFFERS-unprocessed);
+ mNumEmptyWindALBuffers = llmax(mNumEmptyWindALBuffers, 0);
+
+ //llinfos << "mNumEmptyWindALBuffers: " << mNumEmptyWindALBuffers <<" (" << unprocessed << ":" << processed << ")" << llendl;
+
+ while(processed--) // unqueue old buffers
+ {
+ ALuint buffer;
+ ALenum error;
+ alGetError(); /* clear error */
+ alSourceUnqueueBuffers(mWindSource, 1, &buffer);
+ error = alGetError();
+ if(error != AL_NO_ERROR)
+ {
+ llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (unqueuing) buffers" << llendl;
+ }
+ else
+ {
+ alDeleteBuffers(1, &buffer);
+ }
+ }
+
+ unprocessed += mNumEmptyWindALBuffers;
+ while (mNumEmptyWindALBuffers > 0) // fill+queue new buffers
+ {
+ ALuint buffer;
+ alGetError(); /* clear error */
+ alGenBuffers(1,&buffer);
+ if((error=alGetError()) != AL_NO_ERROR)
+ {
+ llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind buffer: " << error << llendl;
+ break;
+ }
+
+ alBufferData(buffer,
+ AL_FORMAT_STEREO16,
+ mWindGen->windGenerate(mWindBuf,
+ mWindBufSamples, 2),
+ mWindBufBytes,
+ mWindBufFreq);
+ error = alGetError();
+ if(error != AL_NO_ERROR)
+ {
+ llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (bufferdata) buffers" << llendl;
+ }
+
+ alSourceQueueBuffers(mWindSource, 1, &buffer);
+ error = alGetError();
+ if(error != AL_NO_ERROR)
+ {
+ llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (queuing) buffers" << llendl;
+ }
+
+ --mNumEmptyWindALBuffers;
+ }
+
+ ALint playing;
+ alGetSourcei(mWindSource, AL_SOURCE_STATE, &playing);
+ if(playing != AL_PLAYING)
+ {
+ alSourcePlay(mWindSource);
+
+ lldebugs << "Wind had stopped - probably ran out of buffers - restarting: " << (unprocessed+mNumEmptyWindALBuffers) << " now queued." << llendl;
+ }
+}
+
diff --git a/indra/llaudio/llaudioengine_openal.h b/indra/llaudio/llaudioengine_openal.h
new file mode 100644
index 0000000000..5aca03e195
--- /dev/null
+++ b/indra/llaudio/llaudioengine_openal.h
@@ -0,0 +1,114 @@
+/**
+ * @file audioengine_openal.cpp
+ * @brief implementation of audio engine using OpenAL
+ * support as a OpenAL 3D implementation
+ *
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+
+#ifndef LL_AUDIOENGINE_OPENAL_H
+#define LL_AUDIOENGINE_OPENAL_H
+
+#include "llaudioengine.h"
+#include "lllistener_openal.h"
+#include "llwindgen.h"
+
+class LLAudioEngine_OpenAL : public LLAudioEngine
+{
+ public:
+ LLAudioEngine_OpenAL();
+ virtual ~LLAudioEngine_OpenAL();
+
+ virtual bool init(const S32 num_channels, void *user_data);
+ virtual std::string getDriverName(bool verbose);
+ virtual void allocateListener();
+
+ virtual void shutdown();
+
+ void setInternalGain(F32 gain);
+
+ LLAudioBuffer* createBuffer();
+ LLAudioChannel* createChannel();
+
+ /*virtual*/ void initWind();
+ /*virtual*/ void cleanupWind();
+ /*virtual*/ void updateWind(LLVector3 direction, F32 camera_altitude);
+
+ private:
+ void * windDSP(void *newbuffer, int length);
+ typedef S16 WIND_SAMPLE_T;
+ LLWindGen<WIND_SAMPLE_T> *mWindGen;
+ S16 *mWindBuf;
+ U32 mWindBufFreq;
+ U32 mWindBufSamples;
+ U32 mWindBufBytes;
+ ALuint mWindSource;
+ int mNumEmptyWindALBuffers;
+
+ static const int MAX_NUM_WIND_BUFFERS = 80;
+ static const float WIND_BUFFER_SIZE_SEC = 0.05f; // 1/20th sec
+};
+
+class LLAudioChannelOpenAL : public LLAudioChannel
+{
+ public:
+ LLAudioChannelOpenAL();
+ virtual ~LLAudioChannelOpenAL();
+ protected:
+ /*virtual*/ void play();
+ /*virtual*/ void playSynced(LLAudioChannel *channelp);
+ /*virtual*/ void cleanup();
+ /*virtual*/ bool isPlaying();
+
+ /*virtual*/ bool updateBuffer();
+ /*virtual*/ void update3DPosition();
+ /*virtual*/ void updateLoop();
+
+ ALuint mALSource;
+ ALint mLastSamplePos;
+};
+
+class LLAudioBufferOpenAL : public LLAudioBuffer{
+ public:
+ LLAudioBufferOpenAL();
+ virtual ~LLAudioBufferOpenAL();
+
+ bool loadWAV(const std::string& filename);
+ U32 getLength();
+
+ friend class LLAudioChannelOpenAL;
+ protected:
+ void cleanup();
+ ALuint getBuffer() {return mALBuffer;}
+
+ ALuint mALBuffer;
+};
+
+#endif
diff --git a/indra/llaudio/lllistener.cpp b/indra/llaudio/lllistener.cpp
new file mode 100644
index 0000000000..846c6bccb5
--- /dev/null
+++ b/indra/llaudio/lllistener.cpp
@@ -0,0 +1,142 @@
+/**
+ * @file listener.cpp
+ * @brief Implementation of LISTENER class abstracting the audio support
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lllistener.h"
+
+#define DEFAULT_AT 0.0f,0.0f,-1.0f
+#define DEFAULT_UP 0.0f,1.0f,0.0f
+
+//-----------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------
+LLListener::LLListener()
+{
+ init();
+}
+
+//-----------------------------------------------------------------------
+LLListener::~LLListener()
+{
+}
+
+//-----------------------------------------------------------------------
+void LLListener::init(void)
+{
+ mPosition.zeroVec();
+ mListenAt.setVec(DEFAULT_AT);
+ mListenUp.setVec(DEFAULT_UP);
+ mVelocity.zeroVec();
+}
+
+//-----------------------------------------------------------------------
+void LLListener::translate(LLVector3 offset)
+{
+ mPosition += offset;
+}
+
+//-----------------------------------------------------------------------
+void LLListener::setPosition(LLVector3 pos)
+{
+ mPosition = pos;
+}
+
+//-----------------------------------------------------------------------
+LLVector3 LLListener::getPosition(void)
+{
+ return(mPosition);
+}
+
+//-----------------------------------------------------------------------
+LLVector3 LLListener::getAt(void)
+{
+ return(mListenAt);
+}
+
+//-----------------------------------------------------------------------
+LLVector3 LLListener::getUp(void)
+{
+ return(mListenUp);
+}
+
+//-----------------------------------------------------------------------
+void LLListener::setVelocity(LLVector3 vel)
+{
+ mVelocity = vel;
+}
+
+//-----------------------------------------------------------------------
+void LLListener::orient(LLVector3 up, LLVector3 at)
+{
+ mListenUp = up;
+ mListenAt = at;
+}
+
+//-----------------------------------------------------------------------
+void LLListener::set(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at)
+{
+ mPosition = pos;
+ mVelocity = vel;
+
+ setPosition(pos);
+ setVelocity(vel);
+ orient(up,at);
+}
+
+//-----------------------------------------------------------------------
+void LLListener::setDopplerFactor(F32 factor)
+{
+}
+
+//-----------------------------------------------------------------------
+F32 LLListener::getDopplerFactor()
+{
+ return (1.f);
+}
+
+//-----------------------------------------------------------------------
+void LLListener::setRolloffFactor(F32 factor)
+{
+}
+
+//-----------------------------------------------------------------------
+F32 LLListener::getRolloffFactor()
+{
+ return (1.f);
+}
+
+//-----------------------------------------------------------------------
+void LLListener::commitDeferredChanges()
+{
+}
+
diff --git a/indra/llaudio/lllistener.h b/indra/llaudio/lllistener.h
new file mode 100644
index 0000000000..e94fbe853f
--- /dev/null
+++ b/indra/llaudio/lllistener.h
@@ -0,0 +1,78 @@
+/**
+ * @file listener.h
+ * @brief Description of LISTENER base class abstracting the audio support.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LISTENER_H
+#define LL_LISTENER_H
+
+#include "v3math.h"
+
+class LLListener
+{
+ private:
+ protected:
+ LLVector3 mPosition;
+ LLVector3 mVelocity;
+ LLVector3 mListenAt;
+ LLVector3 mListenUp;
+
+ public:
+
+ private:
+ protected:
+ public:
+ LLListener();
+ virtual ~LLListener();
+ virtual void init();
+
+ virtual void set(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at);
+
+ virtual void setPosition(LLVector3 pos);
+ virtual void setVelocity(LLVector3 vel);
+
+ virtual void orient(LLVector3 up, LLVector3 at);
+ virtual void translate(LLVector3 offset);
+
+ virtual void setDopplerFactor(F32 factor);
+ virtual void setRolloffFactor(F32 factor);
+
+ virtual LLVector3 getPosition();
+ virtual LLVector3 getAt();
+ virtual LLVector3 getUp();
+
+ virtual F32 getDopplerFactor();
+ virtual F32 getRolloffFactor();
+
+ virtual void commitDeferredChanges();
+};
+
+#endif
+
diff --git a/indra/llaudio/lllistener_ds3d.h b/indra/llaudio/lllistener_ds3d.h
new file mode 100644
index 0000000000..1ff9c170c4
--- /dev/null
+++ b/indra/llaudio/lllistener_ds3d.h
@@ -0,0 +1,74 @@
+/**
+ * @file listener_ds3d.h
+ * @brief Description of LISTENER class abstracting the audio support
+ * as a DirectSound 3D implementation (windows only)
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LISTENER_DS3D_H
+#define LL_LISTENER_DS3D_H
+
+#include "lllistener.h"
+
+#include <dmusici.h>
+#include <dsound.h>
+#include <ks.h>
+
+class LLListener_DS3D : public LLListener
+{
+ private:
+ protected:
+ IDirectSound3DListener8 *m3DListener;
+ public:
+
+ private:
+ protected:
+ public:
+ LLListener_DS3D();
+ virtual ~LLListener_DS3D();
+ virtual void init();
+
+ virtual void setDS3DLPtr (IDirectSound3DListener8 *listener_p);
+
+ virtual void translate(LLVector3 offset);
+ virtual void setPosition(LLVector3 pos);
+ virtual void setVelocity(LLVector3 vel);
+ virtual void orient(LLVector3 up, LLVector3 at);
+
+ virtual void setDopplerFactor(F32 factor);
+ virtual F32 getDopplerFactor();
+ virtual void setRolloffFactor(F32 factor);
+ virtual F32 getRolloffFactor();
+
+ virtual void commitDeferredChanges();
+};
+
+#endif
+
+
diff --git a/indra/llaudio/lllistener_fmod.cpp b/indra/llaudio/lllistener_fmod.cpp
new file mode 100644
index 0000000000..57ad461b02
--- /dev/null
+++ b/indra/llaudio/lllistener_fmod.cpp
@@ -0,0 +1,131 @@
+/**
+ * @file listener_fmod.cpp
+ * @brief implementation of LISTENER class abstracting the audio
+ * support as a FMOD 3D implementation (windows only)
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llaudioengine.h"
+#include "lllistener_fmod.h"
+#include "fmod.h"
+
+//-----------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------
+LLListener_FMOD::LLListener_FMOD()
+{
+ init();
+}
+
+//-----------------------------------------------------------------------
+LLListener_FMOD::~LLListener_FMOD()
+{
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMOD::init(void)
+{
+ // do inherited
+ LLListener::init();
+ mDopplerFactor = 1.0f;
+ mRolloffFactor = 1.0f;
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMOD::translate(LLVector3 offset)
+{
+ LLListener::translate(offset);
+
+ FSOUND_3D_Listener_SetAttributes(mPosition.mV, NULL, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMOD::setPosition(LLVector3 pos)
+{
+ LLListener::setPosition(pos);
+
+ FSOUND_3D_Listener_SetAttributes(pos.mV, NULL, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMOD::setVelocity(LLVector3 vel)
+{
+ LLListener::setVelocity(vel);
+
+ FSOUND_3D_Listener_SetAttributes(NULL, vel.mV, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMOD::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;
+
+ FSOUND_3D_Listener_SetAttributes(NULL, NULL, at.mV[0],at.mV[1],at.mV[2], up.mV[0],up.mV[1],up.mV[2]);
+}
+
+//-----------------------------------------------------------------------
+void LLListener_FMOD::commitDeferredChanges()
+{
+ FSOUND_Update();
+}
+
+
+void LLListener_FMOD::setRolloffFactor(F32 factor)
+{
+ mRolloffFactor = factor;
+ FSOUND_3D_SetRolloffFactor(factor);
+}
+
+
+F32 LLListener_FMOD::getRolloffFactor()
+{
+ return mRolloffFactor;
+}
+
+
+void LLListener_FMOD::setDopplerFactor(F32 factor)
+{
+ mDopplerFactor = factor;
+ FSOUND_3D_SetDopplerFactor(factor);
+}
+
+
+F32 LLListener_FMOD::getDopplerFactor()
+{
+ return mDopplerFactor;
+}
+
+
diff --git a/indra/llaudio/lllistener_fmod.h b/indra/llaudio/lllistener_fmod.h
new file mode 100644
index 0000000000..5a48ec8b68
--- /dev/null
+++ b/indra/llaudio/lllistener_fmod.h
@@ -0,0 +1,64 @@
+/**
+ * @file listener_fmod.h
+ * @brief Description of LISTENER class abstracting the audio support
+ * as an FMOD 3D implementation (windows and Linux)
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LISTENER_FMOD_H
+#define LL_LISTENER_FMOD_H
+
+#include "lllistener.h"
+
+class LLListener_FMOD : public LLListener
+{
+ public:
+ LLListener_FMOD();
+ virtual ~LLListener_FMOD();
+ 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:
+ F32 mDopplerFactor;
+ F32 mRolloffFactor;
+};
+
+#endif
+
+
diff --git a/indra/llaudio/lllistener_openal.cpp b/indra/llaudio/lllistener_openal.cpp
new file mode 100644
index 0000000000..a96ebd5dba
--- /dev/null
+++ b/indra/llaudio/lllistener_openal.cpp
@@ -0,0 +1,116 @@
+/**
+ * @file audioengine_openal.cpp
+ * @brief implementation of audio engine using OpenAL
+ * support as a OpenAL 3D implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llaudioengine.h"
+
+#include "lllistener_openal.h"
+
+LLListener_OpenAL::LLListener_OpenAL()
+{
+ init();
+}
+
+LLListener_OpenAL::~LLListener_OpenAL()
+{
+}
+
+void LLListener_OpenAL::translate(LLVector3 offset)
+{
+ //llinfos << "LLListener_OpenAL::translate() : " << offset << llendl;
+ LLListener::translate(offset);
+}
+
+void LLListener_OpenAL::setPosition(LLVector3 pos)
+{
+ //llinfos << "LLListener_OpenAL::setPosition() : " << pos << llendl;
+ LLListener::setPosition(pos);
+}
+
+void LLListener_OpenAL::setVelocity(LLVector3 vel)
+{
+ LLListener::setVelocity(vel);
+}
+
+void LLListener_OpenAL::orient(LLVector3 up, LLVector3 at)
+{
+ //llinfos << "LLListener_OpenAL::orient() up: " << up << " at: " << at << llendl;
+ LLListener::orient(up, at);
+}
+
+void LLListener_OpenAL::commitDeferredChanges()
+{
+ ALfloat orientation[6];
+ orientation[0] = mListenAt.mV[0];
+ orientation[1] = mListenAt.mV[1];
+ orientation[2] = mListenAt.mV[2];
+ orientation[3] = mListenUp.mV[0];
+ orientation[4] = mListenUp.mV[1];
+ orientation[5] = mListenUp.mV[2];
+
+ ALfloat velocity[3];
+ velocity[0] = mVelocity.mV[0];
+ velocity[1] = mVelocity.mV[1];
+ velocity[2] = mVelocity.mV[2];
+
+ alListenerfv(AL_ORIENTATION, orientation);
+ alListenerfv(AL_POSITION, mPosition.mV);
+ alListenerfv(AL_VELOCITY, velocity);
+}
+
+void LLListener_OpenAL::setDopplerFactor(F32 factor)
+{
+ //llinfos << "LLListener_OpenAL::setDopplerFactor() : " << factor << llendl;
+ alDopplerFactor(factor);
+}
+
+F32 LLListener_OpenAL::getDopplerFactor()
+{
+ ALfloat factor;
+ factor = alGetFloat(AL_DOPPLER_FACTOR);
+ //llinfos << "LLListener_OpenAL::getDopplerFactor() : " << factor << llendl;
+ return factor;
+}
+
+
+void LLListener_OpenAL::setRolloffFactor(F32 factor)
+{
+ mRolloffFactor = factor;
+}
+
+F32 LLListener_OpenAL::getRolloffFactor()
+{
+ return mRolloffFactor;
+}
+
+
diff --git a/indra/llaudio/lllistener_openal.h b/indra/llaudio/lllistener_openal.h
new file mode 100644
index 0000000000..0dfeea5c90
--- /dev/null
+++ b/indra/llaudio/lllistener_openal.h
@@ -0,0 +1,64 @@
+/**
+ * @file listener_openal.h
+ * @brief Description of LISTENER class abstracting the audio support
+ * as an OpenAL implementation
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LISTENER_OPENAL_H
+#define LL_LISTENER_OPENAL_H
+
+#include "lllistener.h"
+
+#include "AL/al.h"
+#include "AL/alut.h"
+
+class LLListener_OpenAL : public LLListener
+{
+ public:
+ LLListener_OpenAL();
+ virtual ~LLListener_OpenAL();
+
+ 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:
+ F32 mRolloffFactor;
+};
+
+#endif
+
diff --git a/indra/llaudio/llstreamingaudio.h b/indra/llaudio/llstreamingaudio.h
new file mode 100644
index 0000000000..aa89e6a177
--- /dev/null
+++ b/indra/llaudio/llstreamingaudio.h
@@ -0,0 +1,56 @@
+/**
+ * @file streamingaudio.h
+ * @author Tofu Linden
+ * @brief Definition of LLStreamingAudioInterface base class abstracting the streaming audio interface
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_STREAMINGAUDIO_H
+#define LL_STREAMINGAUDIO_H
+
+#include "stdtypes.h" // from llcommon
+
+// Entirely abstract. Based exactly on the historic API.
+class LLStreamingAudioInterface
+{
+ public:
+ virtual ~LLStreamingAudioInterface() {}
+
+ virtual void start(const std::string& url) = 0;
+ virtual void stop() = 0;
+ virtual void pause(int pause) = 0;
+ virtual void update() = 0;
+ virtual int isPlaying() = 0;
+ // use a value from 0.0 to 1.0, inclusive
+ virtual void setGain(F32 vol) = 0;
+ virtual F32 getGain() = 0;
+ virtual std::string getURL() = 0;
+};
+
+#endif // LL_STREAMINGAUDIO_H
diff --git a/indra/llaudio/llstreamingaudio_fmod.cpp b/indra/llaudio/llstreamingaudio_fmod.cpp
new file mode 100644
index 0000000000..a71a87203c
--- /dev/null
+++ b/indra/llaudio/llstreamingaudio_fmod.cpp
@@ -0,0 +1,362 @@
+/**
+ * @file streamingaudio_fmod.cpp
+ * @brief LLStreamingAudio_FMOD implementation
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llmath.h"
+
+#include "fmod.h"
+#include "fmod_errors.h"
+
+#include "llstreamingaudio_fmod.h"
+
+
+class LLAudioStreamManagerFMOD
+{
+public:
+ LLAudioStreamManagerFMOD(const std::string& url);
+ int startStream();
+ bool stopStream(); // Returns true if the stream was successfully stopped.
+ bool ready();
+
+ const std::string& getURL() { return mInternetStreamURL; }
+
+ int getOpenState();
+protected:
+ FSOUND_STREAM* mInternetStream;
+ bool mReady;
+
+ std::string mInternetStreamURL;
+};
+
+
+
+//---------------------------------------------------------------------------
+// Internet Streaming
+//---------------------------------------------------------------------------
+LLStreamingAudio_FMOD::LLStreamingAudio_FMOD() :
+ mCurrentInternetStreamp(NULL),
+ mFMODInternetStreamChannel(-1),
+ 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.
+ FSOUND_Stream_SetBufferSize(200);
+
+ // 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_FMOD::~LLStreamingAudio_FMOD()
+{
+ // nothing interesting/safe to do.
+}
+
+
+void LLStreamingAudio_FMOD::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 LLAudioStreamManagerFMOD(url);
+ mURL = url;
+ }
+ else
+ {
+ llinfos << "Set internet stream to null" << llendl;
+ mURL.clear();
+ }
+}
+
+
+void LLStreamingAudio_FMOD::update()
+{
+ // Kill dead internet streams, if possible
+ std::list<LLAudioStreamManagerFMOD *>::iterator iter;
+ for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
+ {
+ LLAudioStreamManagerFMOD *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;
+ }
+
+ int open_state = mCurrentInternetStreamp->getOpenState();
+
+ if (!open_state)
+ {
+ // Stream is live
+
+ // start the stream if it's ready
+ if (mFMODInternetStreamChannel < 0)
+ {
+ mFMODInternetStreamChannel = mCurrentInternetStreamp->startStream();
+
+ if (mFMODInternetStreamChannel != -1)
+ {
+ // Reset volume to previously set volume
+ setGain(getGain());
+ FSOUND_SetPaused(mFMODInternetStreamChannel, false);
+ }
+ }
+ }
+
+ switch(open_state)
+ {
+ default:
+ case 0:
+ // success
+ break;
+ case -1:
+ // stream handle is invalid
+ llwarns << "InternetStream - invalid handle" << llendl;
+ stop();
+ return;
+ case -2:
+ // opening
+ break;
+ case -3:
+ // failed to open, file not found, perhaps
+ llwarns << "InternetSteam - failed to open" << llendl;
+ stop();
+ return;
+ case -4:
+ // connecting
+ break;
+ case -5:
+ // buffering
+ break;
+ }
+
+}
+
+void LLStreamingAudio_FMOD::stop()
+{
+ if (mFMODInternetStreamChannel != -1)
+ {
+ FSOUND_SetPaused(mFMODInternetStreamChannel, true);
+ FSOUND_SetPriority(mFMODInternetStreamChannel, 0);
+ mFMODInternetStreamChannel = -1;
+ }
+
+ 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_FMOD::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_FMOD::isPlaying()
+{
+ if (mCurrentInternetStreamp)
+ {
+ return 1; // Active and playing
+ }
+ else if (!mURL.empty())
+ {
+ return 2; // "Paused"
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+F32 LLStreamingAudio_FMOD::getGain()
+{
+ return mGain;
+}
+
+
+std::string LLStreamingAudio_FMOD::getURL()
+{
+ return mURL;
+}
+
+
+void LLStreamingAudio_FMOD::setGain(F32 vol)
+{
+ mGain = vol;
+
+ if (mFMODInternetStreamChannel != -1)
+ {
+ vol = llclamp(vol, 0.f, 1.f);
+ int vol_int = llround(vol * 255.f);
+ FSOUND_SetVolumeAbsolute(mFMODInternetStreamChannel, vol_int);
+ }
+}
+
+
+///////////////////////////////////////////////////////
+// manager of possibly-multiple internet audio streams
+
+LLAudioStreamManagerFMOD::LLAudioStreamManagerFMOD(const std::string& url) :
+ mInternetStream(NULL),
+ mReady(false)
+{
+ mInternetStreamURL = url;
+ mInternetStream = FSOUND_Stream_Open(url.c_str(), FSOUND_NORMAL | FSOUND_NONBLOCKING, 0, 0);
+ if (!mInternetStream)
+ {
+ llwarns << "Couldn't open fmod stream, error "
+ << FMOD_ErrorString(FSOUND_GetError())
+ << llendl;
+ mReady = false;
+ return;
+ }
+
+ mReady = true;
+}
+
+int LLAudioStreamManagerFMOD::startStream()
+{
+ // We need a live and opened stream before we try and play it.
+ if (!mInternetStream || getOpenState())
+ {
+ llwarns << "No internet stream to start playing!" << llendl;
+ return -1;
+ }
+
+ // Make sure the stream is set to 2D mode.
+ FSOUND_Stream_SetMode(mInternetStream, FSOUND_2D);
+
+ return FSOUND_Stream_PlayEx(FSOUND_FREE, mInternetStream, NULL, true);
+}
+
+bool LLAudioStreamManagerFMOD::stopStream()
+{
+ if (mInternetStream)
+ {
+ int read_percent = 0;
+ int status = 0;
+ int bitrate = 0;
+ unsigned int flags = 0x0;
+ FSOUND_Stream_Net_GetStatus(mInternetStream, &status, &read_percent, &bitrate, &flags);
+
+ bool close = true;
+ switch (status)
+ {
+ case FSOUND_STREAM_NET_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)
+ {
+ FSOUND_Stream_Close(mInternetStream);
+ mInternetStream = NULL;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return true;
+ }
+}
+
+int LLAudioStreamManagerFMOD::getOpenState()
+{
+ int open_state = FSOUND_Stream_GetOpenState(mInternetStream);
+ return open_state;
+}
diff --git a/indra/llaudio/llstreamingaudio_fmod.h b/indra/llaudio/llstreamingaudio_fmod.h
new file mode 100644
index 0000000000..968ab53a0b
--- /dev/null
+++ b/indra/llaudio/llstreamingaudio_fmod.h
@@ -0,0 +1,68 @@
+/**
+ * @file streamingaudio_fmod.h
+ * @author Tofu Linden
+ * @brief Definition of LLStreamingAudio_FMOD implementation
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_STREAMINGAUDIO_FMOD_H
+#define LL_STREAMINGAUDIO_FMOD_H
+
+#include "stdtypes.h" // from llcommon
+
+#include "llstreamingaudio.h"
+
+class LLAudioStreamManagerFMOD;
+
+class LLStreamingAudio_FMOD : public LLStreamingAudioInterface
+{
+ public:
+ LLStreamingAudio_FMOD();
+ /*virtual*/ ~LLStreamingAudio_FMOD();
+
+ /*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();
+
+private:
+ LLAudioStreamManagerFMOD *mCurrentInternetStreamp;
+ int mFMODInternetStreamChannel;
+ std::list<LLAudioStreamManagerFMOD *> mDeadStreams;
+
+ std::string mURL;
+ F32 mGain;
+};
+
+
+#endif // LL_STREAMINGAUDIO_FMOD_H
diff --git a/indra/llaudio/llvorbisencode.cpp b/indra/llaudio/llvorbisencode.cpp
new file mode 100644
index 0000000000..8ee082a245
--- /dev/null
+++ b/indra/llaudio/llvorbisencode.cpp
@@ -0,0 +1,505 @@
+/**
+ * @file vorbisencode.cpp
+ * @brief Vorbis encoding routine routine for Indra.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "vorbis/vorbisenc.h"
+
+#include "llvorbisencode.h"
+#include "llerror.h"
+#include "llrand.h"
+#include "llmath.h"
+#include "llapr.h"
+
+//#if LL_DARWIN
+// MBW -- XXX -- Getting rid of SecondLifeVorbis for now -- no fmod means no name collisions.
+#if 0
+#include "VorbisFramework.h"
+
+#define vorbis_analysis mac_vorbis_analysis
+#define vorbis_analysis_headerout mac_vorbis_analysis_headerout
+#define vorbis_analysis_init mac_vorbis_analysis_init
+#define vorbis_encode_ctl mac_vorbis_encode_ctl
+#define vorbis_encode_setup_init mac_vorbis_encode_setup_init
+#define vorbis_encode_setup_managed mac_vorbis_encode_setup_managed
+
+#define vorbis_info_init mac_vorbis_info_init
+#define vorbis_info_clear mac_vorbis_info_clear
+#define vorbis_comment_init mac_vorbis_comment_init
+#define vorbis_comment_clear mac_vorbis_comment_clear
+#define vorbis_block_init mac_vorbis_block_init
+#define vorbis_block_clear mac_vorbis_block_clear
+#define vorbis_dsp_clear mac_vorbis_dsp_clear
+#define vorbis_analysis_buffer mac_vorbis_analysis_buffer
+#define vorbis_analysis_wrote mac_vorbis_analysis_wrote
+#define vorbis_analysis_blockout mac_vorbis_analysis_blockout
+
+#define ogg_stream_packetin mac_ogg_stream_packetin
+#define ogg_stream_init mac_ogg_stream_init
+#define ogg_stream_flush mac_ogg_stream_flush
+#define ogg_stream_pageout mac_ogg_stream_pageout
+#define ogg_page_eos mac_ogg_page_eos
+#define ogg_stream_clear mac_ogg_stream_clear
+
+#endif
+
+S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& error_msg)
+{
+ U16 num_channels = 0;
+ U32 sample_rate = 0;
+ U32 bits_per_sample = 0;
+ U32 physical_file_size = 0;
+ U32 chunk_length = 0;
+ U32 raw_data_length = 0;
+ U32 bytes_per_sec = 0;
+ BOOL uncompressed_pcm = FALSE;
+
+ unsigned char wav_header[44]; /*Flawfinder: ignore*/
+
+ error_msg.clear();
+
+ //********************************
+ LLAPRFile infile ;
+ infile.open(in_fname,LL_APR_RB);
+ //********************************
+ if (!infile.getFileHandle())
+ {
+ error_msg = "CannotUploadSoundFile";
+ return(LLVORBISENC_SOURCE_OPEN_ERR);
+ }
+
+ infile.read(wav_header, 44);
+ physical_file_size = infile.seek(APR_END,0);
+
+ if (strncmp((char *)&(wav_header[0]),"RIFF",4))
+ {
+ error_msg = "SoundFileNotRIFF";
+ return(LLVORBISENC_WAV_FORMAT_ERR);
+ }
+
+ if (strncmp((char *)&(wav_header[8]),"WAVE",4))
+ {
+ error_msg = "SoundFileNotRIFF";
+ return(LLVORBISENC_WAV_FORMAT_ERR);
+ }
+
+ // parse the chunks
+
+ U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
+
+ while ((file_pos + 8)< physical_file_size)
+ {
+ infile.seek(APR_SET,file_pos);
+ infile.read(wav_header, 44);
+
+ chunk_length = ((U32) wav_header[7] << 24)
+ + ((U32) wav_header[6] << 16)
+ + ((U32) wav_header[5] << 8)
+ + wav_header[4];
+
+// llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
+
+ if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
+ {
+ if ((wav_header[8] == 0x01) && (wav_header[9] == 0x00))
+ {
+ uncompressed_pcm = TRUE;
+ }
+ num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
+ sample_rate = ((U32) wav_header[15] << 24)
+ + ((U32) wav_header[14] << 16)
+ + ((U32) wav_header[13] << 8)
+ + wav_header[12];
+ bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
+ bytes_per_sec = ((U32) wav_header[19] << 24)
+ + ((U32) wav_header[18] << 16)
+ + ((U32) wav_header[17] << 8)
+ + wav_header[16];
+ }
+ else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
+ {
+ raw_data_length = chunk_length;
+ }
+ file_pos += (chunk_length + 8);
+ chunk_length = 0;
+ }
+ //****************
+ infile.close();
+ //****************
+
+ if (!uncompressed_pcm)
+ {
+ error_msg = "SoundFileNotPCM";
+ return(LLVORBISENC_PCM_FORMAT_ERR);
+ }
+
+ if ((num_channels < 1) || (num_channels > 2))
+ {
+ error_msg = "SoundFileInvalidChannelCount";
+ return(LLVORBISENC_MULTICHANNEL_ERR);
+ }
+
+ if (sample_rate != 44100)
+ {
+ error_msg = "SoundFileInvalidSampleRate";
+ return(LLVORBISENC_UNSUPPORTED_SAMPLE_RATE);
+ }
+
+ if ((bits_per_sample != 16) && (bits_per_sample != 8))
+ {
+ error_msg = "SoundFileInvalidWordSize";
+ return(LLVORBISENC_UNSUPPORTED_WORD_SIZE);
+ }
+
+ if (!raw_data_length)
+ {
+ error_msg = "SoundFileInvalidHeader";
+ return(LLVORBISENC_CLIP_TOO_LONG);
+ }
+
+ F32 clip_length = (F32)raw_data_length/(F32)bytes_per_sec;
+
+ if (clip_length > 10.0f)
+ {
+ error_msg = "SoundFileInvalidTooLong";
+ return(LLVORBISENC_CLIP_TOO_LONG);
+ }
+
+ return(LLVORBISENC_NOERR);
+}
+
+S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname)
+{
+#define READ_BUFFER 1024
+ unsigned char readbuffer[READ_BUFFER*4+44]; /* out of the data segment, not the stack */ /*Flawfinder: ignore*/
+
+ ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
+ ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
+ ogg_packet op; /* one raw packet of data for decode */
+
+ vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */
+ vorbis_comment vc; /* struct that stores all the user comments */
+
+ vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
+ vorbis_block vb; /* local working space for packet->PCM decode */
+
+ int eos=0;
+ int result;
+
+ U16 num_channels = 0;
+ U32 sample_rate = 0;
+ U32 bits_per_sample = 0;
+
+ S32 format_error = 0;
+ std::string error_msg;
+ if ((format_error = check_for_invalid_wav_formats(in_fname, error_msg)))
+ {
+ llwarns << error_msg << ": " << in_fname << llendl;
+ return(format_error);
+ }
+
+#if 1
+ unsigned char wav_header[44]; /*Flawfinder: ignore*/
+
+ S32 data_left = 0;
+
+ LLAPRFile infile ;
+ infile.open(in_fname,LL_APR_RB);
+ if (!infile.getFileHandle())
+ {
+ llwarns << "Couldn't open temporary ogg file for writing: " << in_fname
+ << llendl;
+ return(LLVORBISENC_SOURCE_OPEN_ERR);
+ }
+
+ LLAPRFile outfile ;
+ outfile.open(out_fname,LL_APR_WPB);
+ if (!outfile.getFileHandle())
+ {
+ llwarns << "Couldn't open upload sound file for reading: " << in_fname
+ << llendl;
+ return(LLVORBISENC_DEST_OPEN_ERR);
+ }
+
+ // parse the chunks
+ U32 chunk_length = 0;
+ U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
+
+ while (infile.eof() != APR_EOF)
+ {
+ infile.seek(APR_SET,file_pos);
+ infile.read(wav_header, 44);
+
+ chunk_length = ((U32) wav_header[7] << 24)
+ + ((U32) wav_header[6] << 16)
+ + ((U32) wav_header[5] << 8)
+ + wav_header[4];
+
+// llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
+
+ if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
+ {
+ num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
+ sample_rate = ((U32) wav_header[15] << 24)
+ + ((U32) wav_header[14] << 16)
+ + ((U32) wav_header[13] << 8)
+ + wav_header[12];
+ bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
+ }
+ else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
+ {
+ infile.seek(APR_SET,file_pos+8);
+ // leave the file pointer at the beginning of the data chunk data
+ data_left = chunk_length;
+ break;
+ }
+ file_pos += (chunk_length + 8);
+ chunk_length = 0;
+ }
+
+
+ /********** Encode setup ************/
+
+ /* choose an encoding mode */
+ /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */
+ vorbis_info_init(&vi);
+
+ // always encode to mono
+
+ // SL-52913 & SL-53779 determined this quality level to be our 'good
+ // enough' general-purpose quality level with a nice low bitrate.
+ // Equivalent to oggenc -q0.5
+ F32 quality = 0.05f;
+// quality = (bitrate==128000 ? 0.4f : 0.1);
+
+// if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1))
+ if (vorbis_encode_init_vbr(&vi, /* num_channels */ 1 ,sample_rate, quality))
+// if (vorbis_encode_setup_managed(&vi,1,sample_rate,-1,bitrate,-1) ||
+// vorbis_encode_ctl(&vi,OV_ECTL_RATEMANAGE_AVG,NULL) ||
+// vorbis_encode_setup_init(&vi))
+ {
+ llwarns << "unable to initialize vorbis codec at quality " << quality << llendl;
+ // llwarns << "unable to initialize vorbis codec at bitrate " << bitrate << llendl;
+ return(LLVORBISENC_DEST_OPEN_ERR);
+ }
+
+ /* add a comment */
+ vorbis_comment_init(&vc);
+// vorbis_comment_add(&vc,"Linden");
+
+ /* set up the analysis state and auxiliary encoding storage */
+ vorbis_analysis_init(&vd,&vi);
+ vorbis_block_init(&vd,&vb);
+
+ /* set up our packet->stream encoder */
+ /* pick a random serial number; that way we can more likely build
+ chained streams just by concatenation */
+ ogg_stream_init(&os, ll_rand());
+
+ /* Vorbis streams begin with three headers; the initial header (with
+ most of the codec setup parameters) which is mandated by the Ogg
+ bitstream spec. The second header holds any comment fields. The
+ third header holds the bitstream codebook. We merely need to
+ make the headers, then pass them to libvorbis one at a time;
+ libvorbis handles the additional Ogg bitstream constraints */
+
+ {
+ ogg_packet header;
+ ogg_packet header_comm;
+ ogg_packet header_code;
+
+ vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
+ ogg_stream_packetin(&os,&header); /* automatically placed in its own
+ page */
+ ogg_stream_packetin(&os,&header_comm);
+ ogg_stream_packetin(&os,&header_code);
+
+ /* We don't have to write out here, but doing so makes streaming
+ * much easier, so we do, flushing ALL pages. This ensures the actual
+ * audio data will start on a new page
+ */
+ while(!eos){
+ int result=ogg_stream_flush(&os,&og);
+ if(result==0)break;
+ outfile.write(og.header, og.header_len);
+ outfile.write(og.body, og.body_len);
+ }
+
+ }
+
+
+ while(!eos)
+ {
+ long bytes_per_sample = bits_per_sample/8;
+
+ long bytes=(long)infile.read(readbuffer,llclamp((S32)(READ_BUFFER*num_channels*bytes_per_sample),0,data_left)); /* stereo hardwired here */
+
+ if (bytes==0)
+ {
+ /* end of file. this can be done implicitly in the mainline,
+ but it's easier to see here in non-clever fashion.
+ Tell the library we're at end of stream so that it can handle
+ the last frame and mark end of stream in the output properly */
+
+ vorbis_analysis_wrote(&vd,0);
+// eos = 1;
+
+ }
+ else
+ {
+ long i;
+ long samples;
+ int temp;
+
+ data_left -= bytes;
+ /* data to encode */
+
+ /* expose the buffer to submit data */
+ float **buffer=vorbis_analysis_buffer(&vd,READ_BUFFER);
+
+ i = 0;
+ samples = bytes / (num_channels * bytes_per_sample);
+
+ if (num_channels == 2)
+ {
+ if (bytes_per_sample == 2)
+ {
+ /* uninterleave samples */
+ for(i=0; i<samples ;i++)
+ {
+ temp = ((signed char *)readbuffer)[i*4+1]; /*Flawfinder: ignore*/
+ temp += ((signed char *)readbuffer)[i*4+3]; /*Flawfinder: ignore*/
+ temp <<= 8;
+ temp += readbuffer[i*4];
+ temp += readbuffer[i*4+2];
+
+ buffer[0][i] = ((float)temp) / 65536.f;
+ }
+ }
+ else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
+ {
+ /* uninterleave samples */
+ for(i=0; i<samples ;i++)
+ {
+ temp = readbuffer[i*2+0];
+ temp += readbuffer[i*2+1];
+ temp -= 256;
+ buffer[0][i] = ((float)temp) / 256.f;
+ }
+ }
+ }
+ else if (num_channels == 1)
+ {
+ if (bytes_per_sample == 2)
+ {
+ for(i=0; i < samples ;i++)
+ {
+ temp = ((signed char*)readbuffer)[i*2+1];
+ temp <<= 8;
+ temp += readbuffer[i*2];
+ buffer[0][i] = ((float)temp) / 32768.f;
+ }
+ }
+ else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
+ {
+ for(i=0; i < samples ;i++)
+ {
+ temp = readbuffer[i];
+ temp -= 128;
+ buffer[0][i] = ((float)temp) / 128.f;
+ }
+ }
+ }
+
+ /* tell the library how much we actually submitted */
+ vorbis_analysis_wrote(&vd,i);
+ }
+
+ /* vorbis does some data preanalysis, then divvies up blocks for
+ more involved (potentially parallel) processing. Get a single
+ block for encoding now */
+ while(vorbis_analysis_blockout(&vd,&vb)==1)
+ {
+
+ /* analysis */
+ /* Do the main analysis, creating a packet */
+ vorbis_analysis(&vb, NULL);
+ vorbis_bitrate_addblock(&vb);
+
+ while(vorbis_bitrate_flushpacket(&vd, &op))
+ {
+
+ /* weld the packet into the bitstream */
+ ogg_stream_packetin(&os,&op);
+
+ /* write out pages (if any) */
+ while(!eos)
+ {
+ result = ogg_stream_pageout(&os,&og);
+
+ if(result==0)
+ break;
+
+ outfile.write(og.header, og.header_len);
+ outfile.write(og.body, og.body_len);
+
+ /* this could be set above, but for illustrative purposes, I do
+ it here (to show that vorbis does know where the stream ends) */
+
+ if(ogg_page_eos(&og))
+ eos=1;
+
+ }
+ }
+ }
+ }
+
+
+
+ /* clean up and exit. vorbis_info_clear() must be called last */
+
+ ogg_stream_clear(&os);
+ vorbis_block_clear(&vb);
+ vorbis_dsp_clear(&vd);
+ vorbis_comment_clear(&vc);
+ vorbis_info_clear(&vi);
+
+ /* ogg_page and ogg_packet structs always point to storage in
+ libvorbis. They're never freed or manipulated directly */
+
+// fprintf(stderr,"Vorbis encoding: Done.\n");
+ llinfos << "Vorbis encoding: Done." << llendl;
+
+#endif
+ return(LLVORBISENC_NOERR);
+
+}
diff --git a/indra/llaudio/llvorbisencode.h b/indra/llaudio/llvorbisencode.h
new file mode 100644
index 0000000000..ff5ce3a053
--- /dev/null
+++ b/indra/llaudio/llvorbisencode.h
@@ -0,0 +1,53 @@
+/**
+ * @file vorbisencode.h
+ * @brief Vorbis encoding routine routine for Indra.
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_VORBISENCODE_H
+#define LL_VORBISENCODE_H
+
+const S32 LLVORBISENC_NOERR = 0; // no error
+const S32 LLVORBISENC_SOURCE_OPEN_ERR = 1; // error opening source
+const S32 LLVORBISENC_DEST_OPEN_ERR = 2; // error opening destination
+const S32 LLVORBISENC_WAV_FORMAT_ERR = 3; // not a WAV
+const S32 LLVORBISENC_PCM_FORMAT_ERR = 4; // not a PCM
+const S32 LLVORBISENC_MONO_ERR = 5; // can't do mono
+const S32 LLVORBISENC_STEREO_ERR = 6; // can't do stereo
+const S32 LLVORBISENC_MULTICHANNEL_ERR = 7; // can't do stereo
+const S32 LLVORBISENC_UNSUPPORTED_SAMPLE_RATE = 8; // unsupported sample rate
+const S32 LLVORBISENC_UNSUPPORTED_WORD_SIZE = 9; // unsupported word size
+const S32 LLVORBISENC_CLIP_TOO_LONG = 10; // source file is too long
+
+
+S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& error_msg);
+S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname);
+
+#endif
+
diff --git a/indra/llaudio/llwindgen.h b/indra/llaudio/llwindgen.h
new file mode 100644
index 0000000000..847bfa6e9d
--- /dev/null
+++ b/indra/llaudio/llwindgen.h
@@ -0,0 +1,136 @@
+/**
+ * @file windgen.h
+ * @brief Templated wind noise generation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+#ifndef WINDGEN_H
+#define WINDGEN_H
+
+#include "llcommon.h"
+#include "llrand.h"
+
+template <class MIXBUFFERFORMAT_T>
+class LLWindGen
+{
+public:
+ LLWindGen() :
+ mTargetGain(0.f),
+ mTargetFreq(100.f),
+ mTargetPanGainR(0.5f),
+ mbuf0(0.0),
+ mbuf1(0.0),
+ mbuf2(0.0),
+ mbuf3(0.0),
+ mbuf4(0.0),
+ mbuf5(0.0),
+ mY0(0.0),
+ mY1(0.0),
+ mCurrentGain(0.f),
+ mCurrentFreq(100.f),
+ mCurrentPanGainR(0.5f) {};
+
+ static const U32 getInputSamplingRate() {return mInputSamplingRate;}
+
+ // newbuffer = the buffer passed from the previous DSP unit.
+ // numsamples = length in samples-per-channel at this mix time.
+ // stride = number of bytes between start of each sample.
+ // NOTE: generates L/R interleaved stereo
+ MIXBUFFERFORMAT_T* windGenerate(MIXBUFFERFORMAT_T *newbuffer, int numsamples, int stride)
+ {
+ U8 *cursamplep = (U8*)newbuffer;
+
+ double bandwidth = 50.0F;
+ double a0,b1,b2;
+
+ // calculate resonant filter coeffs
+ b2 = exp(-(F_TWO_PI) * (bandwidth / mInputSamplingRate));
+
+ while (numsamples--)
+ {
+ mCurrentFreq = (float)((0.999 * mCurrentFreq) + (0.001 * mTargetFreq));
+ mCurrentGain = (float)((0.999 * mCurrentGain) + (0.001 * mTargetGain));
+ mCurrentPanGainR = (float)((0.999 * mCurrentPanGainR) + (0.001 * mTargetPanGainR));
+ b1 = (-4.0 * b2) / (1.0 + b2) * cos(F_TWO_PI * (mCurrentFreq / mInputSamplingRate));
+ a0 = (1.0 - b2) * sqrt(1.0 - (b1 * b1) / (4.0 * b2));
+ double nextSample;
+
+ // start with white noise
+ nextSample = ll_frand(2.0f) - 1.0f;
+
+ // apply pinking filter
+ mbuf0 = 0.997f * mbuf0 + 0.0126502f * nextSample;
+ mbuf1 = 0.985f * mbuf1 + 0.0139083f * nextSample;
+ mbuf2 = 0.950f * mbuf2 + 0.0205439f * nextSample;
+ mbuf3 = 0.850f * mbuf3 + 0.0387225f * nextSample;
+ mbuf4 = 0.620f * mbuf4 + 0.0465932f * nextSample;
+ mbuf5 = 0.250f * mbuf5 + 0.1093477f * nextSample;
+
+ nextSample = mbuf0 + mbuf1 + mbuf2 + mbuf3 + mbuf4 + mbuf5;
+
+ // do a resonant filter on the noise
+ nextSample = (double)( a0 * nextSample - b1 * mY0 - b2 * mY1 );
+ mY1 = mY0;
+ mY0 = nextSample;
+
+ nextSample *= mCurrentGain;
+
+ MIXBUFFERFORMAT_T sample;
+
+ sample = llfloor(((F32)nextSample*32768.f*(1.0f - mCurrentPanGainR))+0.5f);
+ *(MIXBUFFERFORMAT_T*)cursamplep = llclamp(sample, (MIXBUFFERFORMAT_T)-32768, (MIXBUFFERFORMAT_T)32767);
+ cursamplep += stride;
+
+ sample = llfloor(((F32)nextSample*32768.f*mCurrentPanGainR)+0.5f);
+ *(MIXBUFFERFORMAT_T*)cursamplep = llclamp(sample, (MIXBUFFERFORMAT_T)-32768, (MIXBUFFERFORMAT_T)32767);
+ cursamplep += stride;
+ }
+
+ return newbuffer;
+ }
+
+ F32 mTargetGain;
+ F32 mTargetFreq;
+ F32 mTargetPanGainR;
+
+private:
+ static const U32 mInputSamplingRate = 44100;
+ F64 mbuf0;
+ F64 mbuf1;
+ F64 mbuf2;
+ F64 mbuf3;
+ F64 mbuf4;
+ F64 mbuf5;
+ F64 mY0;
+ F64 mY1;
+ F32 mCurrentGain;
+ F32 mCurrentFreq;
+ F32 mCurrentPanGainR;
+};
+
+#endif