]> git.jsancho.org Git - lugaru.git/blob - Source/openal_wrapper.cpp
First shot at an OpenAL renderer. Sound effects work, no music.
[lugaru.git] / Source / openal_wrapper.cpp
1
2 #if USE_OPENAL
3
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include "MacCompatibility.h"
9 #include "fmod.h"
10
11 #include "AL/al.h"
12 #include "AL/alc.h"
13
14 #include "ogg/ogg.h"
15 #include "vorbis/vorbisfile.h"
16
17 // NOTE:
18 // FMOD uses a Left Handed Coordinate system, OpenAL uses a Right Handed
19 //  one...so we just need to flip the sign on the Z axis when appropriate.
20
21
22 #define DYNAMIC_LOAD_OPENAL 0
23
24 #if DYNAMIC_LOAD_OPENAL
25
26 #include <dlfcn.h>
27
28 #define AL_FUNC(t,ret,fn,params,call,rt) \
29     extern "C" { \
30         static ret ALAPIENTRY (*p##fn) params = NULL; \
31         ret ALAPIENTRY fn params { rt p##fn call; } \
32     }
33 #include "alstubs.h"
34 #undef AL_FUNC
35
36 static void *aldlhandle = NULL;
37
38 static bool lookup_alsym(const char *funcname, void **func, const char *libname)
39 {
40     if (!aldlhandle)
41         return false;
42
43     *func = dlsym(aldlhandle, funcname);
44     if (*func == NULL)
45     {
46         fprintf(stderr, "Failed to find OpenAL symbol \"%s\" in \"%s\"\n",
47                  funcname, libname);
48         return false;
49     }
50     return true;
51 }
52
53 static void unload_alsyms(void)
54 {
55     #define AL_FUNC(t,ret,fn,params,call,rt) p##fn = NULL;
56     #include "alstubs.h"
57     #undef AL_FUNC
58     if (aldlhandle)
59     {
60         dlclose(aldlhandle);
61         aldlhandle = NULL;
62     }
63 }
64
65 static bool lookup_all_alsyms(const char *libname)
66 {
67     if (!aldlhandle)
68     {
69         if ( (aldlhandle = dlopen(libname, RTLD_GLOBAL | RTLD_NOW)) == NULL )
70             return false;
71     }
72
73     bool retval = true;
74     #define AL_FUNC(t,ret,fn,params,call,rt) \
75         if (!lookup_alsym(#fn, (void **) &p##fn, libname)) retval = false;
76     #include "alstubs.h"
77     #undef AL_FUNC
78
79     if (!retval)
80         unload_alsyms();
81
82     return retval;
83 }
84 #else
85 #define lookup_all_alsyms(x) (true)
86 #define unload_alsyms()
87 #endif
88
89
90 typedef struct
91 {
92     ALuint sid;
93     FSOUND_SAMPLE *sample;
94     bool startpaused;
95 } OPENAL_Channels;
96
97 typedef struct FSOUND_SAMPLE
98 {
99     ALuint bid;  // buffer id.
100     int mode;
101 } FSOUND_SAMPLE;
102
103 static size_t num_channels = 0;
104 static OPENAL_Channels *channels = NULL;
105 static bool initialized = false;
106
107
108 void F_API OPENAL_3D_Listener_SetAttributes(const float *pos, const float *vel, float fx, float fy, float fz, float tx, float ty, float tz)
109 {
110     if (!initialized) return;
111     if (pos != NULL)
112         alListener3f(AL_POSITION, pos[0], pos[1], -pos[2]);
113     ALfloat vec[6] = { fx, fy, -fz, tz, ty, -tz };
114     alListenerfv(AL_ORIENTATION, vec);
115
116     // we ignore velocity, since doppler's broken in the Linux AL at the moment...
117 }
118
119 signed char F_API OPENAL_3D_SetAttributes(int channel, const float *pos, const float *vel)
120 {
121     if (!initialized) return FALSE;
122     if ((channel < 0) || (channel >= num_channels)) return FALSE;
123
124     if (pos != NULL)
125         alSource3f(channels[channel].sid, AL_POSITION, pos[0], pos[1], -pos[2]);
126
127     // we ignore velocity, since doppler's broken in the Linux AL at the moment...
128     return TRUE;
129 }
130
131 void F_API OPENAL_3D_SetDopplerFactor(float scale)
132 {
133     if (!initialized) return;
134     // unimplemented...looks like init routines just call this with scale == 0.0f anyhow.
135 }
136
137 signed char F_API OPENAL_Init(int mixrate, int maxsoftwarechannels, unsigned int flags)
138 {
139     if (initialized) return FALSE;
140     if (maxsoftwarechannels == 0) return FALSE;
141
142     if (flags != 0)  // unsupported.
143         return FALSE;
144
145     if (!lookup_all_alsyms("./openal.so"))  // !!! FIXME: linux specific lib name
146     {
147         if (!lookup_all_alsyms("openal.so.1"))  // !!! FIXME: linux specific lib name
148         {
149             if (!lookup_all_alsyms("openal.so"))  // !!! FIXME: linux specific lib name
150                 return FALSE;
151         }
152     }
153
154     ALCdevice *dev = alcOpenDevice(NULL);
155     if (!dev)
156         return FALSE;
157
158     ALint caps[] = { ALC_FREQUENCY, mixrate, 0 };
159     ALCcontext *ctx = alcCreateContext(dev, caps);
160     if (!ctx)
161     {
162         alcCloseDevice(dev);
163         return FALSE;
164     }
165
166     alcMakeContextCurrent(ctx);
167     alcProcessContext(ctx);
168
169     printf("AL_VENDOR: %s\n", (char *) alGetString(AL_VENDOR));
170     printf("AL_RENDERER: %s\n", (char *) alGetString(AL_RENDERER));
171     printf("AL_VERSION: %s\n", (char *) alGetString(AL_VERSION));
172     printf("AL_EXTENSIONS: %s\n", (char *) alGetString(AL_EXTENSIONS));
173
174     num_channels = maxsoftwarechannels;
175     channels = new OPENAL_Channels[maxsoftwarechannels];
176     memset(channels, '\0', sizeof (OPENAL_Channels) * num_channels);
177     for (int i = 0; i < num_channels; i++)
178         alGenSources(1, &channels[i].sid);  // !!! FIXME: verify this didn't fail!
179
180     initialized = true;
181     return TRUE;
182 }
183
184 void F_API OPENAL_Close()
185 {
186     if (!initialized) return;
187
188     ALCcontext *ctx = alcGetCurrentContext();
189     if (ctx)
190     {
191         for (int i = 0; i < num_channels; i++)
192         {
193             alSourceStop(channels[i].sid);
194             alSourcei(channels[i].sid, AL_BUFFER, 0);
195             alDeleteSources(1, &channels[i].sid);
196         }
197         ALCdevice *dev = alcGetContextsDevice(ctx);
198         alcMakeContextCurrent(NULL);
199         alcSuspendContext(ctx);
200         alcDestroyContext(ctx);
201         alcCloseDevice(dev);
202     }
203
204     num_channels = 0;
205     delete[] channels;
206     channels = NULL;
207
208     unload_alsyms();
209     initialized = false;
210 }
211
212 FSOUND_SAMPLE *F_API OPENAL_GetCurrentSample(int channel)
213 {
214     if (!initialized) return NULL;
215     if ((channel < 0) || (channel >= num_channels)) return NULL;
216     return channels[channel].sample;
217 }
218
219 signed char F_API OPENAL_GetPaused(int channel)
220 {
221     if (!initialized) return FALSE;
222     if ((channel < 0) || (channel >= num_channels)) return FALSE;
223     if (channels[channel].startpaused)
224         return(TRUE);
225
226     ALint state = 0;
227     alGetSourceiv(channels[channel].sid, AL_SOURCE_STATE, &state);
228     return((state == AL_PAUSED) ? TRUE : FALSE);
229 }
230
231 unsigned int F_API OPENAL_GetLoopMode(int channel)
232 {
233     if (!initialized) return 0;
234     if ((channel < 0) || (channel >= num_channels)) return 0;
235     ALint loop = 0;
236     alGetSourceiv(channels[channel].sid, AL_LOOPING, &loop);
237     if (loop)
238         return(FSOUND_LOOP_NORMAL);
239     return FSOUND_LOOP_OFF;
240 }
241
242 signed char F_API OPENAL_IsPlaying(int channel)
243 {
244     if (!initialized) return FALSE;
245     if ((channel < 0) || (channel >= num_channels)) return FALSE;
246     ALint state = 0;
247     alGetSourceiv(channels[channel].sid, AL_SOURCE_STATE, &state);
248     return((state == AL_PLAYING) ? TRUE : FALSE);
249 }
250
251 int F_API OPENAL_PlaySoundEx(int channel, FSOUND_SAMPLE *sptr, FSOUND_DSPUNIT *dsp, signed char startpaused)
252 {
253     if (!initialized) return -1;
254     if (sptr == NULL) return -1;
255     if (dsp != NULL) return -1;
256     if (channel == FSOUND_FREE)
257     {
258         for (int i = 0; i < num_channels; i++)
259         {
260             ALint state = 0;
261             alGetSourceiv(channels[i].sid, AL_SOURCE_STATE, &state);
262             if ((state != AL_PLAYING) && (state != AL_PAUSED))
263             {
264                 channel = i;
265                 break;
266             }
267         }
268     }
269
270     if ((channel < 0) || (channel >= num_channels)) return -1;
271     ALint state = 0;
272     alSourceStop(channels[channel].sid);
273     channels[channel].sample = sptr;
274     alSourcei(channels[channel].sid, AL_BUFFER, sptr->bid);
275     alSourcei(channels[channel].sid, AL_LOOPING, (sptr->mode == FSOUND_LOOP_OFF) ? AL_FALSE : AL_TRUE);
276     channels[channel].startpaused = ((startpaused) ? true : false);
277     if (!startpaused)
278         alSourcePlay(channels[channel].sid);
279     return channel;
280 }
281
282
283 static void *decode_to_pcm(const char *_fname, ALenum &format, ALsizei &size, ALuint &freq)
284 {
285     // !!! FIXME: if it's not Ogg, we don't have a decoder. I'm lazy.  :/
286     char *fname = (char *) alloca(strlen(_fname) + 16);
287     strcpy(fname, _fname);
288     char *ptr = strchr(fname, '.');
289     if (ptr) *ptr = NULL;
290     strcat(fname, ".ogg");
291
292     // just in case...
293     #undef fopen
294     FILE *io = fopen(fname, "rb");
295     if (io == NULL)
296         return NULL;
297
298     ALubyte *retval = NULL;
299
300     // Can we just feed it to the AL compressed?
301     if (alIsExtensionPresent((const ALubyte *) "AL_EXT_vorbis"))
302     {
303         format = alGetEnumValue((const ALubyte *) "AL_FORMAT_VORBIS_EXT");
304         freq = 44100;
305         fseek(io, 0, SEEK_END);
306         size = ftell(io);
307         fseek(io, 0, SEEK_SET);
308         retval = (ALubyte *) malloc(size);
309         size_t rc = fread(retval, size, 1, io);
310         fclose(io);
311         if (rc != 1)
312         {
313             free(retval);
314             return NULL;
315         }
316         return retval;
317     }
318
319     // Uncompress and feed to the AL.
320     OggVorbis_File vf;
321     memset(&vf, '\0', sizeof (vf));
322     if (ov_open(io, &vf, NULL, 0) == 0)
323     {
324         int bitstream = 0;
325         vorbis_info *info = ov_info(&vf, -1);
326         size = 0;
327         format = (info->channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
328         freq = info->rate;
329
330         if ((info->channels != 1) && (info->channels != 2))
331         {
332             ov_clear(&vf);
333             return NULL;
334         }
335
336         char buf[1024 * 16];
337         long rc = 0;
338         size_t allocated = 64 * 1024;
339         retval = (ALubyte *) malloc(allocated);
340         while ( (rc = ov_read(&vf, buf, sizeof (buf), 0, 2, 1, &bitstream)) != 0 )
341         {
342             if (rc > 0)
343             {
344                 size += rc;
345                 if (size >= allocated)
346                 {
347                     allocated *= 2;
348                     ALubyte *tmp = (ALubyte *) realloc(retval, allocated);
349                     if (tmp == NULL)
350                     {
351                         free(retval);
352                         retval = NULL;
353                         break;
354                     }
355                     retval = tmp;
356                 }
357                 memcpy(retval + (size - rc), buf, rc);
358             }
359         }
360         ov_clear(&vf);
361         return retval;
362     }
363
364     fclose(io);
365     return NULL;
366 }
367
368
369 FSOUND_SAMPLE * F_API OPENAL_Sample_Load(int index, const char *name_or_data, unsigned int mode, int offset, int length)
370 {
371     if (!initialized) return NULL;
372     if (index != FSOUND_FREE) return NULL;  // this is all the game does...
373     if (offset != 0) return NULL;  // this is all the game does...
374     if (length != 0) return NULL;  // this is all the game does...
375     if ((mode != FSOUND_HW3D) && (mode != FSOUND_2D)) return NULL;  // this is all the game does...
376
377     FSOUND_SAMPLE *retval = NULL;
378     ALuint bufferName = 0;
379     ALenum format = AL_NONE;
380     ALsizei size = 0;
381     ALuint frequency = 0;
382     void *data = decode_to_pcm(name_or_data, format, size, frequency);
383     if (data == NULL)
384         return NULL;
385
386     ALuint bid = 0;
387     alGetError();
388     alGenBuffers(1, &bid);
389     if (alGetError() == AL_NO_ERROR)
390     {
391         alBufferData(bid, format, data, size, frequency);
392         retval = new FSOUND_SAMPLE;
393         retval->bid = bid;
394         retval->mode = FSOUND_LOOP_OFF;
395     }
396
397     free(data);
398     return(retval);
399 }
400
401 void F_API OPENAL_Sample_Free(FSOUND_SAMPLE *sptr)
402 {
403     if (!initialized) return;
404     if (sptr)
405     {
406         for (int i = 0; i < num_channels; i++)
407         {
408             if (channels[i].sample == sptr)
409             {
410                 alSourceStop(channels[i].sid);
411                 alSourcei(channels[i].sid, AL_BUFFER, 0);
412                 channels[i].sample = NULL;
413             }
414         }
415         alDeleteBuffers(1, &sptr->bid);
416         delete sptr;
417     }
418 }
419
420 signed char F_API OPENAL_Sample_SetMode(FSOUND_SAMPLE *sptr, unsigned int mode)
421 {
422     if (!initialized) return FALSE;
423     if (mode != FSOUND_LOOP_NORMAL) return FALSE;
424     if (!sptr) return FALSE;
425     sptr->mode = mode;
426     return TRUE;
427 }
428
429 signed char F_API OPENAL_Sample_SetMinMaxDistance(FSOUND_SAMPLE *sptr, float min, float max)
430 {
431     if (!initialized) return FALSE;
432     if (sptr == NULL) return FALSE;
433     // !!! FIXME: write me
434     return 0;
435 }
436
437 signed char F_API OPENAL_SetFrequency(int channel, int freq)
438 {
439     if (!initialized) return FALSE;
440     return TRUE;  // ignore this for now...
441 }
442
443 signed char F_API OPENAL_SetVolume(int channel, int vol)
444 {
445     if (!initialized) return FALSE;
446     if ((channel < 0) || (channel >= num_channels)) return FALSE;
447     if ((vol < 0) || (vol > 255)) return FALSE;
448     ALfloat gain = ((ALfloat) vol) / 255.0f;
449     alSourcef(channels[channel].sid, AL_GAIN, gain);
450     return TRUE;
451 }
452
453 signed char F_API OPENAL_SetPaused(int channel, signed char paused)
454 {
455     if (!initialized) return FALSE;
456     if ((channel < 0) || (channel >= num_channels)) return FALSE;
457
458     ALint state = 0;
459     if (channels[channel].startpaused)
460         state = AL_PAUSED;
461     else
462         alGetSourceiv(channels[channel].sid, AL_SOURCE_STATE, &state);
463
464     if ((paused) && (state == AL_PLAYING))
465         alSourcePause(channels[channel].sid);
466     else if ((!paused) && (state == AL_PAUSED))
467     {
468         alSourcePlay(channels[channel].sid);
469         channels[channel].startpaused = false;
470     }
471     return TRUE;
472 }
473
474 void F_API OPENAL_SetSFXMasterVolume(int volume)
475 {
476     if (!initialized) return;
477     ALfloat gain = ((ALfloat) volume) / 255.0f;
478     alListenerf(AL_GAIN, gain);
479 }
480
481 signed char F_API OPENAL_StopSound(int channel)
482 {
483     if (!initialized) return FALSE;
484     if ((channel < 0) || (channel >= num_channels)) return FALSE;
485     alSourceStop(channels[channel].sid);
486     return TRUE;
487 }
488
489 FSOUND_STREAM * F_API OPENAL_Stream_Open(const char *name_or_data, unsigned int mode, int offset, int length)
490 {
491     if (!initialized) return NULL;
492     return 0;
493 }
494
495 signed char F_API OPENAL_Stream_Close(FSOUND_STREAM *stream)
496 {
497     if (!initialized) return FALSE;
498     return 0;
499 }
500
501 FSOUND_SAMPLE * F_API OPENAL_Stream_GetSample(FSOUND_STREAM *stream)
502 {
503     if (!initialized) return NULL;
504     return 0;
505 }
506
507 int F_API OPENAL_Stream_PlayEx(int channel, FSOUND_STREAM *stream, FSOUND_DSPUNIT *dsp, signed char startpaused)
508 {
509     if (!initialized) return -1;
510     return 0;
511 }
512
513 signed char F_API OPENAL_Stream_Stop(FSOUND_STREAM *stream)
514 {
515     if (!initialized) return FALSE;
516     return 0;
517 }
518
519 signed char F_API OPENAL_Stream_SetMode(FSOUND_STREAM *stream, unsigned int mode)
520 {
521     if (!initialized) return FALSE;
522     return 0;
523 }
524
525 void F_API OPENAL_Update()
526 {
527     if (!initialized) return;
528     alcProcessContext(alcGetCurrentContext());
529 }
530
531 #endif
532