Imported existing code
This commit is contained in:
548
indra/llaudio/llaudioengine_openal.cpp
Normal file
548
indra/llaudio/llaudioengine_openal.cpp
Normal file
@@ -0,0 +1,548 @@
|
||||
/**
|
||||
* @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
|
||||
}
|
||||
|
||||
// ------------
|
||||
|
||||
bool 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;
|
||||
return false;
|
||||
}
|
||||
|
||||
llinfos << "LLAudioEngine_OpenAL::initWind() done" << llendl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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::updateWind() Error creating wind buffer: " << error << llendl;
|
||||
break;
|
||||
}
|
||||
|
||||
alBufferData(buffer,
|
||||
AL_FORMAT_STEREO16,
|
||||
mWindGen->windGenerate(mWindBuf,
|
||||
mWindBufSamples),
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user