2488 lines
79 KiB
C++
Executable File
2488 lines
79 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "audio/audio.h"
|
|
#include "audio/audioDataBlock.h"
|
|
#include "core/tVector.h"
|
|
#include "console/console.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "game/gameConnection.h"
|
|
#include "core/fileStream.h"
|
|
#include "audio/audioStreamSourceFactory.h"
|
|
|
|
#ifdef TORQUE_OS_MAC
|
|
//#define REL_WORKAROUND
|
|
#endif
|
|
|
|
//-------------------------------------------------------------------------
|
|
#define MAX_AUDIOSOURCES 16 // maximum number of concurrent sources
|
|
#define MIN_GAIN 0.05f // anything with lower gain will not be started
|
|
#define MIN_UNCULL_PERIOD 500 // time before buffer is checked to be unculled
|
|
#define MIN_UNCULL_GAIN 0.1f // min gain of source to be unculled
|
|
|
|
#define ALX_DEF_SAMPLE_RATE 44100 // default values for mixer
|
|
#define ALX_DEF_SAMPLE_BITS 16
|
|
#define ALX_DEF_CHANNELS 2
|
|
|
|
#define FORCED_OUTER_FALLOFF 10000.f // forced falloff distance
|
|
static bool mDisableOuterFalloffs = false; // forced max falloff?
|
|
static F32 mInnerFalloffScale = 1.f; // amount to scale inner falloffs
|
|
|
|
static ALCdevice *mDevice = NULL; // active OpenAL device
|
|
static ALCcontext *mContext = NULL; // active OpenAL context
|
|
F32 mAudioTypeVolume[Audio::NumAudioTypes]; // the attenuation for each of the channel types
|
|
|
|
//-------------------------------------------------------------------------
|
|
struct LoopingImage
|
|
{
|
|
AUDIOHANDLE mHandle;
|
|
Resource<AudioBuffer> mBuffer;
|
|
Audio::Description mDescription;
|
|
AudioSampleEnvironment *mEnvironment;
|
|
|
|
Point3F mPosition;
|
|
Point3F mDirection;
|
|
F32 mPitch;
|
|
F32 mScore;
|
|
U32 mCullTime;
|
|
|
|
LoopingImage() { clear(); }
|
|
|
|
void clear()
|
|
{
|
|
mHandle = NULL_AUDIOHANDLE;
|
|
mBuffer = NULL;
|
|
dMemset(&mDescription, 0, sizeof(Audio::Description));
|
|
mEnvironment = 0;
|
|
mPosition.set(0.f,0.f,0.f);
|
|
mDirection.set(0.f,1.f,0.f);
|
|
mPitch = 1.f;
|
|
mScore = 0.f;
|
|
mCullTime = 0;
|
|
}
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
static F32 mMasterVolume = 1.f; // traped from AL_LISTENER gain (miles has difficulties with 3d sources)
|
|
|
|
static ALuint mSource[MAX_AUDIOSOURCES]; // ALSources
|
|
static AUDIOHANDLE mHandle[MAX_AUDIOSOURCES]; // unique handles
|
|
static Resource<AudioBuffer> mBuffer[MAX_AUDIOSOURCES]; // each of the playing buffers (needed for AudioThread)
|
|
static F32 mScore[MAX_AUDIOSOURCES]; // for figuring out which sources to cull/uncull
|
|
static F32 mSourceVolume[MAX_AUDIOSOURCES]; // the samples current un-attenuated gain (not scaled by master/channel gains)
|
|
static U32 mType[MAX_AUDIOSOURCES]; // the channel which this source belongs
|
|
|
|
static AudioSampleEnvironment* mSampleEnvironment[MAX_AUDIOSOURCES]; // currently playing sample environments
|
|
static bool mEnvironmentEnabled = false; // environment enabled?
|
|
static SimObjectPtr<AudioEnvironment> mCurrentEnvironment; // the last environment set
|
|
|
|
static ALuint mEnvironment = 0; // al environment handle
|
|
|
|
struct LoopingList : VectorPtr<LoopingImage*>
|
|
{
|
|
LoopingList() : VectorPtr<LoopingImage*>(__FILE__, __LINE__) { }
|
|
|
|
LoopingList::iterator findImage(AUDIOHANDLE handle);
|
|
void sort();
|
|
};
|
|
|
|
struct StreamingList : VectorPtr<AudioStreamSource*>
|
|
{
|
|
StreamingList() : VectorPtr<AudioStreamSource*>(__FILE__, __LINE__) { }
|
|
|
|
StreamingList::iterator findImage(AUDIOHANDLE handle);
|
|
void sort();
|
|
};
|
|
|
|
// LoopingList and LoopingFreeList own the images
|
|
static LoopingList mLoopingList; // all the looping sources
|
|
static LoopingList mLoopingFreeList; // free store
|
|
static LoopingList mLoopingInactiveList; // sources which have not been played yet
|
|
static LoopingList mLoopingCulledList; // sources which have been culled (alxPlay called)
|
|
|
|
// StreamingList and StreamingFreeList own the images
|
|
static StreamingList mStreamingList; // all the streaming sources
|
|
//static StreamingList mStreamingFreeList; // free store
|
|
static StreamingList mStreamingInactiveList; // sources which have not been played yet
|
|
static StreamingList mStreamingCulledList; // sources which have been culled (alxPlay called)
|
|
|
|
#define AUDIOHANDLE_LOOPING_BIT (0x80000000)
|
|
#define AUDIOHANDLE_STREAMING_BIT (0x40000000)
|
|
#define AUDIOHANDLE_INACTIVE_BIT (0x20000000)
|
|
#define AUDIOHANDLE_LOADING_BIT (0x10000000)
|
|
#define HANDLE_MASK ~(AUDIOHANDLE_LOOPING_BIT | AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT)
|
|
|
|
// keep the 'AUDIOHANDLE_LOOPING_BIT' on the handle returned to the caller so that
|
|
// the handle can quickly be rejected from looping list queries
|
|
#define RETURN_MASK ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT)
|
|
static AUDIOHANDLE mLastHandle = NULL_AUDIOHANDLE;
|
|
|
|
static bool mForceMaxDistanceUpdate = false; // force gain setting for 3d distances
|
|
static U32 mNumSources = 0; // total number of sources to work with
|
|
static U32 mRequestSources = MAX_AUDIOSOURCES; // number of sources to request from openAL
|
|
|
|
#define INVALID_SOURCE 0xffffffff
|
|
|
|
inline bool areEqualHandles(AUDIOHANDLE a, AUDIOHANDLE b)
|
|
{
|
|
return((a & HANDLE_MASK) == (b & HANDLE_MASK));
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Looping image
|
|
//-------------------------------------------------------------------------
|
|
inline LoopingList::iterator LoopingList::findImage(AUDIOHANDLE handle)
|
|
{
|
|
if(handle & AUDIOHANDLE_LOOPING_BIT)
|
|
{
|
|
LoopingList::iterator itr = begin();
|
|
while(itr != end())
|
|
{
|
|
if(areEqualHandles((*itr)->mHandle, handle))
|
|
return(itr);
|
|
itr++;
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
inline int QSORT_CALLBACK loopingImageSort(const void * p1, const void * p2)
|
|
{
|
|
const LoopingImage * ip1 = *(const LoopingImage**)p1;
|
|
const LoopingImage * ip2 = *(const LoopingImage**)p2;
|
|
|
|
// min->max
|
|
return ip2->mScore - ip1->mScore;
|
|
}
|
|
|
|
void LoopingList::sort()
|
|
{
|
|
dQsort(address(), size(), sizeof(LoopingImage*), loopingImageSort);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// StreamingList
|
|
//-------------------------------------------------------------------------
|
|
inline StreamingList::iterator StreamingList::findImage(AUDIOHANDLE handle)
|
|
{
|
|
if(handle & AUDIOHANDLE_STREAMING_BIT)
|
|
{
|
|
StreamingList::iterator itr = begin();
|
|
while(itr != end())
|
|
{
|
|
if(areEqualHandles((*itr)->mHandle, handle))
|
|
return(itr);
|
|
itr++;
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
inline int QSORT_CALLBACK streamingSourceSort(const void * p1, const void * p2)
|
|
{
|
|
const AudioStreamSource * ip1 = *(const AudioStreamSource**)p1;
|
|
const AudioStreamSource * ip2 = *(const AudioStreamSource**)p2;
|
|
|
|
// min->max
|
|
return ip2->mScore - ip1->mScore;
|
|
}
|
|
|
|
void StreamingList::sort()
|
|
{
|
|
dQsort(address(), size(), sizeof(AudioStreamSource*), streamingSourceSort);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
LoopingImage * createLoopingImage()
|
|
{
|
|
LoopingImage *image;
|
|
if (mLoopingFreeList.size())
|
|
{
|
|
image = mLoopingFreeList.last();
|
|
mLoopingFreeList.pop_back();
|
|
}
|
|
else
|
|
image = new LoopingImage;
|
|
return(image);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
AudioStreamSource * createStreamingSource(const char* filename)
|
|
{
|
|
AudioStreamSource *streamSource = AudioStreamSourceFactory::getNewInstance(filename);
|
|
return(streamSource);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
static AUDIOHANDLE getNewHandle()
|
|
{
|
|
mLastHandle++;
|
|
mLastHandle &= HANDLE_MASK;
|
|
if (mLastHandle == NULL_AUDIOHANDLE)
|
|
mLastHandle++;
|
|
return mLastHandle;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// function declarations
|
|
void alxLoopingUpdate();
|
|
void alxStreamingUpdate();
|
|
void alxUpdateScores(bool);
|
|
|
|
static bool findFreeSource(U32 *index)
|
|
{
|
|
for(U32 i = 0; i < mNumSources; i++)
|
|
if(mHandle[i] == NULL_AUDIOHANDLE)
|
|
{
|
|
*index = i;
|
|
return(true);
|
|
}
|
|
return(false);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// - cull out the min source that is below volume
|
|
// - streams/voice/loading streams are all scored > 2
|
|
// - volumes are attenuated by channel only
|
|
static bool cullSource(U32 *index, F32 volume)
|
|
{
|
|
alGetError();
|
|
|
|
F32 minVolume = volume;
|
|
S32 best = -1;
|
|
for(S32 i = 0; i < mNumSources; i++)
|
|
{
|
|
if(mScore[i] < minVolume)
|
|
{
|
|
minVolume = mScore[i];
|
|
best = i;
|
|
}
|
|
}
|
|
|
|
if(best == -1)
|
|
return(false);
|
|
|
|
// check if culling a looper
|
|
LoopingList::iterator itr = mLoopingList.findImage(mHandle[best]);
|
|
if(itr)
|
|
{
|
|
// check if culling an inactive looper
|
|
if(mHandle[best] & AUDIOHANDLE_INACTIVE_BIT)
|
|
{
|
|
AssertFatal(!mLoopingInactiveList.findImage(mHandle[best]), "cullSource: image already in inactive list");
|
|
AssertFatal(!mLoopingCulledList.findImage(mHandle[best]), "cullSource: image should not be in culled list");
|
|
mLoopingInactiveList.push_back(*itr);
|
|
}
|
|
else
|
|
{
|
|
(*itr)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
|
|
AssertFatal(!mLoopingCulledList.findImage(mHandle[best]), "cullSource: image already in culled list");
|
|
AssertFatal(!mLoopingInactiveList.findImage(mHandle[best]), "cullSource: image should no be in inactive list");
|
|
(*itr)->mCullTime = Platform::getRealMilliseconds();
|
|
mLoopingCulledList.push_back(*itr);
|
|
}
|
|
}
|
|
|
|
// check if culling a streamer
|
|
StreamingList::iterator itr2 = mStreamingList.findImage(mHandle[best]);
|
|
if(itr2)
|
|
{
|
|
// check if culling an inactive streamer
|
|
if(mHandle[best] & AUDIOHANDLE_INACTIVE_BIT)
|
|
{
|
|
AssertFatal(!mStreamingInactiveList.findImage(mHandle[best]), "cullSource: image already in inactive list");
|
|
AssertFatal(!mStreamingCulledList.findImage(mHandle[best]), "cullSource: image should not be in culled list");
|
|
mStreamingInactiveList.push_back(*itr2);
|
|
}
|
|
else
|
|
{
|
|
(*itr2)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
|
|
AssertFatal(!mStreamingCulledList.findImage(mHandle[best]), "cullSource: image already in culled list");
|
|
AssertFatal(!mStreamingInactiveList.findImage(mHandle[best]), "cullSource: image should no be in inactive list");
|
|
(*itr2)->freeStream();
|
|
(*itr2)->mCullTime = Platform::getRealMilliseconds();
|
|
mStreamingCulledList.push_back(*itr2);
|
|
}
|
|
}
|
|
|
|
alSourceStop(mSource[best]);
|
|
mHandle[best] = NULL_AUDIOHANDLE;
|
|
mBuffer[best] = 0;
|
|
*index = best;
|
|
|
|
return(true);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
/** Compute approximate max volume at a particular distance
|
|
ignore cone volume influnces
|
|
*/
|
|
static F32 approximate3DVolume(const Audio::Description *desc, const Point3F &position)
|
|
{
|
|
Point3F p1;
|
|
alGetListener3f(AL_POSITION, &p1.x, &p1.y, &p1.z);
|
|
|
|
p1 -= position;
|
|
F32 distance = p1.magnitudeSafe();
|
|
|
|
if(distance >= desc->mMaxDistance)
|
|
return(0.f);
|
|
else if(distance < desc->mReferenceDistance)
|
|
return 1.0f;
|
|
else
|
|
return 1.0 - (distance - desc->mReferenceDistance) / (desc->mMaxDistance - desc->mReferenceDistance);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
inline U32 alxFindIndex(AUDIOHANDLE handle)
|
|
{
|
|
for (U32 i=0; i<MAX_AUDIOSOURCES; i++)
|
|
if(mHandle[i] && areEqualHandles(mHandle[i], handle))
|
|
return i;
|
|
return MAX_AUDIOSOURCES;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
ALuint alxFindSource(AUDIOHANDLE handle)
|
|
{
|
|
for (U32 i=0; i<MAX_AUDIOSOURCES; i++)
|
|
if(mHandle[i] && areEqualHandles(mHandle[i], handle))
|
|
return mSource[i];
|
|
return(INVALID_SOURCE);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
/** Determmine if an AUDIOHANDLE is valid.
|
|
An AUDIOHANDLE is valid if it is a currently playing source, inactive source,
|
|
or a looping source (basically anything where a alxSource??? call will succeed)
|
|
*/
|
|
bool alxIsValidHandle(AUDIOHANDLE handle)
|
|
{
|
|
if(handle == NULL_AUDIOHANDLE)
|
|
return(false);
|
|
|
|
// inactive sources are valid
|
|
U32 idx = alxFindIndex(handle);
|
|
if(idx != MAX_AUDIOSOURCES)
|
|
{
|
|
if(mHandle[idx] & AUDIOHANDLE_INACTIVE_BIT)
|
|
return(true);
|
|
|
|
// if it is active but not playing then it has stopped...
|
|
ALint state = AL_STOPPED;
|
|
alGetSourcei(mSource[idx], AL_SOURCE_STATE, &state);
|
|
return(state == AL_PLAYING);
|
|
}
|
|
|
|
if(mLoopingList.findImage(handle))
|
|
return(true);
|
|
|
|
if(mStreamingList.findImage(handle))
|
|
return(true);
|
|
|
|
return(false);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
/** Determmine if an AUDIOHANDLE is currently playing
|
|
*/
|
|
bool alxIsPlaying(AUDIOHANDLE handle)
|
|
{
|
|
if(handle == NULL_AUDIOHANDLE)
|
|
return(false);
|
|
|
|
U32 idx = alxFindIndex(handle);
|
|
if(idx == MAX_AUDIOSOURCES)
|
|
return(false);
|
|
|
|
ALint state = 0;
|
|
alGetSourcei(mSource[idx], AL_SOURCE_STATE, &state);
|
|
return(state == AL_PLAYING);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void alxEnvironmentDestroy()
|
|
{
|
|
/* todo
|
|
if(mEnvironment)
|
|
{
|
|
alDeleteEnvironmentIASIG(1, &mEnvironment);
|
|
mEnvironment = 0;
|
|
}
|
|
*/
|
|
}
|
|
|
|
void alxEnvironmentInit()
|
|
{
|
|
/* todo
|
|
alxEnvironmentDestroy();
|
|
if(alIsExtensionPresent((const ALubyte *)"AL_EXT_IASIG"))
|
|
{
|
|
alGenEnvironmentIASIG(1, &mEnvironment);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
mEnvironment = 0;
|
|
}
|
|
*/
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// - setup a sources environmental effect settings
|
|
static void alxSourceEnvironment(ALuint source, F32 environmentLevel, AudioSampleEnvironment * env)
|
|
{
|
|
// environment level is on the AudioDatablock
|
|
/* todo
|
|
alSourcef(source, AL_ENV_SAMPLE_REVERB_MIX_EXT, environmentLevel);
|
|
*/
|
|
if(!env)
|
|
return;
|
|
|
|
/* todo
|
|
alSourcei(source, AL_ENV_SAMPLE_DIRECT_EXT, env->mDirect);
|
|
alSourcei(source, AL_ENV_SAMPLE_DIRECT_HF_EXT, env->mDirectHF);
|
|
alSourcei(source, AL_ENV_SAMPLE_ROOM_EXT, env->mRoom);
|
|
alSourcei(source, AL_ENV_SAMPLE_ROOM_HF_EXT, env->mRoomHF);
|
|
alSourcei(source, AL_ENV_SAMPLE_OUTSIDE_VOLUME_HF_EXT, env->mOutsideVolumeHF);
|
|
alSourcei(source, AL_ENV_SAMPLE_FLAGS_EXT, env->mFlags);
|
|
|
|
alSourcef(source, AL_ENV_SAMPLE_OBSTRUCTION_EXT, env->mObstruction);
|
|
alSourcef(source, AL_ENV_SAMPLE_OBSTRUCTION_LF_RATIO_EXT, env->mObstructionLFRatio);
|
|
alSourcef(source, AL_ENV_SAMPLE_OCCLUSION_EXT, env->mOcclusion);
|
|
alSourcef(source, AL_ENV_SAMPLE_OCCLUSION_LF_RATIO_EXT, env->mOcclusionLFRatio);
|
|
alSourcef(source, AL_ENV_SAMPLE_OCCLUSION_ROOM_RATIO_EXT, env->mOcclusionRoomRatio);
|
|
alSourcef(source, AL_ENV_SAMPLE_ROOM_ROLLOFF_EXT, env->mRoomRolloff);
|
|
alSourcef(source, AL_ENV_SAMPLE_AIR_ABSORPTION_EXT, env->mAirAbsorption);
|
|
*/
|
|
}
|
|
|
|
static void alxSourceEnvironment(ALuint source, LoopingImage * image)
|
|
{
|
|
AssertFatal(image, "alxSourceEnvironment: invalid looping image");
|
|
if(image->mDescription.mIs3D)
|
|
alxSourceEnvironment(source, image->mDescription.mEnvironmentLevel, image->mEnvironment);
|
|
}
|
|
|
|
static void alxSourceEnvironment(ALuint source, AudioStreamSource * image)
|
|
{
|
|
AssertFatal(image, "alxSourceEnvironment: invalid looping image");
|
|
if(image->mDescription.mIs3D)
|
|
alxSourceEnvironment(source, image->mDescription.mEnvironmentLevel, image->mEnvironment);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// setup a source to play... loopers have pitch cached
|
|
// - by default, pitch is 1x (settings not defined in description)
|
|
// - all the settings are cached by openAL (miles version), so no worries setting them here
|
|
static void alxSourcePlay(ALuint source, Resource<AudioBuffer> buffer, const Audio::Description *desc, const MatrixF *transform)
|
|
{
|
|
alSourcei(source, AL_BUFFER, buffer->getALBuffer());
|
|
alSourcef(source, AL_GAIN, Audio::linearToDB(desc->mVolume * mAudioTypeVolume[desc->mType] * mMasterVolume));
|
|
alSourcei(source, AL_LOOPING, desc->mIsLooping ? AL_TRUE : AL_FALSE);
|
|
alSourcef(source, AL_PITCH, 1.f);
|
|
|
|
alSourcei(source, AL_CONE_INNER_ANGLE, desc->mConeInsideAngle);
|
|
alSourcei(source, AL_CONE_OUTER_ANGLE, desc->mConeOutsideAngle);
|
|
alSourcef(source, AL_CONE_OUTER_GAIN, desc->mConeOutsideVolume);
|
|
|
|
if(transform != NULL)
|
|
{
|
|
#ifdef REL_WORKAROUND
|
|
alSourcei(source, AL_SOURCE_ABSOLUTE, AL_TRUE);
|
|
#else
|
|
alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE);
|
|
#endif
|
|
|
|
Point3F p;
|
|
transform->getColumn(3, &p);
|
|
alSource3f(source, AL_POSITION, p.x, p.y, p.z);
|
|
|
|
//Always use ConeVector (which is tied to transform)
|
|
alSource3f(source, AL_DIRECTION, desc->mConeVector.x, desc->mConeVector.y, desc->mConeVector.z);
|
|
|
|
}
|
|
else
|
|
{
|
|
// 2D sound
|
|
alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
|
|
alSource3f(source, AL_POSITION, 0.0f, 0.0f, 1.0f);
|
|
}
|
|
|
|
alSourcef(source, AL_REFERENCE_DISTANCE, desc->mReferenceDistance);
|
|
alSourcef(source, AL_MAX_DISTANCE, desc->mMaxDistance);
|
|
|
|
/* todo
|
|
// environmental audio stuff:
|
|
alSourcef(source, AL_ENV_SAMPLE_REVERB_MIX_EXT, desc->mEnvironmentLevel);
|
|
if(desc->mEnvironmentLevel != 0.f)
|
|
alSourceResetEnvironment_EXT(source);
|
|
*/
|
|
}
|
|
|
|
// helper for looping images
|
|
static void alxSourcePlay(ALuint source, LoopingImage * image)
|
|
{
|
|
AssertFatal(image, "alxSourcePlay: invalid looping image");
|
|
|
|
// 3d source? need position/direction
|
|
if(image->mDescription.mIs3D)
|
|
{
|
|
MatrixF transform(true);
|
|
transform.setColumn(3, image->mPosition);
|
|
transform.setRow(1, image->mDirection);
|
|
alxSourcePlay(source, image->mBuffer, &image->mDescription, &transform);
|
|
}
|
|
else
|
|
{
|
|
// 2d source
|
|
alxSourcePlay(source, image->mBuffer, &image->mDescription, 0);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// setup a streaming source to play
|
|
static void alxSourcePlay(AudioStreamSource *streamSource)
|
|
{
|
|
ALuint source = streamSource->mSource;
|
|
Audio::Description *desc = &streamSource->mDescription;
|
|
|
|
bool ret = streamSource->initStream();
|
|
|
|
alSourcef(source, AL_GAIN, Audio::linearToDB(desc->mVolume * mAudioTypeVolume[desc->mType] * mMasterVolume));
|
|
// alSourcei(source, AL_LOOPING, AL_FALSE);
|
|
alSourcef(source, AL_PITCH, 1.f);
|
|
|
|
alSourcei(source, AL_CONE_INNER_ANGLE, desc->mConeInsideAngle);
|
|
alSourcei(source, AL_CONE_OUTER_ANGLE, desc->mConeOutsideAngle);
|
|
alSourcef(source, AL_CONE_OUTER_GAIN, desc->mConeOutsideVolume);
|
|
|
|
if(streamSource->mDescription.mIs3D)
|
|
{
|
|
MatrixF transform(true);
|
|
transform.setColumn(3, streamSource->mPosition);
|
|
transform.setRow(1, streamSource->mDirection);
|
|
|
|
#ifdef REL_WORKAROUND
|
|
alSourcei(source, AL_SOURCE_ABSOLUTE, AL_TRUE);
|
|
#else
|
|
alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE);
|
|
#endif
|
|
|
|
Point3F p;
|
|
transform.getColumn(3, &p);
|
|
alSource3f(source, AL_POSITION, p.x, p.y, p.z);
|
|
|
|
//Always use ConeVector (which is tied to transform)
|
|
alSource3f(source, AL_DIRECTION, desc->mConeVector.x, desc->mConeVector.y, desc->mConeVector.z);
|
|
}
|
|
else
|
|
{
|
|
// 2D sound
|
|
// JMQ: slam the stream source's position to our desired value
|
|
streamSource->mPosition = Point3F(0.0f, 0.0f, 1.0f);
|
|
alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
|
|
alSource3f(source, AL_POSITION,
|
|
streamSource->mPosition.x,
|
|
streamSource->mPosition.y,
|
|
streamSource->mPosition.z);
|
|
}
|
|
|
|
alSourcef(source, AL_REFERENCE_DISTANCE, desc->mReferenceDistance);
|
|
alSourcef(source, AL_MAX_DISTANCE, desc->mMaxDistance);
|
|
|
|
/* todo
|
|
// environmental audio stuff:
|
|
alSourcef(source, AL_ENV_SAMPLE_REVERB_MIX_EXT, desc->mEnvironmentLevel);
|
|
if(desc->mEnvironmentLevel != 0.f)
|
|
alSourceResetEnvironment_EXT(source);
|
|
*/
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
AUDIOHANDLE alxCreateSource(const Audio::Description *desc,
|
|
const char *filename,
|
|
const MatrixF *transform,
|
|
AudioSampleEnvironment *sampleEnvironment)
|
|
{
|
|
if (!mContext)
|
|
return NULL_AUDIOHANDLE;
|
|
|
|
if(desc == NULL || filename == NULL || *filename == '\0')
|
|
return NULL_AUDIOHANDLE;
|
|
|
|
F32 volume = desc->mVolume;
|
|
|
|
// calculate an approximate attenuation for 3d sounds
|
|
if(transform && desc->mIs3D)
|
|
{
|
|
Point3F position;
|
|
transform->getColumn(3, &position);
|
|
volume *= approximate3DVolume(desc, position);
|
|
}
|
|
|
|
// check the type specific volume
|
|
AssertFatal(desc->mType < Audio::NumAudioTypes, "alxCreateSource: invalid type for source");
|
|
if(desc->mType >= Audio::NumAudioTypes)
|
|
return(NULL_AUDIOHANDLE);
|
|
|
|
// done if channel is muted (and not a looper)
|
|
if(!desc->mIsLooping && !desc->mIsStreaming && (mAudioTypeVolume[desc->mType] == 0.f))
|
|
return(NULL_AUDIOHANDLE);
|
|
|
|
// scale volume by channel attenuation
|
|
volume *= mAudioTypeVolume[desc->mType];
|
|
|
|
// non-loopers don't add if < minvolume
|
|
if(!desc->mIsLooping && !desc->mIsStreaming && (volume <= MIN_GAIN))
|
|
return(NULL_AUDIOHANDLE);
|
|
|
|
U32 index = MAX_AUDIOSOURCES;
|
|
|
|
// try and find an available source: 0 volume loopers get added to inactive list
|
|
if(volume > MIN_GAIN)
|
|
{
|
|
if(!findFreeSource(&index))
|
|
{
|
|
alxUpdateScores(true);
|
|
|
|
// scores do not include master volume
|
|
if(!cullSource(&index, volume))
|
|
index = MAX_AUDIOSOURCES;
|
|
}
|
|
}
|
|
|
|
// make sure that loopers are added
|
|
if(index == MAX_AUDIOSOURCES)
|
|
{
|
|
if(desc->mIsLooping && !(desc->mIsStreaming))
|
|
{
|
|
Resource<AudioBuffer> buffer = AudioBuffer::find(filename);
|
|
if(!(bool)buffer)
|
|
return(NULL_AUDIOHANDLE);
|
|
|
|
// create the inactive looping image
|
|
LoopingImage * image = createLoopingImage();
|
|
|
|
image->mHandle = getNewHandle() | AUDIOHANDLE_LOOPING_BIT | AUDIOHANDLE_INACTIVE_BIT;
|
|
image->mBuffer = buffer;
|
|
image->mDescription = *desc;
|
|
image->mScore = volume;
|
|
image->mEnvironment = sampleEnvironment;
|
|
|
|
// grab position/direction if 3d source
|
|
if(transform)
|
|
{
|
|
transform->getColumn(3, &image->mPosition);
|
|
transform->getColumn(1, &image->mDirection);
|
|
}
|
|
|
|
AssertFatal(!mLoopingInactiveList.findImage(image->mHandle), "alxCreateSource: handle in inactive list");
|
|
AssertFatal(!mLoopingCulledList.findImage(image->mHandle), "alxCreateSource: handle in culled list");
|
|
|
|
// add to the looping and inactive lists
|
|
mLoopingList.push_back(image);
|
|
mLoopingInactiveList.push_back(image);
|
|
return(image->mHandle & RETURN_MASK);
|
|
}
|
|
else
|
|
return(NULL_AUDIOHANDLE);
|
|
}
|
|
|
|
// make sure that streamers are added
|
|
if(index == MAX_AUDIOSOURCES)
|
|
{
|
|
if(desc->mIsStreaming)
|
|
{
|
|
// create the inactive audio stream
|
|
AudioStreamSource * streamSource = createStreamingSource(filename);
|
|
if (streamSource)
|
|
{
|
|
streamSource->mHandle = getNewHandle() | AUDIOHANDLE_STREAMING_BIT | AUDIOHANDLE_INACTIVE_BIT;
|
|
streamSource->mSource = NULL;
|
|
streamSource->mDescription = *desc;
|
|
streamSource->mScore = volume;
|
|
streamSource->mEnvironment = sampleEnvironment;
|
|
|
|
// grab position/direction if 3d source
|
|
if(transform)
|
|
{
|
|
transform->getColumn(3, &streamSource->mPosition);
|
|
transform->getColumn(1, &streamSource->mDirection);
|
|
}
|
|
|
|
AssertFatal(!mStreamingInactiveList.findImage(streamSource->mHandle), "alxCreateSource: handle in inactive list");
|
|
AssertFatal(!mStreamingCulledList.findImage(streamSource->mHandle), "alxCreateSource: handle in culled list");
|
|
|
|
// add to the streaming and inactive lists
|
|
mStreamingList.push_back(streamSource);
|
|
mStreamingInactiveList.push_back(streamSource);
|
|
return(streamSource->mHandle & RETURN_MASK);
|
|
}
|
|
else
|
|
return NULL_AUDIOHANDLE;
|
|
}
|
|
else
|
|
return(NULL_AUDIOHANDLE);
|
|
}
|
|
|
|
// clear the error state
|
|
alGetError();
|
|
|
|
// grab the buffer
|
|
Resource<AudioBuffer> buffer;
|
|
if(!(desc->mIsStreaming)) {
|
|
buffer = AudioBuffer::find(filename);
|
|
if((bool)buffer == false)
|
|
return NULL_AUDIOHANDLE;
|
|
}
|
|
|
|
// init the source (created inactive) and store needed values
|
|
mHandle[index] = getNewHandle() | AUDIOHANDLE_INACTIVE_BIT;
|
|
mType[index] = desc->mType;
|
|
if(!(desc->mIsStreaming)) {
|
|
mBuffer[index] = buffer;
|
|
}
|
|
mScore[index] = volume;
|
|
mSourceVolume[index] = desc->mVolume;
|
|
mSampleEnvironment[index] = sampleEnvironment;
|
|
|
|
ALuint source = mSource[index];
|
|
|
|
// setup play info
|
|
if(!desc->mIsStreaming)
|
|
alxSourcePlay(source, buffer, desc, desc->mIs3D ? transform : 0);
|
|
|
|
if(mEnvironmentEnabled)
|
|
alxSourceEnvironment(source, desc->mEnvironmentLevel, sampleEnvironment);
|
|
|
|
// setup a LoopingImage ONLY if the sound is a looper:
|
|
if(desc->mIsLooping && !(desc->mIsStreaming))
|
|
{
|
|
mHandle[index] |= AUDIOHANDLE_LOOPING_BIT;
|
|
|
|
LoopingImage * image = createLoopingImage();
|
|
image->mHandle = mHandle[index];
|
|
image->mBuffer = buffer;
|
|
image->mDescription = *desc;
|
|
image->mScore = volume;
|
|
image->mEnvironment = sampleEnvironment;
|
|
|
|
// grab position/direction
|
|
if(transform)
|
|
{
|
|
transform->getColumn(3, &image->mPosition);
|
|
transform->getColumn(1, &image->mDirection);
|
|
}
|
|
|
|
AssertFatal(!mLoopingInactiveList.findImage(image->mHandle), "alxCreateSource: handle in inactive list");
|
|
AssertFatal(!mLoopingCulledList.findImage(image->mHandle), "alxCreateSource: handle in culled list");
|
|
|
|
// add to the looping list
|
|
mLoopingList.push_back(image);
|
|
}
|
|
|
|
// setup a AudioStreamSource ONLY if the sound is a streamer:
|
|
if(desc->mIsStreaming)
|
|
{
|
|
// Intangir> why is loading bit never used anywhere else?
|
|
// comes in handy for my oggmixedstream
|
|
// (prevents it from being deleted before it is loaded)
|
|
mHandle[index] |= AUDIOHANDLE_STREAMING_BIT | AUDIOHANDLE_LOADING_BIT;
|
|
|
|
AudioStreamSource * streamSource = createStreamingSource(filename);
|
|
if (streamSource)
|
|
{
|
|
streamSource->mHandle = mHandle[index];
|
|
streamSource->mSource = mSource[index];
|
|
streamSource->mDescription = *desc;
|
|
streamSource->mScore = volume;
|
|
streamSource->mEnvironment = sampleEnvironment;
|
|
|
|
// grab position/direction
|
|
if(transform)
|
|
{
|
|
transform->getColumn(3, &streamSource->mPosition);
|
|
transform->getColumn(1, &streamSource->mDirection);
|
|
}
|
|
|
|
AssertFatal(!mStreamingInactiveList.findImage(streamSource->mHandle), "alxCreateSource: handle in inactive list");
|
|
AssertFatal(!mStreamingCulledList.findImage(streamSource->mHandle), "alxCreateSource: handle in culled list");
|
|
|
|
alxSourcePlay(streamSource);
|
|
|
|
// add to the looping list
|
|
mStreamingList.push_back(streamSource);
|
|
}
|
|
else
|
|
{
|
|
mSampleEnvironment[index] = 0;
|
|
mHandle[index] = NULL_AUDIOHANDLE;
|
|
mBuffer[index] = 0;
|
|
return NULL_AUDIOHANDLE;
|
|
}
|
|
}
|
|
|
|
// clear off all but looping bit
|
|
return(mHandle[index] & RETURN_MASK);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
AUDIOHANDLE alxCreateSource(AudioDescription *descObject,
|
|
const char *filename,
|
|
const MatrixF *transform,
|
|
AudioSampleEnvironment * sampleEnvironment )
|
|
{
|
|
if(!descObject || !descObject->getDescription())
|
|
return(NULL_AUDIOHANDLE);
|
|
return (alxCreateSource(descObject->getDescription(), filename, transform, sampleEnvironment));
|
|
}
|
|
|
|
AUDIOHANDLE alxCreateSource(const AudioProfile *profile, const MatrixF *transform)
|
|
{
|
|
if (profile == NULL)
|
|
return NULL_AUDIOHANDLE;
|
|
|
|
return alxCreateSource(profile->mDescriptionObject, profile->mFilename, transform, profile->mSampleEnvironment);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
extern void threadPlay(AudioBuffer * buffer, AUDIOHANDLE handle);
|
|
|
|
AUDIOHANDLE alxPlay(AUDIOHANDLE handle)
|
|
{
|
|
U32 index = alxFindIndex(handle);
|
|
|
|
if(index != MAX_AUDIOSOURCES)
|
|
{
|
|
// play if not already playing
|
|
if(mHandle[index] & AUDIOHANDLE_INACTIVE_BIT)
|
|
{
|
|
mHandle[index] &= ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT);
|
|
|
|
// make sure the looping image also clears it's inactive bit
|
|
LoopingList::iterator itr = mLoopingList.findImage(handle);
|
|
if(itr)
|
|
(*itr)->mHandle &= ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT);
|
|
|
|
// make sure the streaming image also clears it's inactive bit
|
|
StreamingList::iterator itr2 = mStreamingList.findImage(handle);
|
|
if(itr2)
|
|
(*itr2)->mHandle &= ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT);
|
|
|
|
alSourcePlay(mSource[index]);
|
|
|
|
return(handle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// move inactive loopers to the culled list, try to start the sound
|
|
LoopingList::iterator itr = mLoopingInactiveList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
AssertFatal(!mLoopingCulledList.findImage(handle), "alxPlay: image already in culled list");
|
|
mLoopingCulledList.push_back(*itr);
|
|
mLoopingInactiveList.erase_fast(itr);
|
|
alxLoopingUpdate();
|
|
return(handle);
|
|
}
|
|
else if(mLoopingCulledList.findImage(handle))
|
|
{
|
|
alxLoopingUpdate();
|
|
return(handle);
|
|
}
|
|
else
|
|
return(NULL_AUDIOHANDLE);
|
|
|
|
// move inactive streamers to the culled list, try to start the sound
|
|
StreamingList::iterator itr2 = mStreamingInactiveList.findImage(handle);
|
|
if(itr2)
|
|
{
|
|
AssertFatal(!mStreamingCulledList.findImage(handle), "alxPlay: image already in culled list");
|
|
(*itr2)->freeStream();
|
|
mStreamingCulledList.push_back(*itr2);
|
|
mStreamingInactiveList.erase_fast(itr2);
|
|
alxStreamingUpdate();
|
|
return(handle);
|
|
}
|
|
else if(mStreamingCulledList.findImage(handle))
|
|
{
|
|
alxStreamingUpdate();
|
|
return(handle);
|
|
}
|
|
else
|
|
return(NULL_AUDIOHANDLE);
|
|
}
|
|
|
|
return(handle);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// helper function.. create a source and play it
|
|
AUDIOHANDLE alxPlay(const AudioProfile *profile, const MatrixF *transform, const Point3F* /*velocity*/)
|
|
{
|
|
if(profile == NULL)
|
|
return NULL_AUDIOHANDLE;
|
|
|
|
AUDIOHANDLE handle = alxCreateSource(profile->mDescriptionObject, profile->mFilename, transform, profile->mSampleEnvironment);
|
|
if(handle != NULL_AUDIOHANDLE)
|
|
return(alxPlay(handle));
|
|
return(handle);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void alxStop(AUDIOHANDLE handle)
|
|
{
|
|
U32 index = alxFindIndex(handle);
|
|
|
|
// stop it
|
|
if(index != MAX_AUDIOSOURCES)
|
|
{
|
|
if(!(mHandle[index] & AUDIOHANDLE_INACTIVE_BIT))
|
|
{
|
|
alSourceStop(mSource[index]);
|
|
}
|
|
|
|
mSampleEnvironment[index] = 0;
|
|
mHandle[index] = NULL_AUDIOHANDLE;
|
|
mBuffer[index] = 0;
|
|
}
|
|
|
|
// remove loopingImage and add it to the free list
|
|
LoopingList::iterator itr = mLoopingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
// remove from inactive/culled list
|
|
if((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT)
|
|
{
|
|
LoopingList::iterator tmp = mLoopingInactiveList.findImage(handle);
|
|
|
|
// inactive?
|
|
if(tmp)
|
|
mLoopingInactiveList.erase_fast(tmp);
|
|
else
|
|
{
|
|
//culled?
|
|
tmp = mLoopingCulledList.findImage(handle);
|
|
AssertFatal(tmp, "alxStop: failed to find inactive looping source");
|
|
mLoopingCulledList.erase_fast(tmp);
|
|
}
|
|
}
|
|
|
|
AssertFatal(!mLoopingInactiveList.findImage((*itr)->mHandle), "alxStop: handle in inactive list");
|
|
AssertFatal(!mLoopingCulledList.findImage((*itr)->mHandle), "alxStop: handle in culled list");
|
|
|
|
// remove it
|
|
(*itr)->clear();
|
|
mLoopingFreeList.push_back(*itr);
|
|
mLoopingList.erase_fast(itr);
|
|
}
|
|
|
|
// remove streamingImage and add it to the free list
|
|
StreamingList::iterator itr2 = mStreamingList.findImage(handle);
|
|
if(itr2)
|
|
{
|
|
// remove from inactive/culled list
|
|
if((*itr2)->mHandle & AUDIOHANDLE_INACTIVE_BIT)
|
|
{
|
|
StreamingList::iterator tmp = mStreamingInactiveList.findImage(handle);
|
|
|
|
// inactive?
|
|
if(tmp)
|
|
mStreamingInactiveList.erase_fast(tmp);
|
|
else
|
|
{
|
|
//culled?
|
|
tmp = mStreamingCulledList.findImage(handle);
|
|
AssertFatal(tmp, "alxStop: failed to find inactive looping source");
|
|
mStreamingCulledList.erase_fast(tmp);
|
|
}
|
|
}
|
|
|
|
AssertFatal(!mStreamingInactiveList.findImage((*itr2)->mHandle), "alxStop: handle in inactive list");
|
|
AssertFatal(!mStreamingCulledList.findImage((*itr2)->mHandle), "alxStop: handle in culled list");
|
|
|
|
// remove it
|
|
(*itr2)->freeStream();
|
|
delete(*itr2);
|
|
mStreamingList.erase_fast(itr2);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void alxStopAll()
|
|
{
|
|
// stop all open sources
|
|
for(S32 i = mNumSources - 1; i >= 0; i--)
|
|
if(mHandle[i] != NULL_AUDIOHANDLE)
|
|
alxStop(mHandle[i]);
|
|
|
|
// stop all looping sources
|
|
while(mLoopingList.size())
|
|
alxStop(mLoopingList.last()->mHandle);
|
|
|
|
// stop all streaming sources
|
|
while(mStreamingList.size())
|
|
alxStop(mStreamingList.last()->mHandle);
|
|
}
|
|
|
|
void alxLoopSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat value)
|
|
{
|
|
LoopingList::iterator itr = mLoopingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
case AL_GAIN:
|
|
(*itr)->mDescription.mVolume = Audio::DBToLinear(value);
|
|
break;
|
|
case AL_GAIN_LINEAR:
|
|
(*itr)->mDescription.mVolume = value;
|
|
break;
|
|
case AL_PITCH:
|
|
(*itr)->mPitch = value;
|
|
break;
|
|
case AL_REFERENCE_DISTANCE:
|
|
(*itr)->mDescription.mReferenceDistance = value;
|
|
break;
|
|
case AL_CONE_OUTER_GAIN:
|
|
(*itr)->mDescription.mMaxDistance = value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxLoopSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat value1, ALfloat value2, ALfloat value3)
|
|
{
|
|
LoopingList::iterator itr = mLoopingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
case AL_POSITION:
|
|
(*itr)->mPosition.x = value1;
|
|
(*itr)->mPosition.y = value2;
|
|
(*itr)->mPosition.z = value3;
|
|
break;
|
|
|
|
case AL_DIRECTION:
|
|
(*itr)->mDirection.x = value1;
|
|
(*itr)->mDirection.y = value2;
|
|
(*itr)->mDirection.z = value3;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxLoopSourcei(AUDIOHANDLE handle, ALenum pname, ALint value)
|
|
{
|
|
LoopingList::iterator itr = mLoopingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
//case AL_SOURCE_AMBIENT:
|
|
// (*itr)->mDescription.mIs3D = value;
|
|
// break;
|
|
case AL_CONE_INNER_ANGLE:
|
|
(*itr)->mDescription.mConeInsideAngle = value;
|
|
break;
|
|
case AL_CONE_OUTER_ANGLE:
|
|
(*itr)->mDescription.mConeOutsideAngle = value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxLoopGetSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat *value)
|
|
{
|
|
LoopingList::iterator itr = mLoopingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
case AL_GAIN:
|
|
*value = Audio::linearToDB((*itr)->mDescription.mVolume);
|
|
break;
|
|
case AL_GAIN_LINEAR:
|
|
*value = (*itr)->mDescription.mVolume;
|
|
break;
|
|
case AL_PITCH:
|
|
*value = (*itr)->mPitch;
|
|
break;
|
|
case AL_REFERENCE_DISTANCE:
|
|
*value = (*itr)->mDescription.mReferenceDistance;
|
|
break;
|
|
case AL_CONE_OUTER_GAIN:
|
|
*value = (*itr)->mDescription.mMaxDistance;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxLoopGetSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat *value1, ALfloat *value2, ALfloat *value3)
|
|
{
|
|
LoopingList::iterator itr = mLoopingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
case AL_POSITION:
|
|
*value1 = (*itr)->mPosition.x;
|
|
*value2 = (*itr)->mPosition.y;
|
|
*value3 = (*itr)->mPosition.z;
|
|
break;
|
|
|
|
case AL_DIRECTION:
|
|
*value1 = (*itr)->mDirection.x;
|
|
*value2 = (*itr)->mDirection.y;
|
|
*value3 = (*itr)->mDirection.z;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxLoopGetSourcei(AUDIOHANDLE handle, ALenum pname, ALint *value)
|
|
{
|
|
LoopingList::iterator itr = mLoopingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
//case AL_SOURCE_AMBIENT:
|
|
// *value = (*itr)->mDescription.mIs3D;
|
|
// break;
|
|
case AL_LOOPING:
|
|
*value = true;
|
|
break;
|
|
case AL_CONE_INNER_ANGLE:
|
|
*value = (*itr)->mDescription.mConeInsideAngle;
|
|
break;
|
|
case AL_CONE_OUTER_ANGLE:
|
|
*value = (*itr)->mDescription.mConeOutsideAngle;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------
|
|
|
|
void alxStreamSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat value)
|
|
{
|
|
StreamingList::iterator itr = mStreamingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
case AL_GAIN:
|
|
(*itr)->mDescription.mVolume = Audio::DBToLinear(value);
|
|
break;
|
|
case AL_GAIN_LINEAR:
|
|
(*itr)->mDescription.mVolume = value;
|
|
break;
|
|
case AL_PITCH:
|
|
(*itr)->mPitch = value;
|
|
break;
|
|
case AL_REFERENCE_DISTANCE:
|
|
(*itr)->mDescription.mReferenceDistance = value;
|
|
break;
|
|
case AL_CONE_OUTER_GAIN:
|
|
(*itr)->mDescription.mMaxDistance = value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxStreamSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat value1, ALfloat value2, ALfloat value3)
|
|
{
|
|
StreamingList::iterator itr = mStreamingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
case AL_POSITION:
|
|
(*itr)->mPosition.x = value1;
|
|
(*itr)->mPosition.y = value2;
|
|
(*itr)->mPosition.z = value3;
|
|
break;
|
|
|
|
case AL_DIRECTION:
|
|
(*itr)->mDirection.x = value1;
|
|
(*itr)->mDirection.y = value2;
|
|
(*itr)->mDirection.z = value3;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxStreamSourcei(AUDIOHANDLE handle, ALenum pname, ALint value)
|
|
{
|
|
StreamingList::iterator itr = mStreamingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
//case AL_SOURCE_AMBIENT:
|
|
// (*itr)->mDescription.mIs3D = value;
|
|
// break;
|
|
case AL_CONE_INNER_ANGLE:
|
|
(*itr)->mDescription.mConeInsideAngle = value;
|
|
break;
|
|
case AL_CONE_OUTER_ANGLE:
|
|
(*itr)->mDescription.mConeOutsideAngle = value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxStreamGetSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat *value)
|
|
{
|
|
StreamingList::iterator itr = mStreamingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
case AL_GAIN:
|
|
*value = Audio::linearToDB((*itr)->mDescription.mVolume);
|
|
break;
|
|
case AL_GAIN_LINEAR:
|
|
*value = (*itr)->mDescription.mVolume;
|
|
break;
|
|
case AL_PITCH:
|
|
*value = (*itr)->mPitch;
|
|
break;
|
|
case AL_REFERENCE_DISTANCE:
|
|
*value = (*itr)->mDescription.mReferenceDistance;
|
|
break;
|
|
case AL_CONE_OUTER_GAIN:
|
|
*value = (*itr)->mDescription.mMaxDistance;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxStreamGetSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat *value1, ALfloat *value2, ALfloat *value3)
|
|
{
|
|
StreamingList::iterator itr = mStreamingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
case AL_POSITION:
|
|
*value1 = (*itr)->mPosition.x;
|
|
*value2 = (*itr)->mPosition.y;
|
|
*value3 = (*itr)->mPosition.z;
|
|
break;
|
|
|
|
case AL_DIRECTION:
|
|
*value1 = (*itr)->mDirection.x;
|
|
*value2 = (*itr)->mDirection.y;
|
|
*value3 = (*itr)->mDirection.z;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxStreamGetSourcei(AUDIOHANDLE handle, ALenum pname, ALint *value)
|
|
{
|
|
StreamingList::iterator itr = mStreamingList.findImage(handle);
|
|
if(itr)
|
|
{
|
|
switch(pname)
|
|
{
|
|
//case AL_SOURCE_AMBIENT:
|
|
// *value = (*itr)->mDescription.mIs3D;
|
|
// break;
|
|
case AL_LOOPING:
|
|
*value = true;
|
|
break;
|
|
case AL_CONE_INNER_ANGLE:
|
|
*value = (*itr)->mDescription.mConeInsideAngle;
|
|
break;
|
|
case AL_CONE_OUTER_ANGLE:
|
|
*value = (*itr)->mDescription.mConeOutsideAngle;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// AL get/set methods: Source
|
|
//--------------------------------------------------------------------------
|
|
// - only need to worry about playing sources.. proper volume gets set on
|
|
// create source (so, could get out of sync if someone changes volume between
|
|
// a createSource and playSource call...)
|
|
void alxUpdateTypeGain(U32 type)
|
|
{
|
|
for(U32 i = 0; i < MAX_AUDIOSOURCES; i++)
|
|
{
|
|
if(mHandle[i] == NULL_AUDIOHANDLE)
|
|
continue;
|
|
|
|
if(type != mType[i])
|
|
continue;
|
|
|
|
ALint state = AL_STOPPED;
|
|
alGetSourcei(mSource[i], AL_SOURCE_STATE, &state);
|
|
|
|
if(state == AL_PLAYING)
|
|
{
|
|
// volume = SourceVolume * ChannelVolume * MasterVolume
|
|
F32 vol = mClampF(mSourceVolume[i] * mAudioTypeVolume[mType[i]] * mMasterVolume, 0.f, 1.f);
|
|
alSourcef(mSource[i], AL_GAIN, Audio::linearToDB(vol) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat value)
|
|
{
|
|
ALuint source = alxFindSource(handle);
|
|
|
|
if(source != INVALID_SOURCE)
|
|
{
|
|
// ensure gain_linear
|
|
if(pname == AL_GAIN)
|
|
{
|
|
value = Audio::DBToLinear(value);
|
|
pname = AL_GAIN_LINEAR;
|
|
}
|
|
|
|
// need to process gain settings (so source can be affected by channel/master gains)
|
|
if(pname == AL_GAIN_LINEAR)
|
|
{
|
|
U32 idx = alxFindIndex(handle);
|
|
AssertFatal(idx != MAX_AUDIOSOURCES, "alxSourcef: handle not located for found source");
|
|
if(idx == MAX_AUDIOSOURCES)
|
|
return;
|
|
|
|
// update the stored value
|
|
mSourceVolume[idx] = value;
|
|
|
|
// volume = SourceVolume * ChannelVolume * MasterVolume
|
|
|
|
// #ifdef REL_WORKAROUND
|
|
// ALint val = AL_TRUE;
|
|
// alGetSourcei(source, AL_SOURCE_ABSOLUTE, &val);
|
|
// if(val == AL_FALSE)
|
|
// #else
|
|
// ALint val = AL_FALSE;
|
|
// alGetSourcei(source, AL_SOURCE_RELATIVE, &val);
|
|
// if(val == AL_TRUE)
|
|
// #endif
|
|
{
|
|
F32 vol = mClampF(mSourceVolume[idx] * mAudioTypeVolume[mType[idx]] * mMasterVolume, 0.f, 1.f);
|
|
alSourcef(source, AL_GAIN, Audio::linearToDB(vol) );
|
|
}
|
|
}
|
|
else
|
|
alSourcef(source, pname, value);
|
|
}
|
|
alxLoopSourcef(handle, pname, value);
|
|
alxStreamSourcef(handle, pname, value);
|
|
}
|
|
|
|
void alxSourcefv(AUDIOHANDLE handle, ALenum pname, ALfloat *values)
|
|
{
|
|
ALuint source = alxFindSource(handle);
|
|
if(source != INVALID_SOURCE)
|
|
alSourcefv(source, pname, values);
|
|
|
|
if((pname == AL_POSITION) || (pname == AL_DIRECTION) || (pname == AL_VELOCITY)) {
|
|
alxLoopSource3f(handle, pname, values[0], values[1], values[2]);
|
|
alxStreamSource3f(handle, pname, values[0], values[1], values[2]);
|
|
}
|
|
}
|
|
|
|
void alxSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat value1, ALfloat value2, ALfloat value3)
|
|
{
|
|
ALuint source = alxFindSource(handle);
|
|
if(source != INVALID_SOURCE)
|
|
{
|
|
ALfloat values[3];
|
|
values[0] = value1;
|
|
values[1] = value2;
|
|
values[2] = value3;
|
|
alSourcefv(source, pname, values);
|
|
}
|
|
alxLoopSource3f(handle, pname, value1, value2, value3);
|
|
alxStreamSource3f(handle, pname, value1, value2, value3);
|
|
}
|
|
|
|
void alxSourcei(AUDIOHANDLE handle, ALenum pname, ALint value)
|
|
{
|
|
ALuint source = alxFindSource(handle);
|
|
if(source != INVALID_SOURCE)
|
|
alSourcei(source, pname, value);
|
|
alxLoopSourcei(handle, pname, value);
|
|
alxStreamSourcei(handle, pname, value);
|
|
}
|
|
|
|
// sets the position and direction of the source
|
|
void alxSourceMatrixF(AUDIOHANDLE handle, const MatrixF *transform)
|
|
{
|
|
ALuint source = alxFindSource(handle);
|
|
|
|
Point3F pos;
|
|
transform->getColumn(3, &pos);
|
|
|
|
Point3F dir;
|
|
transform->getColumn(1, &dir);
|
|
|
|
if(source != INVALID_SOURCE)
|
|
{
|
|
// OpenAL uses a Right-Handed corrdinate system so flip the orientation vector
|
|
alSource3f(source, AL_POSITION, pos.x, pos.y, pos.z);
|
|
alSource3f(source, AL_DIRECTION, -dir.x, -dir.y, -dir.z);
|
|
}
|
|
|
|
alxLoopSource3f(handle, AL_POSITION, pos.x, pos.y, pos.z);
|
|
alxLoopSource3f(handle, AL_DIRECTION, dir.x, dir.y, dir.z);
|
|
alxStreamSource3f(handle, AL_POSITION, pos.x, pos.y, pos.z);
|
|
alxStreamSource3f(handle, AL_DIRECTION, dir.x, dir.y, dir.z);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void alxGetSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat *value)
|
|
{
|
|
ALuint source = alxFindSource(handle);
|
|
if(source != INVALID_SOURCE)
|
|
{
|
|
// gain queries return unattenuated values
|
|
if((pname == AL_GAIN) || (pname == AL_GAIN_LINEAR))
|
|
{
|
|
U32 idx = alxFindIndex(handle);
|
|
AssertFatal(idx != MAX_AUDIOSOURCES, "alxGetSourcef: found source but handle is invalid");
|
|
if(idx == MAX_AUDIOSOURCES)
|
|
{
|
|
*value = 0.f;
|
|
return;
|
|
}
|
|
|
|
if(pname == AL_GAIN)
|
|
*value = Audio::linearToDB(mSourceVolume[idx]);
|
|
else
|
|
*value = mSourceVolume[idx];
|
|
}
|
|
else
|
|
alGetSourcef(source, pname, value);
|
|
}
|
|
else if(handle & AUDIOHANDLE_LOOPING_BIT)
|
|
alxLoopGetSourcef(handle, pname, value);
|
|
else
|
|
alxStreamGetSourcef(handle, pname, value);
|
|
}
|
|
|
|
void alxGetSourcefv(AUDIOHANDLE handle, ALenum pname, ALfloat *values)
|
|
{
|
|
if((pname == AL_POSITION) || (pname == AL_DIRECTION) || (pname == AL_VELOCITY))
|
|
alxGetSource3f(handle, pname, &values[0], &values[1], &values[2]);
|
|
}
|
|
|
|
void alxGetSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat *value1, ALfloat *value2, ALfloat *value3)
|
|
{
|
|
ALuint source = alxFindSource(handle);
|
|
if(source != INVALID_SOURCE)
|
|
{
|
|
ALfloat values[3];
|
|
alGetSourcefv(source, pname, values);
|
|
*value1 = values[0];
|
|
*value2 = values[1];
|
|
*value3 = values[2];
|
|
}
|
|
else if(handle & AUDIOHANDLE_LOOPING_BIT)
|
|
alxLoopGetSource3f(handle, pname, value1, value2, value3);
|
|
else
|
|
alxStreamGetSource3f(handle, pname, value1, value2, value3);
|
|
}
|
|
|
|
void alxGetSourcei(AUDIOHANDLE handle, ALenum pname, ALint *value)
|
|
{
|
|
ALuint source = alxFindSource(handle);
|
|
if(source != INVALID_SOURCE)
|
|
alGetSourcei(source, pname, value);
|
|
else if(handle & AUDIOHANDLE_LOOPING_BIT)
|
|
alxLoopGetSourcei(handle, pname, value);
|
|
else
|
|
alxStreamGetSourcei(handle, pname, value);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
/** alListenerfv extension for use with MatrixF's
|
|
Set the listener's position and orientation using a matrix
|
|
*/
|
|
void alxListenerMatrixF(const MatrixF *transform)
|
|
{
|
|
Point3F p1, p2;
|
|
transform->getColumn(3, &p1);
|
|
alListener3f(AL_POSITION, p1.x, p1.y, p1.z);
|
|
|
|
transform->getColumn(2, &p1); // Up Vector
|
|
transform->getColumn(1, &p2); // Forward Vector
|
|
|
|
F32 orientation[6];
|
|
orientation[0] = -p1.x;
|
|
orientation[1] = -p1.y;
|
|
orientation[2] = -p1.z;
|
|
orientation[3] = p2.x;
|
|
orientation[4] = p2.y;
|
|
orientation[5] = p2.z;
|
|
|
|
alListenerfv(AL_ORIENTATION, orientation);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
/** alListenerf extension supporting linear gain
|
|
*/
|
|
void alxListenerf(ALenum param, ALfloat value)
|
|
{
|
|
if (param == AL_GAIN_LINEAR)
|
|
{
|
|
value = Audio::linearToDB(value);
|
|
param = AL_GAIN;
|
|
}
|
|
alListenerf(param, value);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
/** alGetListenerf extension supporting linear gain
|
|
*/
|
|
void alxGetListenerf(ALenum param, ALfloat *value)
|
|
{
|
|
if (param == AL_GAIN_LINEAR)
|
|
{
|
|
alGetListenerf(AL_GAIN, value);
|
|
*value = Audio::DBToLinear(*value);
|
|
}
|
|
else
|
|
alGetListenerf(param, value);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Simple metrics
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
#ifdef TORQUE_GATHER_METRICS
|
|
static void alxGatherMetrics()
|
|
{
|
|
S32 mNumOpenHandles = 0;
|
|
S32 mNumOpenLoopingHandles = 0;
|
|
S32 mNumOpenStreamingHandles = 0;
|
|
|
|
S32 mNumActiveStreams = 0;
|
|
S32 mNumNullActiveStreams = 0;
|
|
S32 mNumActiveLoopingStreams = 0;
|
|
S32 mNumActiveStreamingStreams = 0;
|
|
|
|
S32 mNumLoopingStreams = 0;
|
|
S32 mNumInactiveLoopingStreams = 0;
|
|
S32 mNumCulledLoopingStreams = 0;
|
|
S32 mNumStreamingStreams = 0;
|
|
S32 mNumInactiveStreamingStreams = 0;
|
|
S32 mNumCulledStreamingStreams = 0;
|
|
|
|
// count installed streams and open handles
|
|
for(U32 i = 0; i < mNumSources; i++)
|
|
{
|
|
if(mHandle[i] != NULL_AUDIOHANDLE)
|
|
{
|
|
mNumOpenHandles++;
|
|
if(mHandle[i] & AUDIOHANDLE_LOOPING_BIT)
|
|
mNumOpenLoopingHandles++;
|
|
if(mHandle[i] & AUDIOHANDLE_STREAMING_BIT)
|
|
mNumOpenStreamingHandles++;
|
|
}
|
|
|
|
ALint state = AL_STOPPED;
|
|
alGetSourcei(mSource[i], AL_SOURCE_STATE, &state);
|
|
if(state == AL_PLAYING)
|
|
{
|
|
mNumActiveStreams++;
|
|
if(mHandle[i] == NULL_AUDIOHANDLE)
|
|
mNumNullActiveStreams++;
|
|
if(mHandle[i] & AUDIOHANDLE_LOOPING_BIT)
|
|
mNumActiveLoopingStreams++;
|
|
if(mHandle[i] & AUDIOHANDLE_STREAMING_BIT)
|
|
mNumActiveStreamingStreams++;
|
|
}
|
|
}
|
|
|
|
for(LoopingList::iterator itr = mLoopingList.begin(); itr != mLoopingList.end(); itr++)
|
|
mNumLoopingStreams++;
|
|
for(LoopingList::iterator itr = mLoopingInactiveList.begin(); itr != mLoopingInactiveList.end(); itr++)
|
|
mNumInactiveLoopingStreams++;
|
|
for(LoopingList::iterator itr = mLoopingCulledList.begin(); itr != mLoopingCulledList.end(); itr++)
|
|
mNumCulledLoopingStreams++;
|
|
|
|
for(StreamingList::iterator itr = mStreamingList.begin(); itr != mStreamingList.end(); itr++)
|
|
mNumStreamingStreams++;
|
|
for(StreamingList::iterator itr = mStreamingInactiveList.begin(); itr != mStreamingInactiveList.end(); itr++)
|
|
mNumInactiveStreamingStreams++;
|
|
for(StreamingList::iterator itr = mStreamingCulledList.begin(); itr != mStreamingCulledList.end(); itr++)
|
|
mNumCulledStreamingStreams++;
|
|
|
|
Con::setIntVariable("Audio::numOpenHandles", mNumOpenHandles);
|
|
Con::setIntVariable("Audio::numOpenLoopingHandles", mNumOpenLoopingHandles);
|
|
Con::setIntVariable("Audio::numOpenStreamingHandles", mNumOpenStreamingHandles);
|
|
|
|
Con::setIntVariable("Audio::numActiveStreams", mNumActiveStreams);
|
|
Con::setIntVariable("Audio::numNullActiveStreams", mNumNullActiveStreams);
|
|
Con::setIntVariable("Audio::numActiveLoopingStreams", mNumActiveLoopingStreams);
|
|
Con::setIntVariable("Audio::numActiveStreamingStreams", mNumActiveStreamingStreams);
|
|
|
|
Con::setIntVariable("Audio::numLoopingStreams", mNumLoopingStreams);
|
|
Con::setIntVariable("Audio::numInactiveLoopingStreams", mNumInactiveLoopingStreams);
|
|
Con::setIntVariable("Audio::numCulledLoopingStreams", mNumCulledLoopingStreams);
|
|
|
|
Con::setIntVariable("Audio::numStreamingStreams", mNumStreamingStreams);
|
|
Con::setIntVariable("Audio::numInactiveStreamingStreams", mNumInactiveStreamingStreams);
|
|
Con::setIntVariable("Audio::numCulledStreamingStreams", mNumCulledStreamingStreams);
|
|
}
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Audio Update...
|
|
//--------------------------------------------------------------------------
|
|
void alxLoopingUpdate()
|
|
{
|
|
static LoopingList culledList;
|
|
|
|
U32 updateTime = Platform::getRealMilliseconds();
|
|
|
|
// check if can wakeup the inactive loopers
|
|
if(mLoopingCulledList.size())
|
|
{
|
|
Point3F listener;
|
|
alxGetListenerPoint3F(AL_POSITION, &listener);
|
|
|
|
// get the 'sort' value for this sound (could be based on time played...),
|
|
// and add to the culled list
|
|
LoopingList::iterator itr;
|
|
culledList.clear();
|
|
|
|
for(itr = mLoopingCulledList.begin(); itr != mLoopingCulledList.end(); itr++)
|
|
{
|
|
if((*itr)->mScore <= MIN_UNCULL_GAIN)
|
|
continue;
|
|
|
|
if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
|
|
continue;
|
|
|
|
culledList.push_back(*itr);
|
|
}
|
|
|
|
if(!culledList.size())
|
|
return;
|
|
|
|
U32 index = MAX_AUDIOSOURCES;
|
|
|
|
if(culledList.size() > 1)
|
|
culledList.sort();
|
|
|
|
for(itr = culledList.begin(); itr != culledList.end(); itr++)
|
|
{
|
|
if(!findFreeSource(&index))
|
|
{
|
|
// score does not include master volume
|
|
if(!cullSource(&index, (*itr)->mScore))
|
|
break;
|
|
|
|
// check buffer
|
|
if(!bool((*itr)->mBuffer))
|
|
{
|
|
// remove from culled list
|
|
LoopingList::iterator tmp;
|
|
tmp = mLoopingCulledList.findImage((*itr)->mHandle);
|
|
AssertFatal(tmp, "alxLoopingUpdate: failed to find culled source");
|
|
mLoopingCulledList.erase_fast(tmp);
|
|
|
|
// remove from looping list (and free)
|
|
tmp = mLoopingList.findImage((*itr)->mHandle);
|
|
if(tmp)
|
|
{
|
|
(*tmp)->clear();
|
|
mLoopingFreeList.push_back(*tmp);
|
|
mLoopingList.erase_fast(tmp);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// remove from culled list
|
|
LoopingList::iterator tmp = mLoopingCulledList.findImage((*itr)->mHandle);
|
|
AssertFatal(tmp, "alxLoopingUpdate: failed to find culled source");
|
|
mLoopingCulledList.erase_fast(tmp);
|
|
|
|
// restore all state data
|
|
mHandle[index] = (*itr)->mHandle;
|
|
mBuffer[index] = (*itr)->mBuffer;
|
|
mScore[index] = (*itr)->mScore;
|
|
mSourceVolume[index] = (*itr)->mDescription.mVolume;
|
|
mType[index] = (*itr)->mDescription.mType;
|
|
mSampleEnvironment[index] = (*itr)->mEnvironment;
|
|
|
|
ALuint source = mSource[index];
|
|
|
|
// setup play info
|
|
alGetError();
|
|
|
|
alxSourcePlay(source, *itr);
|
|
if(mEnvironmentEnabled)
|
|
alxSourceEnvironment(source, *itr);
|
|
|
|
alxPlay(mHandle[index]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void alxStreamingUpdate()
|
|
{
|
|
// update buffer queues on active streamers
|
|
// update the loopers
|
|
for(StreamingList::iterator itr = mStreamingList.begin(); itr != mStreamingList.end(); itr++)
|
|
{
|
|
if((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT)
|
|
continue;
|
|
|
|
(*itr)->updateBuffers();
|
|
}
|
|
|
|
static StreamingList culledList;
|
|
|
|
U32 updateTime = Platform::getRealMilliseconds();
|
|
|
|
// check if can wakeup the inactive loopers
|
|
if(mStreamingCulledList.size())
|
|
{
|
|
Point3F listener;
|
|
alxGetListenerPoint3F(AL_POSITION, &listener);
|
|
|
|
// get the 'sort' value for this sound (could be based on time played...),
|
|
// and add to the culled list
|
|
StreamingList::iterator itr;
|
|
culledList.clear();
|
|
|
|
for(itr = mStreamingCulledList.begin(); itr != mStreamingCulledList.end(); itr++)
|
|
{
|
|
if((*itr)->mScore <= MIN_UNCULL_GAIN)
|
|
continue;
|
|
|
|
if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
|
|
continue;
|
|
culledList.push_back(*itr);
|
|
}
|
|
|
|
if(!culledList.size())
|
|
return;
|
|
|
|
U32 index = MAX_AUDIOSOURCES;
|
|
|
|
if(culledList.size() > 1)
|
|
culledList.sort();
|
|
|
|
for(itr = culledList.begin(); itr != culledList.end(); itr++)
|
|
{
|
|
if(!findFreeSource(&index))
|
|
{
|
|
// score does not include master volume
|
|
if(!cullSource(&index, (*itr)->mScore))
|
|
break;
|
|
|
|
// check buffer
|
|
//if(!bool((*itr)->mBuffer))
|
|
//{
|
|
// remove from culled list
|
|
StreamingList::iterator tmp;
|
|
tmp = mStreamingCulledList.findImage((*itr)->mHandle);
|
|
AssertFatal(tmp, "alxStreamingUpdate: failed to find culled source");
|
|
mStreamingCulledList.erase_fast(tmp);
|
|
|
|
// remove from streaming list (and free)
|
|
tmp = mStreamingList.findImage((*itr)->mHandle);
|
|
if(tmp)
|
|
{
|
|
delete(*tmp);
|
|
mStreamingList.erase_fast(tmp);
|
|
}
|
|
|
|
continue;
|
|
//}
|
|
}
|
|
|
|
// remove from culled list
|
|
StreamingList::iterator tmp = mStreamingCulledList.findImage((*itr)->mHandle);
|
|
AssertFatal(tmp, "alxStreamingUpdate: failed to find culled source");
|
|
mStreamingCulledList.erase_fast(tmp);
|
|
alxSourcePlay(*itr);
|
|
// restore all state data
|
|
mHandle[index] = (*itr)->mHandle;
|
|
mScore[index] = (*itr)->mScore;
|
|
mSourceVolume[index] = (*itr)->mDescription.mVolume;
|
|
mType[index] = (*itr)->mDescription.mType;
|
|
mSampleEnvironment[index] = (*itr)->mEnvironment;
|
|
|
|
ALuint source = mSource[index];
|
|
(*itr)->mSource = mSource[index];
|
|
|
|
// setup play info
|
|
alGetError();
|
|
|
|
if(mEnvironmentEnabled)
|
|
alxSourceEnvironment(source, *itr);
|
|
|
|
alxPlay(mHandle[index]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void alxCloseHandles()
|
|
{
|
|
for(U32 i = 0; i < mNumSources; i++)
|
|
{
|
|
if(mHandle[i] & AUDIOHANDLE_LOADING_BIT)
|
|
continue;
|
|
|
|
if(mHandle[i] == NULL_AUDIOHANDLE)
|
|
continue;
|
|
|
|
ALint state = 0;
|
|
alGetSourcei(mSource[i], AL_SOURCE_STATE, &state);
|
|
if(state == AL_PLAYING)
|
|
continue;
|
|
|
|
if(!(mHandle[i] & AUDIOHANDLE_INACTIVE_BIT))
|
|
{
|
|
// should be playing? must have encounted an error.. remove
|
|
LoopingList::iterator itr = mLoopingList.findImage(mHandle[i]);
|
|
if(itr && !((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
|
|
{
|
|
AssertFatal(!mLoopingInactiveList.findImage((*itr)->mHandle), "alxCloseHandles: image incorrectly in inactive list");
|
|
AssertFatal(!mLoopingCulledList.findImage((*itr)->mHandle), "alxCloseHandles: image already in culled list");
|
|
mLoopingCulledList.push_back(*itr);
|
|
(*itr)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
|
|
|
|
mHandle[i] = NULL_AUDIOHANDLE;
|
|
mBuffer[i] = 0;
|
|
}
|
|
|
|
// should be playing? must have encounted an error.. remove
|
|
// StreamingList::iterator itr2 = mStreamingList.findImage(mHandle[i]);
|
|
// if(itr2 && !((*itr2)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
|
|
// {
|
|
// AssertFatal(!mStreamingInactiveList.findImage((*itr2)->mHandle), "alxCloseHandles: image incorrectly in inactive list");
|
|
// AssertFatal(!mStreamingCulledList.findImage((*itr2)->mHandle), "alxCloseHandles: image already in culled list");
|
|
// (*itr2)->freeStream();
|
|
//
|
|
// mStreamingCulledList.push_back(*itr2);
|
|
// (*itr2)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
|
|
//
|
|
// mHandle[i] = NULL_AUDIOHANDLE;
|
|
// mBuffer[i] = 0;
|
|
// }
|
|
}
|
|
|
|
mHandle[i] = NULL_AUDIOHANDLE;
|
|
mBuffer[i] = 0;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// - update the score for each audio source. this is used for culing sources.
|
|
// normal ranges are between 0.f->1.f, voice/loading/music streams are scored
|
|
// outside this range so that they will not be culled
|
|
// - does not scale by attenuated volumes
|
|
void alxUpdateScores(bool sourcesOnly)
|
|
{
|
|
Point3F listener;
|
|
alGetListener3f(AL_POSITION, &listener.x, &listener.y, &listener.z);
|
|
|
|
// do the base sources
|
|
for(U32 i = 0; i < mNumSources; i++)
|
|
{
|
|
if(mHandle[i] == NULL_AUDIOHANDLE)
|
|
{
|
|
mScore[i] = 0.f;
|
|
continue;
|
|
}
|
|
|
|
// grab the volume.. (not attenuated by master for score)
|
|
F32 volume = mSourceVolume[i] * mAudioTypeVolume[mType[i]];
|
|
|
|
// 3d?
|
|
mScore[i] = volume;
|
|
#ifdef REL_WORKAROUND
|
|
ALint val = AL_FALSE;
|
|
alGetSourcei(mSource[i], AL_SOURCE_ABSOLUTE, &val);
|
|
if(val == AL_TRUE)
|
|
#else
|
|
ALint val = AL_FALSE;
|
|
alGetSourcei(mSource[i], AL_SOURCE_RELATIVE, &val);
|
|
if(val == AL_FALSE)
|
|
#endif
|
|
{
|
|
// approximate 3d volume
|
|
Point3F pos;
|
|
alGetSourcefv(mSource[i], AL_POSITION, (ALfloat*)((F32*)pos) );
|
|
|
|
ALfloat min=0, max=1;
|
|
alGetSourcef(mSource[i], AL_REFERENCE_DISTANCE, &min);
|
|
alGetSourcef(mSource[i], AL_MAX_DISTANCE, &max);
|
|
|
|
pos -= listener;
|
|
F32 dist = pos.magnitudeSafe();
|
|
|
|
if(dist >= max)
|
|
mScore[i] = 0.f;
|
|
else if(dist > min)
|
|
mScore[i] *= (max-dist) / (max-min);
|
|
}
|
|
}
|
|
|
|
if(sourcesOnly)
|
|
return;
|
|
|
|
U32 updateTime = Platform::getRealMilliseconds();
|
|
|
|
// update the loopers
|
|
for(LoopingList::iterator itr = mLoopingList.begin(); itr != mLoopingList.end(); itr++)
|
|
{
|
|
if(!((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
|
|
continue;
|
|
|
|
if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
|
|
continue;
|
|
|
|
(*itr)->mScore = (*itr)->mDescription.mVolume;
|
|
if((*itr)->mDescription.mIs3D)
|
|
{
|
|
Point3F pos = (*itr)->mPosition - listener;
|
|
F32 dist = pos.magnitudeSafe();
|
|
|
|
F32 min = (*itr)->mDescription.mReferenceDistance;
|
|
F32 max = (*itr)->mDescription.mMaxDistance;
|
|
|
|
if(dist >= max)
|
|
(*itr)->mScore = 0.f;
|
|
else if(dist > min)
|
|
(*itr)->mScore *= (max-dist) / (max-min);
|
|
}
|
|
|
|
// attenuate by the channel gain
|
|
(*itr)->mScore *= mAudioTypeVolume[(*itr)->mDescription.mType];
|
|
}
|
|
|
|
// update the streamers
|
|
for(StreamingList::iterator itr = mStreamingList.begin(); itr != mStreamingList.end(); itr++)
|
|
{
|
|
if(!((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
|
|
continue;
|
|
|
|
if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
|
|
continue;
|
|
|
|
(*itr)->mScore = (*itr)->mDescription.mVolume;
|
|
if((*itr)->mDescription.mIs3D)
|
|
{
|
|
Point3F pos = (*itr)->mPosition - listener;
|
|
F32 dist = pos.magnitudeSafe();
|
|
|
|
F32 min = (*itr)->mDescription.mReferenceDistance;
|
|
F32 max = (*itr)->mDescription.mMaxDistance;
|
|
|
|
if(dist >= max)
|
|
(*itr)->mScore = 0.f;
|
|
else if(dist > min)
|
|
(*itr)->mScore *= (max-dist) / (max-min);
|
|
}
|
|
|
|
// attenuate by the channel gain
|
|
(*itr)->mScore *= mAudioTypeVolume[(*itr)->mDescription.mType];
|
|
}
|
|
}
|
|
|
|
// the directx buffers are set to mute at max distance, but many of the providers seem to
|
|
// ignore this flag... that is why this is here
|
|
void alxUpdateMaxDistance()
|
|
{
|
|
Point3F listener;
|
|
alGetListener3f(AL_POSITION, &listener.x, &listener.y, &listener.z);
|
|
|
|
for(U32 i = 0; i < mNumSources; i++)
|
|
{
|
|
if(mHandle[i] == NULL_AUDIOHANDLE)
|
|
continue;
|
|
|
|
#ifdef REL_WORKAROUND
|
|
ALint val = AL_FALSE;
|
|
alGetSourcei(mSource[i], AL_SOURCE_ABSOLUTE, &val);
|
|
if(val == AL_FALSE)
|
|
#else
|
|
ALint val = AL_FALSE;
|
|
alGetSourcei(mSource[i], AL_SOURCE_RELATIVE, &val);
|
|
if(val == AL_TRUE)
|
|
#endif
|
|
continue;
|
|
|
|
Point3F pos;
|
|
alGetSourcefv(mSource[i], AL_POSITION, (F32*)pos);
|
|
|
|
F32 dist = 0.f;
|
|
alGetSourcef(mSource[i], AL_MAX_DISTANCE, &dist);
|
|
|
|
pos -= listener;
|
|
dist -= pos.len();
|
|
|
|
F32 gain = (dist < 0.f) ? 0.f : mSourceVolume[i] * mAudioTypeVolume[mType[i]] * mMasterVolume;
|
|
alSourcef(mSource[i], AL_GAIN, Audio::linearToDB(gain));
|
|
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Called to update alx system
|
|
//--------------------------------------------------------------------------
|
|
void alxUpdate()
|
|
{
|
|
//if(mForceMaxDistanceUpdate)
|
|
alxUpdateMaxDistance();
|
|
|
|
alxCloseHandles();
|
|
alxUpdateScores(false);
|
|
alxLoopingUpdate();
|
|
alxStreamingUpdate();
|
|
|
|
#ifdef TORQUE_GATHER_METRICS
|
|
alxGatherMetrics();
|
|
#endif
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Misc
|
|
//--------------------------------------------------------------------------
|
|
// client-side function only
|
|
ALuint alxGetWaveLen(ALuint buffer)
|
|
{
|
|
if(buffer == AL_INVALID)
|
|
return(0);
|
|
|
|
ALint frequency = 0;
|
|
ALint bits = 0;
|
|
ALint channels = 0;
|
|
ALint size;
|
|
|
|
alGetBufferi(buffer, AL_FREQUENCY, &frequency);
|
|
alGetBufferi(buffer, AL_BITS, &bits);
|
|
alGetBufferi(buffer, AL_CHANNELS, &channels);
|
|
alGetBufferi(buffer, AL_SIZE, &size);
|
|
|
|
if(!frequency || !bits || !channels)
|
|
{
|
|
Con::errorf(ConsoleLogEntry::General, "alxGetWaveLen: invalid buffer");
|
|
return(0);
|
|
}
|
|
|
|
F64 len = (F64(size) * 8000.f) / F64(frequency * bits * channels);
|
|
return(len);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Environment:
|
|
//--------------------------------------------------------------------------
|
|
void alxEnvironmenti(ALenum pname, ALint value)
|
|
{
|
|
/* todo
|
|
alEnvironmentiIASIG(mEnvironment, pname, value);
|
|
*/
|
|
}
|
|
|
|
void alxEnvironmentf(ALenum pname, ALfloat value)
|
|
{
|
|
/* todo
|
|
alEnvironmentfIASIG(mEnvironment, pname, value);
|
|
*/
|
|
}
|
|
|
|
void alxGetEnvironmenti(ALenum pname, ALint * value)
|
|
{
|
|
/* todo
|
|
alGetEnvironmentiIASIG_EXT(mEnvironment, pname, value);
|
|
*/
|
|
}
|
|
|
|
void alxGetEnvironmentf(ALenum pname, ALfloat * value)
|
|
{
|
|
/* todo
|
|
alGetEnvironmentfIASIG_EXT(mEnvironment, pname, value);
|
|
*/
|
|
}
|
|
|
|
void alxEnableEnvironmental(bool enable)
|
|
{
|
|
if(mEnvironmentEnabled == enable)
|
|
return;
|
|
|
|
// go through the playing sources and update their reverb mix
|
|
// - only 3d samples get environmental fx
|
|
// - only loopers can reenable fx
|
|
for(U32 i = 0; i < MAX_AUDIOSOURCES; i++)
|
|
{
|
|
if(mHandle[i] == NULL_AUDIOHANDLE)
|
|
continue;
|
|
|
|
ALint val = AL_FALSE;
|
|
|
|
// 3d?
|
|
#ifdef REL_WORKAROUND
|
|
alGetSourcei(mSource[i], AL_SOURCE_ABSOLUTE, &val);
|
|
if(val == AL_FALSE)
|
|
#else
|
|
alGetSourcei(mSource[i], AL_SOURCE_RELATIVE, &val);
|
|
if(val == AL_TRUE)
|
|
#endif
|
|
continue;
|
|
|
|
// stopped?
|
|
val = AL_STOPPED;
|
|
alGetSourcei(mSource[i], AL_SOURCE_STATE, &val);
|
|
|
|
// only looping sources can reenable environmental effects (no description around
|
|
// for the non-loopers)
|
|
if(enable)
|
|
{
|
|
LoopingList::iterator itr = mLoopingList.findImage(mHandle[i]);
|
|
if(!itr)
|
|
continue;
|
|
|
|
/* todo
|
|
alSourcef(mSource[i], AL_ENV_SAMPLE_REVERB_MIX_EXT, (*itr)->mDescription.mEnvironmentLevel);
|
|
*/
|
|
}
|
|
/* todo
|
|
else
|
|
alSourcef(mSource[i], AL_ENV_SAMPLE_REVERB_MIX_EXT, 0.f);
|
|
*/
|
|
}
|
|
|
|
mEnvironmentEnabled = enable;
|
|
}
|
|
|
|
void alxSetEnvironment(const AudioEnvironment * env)
|
|
{
|
|
/* todo
|
|
mCurrentEnvironment = const_cast<AudioEnvironment*>(env);
|
|
|
|
// reset environmental audio?
|
|
if(!env)
|
|
{
|
|
alxEnvironmenti(AL_ENV_ROOM_IASIG, AL_ENVIRONMENT_GENERIC);
|
|
return;
|
|
}
|
|
|
|
// room trashes all the values
|
|
if(env->mUseRoom)
|
|
{
|
|
alxEnvironmenti(AL_ENV_ROOM_IASIG, env->mRoom);
|
|
return;
|
|
}
|
|
|
|
// set all the params
|
|
alxEnvironmenti(AL_ENV_ROOM_HIGH_FREQUENCY_IASIG, env->mRoomHF);
|
|
alxEnvironmenti(AL_ENV_REFLECTIONS_IASIG, env->mReflections);
|
|
alxEnvironmenti(AL_ENV_REVERB_IASIG, env->mReverb);
|
|
|
|
alxEnvironmentf(AL_ENV_ROOM_ROLLOFF_FACTOR_IASIG, env->mRoomRolloffFactor);
|
|
alxEnvironmentf(AL_ENV_DECAY_TIME_IASIG, env->mDecayTime);
|
|
alxEnvironmentf(AL_ENV_DECAY_HIGH_FREQUENCY_RATIO_IASIG, env->mDecayTime);
|
|
alxEnvironmentf(AL_ENV_REFLECTIONS_DELAY_IASIG, env->mReflectionsDelay);
|
|
alxEnvironmentf(AL_ENV_REVERB_DELAY_IASIG, env->mReverbDelay);
|
|
alxEnvironmentf(AL_ENV_DENSITY_IASIG, env->mAirAbsorption);
|
|
alxEnvironmentf(AL_ENV_DIFFUSION_IASIG, env->mEnvironmentDiffusion);
|
|
|
|
// ext:
|
|
alxEnvironmenti(AL_ENV_ROOM_VOLUME_EXT, env->mRoomVolume);
|
|
alxEnvironmenti(AL_ENV_FLAGS_EXT, env->mFlags);
|
|
|
|
alxEnvironmentf(AL_ENV_EFFECT_VOLUME_EXT, env->mEffectVolume);
|
|
alxEnvironmentf(AL_ENV_DAMPING_EXT, env->mDamping);
|
|
alxEnvironmentf(AL_ENV_ENVIRONMENT_SIZE_EXT, env->mEnvironmentSize);
|
|
*/
|
|
}
|
|
|
|
const AudioEnvironment * alxGetEnvironment()
|
|
{
|
|
return(mCurrentEnvironment);
|
|
}
|
|
|
|
F32 alxGetStreamPosition( AUDIOHANDLE handle )
|
|
{
|
|
StreamingList::iterator itr = mStreamingList.findImage(handle);
|
|
if( !itr )
|
|
return -1.f;
|
|
|
|
return (*itr)->getElapsedTime();
|
|
}
|
|
|
|
F32 alxGetStreamDuration( AUDIOHANDLE handle )
|
|
{
|
|
StreamingList::iterator itr = mStreamingList.findImage(handle);
|
|
if( !itr )
|
|
return -1.f;
|
|
|
|
return (*itr)->getTotalTime();
|
|
}
|
|
|
|
// Namespace: Audio ---------------------------------------------------------
|
|
namespace Audio
|
|
{
|
|
|
|
//---------------------------------------------------------------------------
|
|
// the following db<->linear conversion functions come from Loki openAL linux driver
|
|
// code, here more for completeness than anything else (all current audio code
|
|
// uses AL_GAIN_LINEAR)... in Audio:: so that looping updates and audio channel updates
|
|
// can convert gain types and to give the miles driver access
|
|
static const F32 logtab[] = {
|
|
0.00, 0.001, 0.002, 0.003, 0.004,
|
|
0.005, 0.01, 0.011, 0.012, 0.013,
|
|
0.014, 0.015, 0.016, 0.02, 0.021,
|
|
0.022, 0.023, 0.024, 0.025, 0.03,
|
|
0.031, 0.032, 0.033, 0.034, 0.04,
|
|
0.041, 0.042, 0.043, 0.044, 0.05,
|
|
0.051, 0.052, 0.053, 0.054, 0.06,
|
|
0.061, 0.062, 0.063, 0.064, 0.07,
|
|
0.071, 0.072, 0.073, 0.08, 0.081,
|
|
0.082, 0.083, 0.084, 0.09, 0.091,
|
|
0.092, 0.093, 0.094, 0.10, 0.101,
|
|
0.102, 0.103, 0.11, 0.111, 0.112,
|
|
0.113, 0.12, 0.121, 0.122, 0.123,
|
|
0.124, 0.13, 0.131, 0.132, 0.14,
|
|
0.141, 0.142, 0.143, 0.15, 0.151,
|
|
0.152, 0.16, 0.161, 0.162, 0.17,
|
|
0.171, 0.172, 0.18, 0.181, 0.19,
|
|
0.191, 0.192, 0.20, 0.201, 0.21,
|
|
0.211, 0.22, 0.221, 0.23, 0.231,
|
|
0.24, 0.25, 0.251, 0.26, 0.27,
|
|
0.271, 0.28, 0.29, 0.30, 0.301,
|
|
0.31, 0.32, 0.33, 0.34, 0.35,
|
|
0.36, 0.37, 0.38, 0.39, 0.40,
|
|
0.41, 0.43, 0.50, 0.60, 0.65,
|
|
0.70, 0.75, 0.80, 0.85, 0.90,
|
|
0.95, 0.97, 0.99 };
|
|
const int logmax = sizeof logtab / sizeof *logtab;
|
|
|
|
F32 DBToLinear(F32 value)
|
|
{
|
|
if(value <= 0.f)
|
|
return(0.f);
|
|
if(value >= 1.f)
|
|
return(1.f);
|
|
|
|
S32 max = logmax;
|
|
S32 min = 0;
|
|
S32 mid;
|
|
S32 last = -1;
|
|
|
|
mid = (max - min) / 2;
|
|
do {
|
|
last = mid;
|
|
|
|
if(logtab[mid] == value)
|
|
break;
|
|
|
|
if(logtab[mid] < value)
|
|
min = mid;
|
|
else
|
|
max = mid;
|
|
|
|
mid = min + ((max - min) / 2);
|
|
} while(last != mid);
|
|
|
|
return((F32)mid / logmax);
|
|
}
|
|
|
|
F32 linearToDB(F32 value)
|
|
{
|
|
if(value <= 0.f)
|
|
return(0.f);
|
|
if(value >= 1.f)
|
|
return(1.f);
|
|
return(logtab[(U32)(logmax * value)]);
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
static ALvoid errorCallback(ALbyte *msg)
|
|
{
|
|
// used to allow our OpenAL implementation to display info on the console
|
|
Con::errorf(ConsoleLogEntry::General, (const char *)msg);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
bool prepareContext()
|
|
{
|
|
// mForceMaxDistanceUpdate = Con::getBoolVariable("$pref::audio::forceMaxDistanceUpdate", false);
|
|
mForceMaxDistanceUpdate = false;
|
|
|
|
// allocate source channels: can only get 16 sources on NT, so check the max before
|
|
// jff: todo, allow for more than 16 channels on non-NT boxes
|
|
mNumSources = mRequestSources;
|
|
alGenSources(mRequestSources, mSource);
|
|
|
|
// invalidate all existing handles
|
|
dMemset(mHandle, NULL_AUDIOHANDLE, sizeof(mHandle));
|
|
|
|
// pre-load profile data
|
|
SimGroup* grp = Sim::getDataBlockGroup();
|
|
if (grp != NULL)
|
|
{
|
|
SimObjectList::iterator itr = grp->begin();
|
|
for (; itr != grp->end(); itr++)
|
|
{
|
|
AudioProfile *profile = dynamic_cast<AudioProfile*>(*itr);
|
|
if(profile != NULL && profile->isPreload())
|
|
{
|
|
Resource<AudioBuffer> buffer = AudioBuffer::find(profile->mFilename);
|
|
if((bool)buffer)
|
|
ALuint bufferId = buffer->getALBuffer();
|
|
}
|
|
}
|
|
}
|
|
return(true);
|
|
}
|
|
|
|
void shutdownContext()
|
|
{
|
|
// invalidate active handles
|
|
dMemset(mSource, 0, sizeof(mSource));
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
bool OpenALInit()
|
|
{
|
|
OpenALShutdown();
|
|
|
|
if(!OpenALDLLInit())
|
|
return false;
|
|
|
|
// Open a device
|
|
#ifdef TORQUE_OS_LINUX
|
|
const char* deviceSpecifier =
|
|
Con::getVariable("Pref::Unix::OpenALSpecifier");
|
|
if (dStrlen(deviceSpecifier) == 0)
|
|
// use SDL for audio output by default
|
|
deviceSpecifier = "'((devices '(sdl)))";
|
|
mDevice = (ALCdevice *)alcOpenDevice((ALubyte*)deviceSpecifier);
|
|
#else
|
|
mDevice = (ALCdevice *)alcOpenDevice((ALubyte*)NULL);
|
|
#endif
|
|
if (mDevice == (ALCdevice *)NULL)
|
|
return false;
|
|
|
|
// Create an openAL context
|
|
#ifdef TORQUE_OS_LINUX
|
|
int freq = Con::getIntVariable("Pref::Unix::OpenALFrequency");
|
|
if (freq == 0)
|
|
freq = 22050;
|
|
|
|
Con::printf(" Setting OpenAL output frequency to %d", freq);
|
|
// some versions of openal have bugs converting between 22050 and 44100
|
|
// samples when the lib is in 44100 mode.
|
|
int attrlist[] = {
|
|
// this 0x100 is "ALC_FREQUENCY" in the linux openal implementation.
|
|
// it doesn't match the value of the creative headers, so we can't use
|
|
// that define. seems like linux torque really shouldn't be using the
|
|
// creative headers.
|
|
0x100, freq,
|
|
0
|
|
};
|
|
mContext = alcCreateContext(mDevice,attrlist);
|
|
#else
|
|
mContext = alcCreateContext(mDevice,NULL);
|
|
#endif
|
|
if (mContext == NULL)
|
|
return false;
|
|
|
|
// Make this context the active context
|
|
alcMakeContextCurrent(mContext);
|
|
|
|
mNumSources = mRequestSources;
|
|
alGenSources(mRequestSources, mSource);
|
|
|
|
// invalidate all existing handles
|
|
dMemset(mHandle, NULL_AUDIOHANDLE, sizeof(mHandle));
|
|
|
|
// default all channels to full gain
|
|
for(U32 i = 0; i < Audio::NumAudioTypes; i++)
|
|
mAudioTypeVolume[i] = 1.f;
|
|
|
|
// Clear Error Code
|
|
alGetError();
|
|
|
|
// Similiar to DSound Model w/o min distance clamping
|
|
alEnable(AL_DISTANCE_MODEL);
|
|
alDistanceModel(AL_INVERSE_DISTANCE);
|
|
alListenerf(AL_GAIN_LINEAR, 1.f);
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void OpenALShutdown()
|
|
{
|
|
alxStopAll();
|
|
|
|
//if(mInitialized)
|
|
{
|
|
alxEnvironmentDestroy();
|
|
}
|
|
|
|
while(mLoopingList.size())
|
|
{
|
|
mLoopingList.last()->mBuffer.purge();
|
|
delete mLoopingList.last();
|
|
mLoopingList.pop_back();
|
|
}
|
|
|
|
while(mLoopingFreeList.size())
|
|
{
|
|
mLoopingFreeList.last()->mBuffer.purge();
|
|
delete mLoopingFreeList.last();
|
|
mLoopingFreeList.pop_back();
|
|
}
|
|
|
|
for(U32 i = 0; i < MAX_AUDIOSOURCES; i++)
|
|
mBuffer[i] = 0;
|
|
|
|
alDeleteSources(mNumSources, mSource);
|
|
|
|
if (mContext)
|
|
{
|
|
alcDestroyContext(mContext);
|
|
mContext = NULL;
|
|
}
|
|
if (mDevice)
|
|
{
|
|
alcCloseDevice(mDevice);
|
|
mDevice = NULL;
|
|
}
|
|
|
|
OpenALDLLShutdown();
|
|
}
|
|
|
|
|
|
} // end OpenAL namespace
|
|
|
|
AudioStreamSource* alxFindAudioStreamSource(AUDIOHANDLE handle)
|
|
{
|
|
StreamingList::iterator itr2 = mStreamingList.findImage(handle);
|
|
if(itr2)
|
|
return *itr2;
|
|
return NULL;
|
|
}
|