Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / sound / driver / openal / sound_driver_al.cpp
blobe0e478e18df7758c2db8d6010fea0d03ccd6952d
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012-2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
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"
25 #include "ext_al.h"
26 #include "effect_al.h"
28 #ifdef DEBUG_NEW
29 #define new DEBUG_NEW
30 #endif
32 using namespace std;
33 using namespace NLMISC;
35 namespace NLSOUND
38 // Currently, the OpenAL headers are different between Windows and Linux versions !
39 // AL_INVALID_XXXX are part of the spec, though.
40 /*#ifdef NL_OS_UNIX
41 #define AL_INVALID_ENUM AL_ILLEGAL_ENUM
42 #define AL_INVALID_OPERATION AL_ILLEGAL_COMMAND
43 #endif*/
46 #ifdef NL_DEBUG
47 // Test error in debug mode
48 void alTestError()
50 ALuint errcode = alGetError();
51 switch (errcode)
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);
62 #endif
64 #if !FINAL_VERSION
65 void alTestWarning(const char *src)
67 ALuint errcode = alGetError();
68 switch (errcode)
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;
78 #endif
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
87 #ifndef NL_STATIC
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
101 #ifdef NL_OS_WINDOWS
102 #ifdef NL_COMP_MINGW
103 #ifndef NL_STATIC
104 extern "C"
106 #endif
107 #endif
108 // ******************************************************************
110 #ifdef NL_STATIC
111 ISoundDriver* createISoundDriverInstanceOpenAl
112 #else
113 __declspec(dllexport) ISoundDriver *NLSOUND_createISoundDriverInstance
114 #endif
115 (ISoundDriver::IStringMapperProvider *stringMapper)
117 return new CSoundDriverAL(stringMapper);
120 // ******************************************************************
122 #ifdef NL_STATIC
123 uint32 interfaceVersionOpenAl()
124 #else
125 __declspec(dllexport) uint32 NLSOUND_interfaceVersion()
126 #endif
128 return ISoundDriver::InterfaceVersion;
131 // ******************************************************************
133 #ifdef NL_STATIC
134 void outputProfileOpenAl
135 #else
136 __declspec(dllexport) void NLSOUND_outputProfile
137 #endif
138 (string &out)
140 CSoundDriverAL::getInstance()->writeProfile(out);
143 // ******************************************************************
145 #ifdef NL_STATIC
146 ISoundDriver::TDriver getDriverTypeOpenAl()
147 #else
148 __declspec(dllexport) ISoundDriver::TDriver NLSOUND_getDriverType()
149 #endif
151 return ISoundDriver::DriverOpenAl;
154 // ******************************************************************
155 #ifdef NL_COMP_MINGW
156 #ifndef NL_STATIC
158 #endif
159 #endif
160 #elif defined (NL_OS_UNIX)
162 #ifndef NL_STATIC
163 extern "C"
165 #endif
167 #ifdef NL_STATIC
168 ISoundDriver* createISoundDriverInstanceOpenAl(ISoundDriver::IStringMapperProvider *stringMapper)
169 #else
170 ISoundDriver* NLSOUND_createISoundDriverInstance(ISoundDriver::IStringMapperProvider *stringMapper)
171 #endif
173 return new CSoundDriverAL(stringMapper);
176 uint32 NLSOUND_interfaceVersion ()
178 return ISoundDriver::InterfaceVersion;
181 #ifndef NL_STATIC
183 #endif
185 #endif // NL_OS_UNIX
188 * Constructor
190 CSoundDriverAL::CSoundDriverAL(ISoundDriver::IStringMapperProvider *stringMapper)
191 : _StringMapper(stringMapper), _AlDevice(NULL), _AlContext(NULL),
192 _NbExpBuffers(0), _NbExpSources(0), _RolloffFactor(1.f)
194 alExtInit();
198 * Destructor
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
212 _Sources.clear();
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
221 _Effects.clear();
224 // OpenAL exit
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");
242 else
244 nldebug("AL: Listing devices: ");
245 while(deviceNames && *deviceNames)
247 nldebug("AL: - %s", deviceNames);
248 devices.push_back(deviceNames);
249 deviceNames += strlen(deviceNames) + 1;
253 else
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");
269 else
271 while (deviceNames && *deviceNames)
273 if (!strcmp(deviceNames, device.c_str()))
274 return deviceNames;
275 deviceNames += strlen(deviceNames) + 1;
279 else
281 nldebug("AL: ALC_ENUMERATE_ALL_EXT not present");
283 nldebug("AL: Device '%s' not found", device.c_str());
284 return NULL;
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;
302 // set the options
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,
317 // ALC_INVALID };
318 _AlContext = alcCreateContext(_AlDevice, NULL); // attrlist);
319 if (!_AlContext) { alcCloseDevice(_AlDevice); throw ESoundDriver("AL: Failed to create context"); }
320 alcMakeContextCurrent(_AlContext);
321 alTestError();
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);
329 alTestError();
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);
335 #if EAX_AVAILABLE
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");
340 #else
341 nldebug("AL: EAX-RAM: %s, ALC_EXT_EFX: %s",
342 AlExtXRam ? "Present" : "Not available",
343 AlExtEfx ? "Present" : "Not available");
344 #endif
345 alTestError();
347 nldebug("AL: Max. sources: %u, Max. effects: %u", (uint32)countMaxSources(), (uint32)countMaxEffects());
349 if (getOption(OptionEnvironmentEffects))
351 if (!AlExtEfx)
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);
366 alTestError();
368 // Initial buffers and sources allocation
369 allocateNewItems(alGenBuffers, alIsBuffer, _Buffers, _NbExpBuffers, INITIAL_BUFFERS);
370 alTestError();
373 /// Return options that are enabled (including those that cannot be disabled on this driver).
374 ISoundDriver::TSoundOptions CSoundDriverAL::getOptions()
376 return _Options;
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]) );
400 * throwGenException
402 void ThrowGenException( TGenFunctionAL algenfunc )
404 if ( algenfunc == alGenBuffers )
405 throw ESoundDriverGenBuf();
406 else if ( algenfunc == alGenSources )
407 throw ESoundDriverGenSrc();
408 else
409 nlstop;
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 );
420 // Error handling
421 if ( alGetError() != AL_NO_ERROR )
423 ThrowGenException( algenfunc );
426 // Check buffers
427 uint i;
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));
443 return buffer;
448 * Create a source
450 ISource *CSoundDriverAL::createSource()
452 CSourceAL *sourceAl = new CSourceAL(this);
453 _Sources.insert(sourceAl);
454 return 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");
468 return NULL;
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");
486 else
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);
495 return ieffect;
497 #endif
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 */
507 else
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);
516 return ieffect;
521 static uint getMaxNumSourcesInternal()
523 ALuint sources[256];
524 memset(sources, 0, sizeofarray(sources));
525 uint sourceCount = 0;
527 alGetError();
529 for (; sourceCount < 256; ++sourceCount)
531 alGenSources(1, &sources[sourceCount]);
532 if (alGetError() != AL_NO_ERROR)
533 break;
536 alDeleteSources(sourceCount, sources);
537 if (alGetError() != AL_NO_ERROR)
539 for (uint i = 0; i < 256; i++)
541 alDeleteSources(1, &sources[i]);
545 alGetError();
547 return sourceCount;
550 /// Return the maximum number of sources that can created
551 uint CSoundDriverAL::countMaxSources()
553 // ALC_MONO_SOURCES
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 );
587 else
589 // Take the room of the deleted names
590 nlassert(nbalive < names.size());
591 index = nbalive;
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];
601 index++;
602 return itemname;
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;
619 ++ibcompacted;
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);
666 /// Remove a buffer
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");
674 /// Remove a source
675 void CSoundDriverAL::removeSource(CSourceAL *source)
677 if (_Sources.find(source) != _Sources.end()) _Sources.erase(source);
678 else nlwarning("AL: removeSource already called");
681 /// Remove an effect
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() )
694 return false;
696 aldeletefunc( 1, &*ibn );
697 *ibn = AL_NONE;
698 alTestError();
699 return true;
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 )
712 _RolloffFactor = 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);
717 alTestError();
721 /// Helper for loadWavFile()
722 TSampleFormat ALtoNLSoundFormat( ALenum alformat )
724 switch ( 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;
734 } // NLSOUND