1 /*
2  * updated for 4.9 inclusion by Ryan Dickie
3  * Originally done by KC/Milan
4  */
5 
6 #include <stdio.h>
7 #include <string.h>
8 
9 #include "allegro5/allegro.h"
10 
11 #if defined(ALLEGRO_MACOSX) || defined(ALLEGRO_IPHONE)
12 #include <OpenAL/al.h>
13 #include <OpenAL/alc.h>
14 #else /* ALLEGRO_MACOSX */
15 #include <al.h>
16 #include <alc.h>
17 #endif /* ALLEGRO_MACOSX */
18 
19 #include "allegro5/internal/aintern_audio.h"
20 
21 ALLEGRO_DEBUG_CHANNEL("openal")
22 
23 /* OpenAL vars */
24 static ALCdevice  *openal_dev;
25 static ALCcontext *openal_context;
26 
27 /* TODO: make these configurable */
28 static const size_t preferred_frag_size = 1024;
29 static const ALuint preferred_buf_count = 4;
30 
openal_get_err_str(ALenum err)31 static const char *openal_get_err_str(ALenum err)
32 {
33    switch (err) {
34       case AL_NO_ERROR:
35          return "There is no OpenAL error";
36       case AL_INVALID_NAME:
37          return "A bad name (ID) was passed to OpenAL";
38       case AL_INVALID_ENUM:
39          return "An invalid enum was passed to OpenAL";
40       case AL_INVALID_VALUE:
41          return "An Invalid enum was passed to OpenAL";
42       case AL_INVALID_OPERATION:
43          return "The requestion operation is invalid";
44       case AL_OUT_OF_MEMORY:
45          return "OpenAL ran out of memory";
46       default:
47          return "Unknown error";
48    }
49 }
50 
alc_get_err_str(ALCenum err)51 static const char *alc_get_err_str(ALCenum err)
52 {
53    switch (err) {
54       case ALC_NO_ERROR:
55          return "There is no OpenAL error";
56       case ALC_INVALID_DEVICE:
57          return "A bad device was passed to OpenAL";
58       case ALC_INVALID_CONTEXT:
59          return "An bad context was passed to OpenAL";
60       case ALC_INVALID_ENUM:
61          return "An Invalid enum was passed to OpenAL";
62       case ALC_INVALID_VALUE:
63          return "The requestion operation is invalid";
64       case ALC_OUT_OF_MEMORY:
65          return "OpenAL ran out of memory";
66       default:
67          return "Unknown error";
68    }
69 }
70 
71 /* The open method starts up the driver and should lock the device, using the
72    previously set paramters, or defaults. It shouldn't need to start sending
73    audio data to the device yet, however. */
_openal_open(void)74 static int _openal_open(void)
75 {
76    ALenum openal_err;
77    ALCenum alc_err;
78 
79    ALLEGRO_INFO("Starting OpenAL\n");
80 
81    /* clear the error state */
82    openal_err = alGetError();
83 
84    /* pick default device. always a good choice */
85    openal_dev = alcOpenDevice(NULL);
86 
87    alc_err = ALC_NO_ERROR;
88    if (!openal_dev || (alc_err = alcGetError(openal_dev)) != ALC_NO_ERROR) {
89       ALLEGRO_ERROR("Could not open audio device: %s\n",
90          alc_get_err_str(alc_err));
91       return 1;
92    }
93 
94    openal_context = alcCreateContext(openal_dev, NULL);
95    alc_err = ALC_NO_ERROR;
96    if (!openal_context || (alc_err = alcGetError(openal_dev)) != ALC_NO_ERROR) {
97       ALLEGRO_ERROR("Could not create current device context: %s\n",
98          alc_get_err_str(alc_err));
99       return 1;
100    }
101 
102    alcMakeContextCurrent(openal_context);
103 #if !defined ALLEGRO_IPHONE
104    if ((alc_err = alcGetError(openal_dev)) != ALC_NO_ERROR) {
105       ALLEGRO_ERROR("Could not make context current: %s\n",
106          alc_get_err_str(alc_err));
107       return 1;
108    }
109 
110    alDistanceModel(AL_NONE);
111    if ((openal_err = alGetError()) != AL_NO_ERROR) {
112       ALLEGRO_ERROR("Could not set distance model: %s\n",
113          openal_get_err_str(openal_err));
114       return 1;
115    }
116 #endif
117 
118    ALLEGRO_DEBUG("Vendor: %s\n", alGetString(AL_VENDOR));
119    ALLEGRO_DEBUG("Version: %s\n", alGetString(AL_VERSION));
120    ALLEGRO_DEBUG("Renderer: %s\n", alGetString(AL_RENDERER));
121    ALLEGRO_DEBUG("Extensions: %s\n", alGetString(AL_EXTENSIONS));
122 
123    return 0;
124 }
125 
126 /* The close method should close the device, freeing any resources, and allow
127    other processes to use the device */
_openal_close(void)128 static void _openal_close(void)
129 {
130    /* clear error states */
131    alGetError();
132    alcGetError(openal_dev);
133 
134    /* remove traces from openal */
135    alcMakeContextCurrent(NULL);
136    alcDestroyContext(openal_context);
137    alcCloseDevice(openal_dev);
138 
139    /* reset the pointers to NULL */
140    openal_context = NULL;
141    openal_dev = NULL;
142 }
143 
144 /* Custom struct to hold voice information OpenAL needs */
145 /* TODO: review */
146 typedef struct ALLEGRO_AL_DATA {
147    ALuint *buffers;
148 
149    size_t num_buffers;
150    ALuint buffer_size;
151 
152    ALuint source;
153    ALuint format;
154 
155    ALLEGRO_THREAD *thread;
156    bool stopped;
157 } ALLEGRO_AL_DATA;
158 
159 /* Custom routine which runs in another thread to periodically check if OpenAL
160    wants more data for a stream */
161 /* TODO: review */
_openal_update(ALLEGRO_THREAD * self,void * arg)162 static void *_openal_update(ALLEGRO_THREAD *self, void *arg)
163 {
164    ALLEGRO_VOICE *voice = (ALLEGRO_VOICE*) arg;
165    ALLEGRO_AL_DATA *ex_data = (ALLEGRO_AL_DATA*)voice->extra;
166    unsigned int i, samples_per_update;
167    unsigned int bytes_per_sample;
168    const void *data;
169    void *silence;
170 
171    /* Streams should not be set to looping */
172    alSourcei(ex_data->source, AL_LOOPING, AL_FALSE);
173 
174    silence = al_calloc(1, ex_data->buffer_size);
175    if (ex_data->format == AL_FORMAT_STEREO8 ||
176          ex_data->format == AL_FORMAT_MONO8) {
177       memset(silence, 0x80, ex_data->buffer_size);
178    }
179 
180    for (i = 0; i < ex_data->num_buffers; i++) {
181       alBufferData(ex_data->buffers[i], ex_data->format, silence,
182          ex_data->buffer_size, voice->frequency);
183    }
184 
185    alSourceQueueBuffers(ex_data->source, ex_data->num_buffers,
186       ex_data->buffers);
187 
188    alSourcePlay(ex_data->source);
189 
190    switch (ex_data->format) {
191       case AL_FORMAT_STEREO16:
192          bytes_per_sample = 4;
193          break;
194       case AL_FORMAT_STEREO8:
195       case AL_FORMAT_MONO16:
196          bytes_per_sample = 2;
197          break;
198       default:
199          bytes_per_sample = 1;
200          break;
201    }
202 
203    samples_per_update = ex_data->buffer_size / bytes_per_sample;
204 
205    data = silence;
206 
207    while (!al_get_thread_should_stop(self)) {
208       ALint status = 0;
209 
210       alGetSourcei(ex_data->source, AL_BUFFERS_PROCESSED, &status);
211       if (status <= 0) {
212          /* FIXME what is this for ? */
213          al_rest(0.001);
214          continue;
215       }
216 
217       while (--status >= 0) {
218          ALuint buffer;
219 
220          data = _al_voice_update(voice, voice->mutex, &samples_per_update);
221          if (data == NULL)
222             data = silence;
223 
224          alSourceUnqueueBuffers(ex_data->source, 1, &buffer);
225          alBufferData(buffer, ex_data->format, data,
226             samples_per_update * bytes_per_sample, voice->frequency);
227          alSourceQueueBuffers(ex_data->source, 1, &buffer);
228       }
229       alGetSourcei(ex_data->source, AL_SOURCE_STATE, &status);
230       if (status == AL_STOPPED) {
231          alSourcePlay(ex_data->source);
232       }
233    }
234 
235    alSourceStop(ex_data->source);
236 
237    al_free(silence);
238 
239    ex_data->stopped = true;
240    al_broadcast_cond(voice->cond);
241 
242    return NULL;
243 }
244 
245 /* The load_voice method loads a sample into the driver's memory. The voice's
246    'streaming' field will be set to false for these voices, and it's
247    'buffer_size' field will be the total length in bytes of the sample data.
248    The voice's attached sample's looping mode should be honored, and loading
249    must fail if it cannot be. */
_openal_load_voice(ALLEGRO_VOICE * voice,const void * data)250 static int _openal_load_voice(ALLEGRO_VOICE *voice, const void *data)
251 {
252    ALLEGRO_AL_DATA *ex_data = voice->extra;
253    ALenum openal_err;
254 
255    if (voice->attached_stream->loop != ALLEGRO_PLAYMODE_ONCE &&
256            voice->attached_stream->loop != ALLEGRO_PLAYMODE_LOOP) {
257       return 1;
258    }
259 
260    ex_data->buffer_size = voice->buffer_size;
261    if (!ex_data->buffer_size) {
262       ALLEGRO_ERROR("Voice buffer and data buffer size mismatch\n");
263       return 1;
264    }
265    ex_data->num_buffers = 1;
266 
267    alGenSources(1, &ex_data->source);
268    if ((openal_err = alGetError()) != AL_NO_ERROR) {
269       ALLEGRO_ERROR("Could not generate (voice) source: %s\n",
270          openal_get_err_str(openal_err));
271       return 1;
272    }
273 
274    ex_data->buffers = al_malloc(sizeof(ALuint) * ex_data->num_buffers);
275    if (!ex_data->buffers) {
276       alSourcei(ex_data->source, AL_BUFFER, 0);
277       alDeleteSources(1, &ex_data->source);
278       ALLEGRO_ERROR("Could not allocate voice buffer memory\n");
279       return 1;
280    }
281 
282    alGenBuffers(ex_data->num_buffers, ex_data->buffers);
283    if ((openal_err = alGetError()) != AL_NO_ERROR) {
284       alSourcei(ex_data->source, AL_BUFFER, 0);
285       alDeleteSources(1, &ex_data->source);
286       al_free(ex_data->buffers);
287       ex_data->buffers = NULL;
288       ALLEGRO_ERROR("Could not generate (voice) buffer: %s\n",
289          openal_get_err_str(openal_err));
290       return 1;
291    }
292 
293    /* copies data into a buffer */
294    alBufferData(ex_data->buffers[0], ex_data->format,
295                 data, ex_data->buffer_size, voice->frequency);
296 
297    /* sets the buffer */
298    alSourcei(ex_data->source, AL_BUFFER, ex_data->buffers[0]);
299 
300    /* Loop / no loop? */
301    alSourcei(ex_data->source, AL_LOOPING,
302       (voice->attached_stream->loop != ALLEGRO_PLAYMODE_ONCE));
303 
304    /* make sure the volume is on */
305    alSourcef(ex_data->source, AL_GAIN, 1.0f);
306 
307    if ((openal_err = alGetError()) != AL_NO_ERROR) {
308       alSourcei(ex_data->source, AL_BUFFER, 0);
309       alDeleteSources(1, &ex_data->source);
310       alDeleteBuffers(ex_data->num_buffers, ex_data->buffers);
311       al_free(ex_data->buffers);
312       ex_data->buffers = NULL;
313       ALLEGRO_ERROR("Could not attach voice source: %s\n",
314          openal_get_err_str(openal_err));
315       return 1;
316    }
317 
318    return 0;
319 }
320 
321 /* The unload_voice method unloads a sample previously loaded with load_voice.
322    This method should not be called on a streaming voice. */
_openal_unload_voice(ALLEGRO_VOICE * voice)323 static void _openal_unload_voice(ALLEGRO_VOICE *voice)
324 {
325    ALLEGRO_AL_DATA *ex_data = voice->extra;
326 
327    alSourcei(ex_data->source, AL_BUFFER, 0);
328    alDeleteSources(1, &ex_data->source);
329    alDeleteBuffers(ex_data->num_buffers, ex_data->buffers);
330    al_free(ex_data->buffers);
331    ex_data->buffers = NULL;
332    alGetError(); /* required! */
333 }
334 
335 
336 /* The start_voice should, surprise, start the voice. For streaming voices, it
337    should start polling the device and call _al_voice_update for audio data.
338    For non-streaming voices, it should resume playing from the last set
339    position */
_openal_start_voice(ALLEGRO_VOICE * voice)340 static int _openal_start_voice(ALLEGRO_VOICE *voice)
341 {
342    ALLEGRO_AL_DATA *ex_data = voice->extra;
343    ALenum openal_err;
344 
345    /* playing a sample instead of a stream */
346    if (!voice->is_streaming) {
347       alSourcePlay(ex_data->source);
348       if ((openal_err = alGetError()) != AL_NO_ERROR) {
349          ALLEGRO_ERROR("Could not start voice: %s\n",
350             openal_get_err_str(openal_err));
351          return 1;
352       }
353 
354       ALLEGRO_INFO("Starting voice\n");
355       return 0;
356    }
357 
358    {
359       ex_data->buffer_size = voice->buffer_size;
360       if (!ex_data->buffer_size) {
361          switch (ex_data->format) {
362             case AL_FORMAT_STEREO16:
363                ex_data->buffer_size = preferred_frag_size * 4;
364                break;
365             case AL_FORMAT_STEREO8:
366             case AL_FORMAT_MONO16:
367                ex_data->buffer_size = preferred_frag_size * 2;
368                break;
369             default:
370                ex_data->buffer_size = preferred_frag_size;
371                break;
372          }
373       }
374 
375       ex_data->num_buffers = voice->num_buffers;
376       if (!ex_data->num_buffers)
377          ex_data->num_buffers = preferred_buf_count;
378 
379       alGenSources(1, &ex_data->source);
380       if (alGetError() != AL_NO_ERROR)
381          return 1;
382 
383       ex_data->buffers = al_malloc(sizeof(ALuint) * ex_data->num_buffers);
384       if (!ex_data->buffers) {
385          alSourcei(ex_data->source, AL_BUFFER, 0);
386          alDeleteSources(1, &ex_data->source);
387          return 1;
388       }
389 
390       alGenBuffers(ex_data->num_buffers, ex_data->buffers);
391       if (alGetError() != AL_NO_ERROR) {
392          alSourcei(ex_data->source, AL_BUFFER, 0);
393          alDeleteSources(1, &ex_data->source);
394          al_free(ex_data->buffers);
395          ex_data->buffers = NULL;
396          return 1;
397       }
398 
399       alSourcef(ex_data->source, AL_GAIN, 1.0f);
400       if (alGetError() != AL_NO_ERROR) {
401          alSourcei(ex_data->source, AL_BUFFER, 0);
402          alDeleteSources(1, &ex_data->source);
403          alDeleteBuffers(ex_data->num_buffers, ex_data->buffers);
404          al_free(ex_data->buffers);
405          ex_data->buffers = NULL;
406          return 1;
407       }
408 
409       ex_data->stopped = false;
410       ex_data->thread = al_create_thread(_openal_update, (void *)voice);
411       al_start_thread(ex_data->thread);
412    }
413 
414    ALLEGRO_INFO("Starting voice\n");
415    return 0;
416 }
417 
418 /* The stop_voice method should stop playback. For non-streaming voices, it
419    should leave the data loaded, and reset the voice position to 0. */
_openal_stop_voice(ALLEGRO_VOICE * voice)420 static int _openal_stop_voice(ALLEGRO_VOICE* voice)
421 {
422    ALLEGRO_AL_DATA *ex_data = voice->extra;
423    ALenum openal_err;
424 
425    if (!ex_data->buffers) {
426       ALLEGRO_WARN("Trying to stop empty voice buffer\n");
427       return 1;
428    }
429 
430    /* if playing a sample */
431    if (!voice->is_streaming) {
432       alSourceStop(ex_data->source);
433       if ((openal_err = alGetError()) != AL_NO_ERROR) {
434          ALLEGRO_ERROR("Could not stop voice: %s\n",
435             openal_get_err_str(openal_err));
436          return 1;
437       }
438       return 0;
439    }
440 
441    if (ex_data->thread) {
442       al_set_thread_should_stop(ex_data->thread);
443       while (!ex_data->stopped) {
444          al_wait_cond(voice->cond, voice->mutex);
445       }
446       al_join_thread(ex_data->thread, NULL);
447       ex_data->thread = NULL;
448       ex_data->stopped = false;
449    }
450 
451    alSourcei(ex_data->source, AL_BUFFER, 0);
452    alDeleteSources(1, &ex_data->source);
453    alDeleteBuffers(ex_data->num_buffers, ex_data->buffers);
454    al_free(ex_data->buffers);
455    ex_data->buffers = NULL;
456 
457    alGetError(); /* required! */
458    return 0;
459 }
460 
461 /* The voice_is_playing method should only be called on non-streaming sources,
462    and should return true if the voice is playing */
_openal_voice_is_playing(const ALLEGRO_VOICE * voice)463 static bool _openal_voice_is_playing(const ALLEGRO_VOICE *voice)
464 {
465    ALLEGRO_AL_DATA *ex_data = voice->extra;
466    ALint status;
467 
468    if (!ex_data)
469       return false;
470 
471    alGetSourcei(ex_data->source, AL_SOURCE_STATE, &status);
472    return (status == AL_PLAYING);
473 }
474 
475 /* The allocate_voice method should grab a voice from the system, and allocate
476    any data common to streaming and non-streaming sources. */
_openal_allocate_voice(ALLEGRO_VOICE * voice)477 static int _openal_allocate_voice(ALLEGRO_VOICE *voice)
478 {
479    ALLEGRO_AL_DATA *ex_data;
480 
481    /* OpenAL doesn't support very much! */
482    switch (voice->depth) {
483       case ALLEGRO_AUDIO_DEPTH_UINT8:
484          /* format supported */
485          break;
486       case ALLEGRO_AUDIO_DEPTH_INT8:
487          ALLEGRO_WARN("OpenAL requires 8-bit data to be unsigned\n");
488          return 1;
489       case ALLEGRO_AUDIO_DEPTH_UINT16:
490          ALLEGRO_WARN("OpenAL requires 16-bit data to be signed\n");
491          return 1;
492       case ALLEGRO_AUDIO_DEPTH_INT16:
493          /* format supported */
494          break;
495       case ALLEGRO_AUDIO_DEPTH_UINT24:
496          ALLEGRO_WARN("OpenAL does not support 24-bit data\n");
497          return 1;
498       case ALLEGRO_AUDIO_DEPTH_INT24:
499          ALLEGRO_WARN("OpenAL does not support 24-bit data\n");
500          return 1;
501       case ALLEGRO_AUDIO_DEPTH_FLOAT32:
502          ALLEGRO_WARN("OpenAL does not support 32-bit floating data\n");
503          return 1;
504       default:
505          ALLEGRO_WARN("Cannot allocate unknown voice depth\n");
506          return 1;
507    }
508 
509    ex_data = al_calloc(1, sizeof(*ex_data));
510    if (!ex_data) {
511       ALLEGRO_ERROR("Could not allocate voice data memory\n");
512       return 1;
513    }
514 
515    switch (voice->chan_conf) {
516       case ALLEGRO_CHANNEL_CONF_1:
517          /* format supported */
518          if (voice->depth == ALLEGRO_AUDIO_DEPTH_UINT8)
519             ex_data->format = AL_FORMAT_MONO8;
520          else
521             ex_data->format = AL_FORMAT_MONO16;
522          break;
523       case ALLEGRO_CHANNEL_CONF_2:
524          /* format supported */
525          if (voice->depth == ALLEGRO_AUDIO_DEPTH_UINT8)
526             ex_data->format = AL_FORMAT_STEREO8;
527          else
528             ex_data->format = AL_FORMAT_STEREO16;
529          break;
530       case ALLEGRO_CHANNEL_CONF_3:
531          ALLEGRO_ERROR("OpenAL does not support voice with 3 channel configuration\n");
532          al_free(ex_data);
533          return 1;
534       case ALLEGRO_CHANNEL_CONF_4:
535          ex_data->format = alGetEnumValue("AL_FORMAT_QUAD16");
536          if (ex_data->format) {
537             ALLEGRO_ERROR("OpenAL cannot allocate voice with 4.0 channel configuration\n");
538             al_free(ex_data);
539             return 1;
540          }
541          if (voice->depth == ALLEGRO_AUDIO_DEPTH_INT16) {
542             ALLEGRO_ERROR("OpenAL requires 16-bit signed data for 4 channel configuration\n");
543             al_free(ex_data);
544             return 1;
545          }
546          /* else it is supported */
547          break;
548       case ALLEGRO_CHANNEL_CONF_5_1:
549          ex_data->format = alGetEnumValue("AL_FORMAT_51CHN_16");
550          if (!ex_data->format) {
551             ALLEGRO_ERROR("Cannot allocate voice with 5.1 channel configuration\n");
552             al_free(ex_data);
553             return 1;
554          }
555          if (voice->depth == ALLEGRO_AUDIO_DEPTH_UINT16) {
556             ALLEGRO_ERROR("5.1 channel requires 16-bit signed data\n");
557             al_free(ex_data);
558             return 1;
559          }
560          /* else it is supported */
561          break;
562       case ALLEGRO_CHANNEL_CONF_6_1:
563          ex_data->format = alGetEnumValue("AL_FORMAT_61CHN_16");
564          if (!ex_data->format) {
565             ALLEGRO_ERROR("Cannot allocate voice with 6.1 channel configuration\n");
566             al_free(ex_data);
567             return 1;
568          }
569          if (voice->depth == ALLEGRO_AUDIO_DEPTH_UINT16) {
570             ALLEGRO_ERROR("6.1 channel requires 16-bit signed data\n");
571             al_free(ex_data);
572             return 1;
573          }
574          /* else it is supported */
575          break;
576       case ALLEGRO_CHANNEL_CONF_7_1:
577          ex_data->format = alGetEnumValue("AL_FORMAT_71CHN_16");
578          if (!ex_data->format) {
579             ALLEGRO_ERROR("Cannot allocate voice with 7.1 channel configuration\n");
580             al_free(ex_data);
581             return 1;
582          }
583          if (voice->depth == ALLEGRO_AUDIO_DEPTH_UINT16) {
584             ALLEGRO_ERROR("7.1 channel requires 16-bit signed data\n");
585             al_free(ex_data);
586             return 1;
587          }
588          /* else it is supported */
589          break;
590       default:
591          ALLEGRO_ERROR("Cannot allocate voice with unknown channel configuration\n");
592          al_free(ex_data);
593          return 1;
594    }
595 
596    voice->extra = ex_data;
597    ex_data->thread = NULL;
598    ex_data->stopped = false;
599 
600    return 0;
601 }
602 
603 /* The deallocate_voice method should free the resources for the given voice,
604    but still retain a hold on the device. The voice should be stopped and
605    unloaded by the time this is called */
_openal_deallocate_voice(ALLEGRO_VOICE * voice)606 static void _openal_deallocate_voice(ALLEGRO_VOICE *voice)
607 {
608    ALLEGRO_AL_DATA *ex_data = voice->extra;
609    ASSERT(ex_data->thread == NULL);
610    (void)ex_data;
611 
612    al_free(voice->extra);
613    voice->extra = NULL;
614 }
615 
616 /* The get_voice_position method should return the current sample position of
617    the voice (sample_pos = byte_pos / (depth/8) / channels). This should never
618    be called on a streaming voice. */
_openal_get_voice_position(const ALLEGRO_VOICE * voice)619 static unsigned int _openal_get_voice_position(const ALLEGRO_VOICE *voice)
620 {
621    ALLEGRO_AL_DATA *ex_data = voice->extra;
622    ALint pos;
623 
624    alGetSourcei(ex_data->source, AL_SAMPLE_OFFSET, &pos);
625    if (alGetError() != AL_NO_ERROR)
626       return 0;
627    return pos;
628 }
629 
630 /* The set_voice_position method should set the voice's playback position,
631    given the value in samples. This should never be called on a streaming
632    voice. */
_openal_set_voice_position(ALLEGRO_VOICE * voice,unsigned int val)633 static int _openal_set_voice_position(ALLEGRO_VOICE *voice, unsigned int val)
634 {
635    ALLEGRO_AL_DATA *ex_data = voice->extra;
636 
637    alSourcei(ex_data->source, AL_SAMPLE_OFFSET, val);
638    if (alGetError() != AL_NO_ERROR)
639       return 1;
640    return 0;
641 }
642 
643 ALLEGRO_AUDIO_DRIVER _al_kcm_openal_driver = {
644    "OpenAL",
645 
646    _openal_open,
647    _openal_close,
648 
649    _openal_allocate_voice,
650    _openal_deallocate_voice,
651 
652    _openal_load_voice,
653    _openal_unload_voice,
654 
655    _openal_start_voice,
656    _openal_stop_voice,
657 
658    _openal_voice_is_playing,
659 
660    _openal_get_voice_position,
661    _openal_set_voice_position,
662 
663    NULL,
664    NULL
665 };
666 
667 /* vim: set sts=3 sw=3 et: */
668