mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2025-01-09 01:15:22 +00:00
680 lines
19 KiB
C++
680 lines
19 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#include "stdopenal.h"
|
|
#include "sound_driver_al.h"
|
|
|
|
#include <nel/misc/path.h>
|
|
#include <nel/misc/dynloadlib.h>
|
|
#include <nel/misc/fast_mem.h>
|
|
|
|
#include "buffer_al.h"
|
|
#include "listener_al.h"
|
|
#include "effect_al.h"
|
|
#include "source_al.h"
|
|
|
|
using namespace std;
|
|
using namespace NLMISC;
|
|
|
|
namespace NLSOUND {
|
|
|
|
|
|
// Currently, the OpenAL headers are different between Windows and Linux versions !
|
|
// AL_INVALID_XXXX are part of the spec, though.
|
|
/*#ifdef NL_OS_UNIX
|
|
#define AL_INVALID_ENUM AL_ILLEGAL_ENUM
|
|
#define AL_INVALID_OPERATION AL_ILLEGAL_COMMAND
|
|
#endif*/
|
|
|
|
|
|
#ifdef NL_DEBUG
|
|
// Test error in debug mode
|
|
void alTestError()
|
|
{
|
|
ALuint errcode = alGetError();
|
|
switch (errcode)
|
|
{
|
|
case AL_NO_ERROR : break;
|
|
case AL_INVALID_NAME: nlerror("OpenAL: Invalid name");
|
|
case AL_INVALID_ENUM: nlerror("OpenAL: Invalid enum");
|
|
case AL_INVALID_VALUE: nlerror("OpenAL: Invalid value");
|
|
case AL_INVALID_OPERATION: nlerror("OpenAL: Invalid operation");
|
|
case AL_OUT_OF_MEMORY: nlerror("OpenAL: Out of memory");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if !FINAL_VERSION
|
|
void alTestWarning(const char *src)
|
|
{
|
|
ALuint errcode = alGetError();
|
|
switch (errcode)
|
|
{
|
|
case AL_NO_ERROR: break;
|
|
case AL_INVALID_NAME: nlwarning("AL: Invalid Name paramater passed to AL call (%s)", src); break;
|
|
case AL_INVALID_ENUM: nlwarning("AL: Invalid parameter passed to AL call (%s)", src); break;
|
|
case AL_INVALID_VALUE: nlwarning("AL: Invalid enum parameter value (%s)", src); break;
|
|
case AL_INVALID_OPERATION: nlwarning("AL: Illegal call (%s)", src); break;
|
|
case AL_OUT_OF_MEMORY: nlerror("AL: Out of memory (%s)", src); break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#define INITIAL_BUFFERS 8
|
|
#define INITIAL_SOURCES 8
|
|
#define BUFFER_ALLOC_RATE 8
|
|
#define SOURCE_ALLOC_RATE 8
|
|
|
|
#define ROLLOFF_FACTOR_DEFAULT 1.0f
|
|
|
|
#ifndef NL_STATIC
|
|
|
|
class CSoundDriverALNelLibrary : public NLMISC::INelLibrary {
|
|
void onLibraryLoaded(bool /* firstTime */) { }
|
|
void onLibraryUnloaded(bool /* lastTime */) { }
|
|
};
|
|
NLMISC_DECL_PURE_LIB(CSoundDriverALNelLibrary)
|
|
|
|
#endif /* #ifndef NL_STATIC */
|
|
|
|
/*
|
|
* Sound driver instance creation
|
|
*/
|
|
#ifdef NL_OS_WINDOWS
|
|
|
|
// ******************************************************************
|
|
|
|
#ifdef NL_STATIC
|
|
ISoundDriver* createISoundDriverInstanceOpenAl
|
|
#else
|
|
__declspec(dllexport) ISoundDriver *NLSOUND_createISoundDriverInstance
|
|
#endif
|
|
(ISoundDriver::IStringMapperProvider *stringMapper)
|
|
{
|
|
return new CSoundDriverAL(stringMapper);
|
|
}
|
|
|
|
// ******************************************************************
|
|
|
|
#ifdef NL_STATIC
|
|
uint32 interfaceVersionOpenAl()
|
|
#else
|
|
__declspec(dllexport) uint32 NLSOUND_interfaceVersion()
|
|
#endif
|
|
{
|
|
return ISoundDriver::InterfaceVersion;
|
|
}
|
|
|
|
// ******************************************************************
|
|
|
|
#ifdef NL_STATIC
|
|
void outputProfileOpenAl
|
|
#else
|
|
__declspec(dllexport) void NLSOUND_outputProfile
|
|
#endif
|
|
(string &out)
|
|
{
|
|
CSoundDriverAL::getInstance()->writeProfile(out);
|
|
}
|
|
|
|
// ******************************************************************
|
|
|
|
#ifdef NL_STATIC
|
|
ISoundDriver::TDriver getDriverTypeOpenAl()
|
|
#else
|
|
__declspec(dllexport) ISoundDriver::TDriver NLSOUND_getDriverType()
|
|
#endif
|
|
{
|
|
return ISoundDriver::DriverOpenAl;
|
|
}
|
|
|
|
// ******************************************************************
|
|
|
|
#elif defined (NL_OS_UNIX)
|
|
|
|
extern "C"
|
|
{
|
|
ISoundDriver* NLSOUND_createISoundDriverInstance(ISoundDriver::IStringMapperProvider *stringMapper)
|
|
{
|
|
return new CSoundDriverAL(stringMapper);
|
|
}
|
|
|
|
uint32 NLSOUND_interfaceVersion ()
|
|
{
|
|
return ISoundDriver::InterfaceVersion;
|
|
}
|
|
}
|
|
|
|
#endif // NL_OS_UNIX
|
|
|
|
/*
|
|
* Constructor
|
|
*/
|
|
CSoundDriverAL::CSoundDriverAL(ISoundDriver::IStringMapperProvider *stringMapper)
|
|
: _StringMapper(stringMapper), _AlDevice(NULL), _AlContext(NULL),
|
|
_NbExpBuffers(0), _NbExpSources(0), _RolloffFactor(1.f)
|
|
{
|
|
alExtInit();
|
|
}
|
|
|
|
/*
|
|
* Destructor
|
|
*/
|
|
CSoundDriverAL::~CSoundDriverAL()
|
|
{
|
|
// Remove the allocated (but not exported) source and buffer names-
|
|
// Release internal resources of all remaining ISource instances
|
|
if (_Sources.size())
|
|
{
|
|
nlwarning("AL: _Sources.size(): '%u'", (uint32)_Sources.size());
|
|
set<CSourceAL *>::iterator it(_Sources.begin()), end(_Sources.end());
|
|
for (; it != end; ++it) (*it)->release();
|
|
_Sources.clear();
|
|
}
|
|
if (!_Buffers.empty()) alDeleteBuffers(compactAliveNames(_Buffers, alIsBuffer), &*_Buffers.begin());
|
|
// Release internal resources of all remaining IEffect instances
|
|
if (_Effects.size())
|
|
{
|
|
nlwarning("AL: _Effects.size(): '%u'", (uint32)_Effects.size());
|
|
set<CEffectAL *>::iterator it(_Effects.begin()), end(_Effects.end());
|
|
for (; it != end; ++it) (*it)->release();
|
|
_Effects.clear();
|
|
}
|
|
|
|
// OpenAL exit
|
|
if (_AlContext) { alcDestroyContext(_AlContext); _AlContext = NULL; }
|
|
if (_AlDevice) { alcCloseDevice(_AlDevice); _AlDevice = NULL; }
|
|
}
|
|
|
|
/// Return a list of available devices for the user. The value at index 0 is empty, and is used for automatic device selection.
|
|
void CSoundDriverAL::getDevices(std::vector<std::string> &devices)
|
|
{
|
|
devices.push_back(""); // empty
|
|
|
|
if (AlEnumerateAllExt)
|
|
{
|
|
const ALchar* deviceNames = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
|
// const ALchar* defaultDevice = NULL;
|
|
if(!strlen(deviceNames))
|
|
{
|
|
nldebug("AL: No audio devices");
|
|
}
|
|
else
|
|
{
|
|
nldebug("AL: Listing devices: ");
|
|
while(deviceNames && *deviceNames)
|
|
{
|
|
nldebug("AL: - %s", deviceNames);
|
|
devices.push_back(deviceNames);
|
|
deviceNames += strlen(deviceNames) + 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nldebug("AL: ALC_ENUMERATE_ALL_EXT not present");
|
|
}
|
|
}
|
|
|
|
static const ALchar *getDeviceInternal(const std::string &device)
|
|
{
|
|
if (device.empty()) return NULL;
|
|
if (AlEnumerateAllExt)
|
|
{
|
|
const ALchar* deviceNames = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
|
// const ALchar* defaultDevice = NULL;
|
|
if(!strlen(deviceNames))
|
|
{
|
|
nldebug("AL: No audio devices");
|
|
}
|
|
else
|
|
{
|
|
while (deviceNames && *deviceNames)
|
|
{
|
|
if (!strcmp(deviceNames, device.c_str()))
|
|
return deviceNames;
|
|
deviceNames += strlen(deviceNames) + 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nldebug("AL: ALC_ENUMERATE_ALL_EXT not present");
|
|
}
|
|
nldebug("AL: Device '%s' not found", device.c_str());
|
|
return NULL;
|
|
}
|
|
|
|
/// Initialize the driver with a user selected device. If device.empty(), the default or most appropriate device is used.
|
|
void CSoundDriverAL::initDevice(const std::string &device, ISoundDriver::TSoundOptions options)
|
|
{
|
|
// list of supported options in this driver
|
|
// no adpcm, no manual rolloff (for now)
|
|
const sint supportedOptions =
|
|
OptionEnvironmentEffects
|
|
| OptionSoftwareBuffer
|
|
| OptionManualRolloff
|
|
| OptionLocalBufferCopy
|
|
| OptionHasBufferStreaming;
|
|
|
|
// list of forced options in this driver
|
|
const sint forcedOptions = 0;
|
|
|
|
// set the options
|
|
_Options = (TSoundOptions)(((sint)options & supportedOptions) | forcedOptions);
|
|
|
|
/* TODO: multichannel */
|
|
|
|
// OpenAL initialization
|
|
const ALchar *dev = getDeviceInternal(device);
|
|
nldebug("AL: Opening device: '%s'", dev == NULL ? "NULL" : dev);
|
|
_AlDevice = alcOpenDevice(dev);
|
|
if (!_AlDevice) throw ESoundDriver("AL: Failed to open device");
|
|
nldebug("AL: ALC_DEVICE_SPECIFIER: '%s'", alcGetString(_AlDevice, ALC_DEVICE_SPECIFIER));
|
|
//int attrlist[] = { ALC_FREQUENCY, 48000,
|
|
// ALC_MONO_SOURCES, 12,
|
|
// ALC_STEREO_SOURCES, 4,
|
|
// ALC_INVALID };
|
|
_AlContext = alcCreateContext(_AlDevice, NULL); // attrlist);
|
|
if (!_AlContext) { alcCloseDevice(_AlDevice); throw ESoundDriver("AL: Failed to create context"); }
|
|
alcMakeContextCurrent(_AlContext);
|
|
alTestError();
|
|
|
|
// Display version information
|
|
const ALchar *alversion, *alrenderer, *alvendor, *alext;
|
|
alversion = alGetString(AL_VERSION);
|
|
alrenderer = alGetString(AL_RENDERER);
|
|
alvendor = alGetString(AL_VENDOR);
|
|
alext = alGetString(AL_EXTENSIONS);
|
|
alTestError();
|
|
nldebug("AL: AL_VERSION: '%s', AL_RENDERER: '%s', AL_VENDOR: '%s'", alversion, alrenderer, alvendor);
|
|
nldebug("AL: AL_EXTENSIONS: %s", alext);
|
|
|
|
// Load and display extensions
|
|
alExtInitDevice(_AlDevice);
|
|
#if EAX_AVAILABLE
|
|
nlinfo("AL: EAX: %s, EAX-RAM: %s, ALC_EXT_EFX: %s",
|
|
AlExtEax ? "Present" : "Not available",
|
|
AlExtXRam ? "Present" : "Not available",
|
|
AlExtEfx ? "Present" : "Not available");
|
|
#else
|
|
nldebug("AL: EAX-RAM: %s, ALC_EXT_EFX: %s",
|
|
AlExtXRam ? "Present" : "Not available",
|
|
AlExtEfx ? "Present" : "Not available");
|
|
#endif
|
|
alTestError();
|
|
|
|
nldebug("AL: Max. sources: %u, Max. effects: %u", (uint32)countMaxSources(), (uint32)countMaxEffects());
|
|
|
|
if (getOption(OptionEnvironmentEffects))
|
|
{
|
|
if (!AlExtEfx)
|
|
{
|
|
nlwarning("AL: ALC_EXT_EFX is required, environment effects disabled");
|
|
_Options = (TSoundOptions)((uint)_Options & ~OptionEnvironmentEffects);
|
|
}
|
|
else if (!countMaxEffects())
|
|
{
|
|
nlwarning("AL: No effects available, environment effects disabled");
|
|
_Options = (TSoundOptions)((uint)_Options & ~OptionEnvironmentEffects);
|
|
}
|
|
}
|
|
|
|
// Choose the I3DL2 model (same as DirectSound3D if not manual)
|
|
if (getOption(OptionManualRolloff)) alDistanceModel(AL_NONE);
|
|
else alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
|
|
alTestError();
|
|
|
|
// Initial buffers and sources allocation
|
|
allocateNewItems(alGenBuffers, alIsBuffer, _Buffers, _NbExpBuffers, INITIAL_BUFFERS);
|
|
alTestError();
|
|
}
|
|
|
|
/// Return options that are enabled (including those that cannot be disabled on this driver).
|
|
ISoundDriver::TSoundOptions CSoundDriverAL::getOptions()
|
|
{
|
|
return _Options;
|
|
}
|
|
|
|
/// Return if an option is enabled (including those that cannot be disabled on this driver).
|
|
bool CSoundDriverAL::getOption(ISoundDriver::TSoundOptions option)
|
|
{
|
|
return ((uint)_Options & (uint)option) == (uint)option;
|
|
}
|
|
|
|
/*
|
|
* Allocate nb new items
|
|
*/
|
|
void CSoundDriverAL::allocateNewItems(TGenFunctionAL algenfunc, TTestFunctionAL altestfunc,
|
|
vector<ALuint>& names, uint index, uint nb )
|
|
{
|
|
nlassert( index == names.size() );
|
|
names.resize( index + nb );
|
|
// FIXME assumption about inner workings of std::vector;
|
|
// &(names[...]) only works with "names.size() - nbalive == 1"
|
|
generateItems( algenfunc, altestfunc, nb, &(names[index]) );
|
|
}
|
|
|
|
|
|
/*
|
|
* throwGenException
|
|
*/
|
|
void ThrowGenException( TGenFunctionAL algenfunc )
|
|
{
|
|
if ( algenfunc == alGenBuffers )
|
|
throw ESoundDriverGenBuf();
|
|
else if ( algenfunc == alGenSources )
|
|
throw ESoundDriverGenSrc();
|
|
else
|
|
nlstop;
|
|
}
|
|
|
|
/*
|
|
* Generate nb buffers/sources
|
|
*/
|
|
void CSoundDriverAL::generateItems( TGenFunctionAL algenfunc, TTestFunctionAL altestfunc, uint nb, ALuint *array )
|
|
{
|
|
// array is actually a std::vector element address!
|
|
algenfunc( nb, array );
|
|
|
|
// Error handling
|
|
if ( alGetError() != AL_NO_ERROR )
|
|
{
|
|
ThrowGenException( algenfunc );
|
|
}
|
|
|
|
// Check buffers
|
|
uint i;
|
|
for ( i=0; i!=nb; i++ )
|
|
{
|
|
if ( ! altestfunc( array[i] ) )
|
|
{
|
|
ThrowGenException( algenfunc );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create a sound buffer
|
|
*/
|
|
IBuffer *CSoundDriverAL::createBuffer()
|
|
{
|
|
CBufferAL *buffer = new CBufferAL(createItem(alGenBuffers, alIsBuffer, _Buffers, _NbExpBuffers, BUFFER_ALLOC_RATE));
|
|
return buffer;
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a source
|
|
*/
|
|
ISource *CSoundDriverAL::createSource()
|
|
{
|
|
CSourceAL *sourceAl = new CSourceAL(this);
|
|
_Sources.insert(sourceAl);
|
|
return sourceAl;
|
|
}
|
|
|
|
/// Create a reverb effect
|
|
IReverbEffect *CSoundDriverAL::createReverbEffect()
|
|
{
|
|
IReverbEffect *ieffect = NULL;
|
|
CEffectAL *effectal = NULL;
|
|
|
|
ALuint slot = AL_NONE;
|
|
alGenAuxiliaryEffectSlots(1, &slot);
|
|
if (alGetError() != AL_NO_ERROR)
|
|
{
|
|
nlwarning("AL: alGenAuxiliaryEffectSlots failed");
|
|
return NULL;
|
|
}
|
|
|
|
ALuint effect = AL_NONE;
|
|
alGenEffects(1, &effect);
|
|
if (alGetError() != AL_NO_ERROR)
|
|
{
|
|
nlwarning("AL: alGenEffects failed");
|
|
alDeleteAuxiliaryEffectSlots(1, &slot);
|
|
return NULL; /* createEffect */
|
|
}
|
|
|
|
#if EFX_CREATIVE_AVAILABLE
|
|
alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
|
|
if (alGetError() != AL_NO_ERROR)
|
|
{
|
|
nlinfo("AL: Creative Reverb Effect not supported, falling back to standard Reverb Effect");
|
|
}
|
|
else
|
|
{
|
|
alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, effect); alTestError();
|
|
alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, AL_TRUE); alTestError(); // auto only for reverb!
|
|
CCreativeReverbEffectAL *eff = new CCreativeReverbEffectAL(this, effect, slot);
|
|
ieffect = static_cast<IReverbEffect *>(eff);
|
|
effectal = static_cast<CEffectAL *>(eff);
|
|
nlassert(ieffect); nlassert(effectal);
|
|
_Effects.insert(effectal);
|
|
return ieffect;
|
|
}
|
|
#endif
|
|
|
|
alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
|
|
if (alGetError() != AL_NO_ERROR)
|
|
{
|
|
nlwarning("AL: Reverb Effect not supported");
|
|
alDeleteAuxiliaryEffectSlots(1, &slot);
|
|
alDeleteEffects(1, &effect);
|
|
return NULL; /* createEffect */
|
|
}
|
|
else
|
|
{
|
|
alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, effect); alTestError();
|
|
alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_AUXILIARY_SEND_AUTO, AL_TRUE); alTestError(); // auto only for reverb!
|
|
CStandardReverbEffectAL *eff = new CStandardReverbEffectAL(this, effect, slot);
|
|
ieffect = static_cast<IReverbEffect *>(eff);
|
|
effectal = static_cast<CEffectAL *>(eff);
|
|
nlassert(ieffect); nlassert(effectal);
|
|
_Effects.insert(effectal);
|
|
return ieffect;
|
|
}
|
|
}
|
|
|
|
|
|
static uint getMaxNumSourcesInternal()
|
|
{
|
|
ALuint sources[256];
|
|
memset(sources, 0, sizeofarray(sources));
|
|
uint sourceCount = 0;
|
|
|
|
alGetError();
|
|
|
|
for (; sourceCount < 256; ++sourceCount)
|
|
{
|
|
alGenSources(1, &sources[sourceCount]);
|
|
if (alGetError() != AL_NO_ERROR)
|
|
break;
|
|
}
|
|
|
|
alDeleteSources(sourceCount, sources);
|
|
if (alGetError() != AL_NO_ERROR)
|
|
{
|
|
for (uint i = 0; i < 256; i++)
|
|
{
|
|
alDeleteSources(1, &sources[i]);
|
|
}
|
|
}
|
|
|
|
alGetError();
|
|
|
|
return sourceCount;
|
|
}
|
|
|
|
/// Return the maximum number of sources that can created
|
|
uint CSoundDriverAL::countMaxSources()
|
|
{
|
|
// ALC_MONO_SOURCES
|
|
// software allows 256 sources (software audio ftw!)
|
|
// cheap openal cards 32, expensive openal cards 128
|
|
// trying to go too high is safely handled anyways
|
|
return getMaxNumSourcesInternal() + _Sources.size();
|
|
}
|
|
|
|
/// Return the maximum number of effects that can be created, which is only 1 in openal software mode :(
|
|
uint CSoundDriverAL::countMaxEffects()
|
|
{
|
|
if (!getOption(OptionEnvironmentEffects)) return 0;
|
|
if (!AlExtEfx) return 0;
|
|
ALCint max_auxiliary_sends;
|
|
alcGetIntegerv(_AlDevice, ALC_MAX_AUXILIARY_SENDS, 1, &max_auxiliary_sends);
|
|
return (uint)max_auxiliary_sends;
|
|
}
|
|
|
|
/*
|
|
* Create a sound buffer or a sound source
|
|
*/
|
|
ALuint CSoundDriverAL::createItem(TGenFunctionAL algenfunc, TTestFunctionAL altestfunc,
|
|
vector<ALuint>& names, uint& index, uint allocrate)
|
|
{
|
|
nlassert( index <= names.size() );
|
|
if ( index == names.size() )
|
|
{
|
|
// Generate new items
|
|
uint nbalive = compactAliveNames( names, altestfunc );
|
|
if ( nbalive == names.size() )
|
|
{
|
|
// Extend vector of names
|
|
// FIXME? assumption about inner workings of std::vector
|
|
allocateNewItems( algenfunc, altestfunc, names, index, allocrate );
|
|
}
|
|
else
|
|
{
|
|
// Take the room of the deleted names
|
|
nlassert(nbalive < names.size());
|
|
index = nbalive;
|
|
// FIXME assumption about inner workings of std::vector;
|
|
// &(names[...]) only works with "names.size() - nbalive == 1"
|
|
generateItems(algenfunc, altestfunc, names.size() - nbalive, &(names[nbalive]));
|
|
}
|
|
}
|
|
|
|
// Return the name of the item
|
|
nlassert( index < names.size() );
|
|
ALuint itemname = names[index];
|
|
index++;
|
|
return itemname;
|
|
}
|
|
|
|
|
|
/*
|
|
* Remove names of deleted buffers and return the number of valid buffers
|
|
*/
|
|
uint CSoundDriverAL::compactAliveNames( vector<ALuint>& names, TTestFunctionAL altestfunc )
|
|
{
|
|
vector<ALuint>::iterator iball, ibcompacted;
|
|
for ( iball=names.begin(), ibcompacted=names.begin(); iball!=names.end(); ++iball )
|
|
{
|
|
// iball is incremented every iteration
|
|
// ibcompacted is not incremented if a buffer is not valid anymore
|
|
if ( altestfunc( *iball ) )
|
|
{
|
|
*ibcompacted = *iball;
|
|
++ibcompacted;
|
|
}
|
|
}
|
|
nlassert( ibcompacted <= names.end() );
|
|
return ibcompacted - names.begin();
|
|
}
|
|
|
|
|
|
void CSoundDriverAL::commit3DChanges()
|
|
{
|
|
// Sync up sources & listener 3d position.
|
|
if (getOption(OptionManualRolloff))
|
|
{
|
|
for (std::set<CSourceAL *>::iterator it(_Sources.begin()), end(_Sources.end()); it != end; ++it)
|
|
{
|
|
(*it)->updateManualRolloff();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Remove a buffer
|
|
void CSoundDriverAL::removeBuffer(CBufferAL *buffer)
|
|
{
|
|
nlassert(buffer != NULL);
|
|
if (!deleteItem( buffer->bufferName(), alDeleteBuffers, _Buffers))
|
|
nlwarning("AL: Deleting buffer: name not found");
|
|
}
|
|
|
|
/// Remove a source
|
|
void CSoundDriverAL::removeSource(CSourceAL *source)
|
|
{
|
|
if (_Sources.find(source) != _Sources.end()) _Sources.erase(source);
|
|
else nlwarning("AL: removeSource already called");
|
|
}
|
|
|
|
/// Remove an effect
|
|
void CSoundDriverAL::removeEffect(CEffectAL *effect)
|
|
{
|
|
if (_Effects.find(effect) != _Effects.end()) _Effects.erase(effect);
|
|
else nlwarning("AL: removeEffect already called");
|
|
}
|
|
|
|
/// Delete a buffer or a source
|
|
bool CSoundDriverAL::deleteItem( ALuint name, TDeleteFunctionAL aldeletefunc, vector<ALuint>& names )
|
|
{
|
|
vector<ALuint>::iterator ibn = find( names.begin(), names.end(), name );
|
|
if ( ibn == names.end() )
|
|
{
|
|
return false;
|
|
}
|
|
aldeletefunc( 1, &*ibn );
|
|
*ibn = AL_NONE;
|
|
alTestError();
|
|
return true;
|
|
}
|
|
|
|
/// Create the listener instance
|
|
IListener *CSoundDriverAL::createListener()
|
|
{
|
|
nlassert(!CListenerAL::isInitialized());
|
|
return new CListenerAL();
|
|
}
|
|
|
|
/// Apply changes of rolloff factor to all sources
|
|
void CSoundDriverAL::applyRolloffFactor( float f )
|
|
{
|
|
_RolloffFactor = f;
|
|
if (!getOption(OptionManualRolloff))
|
|
{
|
|
set<CSourceAL *>::iterator it(_Sources.begin()), end(_Sources.end());
|
|
for (; it != end; ++it) alSourcef((*it)->getSource(), AL_ROLLOFF_FACTOR, _RolloffFactor);
|
|
alTestError();
|
|
}
|
|
}
|
|
|
|
/// Helper for loadWavFile()
|
|
TSampleFormat ALtoNLSoundFormat( ALenum alformat )
|
|
{
|
|
switch ( alformat )
|
|
{
|
|
case AL_FORMAT_MONO8 : return Mono8;
|
|
case AL_FORMAT_MONO16 : return Mono16;
|
|
case AL_FORMAT_STEREO8 : return Stereo8;
|
|
case AL_FORMAT_STEREO16 : return Stereo16;
|
|
default : nlstop; return Mono8;
|
|
}
|
|
}
|
|
|
|
} // NLSOUND
|