misc/openalbridge/openalbridge.c
changeset 3514 59dbd31e9953
parent 3513 f589230fa21b
child 3515 3e8635f43972
equal deleted inserted replaced
3513:f589230fa21b 3514:59dbd31e9953
     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 (int memorysize) {
       
    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         cache_size += memorysize;
       
    45         fprintf(stderr,"(Bridge Info) - already initialized, resizing cache to %d\n", cache_size);
       
    46         the_sounds = (al_sound_t *)Realloc (the_sounds, sizeof(al_sound_t) * cache_size);
       
    47         for (i = cache_size - memorysize; i < cache_size; i++) {
       
    48             the_sounds[i].filename = NULL;
       
    49             the_sounds[i].buffer = -1;
       
    50             the_sounds[i].source_index = -1;
       
    51             the_sounds[i].stats = 0;
       
    52         }
       
    53         instances_number++;
       
    54         return AL_TRUE;
       
    55     }
       
    56     
       
    57     cache_pointer = 0;
       
    58     instances_number++;
       
    59     
       
    60     // set the memory dimentsion and the increment width when reallocating
       
    61     if (memorysize <= 0)
       
    62         cache_size = 50;
       
    63     else
       
    64         cache_size = memorysize;
       
    65 
       
    66     // open hardware device if present
       
    67     device = alcOpenDevice(NULL);
       
    68     sources_number = 16;
       
    69     if (device == NULL) {
       
    70         fprintf(stderr,"(Bridge Warning) - failed to open sound device, using software renderer\n");
       
    71         device = alcOpenDevice("Generic Software");
       
    72         sources_number = 32;
       
    73         if (device == NULL) {
       
    74             fprintf(stderr,"(Bridge ERROR) - failed to start software renderer, sound will be disabled\n");
       
    75             return -1;
       
    76         }
       
    77     }
       
    78 
       
    79     fprintf(stderr,"(Bridge Info) - output device: %s\n", alcGetString(device, ALC_DEVICE_SPECIFIER));
       
    80 
       
    81     context = alcCreateContext(device, NULL);
       
    82     alcMakeContextCurrent(context);
       
    83     alcProcessContext(context);
       
    84 
       
    85     if (AL_NO_ERROR != alGetError()) {
       
    86         fprintf(stderr,"(Bridge ERROR) - Failed to create a new contex\n");
       
    87         alcMakeContextCurrent(NULL);
       
    88         alcDestroyContext(context);
       
    89         alcCloseDevice(device);
       
    90         return -2;
       
    91     }
       
    92 
       
    93     Sources = (ALuint *)Malloc (sizeof(ALuint) * sources_number);
       
    94     alGenSources(sources_number, Sources);
       
    95     
       
    96     // set the listener gain, position (on xyz axes), velocity (one value for each axe) and orientation
       
    97     // Position, Velocity and Orientation of the listener
       
    98     ALfloat ListenerPos[] = {0.0, 0.0, 0.0};
       
    99     ALfloat ListenerVel[] = {0.0, 0.0, 0.0};
       
   100     ALfloat ListenerOri[] = {0.0, 0.0, -1.0, 0.0, 1.0, 0.0};
       
   101 
       
   102     alListenerf (AL_GAIN,        1.0f       );
       
   103     alListenerfv(AL_POSITION,    ListenerPos);
       
   104     alListenerfv(AL_VELOCITY,    ListenerVel);
       
   105     alListenerfv(AL_ORIENTATION, ListenerOri);
       
   106 
       
   107     if (AL_NO_ERROR != alGetError()) {
       
   108         fprintf(stderr,"(Bridge ERROR) - Failed to set Listener properties\n");
       
   109         return -3;
       
   110     }
       
   111 
       
   112     the_sounds = (al_sound_t *)Malloc (sizeof(al_sound_t) * cache_size);
       
   113     for (i = 0; i < cache_size; i++) {
       
   114         the_sounds[i].filename = NULL;
       
   115         the_sounds[i].buffer = -1;
       
   116         the_sounds[i].source_index = -1;
       
   117         the_sounds[i].stats = 0;
       
   118     }
       
   119 
       
   120     alGetError();
       
   121     return AL_TRUE;
       
   122 }
       
   123 
       
   124 
       
   125 // Stop all sounds, deallocate all memory and close OpenAL context
       
   126 void openal_close (void) {
       
   127     ALCcontext *context;
       
   128     ALCdevice  *device;
       
   129     int i;
       
   130     
       
   131     if (instances_number == 0) {
       
   132         fprintf(stderr,"(Bridge Warning) - OpenAL not initialized\n");
       
   133         return;
       
   134     }
       
   135 
       
   136     instances_number--;
       
   137     if (instances_number > 0) {
       
   138         return;
       
   139     }
       
   140     
       
   141     //TODO: free other stuff also
       
   142     for (i = 0; i < cache_size; i++)
       
   143         alDeleteBuffers (1, &the_sounds[i].buffer);
       
   144     free(the_sounds);
       
   145 
       
   146     alSourceStopv	(sources_number, Sources);
       
   147     alDeleteSources (sources_number, Sources);
       
   148 
       
   149     free(Sources);
       
   150 
       
   151     context = alcGetCurrentContext();
       
   152     device  = alcGetContextsDevice(context);
       
   153 
       
   154     alcMakeContextCurrent(NULL);
       
   155     alcDestroyContext(context);
       
   156     alcCloseDevice(device);
       
   157 
       
   158     fprintf(stderr,"(Bridge Info) - closed\n");
       
   159 
       
   160     return;
       
   161 }
       
   162 
       
   163 
       
   164 ALboolean openal_ready (void) {
       
   165     if (instances_number >= 1) 
       
   166         return AL_TRUE;
       
   167     else
       
   168         return AL_FALSE;
       
   169 }
       
   170 
       
   171 
       
   172 // Open a file, load into memory and allocate the Source buffer for playing
       
   173 int openal_loadfile (const char *filename){
       
   174     ALenum format, error;
       
   175     ALsizei bitsize, freq;
       
   176     uint32_t fileformat;
       
   177     al_sound_t soundData;
       
   178     int len, i;
       
   179     char *data;
       
   180     FILE *fp;
       
   181     
       
   182     if (openal_ready() == AL_FALSE) {
       
   183         fprintf(stderr,"(Bridge Warning) - not initialized\n");
       
   184         return -1;
       
   185     }
       
   186     
       
   187     // if this sound is already loaded return the index from the_sounds
       
   188     len = strlen(filename);
       
   189     for (i = 0; i < cache_size; i++) {
       
   190         if (the_sounds[i].filename != NULL && strncmp(the_sounds[i].filename, filename, len) == 0) {
       
   191 #ifdef DEBUG
       
   192             fprintf(stderr,"(Bridge Debug) - sound %d is already loaded\n", i);
       
   193 #endif
       
   194             return i;
       
   195         }
       
   196     }
       
   197 
       
   198     if (cache_pointer >= cache_size) {
       
   199         fprintf(stderr,"(Bridge ERROR) - Cache size limit reached; consider allocating more space\n", filename);
       
   200         return -2;
       
   201     }
       
   202     
       
   203     // detect the file format, as written in the first 4 bytes of the header
       
   204     fp = Fopen (filename, "rb");
       
   205 
       
   206     if (fp == NULL) {
       
   207         fprintf(stderr,"(Bridge ERROR) - File %s not loaded\n", filename);
       
   208         return -3;
       
   209     }
       
   210 
       
   211     error = fread (&fileformat, sizeof(uint32_t), 1, fp);
       
   212     fclose (fp);
       
   213 
       
   214     if (error < 0) {
       
   215         fprintf(stderr,"(Bridge ERROR) - File %s is too short\n", filename);
       
   216         return -4;
       
   217     }
       
   218 
       
   219     switch (ENDIAN_BIG_32(fileformat)) {
       
   220         case OGG_FILE_FORMAT:
       
   221             error = load_oggvorbis (filename, &format, &data, &bitsize, &freq);
       
   222             break;
       
   223         case WAV_FILE_FORMAT:
       
   224             error = load_wavpcm (filename, &format, &data, &bitsize, &freq);
       
   225             break;
       
   226         default:
       
   227             fprintf(stderr,"(Bridge ERROR) - File format (%08X) not supported\n", ENDIAN_BIG_32(fileformat));
       
   228             return -5;
       
   229             break;
       
   230     }
       
   231 
       
   232     if (error != 0) {
       
   233         fprintf(stderr,"(Bridge ERROR) - error loading file %s\n", filename);
       
   234         free(data);
       
   235         return -6;
       
   236     }
       
   237 
       
   238     alGenBuffers(1, &soundData.buffer);
       
   239     soundData.filename = filename;
       
   240     soundData.source_index = -1;
       
   241     soundData.stats = 0;
       
   242     
       
   243     if (AL_NO_ERROR != alGetError()) {
       
   244         fprintf(stderr,"(Bridge ERROR) - Failed to allocate memory for buffers\n");
       
   245         return -5;
       
   246     }
       
   247     
       
   248     // copy pcm data in one buffer and free it
       
   249     alBufferData(soundData.buffer, format, data, bitsize, freq);
       
   250     free(data);
       
   251 
       
   252     if (AL_NO_ERROR != alGetError()) {
       
   253         fprintf(stderr,"(Bridge ERROR) - Failed to write data to buffers\n");
       
   254         return -8;
       
   255     }
       
   256     
       
   257     // clear any AL errors beforehand
       
   258     alGetError();
       
   259 
       
   260     fprintf(stderr,"(Bridge Info) - successfully loaded %s\n", filename);
       
   261 
       
   262     // returns the index of the source you just loaded, increments it and exits
       
   263     the_sounds[cache_pointer] = soundData;
       
   264     return cache_pointer++;
       
   265 }