Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/openal-0.0.8/src/extensions/al_ext_vorbis.c @ 17

Last change on this file since 17 was 17, checked in by landauf, 16 years ago

added openal

File size: 10.4 KB
Line 
1/* -*- mode: C; tab-width:8; c-basic-offset:8 -*-
2 * vi:set ts=8:
3 *
4 * al_ext_vorbis.c
5 *
6 * Temporary hack.
7 */
8#include "al_siteconfig.h"
9
10#include <AL/al.h>
11
12/* for alutLoadVorbis_LOKI */
13#include "AL/alext.h"
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18
19#include "al_ext_needed.h"
20#include "al_ext_vorbis.h"
21
22#include "al_buffer.h"
23#include "al_ext.h"
24
25#include "alc/alc_context.h"
26
27#define MAX_VORBIS 64
28
29#ifdef ENABLE_EXTENSION_AL_EXT_VORBIS
30#include <vorbis/vorbisfile.h>
31
32#ifdef OPENAL_DLOPEN_VORBIS
33#include <dlfcn.h>
34#endif
35
36
37static size_t ovfd_read(void *ptr, size_t size, size_t nmemb, void *datasource);
38static int ovfd_seek(void *datasource, int64_t offset, int whence);
39static int ovfd_close(void *datasource);
40static long ovfd_tell(void *datasource);
41
42typedef struct {
43        OggVorbis_File of;
44
45        void *data;
46
47        struct {
48                int size;
49                int offset; /* set by the source */
50        } fh;
51} VorbHandle;
52
53static ov_callbacks ov_fromdata = {
54        ovfd_read,
55        ovfd_seek,
56        ovfd_close,
57        ovfd_tell
58};
59
60/* maximum MAX_VORBIS simultaneous sid/offset */
61static struct {
62        ALuint bid;
63        VorbHandle *vorb;
64} vorbid[MAX_VORBIS];
65
66static struct {
67        ALuint sid;
68        ALuint offset;
69        ALint current_section;
70} vorbmap[MAX_VORBIS];
71
72#ifdef OPENAL_EXTENSION
73
74/*
75 * we are not being build into the library, therefore define the
76 * table that informs openal how to register the extensions upon
77 * dlopen.
78 */
79struct { ALubyte *name; void *addr; } alExtension_03282000 [] = {
80        AL_EXT_PAIR(alutLoadVorbis_LOKI),
81        { NULL, NULL }
82};
83
84void alExtInit_03282000(void) {
85        int i;
86
87        for(i = 0; i < MAX_VORBIS; i++) {
88                vorbmap[i].sid     = 0;
89                vorbmap[i].offset  = 0;
90
91                vorbid[i].bid  = 0;
92                vorbid[i].vorbis = NULL;
93        }
94
95        return;
96}
97
98void alExtFini_03282000(void) {
99        int i;
100
101        fprintf(stderr, "alExtFini_03282000 STUB\n");
102
103        for(i = 0; i < MAX_VORBIS; i++) {
104                if(vorbid[i].vorbis != NULL) {
105                        /* Do something */
106                }
107        }
108
109        return;
110}
111
112#endif
113
114static int signed_format(ALenum format);
115
116static void VorbHandle_delete(VorbHandle *vorb);
117
118static int  vorbid_get(ALuint bid, VorbHandle **vorbp);
119static int  vorbid_insert(ALuint bid, VorbHandle *vorb);
120static void vorbid_remove(ALuint bid);
121
122static int  vorbmap_get(ALuint sid, ALuint *offset, ALint *current_section);
123static int  vorbmap_insert(ALuint sid);
124static void vorbmap_update(int i, ALuint offset, ALint current_section);
125static void vorbmap_remove(ALuint sid);
126
127
128static int openal_load_vorbisfile_library(void);
129
130/*
131 * vorbisfile library functions.
132 */
133static int (*pov_clear)(OggVorbis_File *vf);
134static int (*pov_open_callbacks)(void *datasource, OggVorbis_File *vf,
135                char *initial, long ibytes, ov_callbacks callbacks);
136static vorbis_info* (*pov_info)(OggVorbis_File *vf,int link);
137static long (*pov_read)(OggVorbis_File *vf,char *buffer,int length,
138                    int bigendianp,int word,int sgned,int *bitstream);
139
140/*
141 * vorbisfile library handle.
142 */
143static void * vorbisfile_lib_handle = NULL;
144
145static int openal_load_vorbisfile_library(void)
146{
147#ifdef OPENAL_DLOPEN_VORBIS
148        char * error = NULL;
149#endif
150   
151        if (vorbisfile_lib_handle != NULL)
152                return 1;  /* already loaded. */
153
154        #ifdef OPENAL_DLOPEN_VORBIS
155                #define OPENAL_LOAD_VORBISFILE_SYMBOL(x) p##x = dlsym(vorbisfile_lib_handle, #x); \
156                                                   error = dlerror(); \
157                                                   if (p##x == NULL) { \
158                                                           fprintf(stderr,"Could not resolve vorbisfile symbol %s: %s\n", #x, ((error!=NULL)?(error):("(null)"))); \
159                                                           dlclose(vorbisfile_lib_handle); vorbisfile_lib_handle = NULL; \
160                                                           return 0; }
161                dlerror(); /* clear error state */
162                vorbisfile_lib_handle = dlopen("libvorbisfile.so", RTLD_LAZY | RTLD_GLOBAL);
163                error = dlerror();
164                if (vorbisfile_lib_handle == NULL) {
165                        fprintf(stderr,"Could not open vorbisfile library: %s\n",((error!=NULL)?(error):("(null)")));
166                        return 0;
167                }
168        #else
169                #define OPENAL_LOAD_VORBISFILE_SYMBOL(x) p##x = x;
170                vorbisfile_lib_handle = (void *) 0xF00DF00D;
171        #endif
172
173        OPENAL_LOAD_VORBISFILE_SYMBOL(ov_clear);
174        OPENAL_LOAD_VORBISFILE_SYMBOL(ov_open_callbacks);
175        OPENAL_LOAD_VORBISFILE_SYMBOL(ov_info);
176        OPENAL_LOAD_VORBISFILE_SYMBOL(ov_read);
177
178        return 1;
179}
180
181
182ALboolean alutLoadVorbis_LOKI(ALuint bid,
183                              const ALvoid *data,
184                              ALint size) {
185        static void (*palBufferi_LOKI)(ALuint, ALenum, ALint) = NULL;
186        VorbHandle *vorb;
187        int err;
188        vorbis_info *vi;
189
190        if(palBufferi_LOKI == NULL) {
191                palBufferi_LOKI = (void (*)(ALuint, ALenum, ALint))
192                        alGetProcAddress((const ALchar *) "alBufferi_LOKI");
193
194                if(palBufferi_LOKI == NULL) {
195                        fprintf(stderr, "Need alBufferi_LOKI\n");
196                        return AL_FALSE;
197                }
198               
199                if (!openal_load_vorbisfile_library())
200                        return AL_FALSE;
201        }
202
203        vorb = malloc(sizeof *vorb);
204        if(vorb == NULL) {
205                fprintf(stderr, "vorbis problems\n");
206
207                return AL_FALSE;
208        }
209
210        vorb->data = malloc(size);
211        if(vorb->data == NULL) {
212                fprintf(stderr, "vorbis out of memory \n");
213                free(vorb);
214                return AL_FALSE;
215        }
216
217        memcpy(vorb->data, data, size);
218
219        vorb->fh.offset = 0;
220        vorb->fh.size   = size;
221
222        /* NOTE: Ogg Vorbis' header are not const-correct, so we get a warning for "data" below! */
223        err = pov_open_callbacks(vorb, &vorb->of, data, size, ov_fromdata);
224        if(err < 0) {
225                fprintf(stderr, "vorbis problems\n");
226                free(vorb->data);
227                free(vorb);
228                return AL_FALSE;
229        }
230
231        /* set multi channel stuff */
232        vi = pov_info(&vorb->of, 0);
233        if(vi != NULL) {
234                palBufferi_LOKI(bid, AL_CHANNELS, vi->channels);
235        }
236
237        /* insert new bid */
238        vorbid_insert(bid, vorb);
239
240        _alBufferDataWithCallback_LOKI(bid,
241                                Vorbis_Callback,
242                                vorbmap_remove,
243                                vorbid_remove);
244
245        return AL_TRUE;
246}
247
248
249ALint Vorbis_Callback(UNUSED(ALuint sid),
250                ALuint bid,
251                ALshort *outdata,
252                ALenum format,
253                UNUSED(ALint freq),
254                ALint samples) {
255        VorbHandle *vorb;
256        int index;
257        ALuint offset;
258        long ret = 0;
259        int bps = _alGetBitsFromFormat(format)>>3; /* bytes per sample */
260        int bytesToRead = samples * bps;
261        ALint current_section;
262        ALint retval = 0;
263        char *datap;
264
265        datap = (char *) outdata;
266
267        index = vorbid_get(bid, &vorb);
268        if(index == -1) {
269                fprintf(stderr, "weird vorbid_get\n");
270                return -1;
271        }
272
273        index = vorbmap_get(sid, &offset, &current_section);
274        if(index == -1) {
275                /* first time */
276                index = vorbmap_insert(sid);
277
278                offset = 0;
279                current_section = 0;
280        }
281
282        vorb->fh.offset = offset; /* set per sid offset */
283
284        while(bytesToRead > 0) {
285                /* FIXME: handle format differences etc
286                 *
287                 * should be below VDRATB now */
288                ret = pov_read(&vorb->of,
289                              datap,
290                              bytesToRead,
291#ifdef WORDS_BIGENDIAN
292                              1,
293#else
294                              0,
295#endif
296                              bps,
297                              signed_format(format),
298                              &current_section);
299
300                if(ret == OV_HOLE)
301                        continue;
302
303                if(ret <= 0) {
304                        /* eof or error */
305                        vorbmap_update(index, 0, 0);
306                        return 0;
307                }
308
309                bytesToRead -= ret;
310                retval      += ret;
311                datap       += ret;
312        }
313
314        vorbmap_update(index, vorb->fh.offset, current_section);
315
316        return retval / bps;
317}
318
319static int vorbid_get(ALuint bid, VorbHandle **vorbp) {
320        int i;
321
322        for(i = 0; i < MAX_VORBIS; i++) {
323                if(vorbid[i].bid == bid) {
324                        *vorbp = vorbid[i].vorb;
325
326                        return i;
327                }
328        }
329
330        return -1;
331}
332
333static int vorbid_insert(ALuint bid, VorbHandle *vorb) {
334        int i;
335
336        for(i = 0; i < MAX_VORBIS; i++) {
337                if(vorbid[i].bid == bid) {
338                        if(vorbid[i].vorb != NULL) {
339                                VorbHandle_delete(vorbid[i].vorb);
340                                vorbid[i].vorb = NULL;
341                        }
342
343                        vorbid[i].bid = 0; /* flag for next */
344                }
345
346                if(vorbid[i].bid == 0) {
347                        vorbid[i].bid  = bid;
348                        vorbid[i].vorb = vorb;
349
350                        return i;
351                }
352        }
353
354        return 0;
355}
356
357static void vorbid_remove(ALuint bid) {
358        int i = 0;
359
360        for(i = 0; i < MAX_VORBIS; i++) {
361                if(vorbid[i].bid == (ALuint) bid) {
362                        if(vorbid[i].vorb != NULL) {
363                                VorbHandle_delete(vorbid[i].vorb);
364                                vorbid[i].vorb = NULL;
365                        }
366
367                        vorbid[i].bid = 0;
368                        return;
369                }
370        }
371
372        return;
373}
374
375/* FIXME: make binary search */
376static int vorbmap_get(ALuint sid, ALuint *offset, ALint *current_section) {
377        int i;
378
379        for(i = 0; i < MAX_VORBIS; i++) {
380                if(vorbmap[i].sid == sid) {
381                        *offset          = vorbmap[i].offset;
382                        *current_section = vorbmap[i].current_section;
383
384                        return i;
385                }
386        }
387
388        return -1;
389}
390
391/* FIXME: sorted insert for binary search */
392static int vorbmap_insert(ALuint sid) {
393        int i;
394
395        for(i = 0; i < MAX_VORBIS; i++) {
396                if((vorbmap[i].sid == 0) ||
397                   (vorbmap[i].sid == sid)) {
398                        vorbmap[i].sid    = sid;
399                        vorbmap[i].offset  = AL_FALSE;
400
401                        return i;
402                }
403        }
404
405        return 0;
406}
407
408static void vorbmap_update(int i, ALuint offset, ALint current_section) {
409        if(i < 0) {
410                return;
411        }
412
413        if(i >= MAX_VORBIS) {
414                return;
415        }
416
417        vorbmap[i].offset          = offset;
418        vorbmap[i].current_section = current_section;
419
420        return;
421}
422
423static void vorbmap_remove(ALuint sid) {
424        int i;
425
426        for(i = 0; i < MAX_VORBIS; i++) {
427                if(vorbmap[i].sid == sid) {
428                        vorbmap[i].sid   = 0;
429                        vorbmap[i].offset = AL_FALSE;
430
431                        return;
432                }
433        }
434
435        return;
436}
437
438static void VorbHandle_delete(VorbHandle *vorb) {
439        pov_clear(&vorb->of);
440        free(vorb->data);
441        free(vorb);
442
443        return;
444}
445
446static size_t ovfd_read(void *ptr, size_t size, size_t nmemb, void *datasource){
447        VorbHandle *vb = datasource;
448        int bytesToRead;
449        char *datap;
450
451        bytesToRead = size * nmemb;
452        datap = vb->data;
453
454        if(vb->fh.offset >= vb->fh.size) {
455                /* no more reading, we're at the end */
456                return 0;
457        }
458
459        if(vb->fh.offset + bytesToRead >= vb->fh.size) {
460                bytesToRead = vb->fh.size - vb->fh.offset;
461        }
462
463        datap += vb->fh.offset;
464
465        memcpy(ptr, datap, bytesToRead);
466
467        vb->fh.offset += bytesToRead;
468
469        return bytesToRead;
470}
471
472static int ovfd_seek(void *datasource, int64_t offset, int whence) {
473        VorbHandle *vb = datasource;
474
475        switch(whence) {
476                case SEEK_SET:
477                        if(vb->fh.size < offset) {
478                                return -1;
479                        }
480                        vb->fh.offset = offset;
481                        break;
482                case SEEK_CUR:
483                        if(vb->fh.size < vb->fh.offset + offset) {
484                                return -1;
485                        }
486
487                        vb->fh.offset += offset;
488                        break;
489                case SEEK_END:
490                        if(vb->fh.size < vb->fh.size + offset) {
491                                return -1;
492                        }
493
494                        vb->fh.offset = vb->fh.size + offset;
495                        break;
496        }
497
498        return 0;
499}
500
501static int ovfd_close(void *datasource) {
502        VorbHandle *vb = datasource;
503
504        vb->fh.offset = 0;
505
506        return 0;
507}
508
509static long ovfd_tell(void *datasource) {
510        VorbHandle *vb = datasource;
511
512        return vb->fh.offset;
513}
514
515static int signed_format(ALenum format) {
516        switch(format) {
517                case AL_FORMAT_MONO16:
518                case AL_FORMAT_STEREO16:
519                        return 1;
520                        break;
521                default:
522                        break;
523        }
524
525        return 0;
526}
527
528#else
529
530/* without vorbis support, we don't do jack */
531
532#endif /* ENABLE_EXTENSION_AL_EXT_VORBIS */
Note: See TracBrowser for help on using the repository browser.