1 /*
2  * ALURE  OpenAL utility library
3  * Copyright (c) 2009 by Chris Robinson.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 /* Title: Main and Miscellanious */
25 
26 #include "config.h"
27 
28 #include "main.h"
29 
30 #include <string.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <time.h>
34 #ifdef HAVE_WINDOWS_H
35 #include <windows.h>
36 #endif
37 
38 #include <vector>
39 #include <string>
40 #include <map>
41 
42 std::map<ALint,UserCallbacks> InstalledCallbacks;
43 CRITICAL_SECTION cs_StreamPlay;
44 alureStream::ListType alureStream::StreamList;
45 
46 PFNALCSETTHREADCONTEXTPROC palcSetThreadContext;
47 PFNALCGETTHREADCONTEXTPROC palcGetThreadContext;
48 
49 
50 template<typename T>
LoadALCProc(ALCdevice * dev,const char * name,T ** ptr)51 static inline void LoadALCProc(ALCdevice *dev, const char *name, T **ptr)
52 { *ptr = reinterpret_cast<T*>(alcGetProcAddress(dev, name)); }
53 
54 
55 #ifdef HAVE_GCC_CONSTRUCTOR
56 static void init_alure(void) __attribute__((constructor));
57 static void deinit_alure(void) __attribute__((destructor));
58 static struct MyConstructorClass {
~MyConstructorClassMyConstructorClass59     ~MyConstructorClass()
60     { alureStream::Clear(); };
61 } MyConstructor;
62 #elif defined(_WIN32) && !defined(ALURE_STATIC_LIBRARY)
63 static void init_alure(void);
64 static void deinit_alure(void);
65 static struct MyConstructorClass {
~MyConstructorClassMyConstructorClass66     ~MyConstructorClass()
67     { alureStream::Clear(); };
68 } MyConstructor;
69 
DllMain(HINSTANCE module,DWORD reason,LPVOID)70 extern "C" BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/)
71 {
72     // Perform actions based on the reason for calling.
73     switch(reason)
74     {
75         case DLL_PROCESS_ATTACH:
76             DisableThreadLibraryCalls(module);
77             init_alure();
78             break;
79 
80         case DLL_PROCESS_DETACH:
81             deinit_alure();
82             break;
83     }
84     return TRUE;
85 }
86 #else
87 static void init_alure(void);
88 static void deinit_alure(void);
89 
90 static struct MyConstructorClass {
MyConstructorClassMyConstructorClass91     MyConstructorClass()
92     { init_alure(); };
~MyConstructorClassMyConstructorClass93     ~MyConstructorClass()
94     { alureStream::Clear();
95       deinit_alure(); };
96 } MyConstructor;
97 #endif
98 
init_alure(void)99 static void init_alure(void)
100 {
101     InitializeCriticalSection(&cs_StreamPlay);
102 
103     // These calls actually just return references to the codecs' Decoder
104     // objects. They aren't really used for anything other than to prevent the
105     // compiler from removing the codec initializers.
106     alure_init_wav();
107     alure_init_aiff();
108 #ifdef HAS_VORBISFILE
109     alure_init_vorbisfile();
110 #endif
111 #ifdef HAS_FLAC
112     alure_init_flac();
113 #endif
114 #ifdef HAS_SNDFILE
115     alure_init_sndfile();
116 #endif
117 #ifdef HAS_FLUIDSYNTH
118     alure_init_fluidsynth();
119 #endif
120 #ifdef HAS_DUMB
121     alure_init_dumb();
122 #endif
123 #ifdef HAS_MODPLUG
124     alure_init_modplug();
125 #endif
126 #ifdef HAS_MPG123
127     alure_init_mpg123();
128 #endif
129 
130     if(alcIsExtensionPresent(NULL, "ALC_EXT_thread_local_context"))
131     {
132         LoadALCProc(NULL, "alcSetThreadContext", &palcSetThreadContext);
133         LoadALCProc(NULL, "alcGetThreadContext", &palcGetThreadContext);
134         if(!palcSetThreadContext || !palcGetThreadContext)
135         {
136             fprintf(stderr, "Alure lib: ALC_EXT_thread_local_context advertised, but missing function:\n"
137                             "    alcSetThreadContext=%p\n"
138                             "    alcGetThreadContext=%p\n",
139                             palcSetThreadContext, palcGetThreadContext);
140             palcSetThreadContext = NULL;
141             palcGetThreadContext = NULL;
142         }
143     }
144 }
145 
deinit_alure(void)146 static void deinit_alure(void)
147 {
148     alureUpdateInterval(0.0f);
149     DeleteCriticalSection(&cs_StreamPlay);
150 }
151 
152 
153 #ifndef HAVE_WINDOWS_H
154 
EnterCriticalSection(CRITICAL_SECTION * cs)155 void EnterCriticalSection(CRITICAL_SECTION *cs)
156 {
157     int ret;
158     ret = pthread_mutex_lock(cs);
159     assert(ret == 0);
160 }
LeaveCriticalSection(CRITICAL_SECTION * cs)161 void LeaveCriticalSection(CRITICAL_SECTION *cs)
162 {
163     int ret;
164     ret = pthread_mutex_unlock(cs);
165     assert(ret == 0);
166 }
InitializeCriticalSection(CRITICAL_SECTION * cs)167 void InitializeCriticalSection(CRITICAL_SECTION *cs)
168 {
169     pthread_mutexattr_t attrib;
170     int ret;
171 
172     ret = pthread_mutexattr_init(&attrib);
173     assert(ret == 0);
174 
175     ret = pthread_mutexattr_settype(&attrib, PTHREAD_MUTEX_RECURSIVE);
176 #ifdef HAVE_PTHREAD_NP_H
177     if(ret != 0)
178         ret = pthread_mutexattr_setkind_np(&attrib, PTHREAD_MUTEX_RECURSIVE);
179 #endif
180     assert(ret == 0);
181     ret = pthread_mutex_init(cs, &attrib);
182     assert(ret == 0);
183 
184     pthread_mutexattr_destroy(&attrib);
185 }
DeleteCriticalSection(CRITICAL_SECTION * cs)186 void DeleteCriticalSection(CRITICAL_SECTION *cs)
187 {
188     int ret;
189     ret = pthread_mutex_destroy(cs);
190     assert(ret == 0);
191 }
192 
193 #endif
194 
195 #ifdef DYNLOAD
196 #ifdef _WIN32
OpenLib(const char * libname)197 void *OpenLib(const char *libname)
198 { return LoadLibraryA(libname); }
CloseLib(void * hdl)199 void CloseLib(void *hdl)
200 { FreeLibrary((HINSTANCE)hdl); }
GetLibProc(void * hdl,const char * funcname)201 void *GetLibProc(void *hdl, const char *funcname)
202 { return (void*)GetProcAddress((HINSTANCE)hdl, funcname); }
203 #else
OpenLib(const char * libname)204 void *OpenLib(const char *libname)
205 {
206     const char *err = dlerror();
207     void *hdl = dlopen(libname, RTLD_NOW);
208     if((err=dlerror()) != NULL)
209     {
210         fprintf(stderr, "Error loading %s: %s\n", libname, err);
211         return NULL;
212     }
213     return hdl;
214 }
GetLibProc(void * hdl,const char * funcname)215 void *GetLibProc(void *hdl, const char *funcname)
216 {
217     const char *err = dlerror();
218     void *fn = dlsym(hdl, funcname);
219     if((err=dlerror()) != NULL)
220     {
221         fprintf(stderr, "Error loading %s: %s\n", funcname, err);
222         return NULL;
223     }
224     return fn;
225 }
CloseLib(void * hdl)226 void CloseLib(void *hdl)
227 { dlclose(hdl); }
228 #endif
229 #endif
230 
231 
232 static const ALchar *last_error = "No error";
SetError(const char * err)233 void SetError(const char *err)
234 {
235     last_error = err;
236 }
237 
238 
DetectBlockAlignment(ALenum format)239 ALuint DetectBlockAlignment(ALenum format)
240 {
241     switch(format)
242     {
243 #define CHECK_RET(f,s) case (f): return (s)
244         CHECK_RET(AL_FORMAT_MONO8, sizeof(ALubyte));
245         CHECK_RET(AL_FORMAT_MONO16, sizeof(ALshort));
246         CHECK_RET(AL_FORMAT_MONO_FLOAT32, sizeof(ALfloat));
247         CHECK_RET(AL_FORMAT_MONO_DOUBLE_EXT, sizeof(ALdouble));
248         CHECK_RET(AL_FORMAT_MONO_MULAW, sizeof(ALubyte)*1);
249 
250         CHECK_RET(AL_FORMAT_STEREO8, sizeof(ALubyte)*2);
251         CHECK_RET(AL_FORMAT_STEREO16, sizeof(ALshort)*2);
252         CHECK_RET(AL_FORMAT_STEREO_FLOAT32, sizeof(ALfloat)*2);
253         CHECK_RET(AL_FORMAT_STEREO_DOUBLE_EXT, sizeof(ALdouble)*2);
254         CHECK_RET(AL_FORMAT_STEREO_MULAW, sizeof(ALubyte)*2);
255 
256         CHECK_RET(AL_FORMAT_QUAD8, sizeof(ALubyte)*4);
257         CHECK_RET(AL_FORMAT_QUAD16, sizeof(ALshort)*4);
258         CHECK_RET(AL_FORMAT_QUAD32, sizeof(ALfloat)*4);
259         CHECK_RET(AL_FORMAT_QUAD_MULAW, sizeof(ALubyte)*4);
260 
261         CHECK_RET(AL_FORMAT_REAR8, sizeof(ALubyte)*2);
262         CHECK_RET(AL_FORMAT_REAR16, sizeof(ALshort)*2);
263         CHECK_RET(AL_FORMAT_REAR32, sizeof(ALfloat)*2);
264         CHECK_RET(AL_FORMAT_REAR_MULAW, sizeof(ALubyte)*2);
265 
266         CHECK_RET(AL_FORMAT_51CHN8, sizeof(ALubyte)*6);
267         CHECK_RET(AL_FORMAT_51CHN16, sizeof(ALshort)*6);
268         CHECK_RET(AL_FORMAT_51CHN32, sizeof(ALfloat)*6);
269         CHECK_RET(AL_FORMAT_51CHN_MULAW, sizeof(ALubyte)*6);
270 
271         CHECK_RET(AL_FORMAT_61CHN8, sizeof(ALubyte)*7);
272         CHECK_RET(AL_FORMAT_61CHN16, sizeof(ALshort)*7);
273         CHECK_RET(AL_FORMAT_61CHN32, sizeof(ALfloat)*7);
274         CHECK_RET(AL_FORMAT_61CHN_MULAW, sizeof(ALubyte)*7);
275 
276         CHECK_RET(AL_FORMAT_71CHN8, sizeof(ALubyte)*8);
277         CHECK_RET(AL_FORMAT_71CHN16, sizeof(ALshort)*8);
278         CHECK_RET(AL_FORMAT_71CHN32, sizeof(ALfloat)*8);
279         CHECK_RET(AL_FORMAT_71CHN_MULAW, sizeof(ALubyte)*8);
280 
281         CHECK_RET(AL_FORMAT_MONO_IMA4, 36);
282         CHECK_RET(AL_FORMAT_STEREO_IMA4, 36*2);
283 #undef CHECK_RET
284     }
285     return 0;
286 }
287 
DetectCompressionRate(ALenum format)288 ALuint DetectCompressionRate(ALenum format)
289 {
290     switch(format)
291     {
292     case AL_FORMAT_MONO8:
293     case AL_FORMAT_MONO16:
294     case AL_FORMAT_MONO_FLOAT32:
295     case AL_FORMAT_MONO_DOUBLE_EXT:
296     case AL_FORMAT_STEREO8:
297     case AL_FORMAT_STEREO16:
298     case AL_FORMAT_STEREO_FLOAT32:
299     case AL_FORMAT_STEREO_DOUBLE_EXT:
300     case AL_FORMAT_QUAD8:
301     case AL_FORMAT_QUAD16:
302     case AL_FORMAT_QUAD32:
303     case AL_FORMAT_REAR8:
304     case AL_FORMAT_REAR16:
305     case AL_FORMAT_REAR32:
306     case AL_FORMAT_51CHN8:
307     case AL_FORMAT_51CHN16:
308     case AL_FORMAT_51CHN32:
309     case AL_FORMAT_61CHN8:
310     case AL_FORMAT_61CHN16:
311     case AL_FORMAT_61CHN32:
312     case AL_FORMAT_71CHN8:
313     case AL_FORMAT_71CHN16:
314     case AL_FORMAT_71CHN32:
315         return 1;
316 
317     case AL_FORMAT_MONO_MULAW:
318     case AL_FORMAT_STEREO_MULAW:
319     case AL_FORMAT_QUAD_MULAW:
320     case AL_FORMAT_REAR_MULAW:
321     case AL_FORMAT_51CHN_MULAW:
322     case AL_FORMAT_61CHN_MULAW:
323     case AL_FORMAT_71CHN_MULAW:
324         return 1;
325 
326     case AL_FORMAT_MONO_IMA4:
327     case AL_FORMAT_STEREO_IMA4:
328         return 65;
329     }
330     fprintf(stderr, "Alure lib: Unhandled format: %#x\n", format);
331     return 0;
332 }
333 
GetSampleFormat(ALuint channels,ALuint bits,bool isFloat)334 ALenum GetSampleFormat(ALuint channels, ALuint bits, bool isFloat)
335 {
336 #define CHECK_FMT_RET(f) do {                                                 \
337     ALenum fmt = alGetEnumValue(#f);                                          \
338     if(alGetError() == AL_NO_ERROR && fmt != 0 && fmt != -1)                  \
339         return fmt;                                                           \
340 } while(0)
341     if(!isFloat)
342     {
343         if(bits == 8)
344         {
345             if(channels == 1) CHECK_FMT_RET(AL_FORMAT_MONO8);
346             if(channels == 2) CHECK_FMT_RET(AL_FORMAT_STEREO8);
347             if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
348             {
349                 if(channels == 4) CHECK_FMT_RET(AL_FORMAT_QUAD8);
350                 if(channels == 6) CHECK_FMT_RET(AL_FORMAT_51CHN8);
351                 if(channels == 7) CHECK_FMT_RET(AL_FORMAT_61CHN8);
352                 if(channels == 8) CHECK_FMT_RET(AL_FORMAT_71CHN8);
353             }
354             if(alIsExtensionPresent("AL_LOKI_quadriphonic"))
355             {
356                 if(channels == 4) CHECK_FMT_RET(AL_FORMAT_QUAD8_LOKI);
357             }
358             SetError("Unsupported 8-bit channel count\n");
359             return AL_NONE;
360         }
361         if(bits == 16)
362         {
363             if(channels == 1) CHECK_FMT_RET(AL_FORMAT_MONO16);
364             if(channels == 2) CHECK_FMT_RET(AL_FORMAT_STEREO16);
365             if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
366             {
367                 if(channels == 4) CHECK_FMT_RET(AL_FORMAT_QUAD16);
368                 if(channels == 6) CHECK_FMT_RET(AL_FORMAT_51CHN16);
369                 if(channels == 7) CHECK_FMT_RET(AL_FORMAT_61CHN16);
370                 if(channels == 8) CHECK_FMT_RET(AL_FORMAT_71CHN16);
371             }
372             if(alIsExtensionPresent("AL_LOKI_quadriphonic"))
373             {
374                 if(channels == 4) CHECK_FMT_RET(AL_FORMAT_QUAD16_LOKI);
375             }
376             SetError("Unsupported 16-bit channel count\n");
377             return AL_NONE;
378         }
379         SetError("Unsupported PCM bit depth\n");
380         return AL_NONE;
381     }
382 
383     if(bits == 32 && alIsExtensionPresent("AL_EXT_FLOAT32"))
384     {
385         if(channels == 1) CHECK_FMT_RET(AL_FORMAT_MONO_FLOAT32);
386         if(channels == 2) CHECK_FMT_RET(AL_FORMAT_STEREO_FLOAT32);
387         if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
388         {
389             if(channels == 4) CHECK_FMT_RET(AL_FORMAT_QUAD32);
390             if(channels == 6) CHECK_FMT_RET(AL_FORMAT_51CHN32);
391             if(channels == 7) CHECK_FMT_RET(AL_FORMAT_61CHN32);
392             if(channels == 8) CHECK_FMT_RET(AL_FORMAT_71CHN32);
393         }
394         SetError("Unsupported float32 channel count\n");
395         return AL_NONE;
396     }
397     if(bits == 64 && alIsExtensionPresent("AL_EXT_DOUBLE"))
398     {
399         if(channels == 1) CHECK_FMT_RET(AL_FORMAT_MONO_DOUBLE_EXT);
400         if(channels == 2) CHECK_FMT_RET(AL_FORMAT_STEREO_DOUBLE_EXT);
401         SetError("Unsupported double channel count\n");
402         return AL_NONE;
403     }
404 #undef CHECK_FMT_RET
405 
406     SetError("Unsupported float bit depth\n");
407     return AL_NONE;
408 }
409 
410 extern "C" {
411 
412 /* Function: alureGetVersion
413  *
414  * Stores the major and minor version of the library. If either major or minor
415  * are NULL, that value is not provided.
416  */
alureGetVersion(ALuint * major,ALuint * minor)417 ALURE_API void ALURE_APIENTRY alureGetVersion(ALuint *major, ALuint *minor)
418 {
419     if(major) *major = ALURE_VER_MAJOR;
420     if(minor) *minor = ALURE_VER_MINOR;
421 }
422 
423 /* Function: alureGetErrorString
424  *
425  * Returns a string describing the last error encountered.
426  */
alureGetErrorString(void)427 ALURE_API const ALchar* ALURE_APIENTRY alureGetErrorString(void)
428 {
429     const ALchar *ret = last_error;
430     last_error = "No error";
431     return ret;
432 }
433 
434 
435 /* Function: alureGetDeviceNames
436  *
437  * Gets an array of device name strings from OpenAL. This encapsulates
438  * AL_ENUMERATE_ALL_EXT (if supported and 'all' is true) and standard
439  * enumeration, with 'count' being set to the number of returned device
440  * names.
441  *
442  * Returns:
443  * An array of device name strings, or NULL on error.
444  *
445  * See Also:
446  * <alureFreeDeviceNames>
447  */
alureGetDeviceNames(ALCboolean all,ALCsizei * count)448 ALURE_API const ALCchar** ALURE_APIENTRY alureGetDeviceNames(ALCboolean all, ALCsizei *count)
449 {
450     const ALCchar *list = NULL;
451     if(all && alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
452         list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
453     else
454         list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
455     if(!list)
456     {
457         alcGetError(NULL);
458         SetError("No device names found");
459         return NULL;
460     }
461 
462     const ALCchar *cur = list;
463     ALuint retlistLen = 0;
464     while(*cur)
465     {
466         cur += strlen(cur)+1;
467         retlistLen++;
468     }
469 
470     const ALCchar **retlist = new const ALCchar*[retlistLen+1];
471     retlistLen = 0;
472     cur = list;
473     while(*cur)
474     {
475         ALCuint len = strlen(cur)+1;
476         ALCchar *newstr = new ALCchar[len];
477 
478         memcpy(newstr, cur, len);
479         cur += len;
480 
481         retlist[retlistLen] = newstr;
482         retlistLen++;
483     }
484     retlist[retlistLen] = NULL;
485 
486     *count = retlistLen;
487     return retlist;
488 }
489 
490 /* Function: alureFreeDeviceNames
491  *
492  * Frees the device name array returned from alureGetDeviceNames.
493  *
494  * See Also:
495  * <alureGetDeviceNames>
496  */
alureFreeDeviceNames(const ALCchar ** names)497 ALURE_API ALvoid ALURE_APIENTRY alureFreeDeviceNames(const ALCchar **names)
498 {
499     if(names)
500     {
501         for(ALCuint i = 0;names[i];i++)
502             delete[] const_cast<ALCchar*>(names[i]);
503         delete[] names;
504     }
505 }
506 
507 
508 /* Function: alureInitDevice
509  *
510  * Opens the named device, creates a context with the given attributes, and
511  * sets that context as current. The name and attribute list would be the same
512  * as what's passed to alcOpenDevice and alcCreateContext respectively.
513  *
514  * Returns:
515  * AL_FALSE on error.
516  *
517  * See Also:
518  * <alureShutdownDevice>
519  */
alureInitDevice(const ALCchar * name,const ALCint * attribs)520 ALURE_API ALboolean ALURE_APIENTRY alureInitDevice(const ALCchar *name, const ALCint *attribs)
521 {
522     ALCdevice *device = alcOpenDevice(name);
523     if(!device)
524     {
525         alcGetError(NULL);
526 
527         SetError("Device open failed");
528         return AL_FALSE;
529     }
530 
531     ALCcontext *context = alcCreateContext(device, attribs);
532     if(!context || alcMakeContextCurrent(context) == ALC_FALSE)
533     {
534         if(context)
535             alcDestroyContext(context);
536         alcCloseDevice(device);
537 
538         SetError("Context setup failed");
539         return AL_FALSE;
540     }
541     alcGetError(device);
542 
543     return AL_TRUE;
544 }
545 
546 /* Function: alureShutdownDevice
547  *
548  * Destroys the current context and closes its associated device.
549  *
550  * Returns:
551  * AL_FALSE on error.
552  *
553  * See Also:
554  * <alureInitDevice>
555  */
alureShutdownDevice(void)556 ALURE_API ALboolean ALURE_APIENTRY alureShutdownDevice(void)
557 {
558     ALCcontext *context = alcGetCurrentContext();
559     ALCdevice *device = alcGetContextsDevice(context);
560     if(!context || !device)
561     {
562         alcGetError(device);
563         SetError("Failed to get current device");
564         return AL_FALSE;
565     }
566 
567     if(alcMakeContextCurrent(NULL) == ALC_FALSE)
568     {
569         alcGetError(NULL);
570         SetError("Failed to unset current context");
571         return AL_FALSE;
572     }
573 
574     alcDestroyContext(context);
575     alcCloseDevice(device);
576     alcGetError(NULL);
577 
578     return AL_TRUE;
579 }
580 
581 
582 /* Function: alureGetSampleFormat
583  *
584  * Retrieves an OpenAL format for the given sample format. If bits is non-0,
585  * floatbits must be 0, and if floatbits is non-0, bits must be 0. The
586  * application should not rely on any particular format enum being returned as
587  * it is dependant on the available extensions. The returned format will be
588  * valid for the current context. Requires an active context.
589  *
590  * Returns:
591  * An OpenAL format enum for the given sample format, or AL_NONE if one can't
592  * be found.
593  */
alureGetSampleFormat(ALuint channels,ALuint bits,ALuint floatbits)594 ALURE_API ALenum ALURE_APIENTRY alureGetSampleFormat(ALuint channels, ALuint bits, ALuint floatbits)
595 {
596     if(alGetError() != AL_NO_ERROR)
597     {
598         SetError("Existing OpenAL error");
599         return AL_NONE;
600     }
601 
602     if(bits && floatbits)
603     {
604         SetError("Both bit-types specified");
605         return AL_NONE;
606     }
607 
608     if(bits)
609         return GetSampleFormat(channels, bits, false);
610     return GetSampleFormat(channels, floatbits, true);
611 }
612 
613 
614 /* Function: alureInstallDecodeCallbacks
615  *
616  * Installs callbacks to enable ALURE to handle more file types. The index is
617  * the order that each given set of callbacks will be tried, starting at the
618  * most negative number (INT_MIN) and going up. Negative indices will be tried
619  * before the built-in decoders, and positive indices will be tried after.
620  * Installing callbacks onto the same index multiple times will remove the
621  * previous callbacks, and removing old callbacks won't affect any opened files
622  * using them (they'll continue to use the old functions until properly closed,
623  * although newly opened files will use the new ones). Passing NULL for all
624  * callbacks is a valid way to remove an installed set, otherwise certain
625  * callbacks must be specified. Callbacks that are not specified will assume
626  * failure.
627  *
628  * Parameters:
629  * open_file - This callback is expected to open the named file and prepare it
630  *             for decoding. If the callbacks cannot decode the file, NULL
631  *             should be returned to indicate failure. Upon success, a non-NULL
632  *             handle must be returned, which will be used as a unique
633  *             identifier for the decoder instance. This callback is required
634  *             if open_memory is not specified.
635  * open_memory - This callback behaves the same as open_file, except it takes a
636  *               memory segment for input instead of a filename. The given
637  *               memory will remain valid while the instance is open. This
638  *               callback is required if open_file is not specified.
639  * get_format - This callback is used to retrieve the format of the decoded
640  *              data for the given instance. It is the responsibility of the
641  *              function to make sure the returned format is valid for the
642  *              current AL context (eg. don't return AL_FORMAT_QUAD16 if the
643  *              AL_EXT_MCFORMATS extension isn't available). Returning 0 for
644  *              samplerate or blocksize, or returning AL_NONE for format, will
645  *              cause a failure. Returning AL_FALSE indicates failure. This
646  *              callback is required.
647  * decode - This callback is called to get more decoded data. Up to the
648  *          specified amount of bytes should be written to the data pointer.
649  *          The number of bytes written should be a multiple of the block size,
650  *          otherwise an OpenAL error may occur during buffering. The function
651  *          should return the number of bytes written. This callback is
652  *          required.
653  * rewind - This callback is for rewinding the instance so that the next decode
654  *          calls for it will get audio data from the start of the sound file.
655  *          If the stream fails to rewind, AL_FALSE should be returned.
656  * close - This callback is called at the end of processing for a particular
657  *         instance. The handle will not be used further and any associated
658  *         data may be deleted.
659  *
660  * Returns:
661  * AL_FALSE on error.
662  */
alureInstallDecodeCallbacks(ALint index,void * (* open_file)(const ALchar * filename),void * (* open_memory)(const ALubyte * data,ALuint length),ALboolean (* get_format)(void * instance,ALenum * format,ALuint * samplerate,ALuint * blocksize),ALuint (* decode)(void * instance,ALubyte * data,ALuint bytes),ALboolean (* rewind)(void * instance),void (* close)(void * instance))663 ALURE_API ALboolean ALURE_APIENTRY alureInstallDecodeCallbacks(ALint index,
664       void*     (*open_file)(const ALchar *filename),
665       void*     (*open_memory)(const ALubyte *data, ALuint length),
666       ALboolean (*get_format)(void *instance, ALenum *format, ALuint *samplerate, ALuint *blocksize),
667       ALuint    (*decode)(void *instance, ALubyte *data, ALuint bytes),
668       ALboolean (*rewind)(void *instance),
669       void      (*close)(void *instance))
670 {
671     if(!open_file && !open_memory && !get_format && !decode && !rewind && !close)
672     {
673         std::map<ALint,UserCallbacks>::iterator i = InstalledCallbacks.find(index);
674         if(i != InstalledCallbacks.end())
675             InstalledCallbacks.erase(i);
676         return AL_TRUE;
677     }
678 
679     if((!open_file && !open_memory) || !get_format || !decode)
680     {
681         SetError("Missing callback functions");
682         return AL_FALSE;
683     }
684 
685     UserCallbacks newcb;
686     newcb.open_file = open_file;
687     newcb.open_mem  = open_memory;
688     newcb.get_fmt   = get_format;
689     newcb.decode    = decode;
690     newcb.rewind    = rewind;
691     newcb.close     = close;
692 
693     InstalledCallbacks[index] = newcb;
694 
695     return AL_TRUE;
696 }
697 
698 
699 /* Function: alureSleep
700  *
701  * Rests the calling thread for the given number of seconds.
702  *
703  * Returns:
704  * AL_FALSE on error.
705  */
alureSleep(ALfloat duration)706 ALURE_API ALboolean ALURE_APIENTRY alureSleep(ALfloat duration)
707 {
708     if(duration < 0.0f)
709     {
710         SetError("Invalid duration");
711         return AL_FALSE;
712     }
713 
714     ALuint seconds = (ALuint)duration;
715     ALdouble rest = duration - (ALdouble)seconds;
716 
717 #ifdef HAVE_NANOSLEEP
718 
719     struct timespec t, remainingTime;
720     t.tv_sec = (time_t)seconds;
721     t.tv_nsec = (long)(rest*1000000000);
722 
723     while(nanosleep(&t, &remainingTime) < 0 && errno == EINTR)
724         t = remainingTime;
725 
726 #elif defined(HAVE_WINDOWS_H)
727 
728     while(seconds > 0)
729     {
730         Sleep(1000);
731         seconds--;
732     }
733     Sleep((DWORD)(rest * 1000));
734 
735 #endif
736 
737     return AL_TRUE;
738 }
739 
740 
741 /* Function: alureGetProcAddress
742  *
743  * Returns a pointer for the named ALURE function.
744  *
745  * Returns:
746  * NULL on error.
747  *
748  * *Version Added*: 1.1
749  */
alureGetProcAddress(const ALchar * funcname)750 ALURE_API void* ALURE_APIENTRY alureGetProcAddress(const ALchar *funcname)
751 {
752     static const struct {
753         const char *name;
754         void *func;
755     } FunctionList[] = {
756 #define ADD_FUNCTION(x) { #x, (void*)x },
757         ADD_FUNCTION(alureGetVersion)
758         ADD_FUNCTION(alureGetErrorString)
759         ADD_FUNCTION(alureGetDeviceNames)
760         ADD_FUNCTION(alureFreeDeviceNames)
761         ADD_FUNCTION(alureInitDevice)
762         ADD_FUNCTION(alureShutdownDevice)
763         ADD_FUNCTION(alureGetSampleFormat)
764         ADD_FUNCTION(alureSleep)
765         ADD_FUNCTION(alureCreateBufferFromFile)
766         ADD_FUNCTION(alureCreateBufferFromMemory)
767         ADD_FUNCTION(alureBufferDataFromFile)
768         ADD_FUNCTION(alureBufferDataFromMemory)
769         ADD_FUNCTION(alureCreateStreamFromFile)
770         ADD_FUNCTION(alureCreateStreamFromMemory)
771         ADD_FUNCTION(alureCreateStreamFromStaticMemory)
772         ADD_FUNCTION(alureCreateStreamFromCallback)
773         ADD_FUNCTION(alureGetStreamLength)
774         ADD_FUNCTION(alureRewindStream)
775         ADD_FUNCTION(alureDestroyStream)
776         ADD_FUNCTION(alureSetStreamOrder)
777         ADD_FUNCTION(alureSetStreamPatchset)
778         ADD_FUNCTION(alureInstallDecodeCallbacks)
779         ADD_FUNCTION(alureSetIOCallbacks)
780         ADD_FUNCTION(alureGetProcAddress)
781         ADD_FUNCTION(alurePlaySourceStream)
782         ADD_FUNCTION(alurePlaySource)
783         ADD_FUNCTION(alureStopSource)
784 #undef ADD_FUNCTION
785         { NULL, NULL }
786     };
787 
788     size_t i;
789     for(i = 0;FunctionList[i].name;i++)
790     {
791         if(strcmp(FunctionList[i].name, funcname) == 0)
792             break;
793     }
794 
795     if(!FunctionList[i].name)
796         SetError("Function not found");
797     return FunctionList[i].func;
798 }
799 
800 } // extern "C"
801