misc/libopenalbridge/openalbridge.c
branchwebgl
changeset 9521 8054d9d775fd
parent 9282 92af50454cf2
parent 9519 b8b5c82eb61b
child 9950 2759212a27de
equal deleted inserted replaced
9282:92af50454cf2 9521:8054d9d775fd
     1 /*
       
     2 * OpenAL Bridge - a simple portable library for OpenAL interface
       
     3 * Copyright (c) 2009 Vittorio Giovara <vittorio.giovara@gmail.com>
       
     4 *
       
     5 * This program is free software; you can redistribute it and/or modify
       
     6 * it under the terms of the GNU Lesser General Public License as published by
       
     7 * the Free Software Foundation; version 2 of the License
       
     8 *
       
     9 * This program is distributed in the hope that it will be useful,
       
    10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12 * GNU Lesser General Public License for more details.
       
    13 *
       
    14 * You should have received a copy of the GNU Lesser General Public License
       
    15 * along with this program; if not, write to the Free Software
       
    16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
       
    17 */
       
    18 
       
    19 #include "openalbridge.h"
       
    20 #include "globals.h"
       
    21 #include "al.h"
       
    22 #include "alc.h"
       
    23 #include "wrappers.h"
       
    24 #include "loaders.h"
       
    25 #include "string.h"
       
    26 
       
    27 // Sources are points emitting sound, their number is limited, but a single source can play many buffers
       
    28 // Buffers hold sound data and are unlimited
       
    29 ALuint *Sources;
       
    30 ALuint cache_size, cache_index, sources_number;
       
    31 ALboolean instances_number;
       
    32 al_sound_t *the_sounds;
       
    33 ALint cache_pointer;
       
    34 
       
    35 // Initialize an OpenAL contex and allocate memory space for data and buffers
       
    36 // It can be called twice to increase the cache size
       
    37 int openal_init (void) {
       
    38     ALCcontext *context;
       
    39     ALCdevice *device;
       
    40     int i;
       
    41 
       
    42     // reuse old context and resize the existing
       
    43     if (openal_ready() == AL_TRUE) {
       
    44         fprintf(stderr,"(Bridge Info) - already initialized\n");
       
    45         instances_number++;
       
    46         return AL_TRUE;
       
    47     }
       
    48 
       
    49     cache_pointer = 0;
       
    50     instances_number++;
       
    51 
       
    52     // initial memory size
       
    53     cache_size = 50;
       
    54 
       
    55     // open hardware device if present
       
    56     device = alcOpenDevice(NULL);
       
    57     sources_number = 16;
       
    58     if (device == NULL) {
       
    59         fprintf(stderr,"(Bridge Warning) - failed to open sound device, using software renderer\n");
       
    60         device = alcOpenDevice("Generic Software");
       
    61         sources_number = 32;
       
    62         if (device == NULL) {
       
    63             fprintf(stderr,"(Bridge ERROR) - failed to start software renderer, sound will be disabled\n");
       
    64             return -1;
       
    65         }
       
    66     }
       
    67 
       
    68     fprintf(stderr,"(Bridge Info) - output device: %s\n", alcGetString(device, ALC_DEVICE_SPECIFIER));
       
    69 
       
    70     context = alcCreateContext(device, NULL);
       
    71     alcMakeContextCurrent(context);
       
    72     alcProcessContext(context);
       
    73 
       
    74     if (AL_NO_ERROR != alGetError()) {
       
    75         fprintf(stderr,"(Bridge ERROR) - Failed to create a new contex\n");
       
    76         alcMakeContextCurrent(NULL);
       
    77         alcDestroyContext(context);
       
    78         alcCloseDevice(device);
       
    79         return -2;
       
    80     }
       
    81 
       
    82     Sources = (ALuint *)Malloc (sizeof(ALuint) * sources_number);
       
    83     alGenSources(sources_number, Sources);
       
    84 
       
    85     // set the listener gain, position (on xyz axes), velocity (one value for each axe) and orientation
       
    86     // Position, Velocity and Orientation of the listener
       
    87     ALfloat ListenerPos[] = {0.0, 0.0, 0.0};
       
    88     ALfloat ListenerVel[] = {0.0, 0.0, 0.0};
       
    89     ALfloat ListenerOri[] = {0.0, 0.0, -1.0, 0.0, 1.0, 0.0};
       
    90 
       
    91     alListenerf (AL_GAIN,        1.0f       );
       
    92     alListenerfv(AL_POSITION,    ListenerPos);
       
    93     alListenerfv(AL_VELOCITY,    ListenerVel);
       
    94     alListenerfv(AL_ORIENTATION, ListenerOri);
       
    95 
       
    96     if (AL_NO_ERROR != alGetError()) {
       
    97         fprintf(stderr,"(Bridge ERROR) - Failed to set Listener properties\n");
       
    98         return -3;
       
    99     }
       
   100 
       
   101     the_sounds = (al_sound_t *)Malloc (sizeof(al_sound_t) * cache_size);
       
   102     for (i = 0; i < cache_size; i++)
       
   103         the_sounds[i] = new_sound_el();
       
   104 
       
   105     alGetError();
       
   106     return AL_TRUE;
       
   107 }
       
   108 
       
   109 
       
   110 // Stop all sounds, deallocate all memory and close OpenAL context
       
   111 void openal_close (void) {
       
   112     ALCcontext *context;
       
   113     ALCdevice  *device;
       
   114     int i;
       
   115 
       
   116     if (instances_number == 0) {
       
   117         fprintf(stderr,"(Bridge Warning) - OpenAL not initialized\n");
       
   118         return;
       
   119     }
       
   120 
       
   121     instances_number--;
       
   122     if (instances_number > 0) {
       
   123         // release memory only when last session ends
       
   124         return;
       
   125     }
       
   126 
       
   127     for (i = 0; i < cache_size; i++) {
       
   128         openal_unloadfile(i);
       
   129     }
       
   130     free(the_sounds);
       
   131 
       
   132     alSourceStopv (sources_number, Sources);
       
   133     alDeleteSources (sources_number, Sources);
       
   134 
       
   135     free(Sources);
       
   136 
       
   137     context = alcGetCurrentContext();
       
   138     device  = alcGetContextsDevice(context);
       
   139 
       
   140     alcMakeContextCurrent(NULL);
       
   141     alcDestroyContext(context);
       
   142     alcCloseDevice(device);
       
   143 
       
   144     fprintf(stderr,"(Bridge Info) - closed\n");
       
   145 
       
   146     return;
       
   147 }
       
   148 
       
   149 
       
   150 ALboolean openal_ready (void) {
       
   151     if (instances_number >= 1)
       
   152         return AL_TRUE;
       
   153     else
       
   154         return AL_FALSE;
       
   155 }
       
   156 
       
   157 
       
   158 // Open a file, load into memory and allocate the Source buffer for playing
       
   159 int openal_loadfile (const char *filename){
       
   160     ALenum format, error;
       
   161     ALsizei bitsize, freq;
       
   162     uint32_t fileformat;
       
   163     al_sound_t sound_data;
       
   164     int len, i, index = -1;
       
   165     char *data;
       
   166     FILE *fp;
       
   167 
       
   168     if (openal_ready() == AL_FALSE) {
       
   169         fprintf(stderr,"(Bridge Warning) - not initialized\n");
       
   170         return -1;
       
   171     }
       
   172 
       
   173     // if this sound is already loaded return the index from the_sounds
       
   174     len = strlen(filename);
       
   175     for (i = 0; i < cache_size; i++) {
       
   176         if (the_sounds[i].filename != NULL && strncmp(the_sounds[i].filename, filename, len) == 0) {
       
   177 #ifdef DEBUG
       
   178             fprintf(stderr,"(Bridge Debug) - sound %d is already loaded\n", i);
       
   179 #endif
       
   180             return i;
       
   181         }
       
   182         // if we don't have memory available search for a free element
       
   183         if (cache_pointer >= cache_size)
       
   184             if (the_sounds[i].is_used == AL_FALSE)
       
   185                 index = i;
       
   186     }
       
   187 
       
   188     if (index == -1 && cache_pointer >= cache_size) {
       
   189         fprintf(stderr,"(Bridge Info) - No free spots found; doubling cache size\n", filename);
       
   190         cache_size *= 2;
       
   191         the_sounds = (al_sound_t *)Realloc (the_sounds, sizeof(al_sound_t) * cache_size);
       
   192         for (i = cache_size - 50; i < cache_size; i++)
       
   193             the_sounds[i] = new_sound_el();
       
   194     } else
       
   195         index = ++cache_pointer;
       
   196 
       
   197 
       
   198     // detect the file format, as written in the first 4 bytes of the header
       
   199     fp = Fopen (filename, "rb");
       
   200     if (fp == NULL) {
       
   201         fprintf(stderr,"(Bridge ERROR) - File %s not loaded\n", filename);
       
   202         return -3;
       
   203     }
       
   204 
       
   205     error = fread (&fileformat, sizeof(uint32_t), 1, fp);
       
   206     fclose (fp);
       
   207     if (error < 0) {
       
   208         fprintf(stderr,"(Bridge ERROR) - File %s is too short\n", filename);
       
   209         return -4;
       
   210     }
       
   211 
       
   212     switch (ENDIAN_BIG_32(fileformat)) {
       
   213         case OGG_FILE_FORMAT:
       
   214             error = load_oggvorbis (filename, &format, &data, &bitsize, &freq);
       
   215             break;
       
   216         case WAV_FILE_FORMAT:
       
   217             error = load_wavpcm (filename, &format, &data, &bitsize, &freq);
       
   218             break;
       
   219         default:
       
   220             fprintf(stderr,"(Bridge ERROR) - File format (%08X) not supported\n", ENDIAN_BIG_32(fileformat));
       
   221             return -5;
       
   222             break;
       
   223     }
       
   224 
       
   225     if (error != 0) {
       
   226         fprintf(stderr,"(Bridge ERROR) - error loading file %s\n", filename);
       
   227         if(data)
       
   228             free(data);
       
   229         return -6;
       
   230     }
       
   231 
       
   232     // alGenBuffers happens here
       
   233     sound_data = init_sound_el(filename);
       
   234 
       
   235     if (AL_NO_ERROR != alGetError()) {
       
   236         fprintf(stderr,"(Bridge ERROR) - Failed to allocate memory for buffer %d\n", index);
       
   237         free(data);
       
   238         return -5;
       
   239     }
       
   240 
       
   241     // copy pcm data in one buffer and free it
       
   242     alBufferData(sound_data.buffer, format, data, bitsize, freq);
       
   243     free(data);
       
   244 
       
   245     if (AL_NO_ERROR != alGetError()) {
       
   246         fprintf(stderr,"(Bridge ERROR) - Failed to write data to buffer %d\n", index);
       
   247         return -8;
       
   248     }
       
   249 
       
   250     // clear any AL errors beforehand
       
   251     alGetError();
       
   252 
       
   253     fprintf(stderr,"(Bridge Info) - successfully loaded %s\n", filename);
       
   254 
       
   255     // returns the index of the source you just loaded, increments it and exits
       
   256     the_sounds[index] = sound_data;
       
   257     return index;
       
   258 }
       
   259 
       
   260 
       
   261 void openal_unloadfile (uint32_t index) {
       
   262     ALint state;
       
   263 
       
   264     if (openal_ready() == AL_TRUE && index < cache_size && the_sounds[index].is_used == AL_TRUE) {
       
   265         alGetSourcei (Sources[the_sounds[index].source_index], AL_SOURCE_STATE, &state);
       
   266         if (state == AL_PLAYING || state == AL_PAUSED)
       
   267             openal_stopsound(index);
       
   268 
       
   269         // free memory and
       
   270         alDeleteBuffers (1, &the_sounds[index].buffer);
       
   271         the_sounds[index] = new_sound_el();
       
   272     }
       
   273 }