# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

sound_driver_al.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2001 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #include "stdopenal.h"
00027 
00028 #include "buffer_al.h"
00029 #include "listener_al.h"
00030 
00031 using namespace std;
00032 
00033 
00034 namespace NLSOUND {
00035 
00036 
00037 #ifdef EAX_AVAILABLE
00038 // EAXSet global function
00039 EAXSet                                  EAXSetProp = NULL;
00040 
00041 // EAXGet global function
00042 EAXGet                                  EAXGetProp = NULL;
00043 #endif
00044 
00045 
00046 // Currently, the OpenAL headers are different between Windows and Linux versions !
00047 // AL_INVALID_XXXX are part of the spec, though.
00048 #ifdef NL_OS_UNIX
00049 #define AL_INVALID_ENUM AL_ILLEGAL_ENUM  
00050 #define AL_INVALID_OPERATION AL_ILLEGAL_COMMAND 
00051 #endif
00052 
00053 
00054 #ifdef NL_DEBUG
00055 // Test error in debug mode
00056 void TestALError()
00057 {
00058         ALuint errcode = alGetError();
00059         switch( errcode )
00060         {
00061         case AL_NO_ERROR : break;
00062         case AL_INVALID_NAME : nlerror( "OpenAL: Invalid name" );
00063         case AL_INVALID_ENUM  : nlerror( "OpenAL: Invalid enum" );
00064         case AL_INVALID_VALUE  : nlerror( "OpenAL: Invalid value" );
00065         case AL_INVALID_OPERATION  : nlerror( "OpenAL: Invalid operation" );
00066         case AL_OUT_OF_MEMORY  : nlerror( "OpenAL: Out of memory" );
00067         // alGetString( errcode ) seems to fail !
00068         }
00069 }
00070 #endif
00071 
00072 
00073 #define INITIAL_BUFFERS 8
00074 #define INITIAL_SOURCES 8
00075 #define BUFFER_ALLOC_RATE 8
00076 #define SOURCE_ALLOC_RATE 8
00077 
00078 #define ROLLOFF_FACTOR_DEFAULT 1.0f
00079 
00080 /*
00081  * Sound driver instance creation
00082  */
00083 #ifdef NL_OS_WINDOWS
00084         
00085 __declspec(dllexport) ISoundDriver *NLSOUND_createISoundDriverInstance()
00086 {
00087         CSoundDriverAL *driver = new CSoundDriverAL();
00088         driver->init();
00089         return driver;
00090 }
00091 
00092 __declspec(dllexport) uint32 NLSOUND_interfaceVersion ()
00093 {
00094         return ISoundDriver::InterfaceVersion;
00095 }
00096 
00097 #elif defined (NL_OS_UNIX)
00098 
00099 extern "C"
00100 {
00101 ISoundDriver* NLSOUND_createISoundDriverInstance ()
00102 {
00103         CSoundDriverAL *driver = new CSoundDriverAL();
00104         driver->init();
00105         return driver;
00106 }
00107 
00108 uint32 NLSOUND_interfaceVersion ()
00109 {
00110         return ISoundDriver::InterfaceVersion;
00111 }
00112 }
00113 
00114 #endif // NL_OS_UNIX
00115 
00116 
00117 // The instance of the singleton
00118 CSoundDriverAL  *CSoundDriverAL::_Instance = NULL;
00119 
00120 
00121 /*
00122  * Constructor
00123  */
00124 CSoundDriverAL::CSoundDriverAL() :
00125         ISoundDriver(), _NbExpBuffers( 0 ), _NbExpSources( 0 ), _RolloffFactor( 1.0f )
00126 {
00127         if ( _Instance == NULL )
00128         {
00129                 _Instance = this;
00130         }
00131         else
00132         {
00133                 nlerror( "Sound driver singleton instanciated twice" );
00134         }
00135 }
00136 
00137 
00138 /*
00139  * Destructor
00140  */
00141 CSoundDriverAL::~CSoundDriverAL()
00142 {
00143         // Remove the allocated (but not exported) source and buffer names
00144         alDeleteSources( compactAliveNames( _Sources, alIsSource ), &*_Sources.begin() );
00145         alDeleteBuffers( compactAliveNames( _Buffers, alIsBuffer ), &*_Buffers.begin() );
00146 
00147         // OpenAL exit
00148         alutExit();
00149 
00150         _Instance = NULL;
00151 }
00152 
00153 
00154 /*
00155  * Initialization
00156  */
00157 bool            CSoundDriverAL::init()
00158 {
00159         // OpenAL initialization
00160         alutInit( NULL, NULL );
00161 
00162         // Display version information
00163         const ALubyte *alversion, *alrenderer, *alvendor, *alext;
00164         alversion = alGetString( AL_VERSION );
00165         alrenderer = alGetString( AL_RENDERER );
00166         alvendor = alGetString( AL_VENDOR );
00167         alext = alGetString( AL_EXTENSIONS );
00168         TestALError();
00169         nlinfo( "Loading OpenAL lib: %s, %s, %s", alversion, alrenderer, alvendor );
00170         nlinfo( "Listing extensions: %s", alext );
00171 
00172 #ifdef EAX_AVAILABLE
00173     // Set EAX environment if EAX is available
00174         if ( alIsExtensionPresent((ALubyte *)"EAX") == AL_TRUE ) // or EAX2.0
00175         {
00176                 nlinfo( "Initializing EAX" );
00177             EAXSetProp = (EAXSet)alGetProcAddress((ALubyte*)"EAXSet");
00178                 EAXGetProp = (EAXGet)alGetProcAddress((ALubyte*)"EAXGet");
00179                 if ( EAXSetProp != NULL )
00180                 {
00181                         unsigned long ulEAXVal;
00182                         long lGlobalReverb;
00183                     lGlobalReverb = 0;
00184                         EAXSetProp( &DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ROOM, 0, &lGlobalReverb, sizeof(unsigned long) );
00185                         ulEAXVal = EAX_ENVIRONMENT_GENERIC;
00186                         EAXSetProp( &DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ENVIRONMENT, 0, &ulEAXVal, sizeof(unsigned long) );
00187                 }
00188         }
00189         else
00190 #endif
00191         {
00192                 nlinfo( "EAX not available" );
00193         }
00194 
00195         // Choose the I3DL2 model (same as DirectSound3D)
00196         alDistanceModel( AL_INVERSE_DISTANCE_CLAMPED );
00197         TestALError();
00198 
00199         // Initial buffers and sources allocation
00200         allocateNewItems( alGenBuffers, alIsBuffer, _Buffers, _NbExpBuffers, INITIAL_BUFFERS );
00201         allocateNewItems( alGenSources, alIsSource, _Sources, _NbExpSources, INITIAL_SOURCES );
00202 
00203         return true;
00204 }
00205 
00206 
00207 /*
00208  * Allocate nb new items
00209  */
00210 void            CSoundDriverAL::allocateNewItems( TGenFunctionAL algenfunc, TTestFunctionAL altestfunc,
00211                                                                                           vector<ALuint>& names, uint index, uint nb )
00212 {
00213         nlassert( index == names.size() );
00214         names.resize( index + nb );
00215         generateItems( algenfunc, altestfunc, nb, &(names[index]) );
00216 }
00217 
00218 
00219 /*
00220  * throwGenException
00221  */
00222 void ThrowGenException( TGenFunctionAL algenfunc )
00223 {
00224         if ( algenfunc == alGenBuffers )
00225                 throw ESoundDriverGenBuf();
00226         else if ( algenfunc == alGenSources )
00227                 throw ESoundDriverGenSrc();
00228         else
00229                 nlstop;
00230 }
00231 
00232 /*
00233  * Generate nb buffers/sources
00234  */
00235 void            CSoundDriverAL::generateItems( TGenFunctionAL algenfunc, TTestFunctionAL altestfunc, uint nb, ALuint *array )
00236 {
00237         algenfunc( nb, array );
00238 
00239         // Error handling
00240         if ( alGetError() != AL_NO_ERROR )
00241         {
00242                 ThrowGenException( algenfunc );
00243         }
00244 
00245         // Check buffers
00246         uint i;
00247         for ( i=0; i!=nb; i++ )
00248         {
00249                 if ( ! altestfunc( array[i] ) )
00250                 {
00251                         ThrowGenException( algenfunc );
00252                 }
00253         }
00254 }
00255 
00256 
00257 /*
00258  * Create a sound buffer
00259  */
00260 IBuffer                 *CSoundDriverAL::createBuffer()
00261 {
00262         return new CBufferAL( createItem( alGenBuffers, alIsBuffer, _Buffers, _NbExpBuffers, BUFFER_ALLOC_RATE ) );
00263 }
00264 
00265 
00266 /*
00267  * Create a source
00268  */
00269 ISource                 *CSoundDriverAL::createSource()
00270 {
00271         CSourceAL *sourceal = new CSourceAL( createItem( alGenSources, alIsSource, _Sources, _NbExpSources, SOURCE_ALLOC_RATE ) );
00272         if ( _RolloffFactor != ROLLOFF_FACTOR_DEFAULT )
00273         {
00274                 alSourcef( sourceal->sourceName(), AL_ROLLOFF_FACTOR, _RolloffFactor );
00275         }
00276         return sourceal;
00277 }
00278 
00279 
00280 /*
00281  * Create a sound buffer or a sound source
00282  */
00283 ALuint                  CSoundDriverAL::createItem( TGenFunctionAL algenfunc, TTestFunctionAL altestfunc,
00284                                                                                         vector<ALuint>& names, uint& index, uint allocrate )
00285 {
00286         nlassert( index <= names.size() );
00287         if ( index == names.size() )
00288         {
00289                 // Generate new items
00290                 uint nbalive = compactAliveNames( names, altestfunc );
00291                 if ( nbalive == names.size() )
00292                 {
00293                         // Extend vector of names
00294                         allocateNewItems( algenfunc, altestfunc, names, index, allocrate );
00295                 }
00296                 else
00297                 {
00298                         // Take the room of the deleted names
00299                         nlassert( nbalive < names.size() );
00300                         index = nbalive;
00301                         generateItems( algenfunc, altestfunc, names.size() - nbalive, &(names[nbalive]) );
00302                 }
00303         }
00304 
00305         // Return the name of the item
00306         nlassert( index < names.size() );
00307         ALuint itemname = names[index];
00308         index++;
00309         return itemname;
00310 }
00311 
00312 
00313 /*
00314  * Remove names of deleted buffers and return the number of valid buffers
00315  */
00316 uint                    CSoundDriverAL::compactAliveNames( vector<ALuint>& names, TTestFunctionAL altestfunc )
00317 {
00318         vector<ALuint>::iterator iball, ibcompacted;
00319         for ( iball=names.begin(), ibcompacted=names.begin(); iball!=names.end(); ++iball )
00320         {
00321                 // iball is incremented every iteration
00322                 // ibcompacted is not incremented if a buffer is not valid anymore
00323                 if ( altestfunc( *iball ) )
00324                 {
00325                         *ibcompacted = *iball;
00326                         ++ibcompacted;
00327                 }
00328         }
00329         nlassert( ibcompacted <= names.end() );
00330         return ibcompacted - names.begin();
00331 }
00332 
00333 
00334 /*
00335  * Remove a buffer
00336  */
00337 void                    CSoundDriverAL::removeBuffer( IBuffer *buffer )
00338 {
00339         nlassert( buffer != NULL );
00340         CBufferAL *bufferAL = dynamic_cast<CBufferAL*>(buffer);
00341         if ( ! deleteItem( bufferAL->bufferName(), alDeleteBuffers, _Buffers ) )
00342         {
00343                 nlwarning( "Deleting buffer: name not found" );
00344         }
00345 }
00346 
00347 
00348 /*
00349  * Remove a source
00350  */
00351 void                    CSoundDriverAL::removeSource( ISource *source )
00352 {
00353         nlassert( source != NULL );
00354         CSourceAL *sourceAL = dynamic_cast<CSourceAL*>(source);
00355         if ( ! deleteItem( sourceAL->sourceName(), alDeleteSources, _Sources ) )
00356         {
00357                 nlwarning( "Deleting source: name not found" );
00358         }
00359 }
00360 
00361 
00362 /*
00363  * Delete a buffer or a source
00364  */
00365 bool                    CSoundDriverAL::deleteItem( ALuint name, TGenFunctionAL aldeletefunc, vector<ALuint>& names )
00366 {
00367         vector<ALuint>::iterator ibn = find( names.begin(), names.end(), name );
00368         if ( ibn == names.end() )
00369         {
00370                 return false;
00371         }
00372         aldeletefunc( 1, &*ibn );
00373         *ibn = AL_NONE;
00374         TestALError();
00375         return true;
00376 }
00377 
00378 
00379 /*
00380  * Create the listener instance
00381  */
00382 IListener               *CSoundDriverAL::createListener()
00383 {
00384         nlassert( CListenerAL::instance() == NULL );
00385         return new CListenerAL();
00386 }
00387 
00388 
00389 /*
00390  * Apply changes of rolloff factor to all sources
00391  */
00392 void                    CSoundDriverAL::applyRolloffFactor( float f )
00393 {
00394         _RolloffFactor = f;
00395         vector<ALuint>::iterator ibn;
00396         for ( ibn=_Sources.begin(); ibn!=_Sources.end(); ++ibn )
00397         {
00398                 alSourcef( *ibn, AL_ROLLOFF_FACTOR, _RolloffFactor );
00399         }
00400         TestALError();
00401 }
00402 
00403 
00404 /*
00405  * Helper for loadWavFile()
00406  */
00407 TSampleFormat ALtoNLSoundFormat( ALenum alformat )
00408 {
00409         switch ( alformat )
00410         {
00411         case AL_FORMAT_MONO8 : return Mono8;
00412         case AL_FORMAT_MONO16 : return Mono16;
00413         case AL_FORMAT_STEREO8 : return Stereo8;
00414         case AL_FORMAT_STEREO16 : return Stereo16;
00415         default : nlstop; return Mono8;
00416         }
00417 }
00418 
00419 
00420 /*
00421  * Temp
00422  */
00423 bool                    CSoundDriverAL::loadWavFile( IBuffer *destbuffer, const char *filename )
00424 {
00425 // Currently, the OpenAL UT headers are different between Windows and Linux versions
00426 #ifdef NL_OS_WINDOWS
00427         ALsizei size,freq;
00428         ALenum format;
00429         ALvoid *data;
00430         ALboolean loop;
00431         alutLoadWAVFile( const_cast<char*>(filename), &format, &data, &size, &freq, &loop ); // last arg in some al.h
00432 #else
00433         ALsizei bits;
00434         ALsizei size,freq;
00435         ALsizei format;
00436         ALvoid *data;
00437         alutLoadWAV( const_cast<char*>(filename), &data, &format, &size, &bits, &freq ); 
00438 #endif
00439         if ( data == NULL )
00440         {
00441                 return false;
00442         }
00443         destbuffer->setFormat( ALtoNLSoundFormat(format), freq );
00444         destbuffer->fillBuffer( data, size );
00445 #ifdef NL_OS_WINDOWS
00446         alutUnloadWAV(format,data,size,freq); // Where is it on Linux ?!?
00447 #endif
00448         return true;
00449 }
00450 
00451 
00452 } // NLSOUND