1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012-2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdopenal.h"
21 #include "sound_driver_al.h"
22 #include "buffer_al.h"
23 #include "listener_al.h"
24 #include "source_al.h"
26 #include "effect_al.h"
33 using namespace NLMISC
;
38 // Currently, the OpenAL headers are different between Windows and Linux versions !
39 // AL_INVALID_XXXX are part of the spec, though.
41 #define AL_INVALID_ENUM AL_ILLEGAL_ENUM
42 #define AL_INVALID_OPERATION AL_ILLEGAL_COMMAND
47 // Test error in debug mode
50 ALuint errcode
= alGetError();
53 case AL_NO_ERROR
: break;
54 case AL_INVALID_NAME
: nlerror("OpenAL: Invalid name");
55 case AL_INVALID_ENUM
: nlerror("OpenAL: Invalid enum");
56 case AL_INVALID_VALUE
: nlerror("OpenAL: Invalid value");
57 case AL_INVALID_OPERATION
: nlerror("OpenAL: Invalid operation");
58 case AL_OUT_OF_MEMORY
: nlerror("OpenAL: Out of memory");
59 default: nlerror("OpenAL: Unknown error %x", errcode
);
65 void alTestWarning(const char *src
)
67 ALuint errcode
= alGetError();
70 case AL_NO_ERROR
: break;
71 case AL_INVALID_NAME
: nlwarning("AL: Invalid Name parameter passed to AL call (%s)", src
); break;
72 case AL_INVALID_ENUM
: nlwarning("AL: Invalid parameter passed to AL call (%s)", src
); break;
73 case AL_INVALID_VALUE
: nlwarning("AL: Invalid enum parameter value (%s)", src
); break;
74 case AL_INVALID_OPERATION
: nlwarning("AL: Illegal call (%s)", src
); break;
75 case AL_OUT_OF_MEMORY
: nlerror("AL: Out of memory (%s)", src
); break;
80 #define INITIAL_BUFFERS 8
81 #define INITIAL_SOURCES 8
82 #define BUFFER_ALLOC_RATE 8
83 #define SOURCE_ALLOC_RATE 8
85 #define ROLLOFF_FACTOR_DEFAULT 1.0f
89 class CSoundDriverALNelLibrary
: public NLMISC::INelLibrary
91 void onLibraryLoaded(bool /* firstTime */) { }
92 void onLibraryUnloaded(bool /* lastTime */) { }
94 NLMISC_DECL_PURE_LIB(CSoundDriverALNelLibrary
)
96 #endif /* #ifndef NL_STATIC */
99 * Sound driver instance creation
108 // ******************************************************************
111 ISoundDriver
* createISoundDriverInstanceOpenAl
113 __declspec(dllexport
) ISoundDriver
*NLSOUND_createISoundDriverInstance
115 (ISoundDriver::IStringMapperProvider
*stringMapper
)
117 return new CSoundDriverAL(stringMapper
);
120 // ******************************************************************
123 uint32
interfaceVersionOpenAl()
125 __declspec(dllexport
) uint32
NLSOUND_interfaceVersion()
128 return ISoundDriver::InterfaceVersion
;
131 // ******************************************************************
134 void outputProfileOpenAl
136 __declspec(dllexport
) void NLSOUND_outputProfile
140 CSoundDriverAL::getInstance()->writeProfile(out
);
143 // ******************************************************************
146 ISoundDriver::TDriver
getDriverTypeOpenAl()
148 __declspec(dllexport
) ISoundDriver::TDriver
NLSOUND_getDriverType()
151 return ISoundDriver::DriverOpenAl
;
154 // ******************************************************************
160 #elif defined (NL_OS_UNIX)
168 ISoundDriver
* createISoundDriverInstanceOpenAl(ISoundDriver::IStringMapperProvider
*stringMapper
)
170 ISoundDriver
* NLSOUND_createISoundDriverInstance(ISoundDriver::IStringMapperProvider
*stringMapper
)
173 return new CSoundDriverAL(stringMapper
);
176 uint32
NLSOUND_interfaceVersion ()
178 return ISoundDriver::InterfaceVersion
;
190 CSoundDriverAL::CSoundDriverAL(ISoundDriver::IStringMapperProvider
*stringMapper
)
191 : _StringMapper(stringMapper
), _AlDevice(NULL
), _AlContext(NULL
),
192 _NbExpBuffers(0), _NbExpSources(0), _RolloffFactor(1.f
)
200 CSoundDriverAL::~CSoundDriverAL()
202 // WARNING: Only internal resources are released here,
203 // the created instances must still be released by the user!
205 // Remove the allocated (but not exported) source and buffer names-
206 // Release internal resources of all remaining ISource instances
207 if (!_Sources
.empty())
209 nlwarning("AL: _Sources.size(): '%u'", (uint32
)_Sources
.size());
210 set
<CSourceAL
*>::iterator
it(_Sources
.begin()), end(_Sources
.end());
211 for (; it
!= end
; ++it
) (*it
)->release(); // CSourceAL will be deleted by user
214 if (!_Buffers
.empty()) alDeleteBuffers(compactAliveNames(_Buffers
, alIsBuffer
), &*_Buffers
.begin());
215 // Release internal resources of all remaining IEffect instances
216 if (!_Effects
.empty())
218 nlwarning("AL: _Effects.size(): '%u'", (uint32
)_Effects
.size());
219 set
<CEffectAL
*>::iterator
it(_Effects
.begin()), end(_Effects
.end());
220 for (; it
!= end
; ++it
) (*it
)->release(); // CEffectAL will be deleted by user
225 if (_AlContext
) { alcDestroyContext(_AlContext
); _AlContext
= NULL
; }
226 if (_AlDevice
) { alcCloseDevice(_AlDevice
); _AlDevice
= NULL
; }
229 /// Return a list of available devices for the user. The value at index 0 is empty, and is used for automatic device selection.
230 void CSoundDriverAL::getDevices(std::vector
<std::string
> &devices
)
232 devices
.push_back(""); // empty
234 if (AlEnumerateAllExt
)
236 const ALchar
* deviceNames
= alcGetString(NULL
, ALC_ALL_DEVICES_SPECIFIER
);
237 // const ALchar* defaultDevice = NULL;
238 if(!strlen(deviceNames
))
240 nldebug("AL: No audio devices");
244 nldebug("AL: Listing devices: ");
245 while(deviceNames
&& *deviceNames
)
247 nldebug("AL: - %s", deviceNames
);
248 devices
.push_back(deviceNames
);
249 deviceNames
+= strlen(deviceNames
) + 1;
255 nldebug("AL: ALC_ENUMERATE_ALL_EXT not present");
259 static const ALchar
*getDeviceInternal(const std::string
&device
)
261 if (device
.empty()) return NULL
;
262 if (AlEnumerateAllExt
)
264 const ALchar
* deviceNames
= alcGetString(NULL
, ALC_ALL_DEVICES_SPECIFIER
);
265 if(!strlen(deviceNames
))
267 nldebug("AL: No audio devices");
271 while (deviceNames
&& *deviceNames
)
273 if (!strcmp(deviceNames
, device
.c_str()))
275 deviceNames
+= strlen(deviceNames
) + 1;
281 nldebug("AL: ALC_ENUMERATE_ALL_EXT not present");
283 nldebug("AL: Device '%s' not found", device
.c_str());
287 /// Initialize the driver with a user selected device. If device.empty(), the default or most appropriate device is used.
288 void CSoundDriverAL::initDevice(const std::string
&device
, ISoundDriver::TSoundOptions options
)
290 // list of supported options in this driver
291 // no adpcm, no manual rolloff (for now)
292 const sint supportedOptions
=
293 OptionEnvironmentEffects
294 | OptionSoftwareBuffer
295 | OptionManualRolloff
296 | OptionLocalBufferCopy
297 | OptionHasBufferStreaming
;
299 // list of forced options in this driver
300 const sint forcedOptions
= 0;
303 _Options
= (TSoundOptions
)(((sint
)options
& supportedOptions
) | forcedOptions
);
305 /* TODO: multichannel */
307 // OpenAL initialization
308 const ALchar
*dev
= getDeviceInternal(device
);
309 if (!dev
) dev
= alcGetString(NULL
, ALC_DEFAULT_DEVICE_SPECIFIER
);
310 nldebug("AL: Opening device: '%s'", dev
== NULL
? "NULL" : dev
);
311 _AlDevice
= alcOpenDevice(dev
);
312 if (!_AlDevice
) throw ESoundDriver("AL: Failed to open device");
313 nldebug("AL: ALC_DEVICE_SPECIFIER: '%s'", alcGetString(_AlDevice
, ALC_DEVICE_SPECIFIER
));
314 //int attrlist[] = { ALC_FREQUENCY, 48000,
315 // ALC_MONO_SOURCES, 12,
316 // ALC_STEREO_SOURCES, 4,
318 _AlContext
= alcCreateContext(_AlDevice
, NULL
); // attrlist);
319 if (!_AlContext
) { alcCloseDevice(_AlDevice
); throw ESoundDriver("AL: Failed to create context"); }
320 alcMakeContextCurrent(_AlContext
);
323 // Display version information
324 const ALchar
*alversion
, *alrenderer
, *alvendor
, *alext
;
325 alversion
= alGetString(AL_VERSION
);
326 alrenderer
= alGetString(AL_RENDERER
);
327 alvendor
= alGetString(AL_VENDOR
);
328 alext
= alGetString(AL_EXTENSIONS
);
330 nldebug("AL: AL_VERSION: '%s', AL_RENDERER: '%s', AL_VENDOR: '%s'", alversion
, alrenderer
, alvendor
);
331 nldebug("AL: AL_EXTENSIONS: %s", alext
);
333 // Load and display extensions
334 alExtInitDevice(_AlDevice
);
336 nlinfo("AL: EAX: %s, EAX-RAM: %s, ALC_EXT_EFX: %s",
337 AlExtEax
? "Present" : "Not available",
338 AlExtXRam
? "Present" : "Not available",
339 AlExtEfx
? "Present" : "Not available");
341 nldebug("AL: EAX-RAM: %s, ALC_EXT_EFX: %s",
342 AlExtXRam
? "Present" : "Not available",
343 AlExtEfx
? "Present" : "Not available");
347 nldebug("AL: Max. sources: %u, Max. effects: %u", (uint32
)countMaxSources(), (uint32
)countMaxEffects());
349 if (getOption(OptionEnvironmentEffects
))
353 nlwarning("AL: ALC_EXT_EFX is required, environment effects disabled");
354 _Options
= (TSoundOptions
)((uint
)_Options
& ~OptionEnvironmentEffects
);
356 else if (!countMaxEffects())
358 nlwarning("AL: No effects available, environment effects disabled");
359 _Options
= (TSoundOptions
)((uint
)_Options
& ~OptionEnvironmentEffects
);
363 // Choose the I3DL2 model (same as DirectSound3D if not manual)
364 if (getOption(OptionManualRolloff
)) alDistanceModel(AL_NONE
);
365 else alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED
);
368 // Initial buffers and sources allocation
369 allocateNewItems(alGenBuffers
, alIsBuffer
, _Buffers
, _NbExpBuffers
, INITIAL_BUFFERS
);
373 /// Return options that are enabled (including those that cannot be disabled on this driver).
374 ISoundDriver::TSoundOptions
CSoundDriverAL::getOptions()
379 /// Return if an option is enabled (including those that cannot be disabled on this driver).
380 bool CSoundDriverAL::getOption(ISoundDriver::TSoundOptions option
)
382 return ((uint
)_Options
& (uint
)option
) == (uint
)option
;
386 * Allocate nb new items
388 void CSoundDriverAL::allocateNewItems(TGenFunctionAL algenfunc
, TTestFunctionAL altestfunc
,
389 vector
<ALuint
>& names
, uint index
, uint nb
)
391 nlassert( index
== names
.size() );
392 names
.resize( index
+ nb
);
393 // FIXME assumption about inner workings of std::vector;
394 // &(names[...]) only works with "names.size() - nbalive == 1"
395 generateItems( algenfunc
, altestfunc
, nb
, &(names
[index
]) );
402 void ThrowGenException( TGenFunctionAL algenfunc
)
404 if ( algenfunc
== alGenBuffers
)
405 throw ESoundDriverGenBuf();
406 else if ( algenfunc
== alGenSources
)
407 throw ESoundDriverGenSrc();
413 * Generate nb buffers/sources
415 void CSoundDriverAL::generateItems( TGenFunctionAL algenfunc
, TTestFunctionAL altestfunc
, uint nb
, ALuint
*array
)
417 // array is actually a std::vector element address!
418 algenfunc( nb
, array
);
421 if ( alGetError() != AL_NO_ERROR
)
423 ThrowGenException( algenfunc
);
428 for ( i
=0; i
!=nb
; i
++ )
430 if ( ! altestfunc( array
[i
] ) )
432 ThrowGenException( algenfunc
);
438 * Create a sound buffer
440 IBuffer
*CSoundDriverAL::createBuffer()
442 CBufferAL
*buffer
= new CBufferAL(createItem(alGenBuffers
, alIsBuffer
, _Buffers
, _NbExpBuffers
, BUFFER_ALLOC_RATE
));
450 ISource
*CSoundDriverAL::createSource()
452 CSourceAL
*sourceAl
= new CSourceAL(this);
453 _Sources
.insert(sourceAl
);
457 /// Create a reverb effect
458 IReverbEffect
*CSoundDriverAL::createReverbEffect()
460 IReverbEffect
*ieffect
= NULL
;
461 CEffectAL
*effectal
= NULL
;
463 ALuint slot
= AL_NONE
;
464 alGenAuxiliaryEffectSlots(1, &slot
);
465 if (alGetError() != AL_NO_ERROR
)
467 nlwarning("AL: alGenAuxiliaryEffectSlots failed");
471 ALuint effect
= AL_NONE
;
472 alGenEffects(1, &effect
);
473 if (alGetError() != AL_NO_ERROR
)
475 nlwarning("AL: alGenEffects failed");
476 alDeleteAuxiliaryEffectSlots(1, &slot
);
477 return NULL
; /* createEffect */
480 #if EFX_CREATIVE_AVAILABLE
481 alEffecti(effect
, AL_EFFECT_TYPE
, AL_EFFECT_EAXREVERB
);
482 if (alGetError() != AL_NO_ERROR
)
484 nlinfo("AL: Creative Reverb Effect not supported, falling back to standard Reverb Effect");
488 alAuxiliaryEffectSloti(slot
, AL_EFFECTSLOT_EFFECT
, effect
); alTestError();
489 alAuxiliaryEffectSloti(slot
, AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
, AL_TRUE
); alTestError(); // auto only for reverb!
490 CCreativeReverbEffectAL
*eff
= new CCreativeReverbEffectAL(this, effect
, slot
);
491 ieffect
= static_cast<IReverbEffect
*>(eff
);
492 effectal
= static_cast<CEffectAL
*>(eff
);
493 nlassert(ieffect
); nlassert(effectal
);
494 _Effects
.insert(effectal
);
499 alEffecti(effect
, AL_EFFECT_TYPE
, AL_EFFECT_REVERB
);
500 if (alGetError() != AL_NO_ERROR
)
502 nlwarning("AL: Reverb Effect not supported");
503 alDeleteAuxiliaryEffectSlots(1, &slot
);
504 alDeleteEffects(1, &effect
);
505 return NULL
; /* createEffect */
509 alAuxiliaryEffectSloti(slot
, AL_EFFECTSLOT_EFFECT
, effect
); alTestError();
510 alAuxiliaryEffectSloti(slot
, AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
, AL_TRUE
); alTestError(); // auto only for reverb!
511 CStandardReverbEffectAL
*eff
= new CStandardReverbEffectAL(this, effect
, slot
);
512 ieffect
= static_cast<IReverbEffect
*>(eff
);
513 effectal
= static_cast<CEffectAL
*>(eff
);
514 nlassert(ieffect
); nlassert(effectal
);
515 _Effects
.insert(effectal
);
521 static uint
getMaxNumSourcesInternal()
524 memset(sources
, 0, sizeofarray(sources
));
525 uint sourceCount
= 0;
529 for (; sourceCount
< 256; ++sourceCount
)
531 alGenSources(1, &sources
[sourceCount
]);
532 if (alGetError() != AL_NO_ERROR
)
536 alDeleteSources(sourceCount
, sources
);
537 if (alGetError() != AL_NO_ERROR
)
539 for (uint i
= 0; i
< 256; i
++)
541 alDeleteSources(1, &sources
[i
]);
550 /// Return the maximum number of sources that can created
551 uint
CSoundDriverAL::countMaxSources()
554 // software allows 256 sources (software audio ftw!)
555 // cheap openal cards 32, expensive openal cards 128
556 // trying to go too high is safely handled anyways
557 return getMaxNumSourcesInternal() + (uint
)_Sources
.size();
560 /// Return the maximum number of effects that can be created, which is only 1 in openal software mode :(
561 uint
CSoundDriverAL::countMaxEffects()
563 if (!getOption(OptionEnvironmentEffects
)) return 0;
564 if (!AlExtEfx
) return 0;
565 ALCint max_auxiliary_sends
;
566 alcGetIntegerv(_AlDevice
, ALC_MAX_AUXILIARY_SENDS
, 1, &max_auxiliary_sends
);
567 return (uint
)max_auxiliary_sends
;
571 * Create a sound buffer or a sound source
573 ALuint
CSoundDriverAL::createItem(TGenFunctionAL algenfunc
, TTestFunctionAL altestfunc
,
574 vector
<ALuint
>& names
, uint
& index
, uint allocrate
)
576 nlassert( index
<= names
.size() );
577 if ( index
== names
.size() )
579 // Generate new items
580 uint nbalive
= compactAliveNames( names
, altestfunc
);
581 if ( nbalive
== names
.size() )
583 // Extend vector of names
584 // FIXME? assumption about inner workings of std::vector
585 allocateNewItems( algenfunc
, altestfunc
, names
, index
, allocrate
);
589 // Take the room of the deleted names
590 nlassert(nbalive
< names
.size());
592 // FIXME assumption about inner workings of std::vector;
593 // &(names[...]) only works with "names.size() - nbalive == 1"
594 generateItems(algenfunc
, altestfunc
, (uint
)names
.size() - nbalive
, &(names
[nbalive
]));
598 // Return the name of the item
599 nlassert( index
< names
.size() );
600 ALuint itemname
= names
[index
];
607 * Remove names of deleted buffers and return the number of valid buffers
609 uint
CSoundDriverAL::compactAliveNames( vector
<ALuint
>& names
, TTestFunctionAL altestfunc
)
611 vector
<ALuint
>::iterator iball
, ibcompacted
;
612 for ( iball
=names
.begin(), ibcompacted
=names
.begin(); iball
!=names
.end(); ++iball
)
614 // iball is incremented every iteration
615 // ibcompacted is not incremented if a buffer is not valid anymore
616 if ( altestfunc( *iball
) )
618 *ibcompacted
= *iball
;
622 nlassert( ibcompacted
<= names
.end() );
623 return (uint
)(ibcompacted
- names
.begin());
627 void CSoundDriverAL::commit3DChanges()
629 // Sync up sources & listener 3d position.
630 if (getOption(OptionManualRolloff
))
632 for (std::set
<CSourceAL
*>::iterator
it(_Sources
.begin()), end(_Sources
.end()); it
!= end
; ++it
)
633 (*it
)->updateManualRolloff();
637 /// Write information about the driver to the output stream.
638 void CSoundDriverAL::writeProfile(std::string
& out
)
640 out
= toString("OpenAL\n");
641 out
+= toString("Source size: %u\n", (uint32
)_Sources
.size());
642 out
+= toString("Effects size: %u\n", (uint32
)_Effects
.size());
644 // TODO: write other useful information like OpenAL version and supported extensions
647 // Does not create a sound loader .. what does it do then?
648 void CSoundDriverAL::startBench()
650 NLMISC::CHTimer::startBench();
653 void CSoundDriverAL::endBench()
655 NLMISC::CHTimer::endBench();
658 void CSoundDriverAL::displayBench(NLMISC::CLog
*log
)
660 NLMISC::CHTimer::displayHierarchicalByExecutionPathSorted(log
, CHTimer::TotalTime
, true, 48, 2);
661 NLMISC::CHTimer::displayHierarchical(log
, true, 48, 2);
662 NLMISC::CHTimer::displayByExecutionPath(log
, CHTimer::TotalTime
);
663 NLMISC::CHTimer::display(log
, CHTimer::TotalTime
);
667 void CSoundDriverAL::removeBuffer(CBufferAL
*buffer
)
669 nlassert(buffer
!= NULL
);
670 if (!deleteItem( buffer
->bufferName(), alDeleteBuffers
, _Buffers
))
671 nlwarning("AL: Deleting buffer: name not found");
675 void CSoundDriverAL::removeSource(CSourceAL
*source
)
677 if (_Sources
.find(source
) != _Sources
.end()) _Sources
.erase(source
);
678 else nlwarning("AL: removeSource already called");
682 void CSoundDriverAL::removeEffect(CEffectAL
*effect
)
684 if (_Effects
.find(effect
) != _Effects
.end()) _Effects
.erase(effect
);
685 else nlwarning("AL: removeEffect already called");
688 /// Delete a buffer or a source
689 bool CSoundDriverAL::deleteItem( ALuint name
, TDeleteFunctionAL aldeletefunc
, vector
<ALuint
>& names
)
691 vector
<ALuint
>::iterator ibn
= find( names
.begin(), names
.end(), name
);
692 if ( ibn
== names
.end() )
696 aldeletefunc( 1, &*ibn
);
702 /// Create the listener instance
703 IListener
*CSoundDriverAL::createListener()
705 nlassert(!CListenerAL::isInitialized());
706 return new CListenerAL();
709 /// Apply changes of rolloff factor to all sources
710 void CSoundDriverAL::applyRolloffFactor( float f
)
713 if (!getOption(OptionManualRolloff
))
715 set
<CSourceAL
*>::iterator
it(_Sources
.begin()), end(_Sources
.end());
716 for (; it
!= end
; ++it
) alSourcef((*it
)->getSource(), AL_ROLLOFF_FACTOR
, _RolloffFactor
);
721 /// Helper for loadWavFile()
722 TSampleFormat
ALtoNLSoundFormat( ALenum alformat
)
726 case AL_FORMAT_MONO8
: return Mono8
;
727 case AL_FORMAT_MONO16
: return Mono16
;
728 case AL_FORMAT_STEREO8
: return Stereo8
;
729 case AL_FORMAT_STEREO16
: return Stereo16
;
730 default : nlstop
; return Mono8
;