Fix the open_file callback prototype
[alure.git] / src / alure.cpp
blob66fd9e763fbdbcd66228009fb70648cc6bf44ad9
1 /* Title: Main and Miscellanious */
3 #include "config.h"
5 #include "main.h"
7 #include <string.h>
8 #include <errno.h>
9 #include <time.h>
10 #ifdef HAVE_WINDOWS_H
11 #include <windows.h>
12 #endif
14 #include <vector>
16 std::map<ALint,UserCallbacks> InstalledCallbacks;
19 #define MAKE_FUNC(x) typeof(x) * p##x
20 #ifdef HAS_SNDFILE
21 void *sndfile_handle;
22 MAKE_FUNC(sf_open);
23 MAKE_FUNC(sf_open_virtual);
24 MAKE_FUNC(sf_close);
25 MAKE_FUNC(sf_readf_short);
26 MAKE_FUNC(sf_seek);
27 #endif
28 #ifdef HAS_VORBISFILE
29 void *vorbisfile_handle;
30 MAKE_FUNC(ov_open_callbacks);
31 MAKE_FUNC(ov_clear);
32 MAKE_FUNC(ov_info);
33 MAKE_FUNC(ov_read);
34 MAKE_FUNC(ov_pcm_seek);
35 #endif
36 #ifdef HAS_MPG123
37 void *mpg123_hdl;
38 MAKE_FUNC(mpg123_init);
39 MAKE_FUNC(mpg123_new);
40 MAKE_FUNC(mpg123_open_64);
41 MAKE_FUNC(mpg123_open_feed);
42 MAKE_FUNC(mpg123_delete);
43 MAKE_FUNC(mpg123_decode);
44 MAKE_FUNC(mpg123_read);
45 MAKE_FUNC(mpg123_format_none);
46 MAKE_FUNC(mpg123_getformat);
47 MAKE_FUNC(mpg123_format);
48 MAKE_FUNC(mpg123_seek_64);
49 #endif
50 #undef MAKE_FUNC
53 static void init_libs()
55 #if defined(HAVE_WINDOWS_H) && defined(HAS_LOADLIBRARY)
56 # define LOAD_FUNC(x, f) do { \
57 p##f = reinterpret_cast<typeof(f)*>(GetProcAddress((HMODULE)x, #f)); \
58 if(!(p##f)) \
59 fprintf(stderr, "Could not load "#f"\n"); \
60 } while(0)
62 #ifdef HAS_SNDFILE
63 sndfile_handle = LoadLibrary("sndfile.dll");
64 #endif
65 #ifdef HAS_VORBISFILE
66 vorbisfile_handle = LoadLibrary("vorbisfile.dll");
67 #endif
68 #ifdef HAS_MPG123
69 mpg123_hdl = LoadLibrary("mpg123.dll");
70 #endif
72 #elif defined(HAS_DLOPEN)
73 # define LOAD_FUNC(x, f) do { \
74 p##f = reinterpret_cast<typeof(f)*>(dlsym(x, #f)); \
75 if((err=dlerror()) != NULL) { \
76 fprintf(stderr, "Could not load "#f": %s\n", err); \
77 p##f = NULL; \
78 } \
79 } while(0)
81 #ifdef __APPLE__
82 # define VER_PREFIX
83 # define VER_POSTFIX ".dylib"
84 #else
85 # define VER_PREFIX ".so"
86 # define VER_POSTFIX
87 #endif
89 const char *err;
90 #ifdef HAS_SNDFILE
91 sndfile_handle = dlopen("libsndfile"VER_PREFIX".1"VER_POSTFIX, RTLD_NOW);
92 #endif
93 #ifdef HAS_VORBISFILE
94 vorbisfile_handle = dlopen("libvorbisfile"VER_PREFIX".3"VER_POSTFIX, RTLD_NOW);
95 #endif
96 #ifdef HAS_MPG123
97 mpg123_hdl = dlopen("libmpg123"VER_PREFIX".0"VER_POSTFIX, RTLD_NOW);
98 #endif
100 #undef VER_PREFIX
101 #undef VER_POSTFIX
103 #else
104 # define LOAD_FUNC(m, x) (p##x = x)
106 #ifdef HAS_SNDFILE
107 sndfile_handle = (void*)0xDECAFBAD;
108 #endif
109 #ifdef HAS_VORBISFILE
110 vorbisfile_handle = (void*)0xDEADBEEF;
111 #endif
112 #ifdef HAS_MPG123
113 mpg123_hdl = (void*)0xD00FBA11;
114 #endif
115 #endif
118 #ifdef HAS_SNDFILE
119 if(sndfile_handle)
121 LOAD_FUNC(sndfile_handle, sf_open);
122 LOAD_FUNC(sndfile_handle, sf_open_virtual);
123 LOAD_FUNC(sndfile_handle, sf_close);
124 LOAD_FUNC(sndfile_handle, sf_readf_short);
125 LOAD_FUNC(sndfile_handle, sf_seek);
126 if(!psf_open || !psf_open_virtual || !psf_close || !psf_readf_short ||
127 !psf_seek)
128 sndfile_handle = NULL;
130 #endif
131 #ifdef HAS_VORBISFILE
132 if(vorbisfile_handle)
134 LOAD_FUNC(vorbisfile_handle, ov_open_callbacks);
135 LOAD_FUNC(vorbisfile_handle, ov_clear);
136 LOAD_FUNC(vorbisfile_handle, ov_info);
137 LOAD_FUNC(vorbisfile_handle, ov_read);
138 LOAD_FUNC(vorbisfile_handle, ov_pcm_seek);
139 if(!pov_open_callbacks || !pov_clear || !pov_info || !pov_read ||
140 !pov_pcm_seek)
141 vorbisfile_handle = NULL;
143 #endif
144 #ifdef HAS_MPG123
145 if(mpg123_hdl)
147 LOAD_FUNC(mpg123_hdl, mpg123_init);
148 LOAD_FUNC(mpg123_hdl, mpg123_new);
149 LOAD_FUNC(mpg123_hdl, mpg123_open_64);
150 LOAD_FUNC(mpg123_hdl, mpg123_open_feed);
151 LOAD_FUNC(mpg123_hdl, mpg123_decode);
152 LOAD_FUNC(mpg123_hdl, mpg123_read);
153 LOAD_FUNC(mpg123_hdl, mpg123_getformat);
154 LOAD_FUNC(mpg123_hdl, mpg123_format_none);
155 LOAD_FUNC(mpg123_hdl, mpg123_format);
156 LOAD_FUNC(mpg123_hdl, mpg123_delete);
157 LOAD_FUNC(mpg123_hdl, mpg123_seek_64);
158 if(!pmpg123_init || !pmpg123_new || !pmpg123_open_64 ||
159 !pmpg123_open_feed || !pmpg123_decode || !pmpg123_read ||
160 !pmpg123_getformat || !pmpg123_format_none || !pmpg123_format ||
161 !pmpg123_delete || !pmpg123_seek_64 || pmpg123_init() != MPG123_OK)
162 mpg123_hdl = NULL;
164 #endif
166 #undef LOAD_FUNC
169 void init_alure()
171 static bool done = false;
172 if(done) return;
173 done = true;
175 init_libs();
179 static const ALchar *last_error = "No error";
181 void SetError(const char *err)
183 last_error = err;
186 extern "C" {
188 /* Function: alureGetErrorString
190 * Returns a string describing the last error encountered.
192 ALURE_API const ALchar* ALURE_APIENTRY alureGetErrorString(void)
194 const ALchar *ret = last_error;
195 last_error = "No error";
196 return ret;
200 /* Function: alureGetDeviceNames
202 * Gets an array of device name strings from OpenAL. This encapsulates
203 * AL_ENUMERATE_ALL_EXT (if supported and 'all' is true) and standard
204 * enumeration, with 'count' being set to the number of returned device
205 * names.
207 * Returns:
208 * An array of device name strings, or NULL on error.
210 * See Also:
211 * <alureFreeDeviceNames>
213 ALURE_API const ALCchar** ALURE_APIENTRY alureGetDeviceNames(ALCboolean all, ALCsizei *count)
215 init_alure();
217 const ALCchar *list = NULL;
218 if(all && alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
219 list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
220 else
221 list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
222 if(!list)
224 alcGetError(NULL);
225 SetError("No device names found");
226 return NULL;
229 const ALCchar *cur = list;
230 ALuint retlistLen = 0;
231 while(*cur)
233 cur += strlen(cur)+1;
234 retlistLen++;
237 const ALCchar **retlist = new const ALCchar*[retlistLen];
238 retlistLen = 0;
239 cur = list;
240 while(*cur)
242 retlist[retlistLen] = cur;
243 cur += strlen(cur)+1;
244 retlistLen++;
247 *count = retlistLen;
248 return retlist;
251 /* Function: alureFreeDeviceNames
253 * Frees the device name array returned from alureGetDeviceNames.
255 * See Also:
256 * <alureGetDeviceNames>
258 ALURE_API ALvoid ALURE_APIENTRY alureFreeDeviceNames(const ALCchar **names)
260 init_alure();
262 delete[] names;
266 /* Function: alureInitDevice
268 * Opens the named device, creates a context with the given attributes, and
269 * sets that context as current. The name and attribute list would be the same
270 * as what's passed to alcOpenDevice and alcCreateContext respectively.
272 * Returns:
273 * AL_FALSE on error.
275 * See Also:
276 * <alureShutdownDevice>
278 ALURE_API ALboolean ALURE_APIENTRY alureInitDevice(const ALCchar *name, const ALCint *attribs)
280 init_alure();
282 ALCdevice *device = alcOpenDevice(name);
283 if(!device)
285 alcGetError(NULL);
287 SetError("Device open failed");
288 return AL_FALSE;
291 ALCcontext *context = alcCreateContext(device, attribs);
292 if(alcGetError(device) != ALC_NO_ERROR || !context)
294 alcCloseDevice(device);
296 SetError("Context creation failed");
297 return AL_FALSE;
300 alcMakeContextCurrent(context);
301 if(alcGetError(device) != AL_NO_ERROR)
303 alcDestroyContext(context);
304 alcCloseDevice(device);
306 SetError("Context setup failed");
307 return AL_FALSE;
310 return AL_TRUE;
313 /* Function: alureShutdownDevice
315 * Destroys the current context and closes its associated device.
317 * Returns:
318 * AL_FALSE on error.
320 * See Also:
321 * <alureInitDevice>
323 ALURE_API ALboolean ALURE_APIENTRY alureShutdownDevice(void)
325 init_alure();
327 ALCcontext *context = alcGetCurrentContext();
328 ALCdevice *device = alcGetContextsDevice(context);
329 if(alcGetError(device) != ALC_NO_ERROR || !device)
331 SetError("Failed to get current device");
332 return AL_FALSE;
335 alcMakeContextCurrent(NULL);
336 alcDestroyContext(context);
337 alcCloseDevice(device);
338 alcGetError(NULL);
340 return AL_TRUE;
344 /* Function: alureGetSampleFormat
346 * Retrieves an OpenAL format for the given sample format. If bits is non-0,
347 * floatbits must be 0, and if floatbits is non-0, bits must be 0. The
348 * application should not rely on any particular format enum being returned as
349 * it is dependant on the available extensions. The returned format will be
350 * valid for the current context. Requires an active context.
352 * Returns:
353 * An OpenAL format enum for the given sample format, or AL_NONE if one can't
354 * be found.
356 ALURE_API ALenum ALURE_APIENTRY alureGetSampleFormat(ALuint channels, ALuint bits, ALuint floatbits)
358 init_alure();
360 if(alGetError() != AL_NO_ERROR)
362 SetError("Existing OpenAL error");
363 return AL_NONE;
366 if(bits && floatbits)
368 SetError("Both bit-types specified");
369 return AL_NONE;
372 if(bits == 8)
374 if(channels == 1) return AL_FORMAT_MONO8;
375 if(channels == 2) return AL_FORMAT_STEREO8;
376 if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
378 if(channels == 4) return AL_FORMAT_QUAD8;
379 if(channels == 6) return AL_FORMAT_51CHN8;
380 if(channels == 7) return AL_FORMAT_61CHN8;
381 if(channels == 8) return AL_FORMAT_71CHN8;
383 return AL_NONE;
385 if(bits == 16)
387 if(channels == 1) return AL_FORMAT_MONO16;
388 if(channels == 2) return AL_FORMAT_STEREO16;
389 if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
391 if(channels == 4) return AL_FORMAT_QUAD16;
392 if(channels == 6) return AL_FORMAT_51CHN16;
393 if(channels == 7) return AL_FORMAT_61CHN16;
394 if(channels == 8) return AL_FORMAT_71CHN16;
396 return AL_NONE;
398 if(floatbits == 32)
400 if(alIsExtensionPresent("AL_EXT_FLOAT32"))
402 if(channels == 1) return AL_FORMAT_MONO_FLOAT32;
403 if(channels == 2) return AL_FORMAT_STEREO_FLOAT32;
404 if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
406 if(channels == 4) return AL_FORMAT_QUAD32;
407 if(channels == 6) return AL_FORMAT_51CHN32;
408 if(channels == 7) return AL_FORMAT_61CHN32;
409 if(channels == 8) return AL_FORMAT_71CHN32;
412 return AL_NONE;
415 return AL_NONE;
419 /* Function: alureInstallDecodeCallbacks
421 * Installs callbacks to enable ALURE to handle more file types. The index is
422 * the order that each given set of callbacks will be tried, starting at the
423 * most negative number (INT_MIN) and going up. Negative indices will be tried
424 * before the built-in decoders, and positive indices will be tried after.
425 * Installing callbacks onto the same index multiple times will remove the
426 * previous callbacks, and removing old callbacks won't affect any opened files
427 * using them (they'll continue to use the old functions until properly closed,
428 * although newly opened files will use the new ones). Passing NULL for all
429 * callbacks is a valid way to remove an installed set, otherwise all callbacks
430 * must be specified.
432 * Parameters:
433 * open_file - This callback is expected to open the named file and prepare it
434 * for decoding. If the callbacks cannot decode the file, NULL
435 * should be returned to indicate failure. Upon success, a non-NULL
436 * handle must be returned, which will be used as a unique
437 * identifier for the decoder instance.
438 * open_memory - This callback behaves the same as open_file, except it takes a
439 * memory segment for input instead of a filename. The given
440 * memory will remain valid while the instance is open.
441 * get_format - This callback is used to retrieve the format of the decoded
442 * data for the given instance. It is the responsibility of the
443 * function to make sure the returned format is valid for the
444 * current AL context (eg. don't return AL_FORMAT_QUAD16 if the
445 * AL_EXT_MCFORMATS extension isn't available). Returning 0 for
446 * blocksize will cause a failure. Returning AL_FALSE indicates
447 * failure.
448 * decode - This callback is called to get more decoded data. Up to the
449 * specified amount of bytes should be written to the data pointer.
450 * The number of bytes written should be a multiple of the block size,
451 * otherwise an OpenAL error may occur during buffering. The function
452 * should return the number of bytes written.
453 * rewind - This callback is for rewinding the instance so that the next decode
454 * calls for it will get audio data from the start of the sound file.
455 * If the stream fails to rewind, AL_FALSE should be returned.
456 * close - This callback is called at the end of processing for a particular
457 * instance. The handle will not be used further and any associated
458 * data may be deleted.
460 * Returns:
461 * AL_FALSE on error.
463 ALURE_API ALboolean ALURE_APIENTRY alureInstallDecodeCallbacks(ALint index,
464 void* (*open_file)(const ALchar *filename),
465 void* (*open_memory)(const ALubyte *data, ALuint length),
466 ALboolean (*get_format)(void *instance, ALenum *format, ALuint *samplerate, ALuint *blocksize),
467 ALuint (*decode)(void *instance, ALubyte *data, ALuint bytes),
468 ALboolean (*rewind)(void *instance),
469 void (*close)(void *instance))
471 if(!open_file && !open_memory && !get_format && !decode && !rewind && !close)
473 std::map<ALint,UserCallbacks>::iterator i = InstalledCallbacks.find(index);
474 if(i != InstalledCallbacks.end())
475 InstalledCallbacks.erase(i);
476 return AL_TRUE;
479 if(!open_file || !open_memory || !get_format || !decode || !rewind || !close)
481 SetError("Missing callback functions");
482 return AL_FALSE;
485 UserCallbacks newcb;
486 newcb.open_file = open_file;
487 newcb.open_mem = open_memory;
488 newcb.get_fmt = get_format;
489 newcb.decode = decode;
490 newcb.rewind = rewind;
491 newcb.close = close;
493 InstalledCallbacks[index] = newcb;
495 return AL_TRUE;
499 /* Function: alureSleep
501 * Rests the calling thread for the given number of seconds.
503 * Returns:
504 * AL_FALSE on error.
506 ALURE_API ALboolean ALURE_APIENTRY alureSleep(ALfloat duration)
508 init_alure();
510 if(duration < 0.0f)
512 SetError("Invalid duration");
513 return AL_FALSE;
516 ALuint seconds = (ALuint)duration;
517 ALfloat rest = duration - (ALfloat)seconds;
519 #ifdef HAVE_NANOSLEEP
521 struct timespec t, remainingTime;
522 t.tv_sec = (time_t)seconds;
523 t.tv_nsec = (long)(rest*1000000000);
525 while(nanosleep(&t, &remainingTime) < 0 && errno == EINTR)
526 t = remainingTime;
528 #elif defined(HAVE_WINDOWS_H)
530 while(seconds > 0)
532 Sleep(1000);
533 seconds--;
535 Sleep((DWORD)(rest * 1000));
537 #endif
539 return AL_TRUE;
543 } // extern "C"