Fix QSettings problems:
- Reopen file in ReadOnly mode if it was open in ReadWrite mode
and is being read. This is needed for stupid QSettings which
opens file in ReadWrite mode just to call readAll() on it.
- Implement setSize(0)
/*
* OpenAL Bridge - a simple portable library for OpenAL interface
* Copyright (c) 2009 Vittorio Giovara <vittorio.giovara@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include "openalbridge.h"
#include "globals.h"
#include "al.h"
#include "alc.h"
#include "wrappers.h"
#include "loaders.h"
#include "string.h"
// Sources are points emitting sound, their number is limited, but a single source can play many buffers
// Buffers hold sound data and are unlimited
ALuint *Sources;
ALuint cache_size, cache_index, sources_number;
ALboolean instances_number;
al_sound_t *the_sounds;
ALint cache_pointer;
// Initialize an OpenAL contex and allocate memory space for data and buffers
// It can be called twice to increase the cache size
int openal_init (void) {
ALCcontext *context;
ALCdevice *device;
int i;
// reuse old context and resize the existing
if (openal_ready() == AL_TRUE) {
fprintf(stderr,"(Bridge Info) - already initialized\n");
instances_number++;
return AL_TRUE;
}
cache_pointer = 0;
instances_number++;
// initial memory size
cache_size = 50;
// open hardware device if present
device = alcOpenDevice(NULL);
sources_number = 16;
if (device == NULL) {
fprintf(stderr,"(Bridge Warning) - failed to open sound device, using software renderer\n");
device = alcOpenDevice("Generic Software");
sources_number = 32;
if (device == NULL) {
fprintf(stderr,"(Bridge ERROR) - failed to start software renderer, sound will be disabled\n");
return -1;
}
}
fprintf(stderr,"(Bridge Info) - output device: %s\n", alcGetString(device, ALC_DEVICE_SPECIFIER));
context = alcCreateContext(device, NULL);
alcMakeContextCurrent(context);
alcProcessContext(context);
if (AL_NO_ERROR != alGetError()) {
fprintf(stderr,"(Bridge ERROR) - Failed to create a new contex\n");
alcMakeContextCurrent(NULL);
alcDestroyContext(context);
alcCloseDevice(device);
return -2;
}
Sources = (ALuint *)Malloc (sizeof(ALuint) * sources_number);
alGenSources(sources_number, Sources);
// set the listener gain, position (on xyz axes), velocity (one value for each axe) and orientation
// Position, Velocity and Orientation of the listener
ALfloat ListenerPos[] = {0.0, 0.0, 0.0};
ALfloat ListenerVel[] = {0.0, 0.0, 0.0};
ALfloat ListenerOri[] = {0.0, 0.0, -1.0, 0.0, 1.0, 0.0};
alListenerf (AL_GAIN, 1.0f );
alListenerfv(AL_POSITION, ListenerPos);
alListenerfv(AL_VELOCITY, ListenerVel);
alListenerfv(AL_ORIENTATION, ListenerOri);
if (AL_NO_ERROR != alGetError()) {
fprintf(stderr,"(Bridge ERROR) - Failed to set Listener properties\n");
return -3;
}
the_sounds = (al_sound_t *)Malloc (sizeof(al_sound_t) * cache_size);
for (i = 0; i < cache_size; i++)
the_sounds[i] = new_sound_el();
alGetError();
return AL_TRUE;
}
// Stop all sounds, deallocate all memory and close OpenAL context
void openal_close (void) {
ALCcontext *context;
ALCdevice *device;
int i;
if (instances_number == 0) {
fprintf(stderr,"(Bridge Warning) - OpenAL not initialized\n");
return;
}
instances_number--;
if (instances_number > 0) {
// release memory only when last session ends
return;
}
for (i = 0; i < cache_size; i++) {
openal_unloadfile(i);
}
free(the_sounds);
alSourceStopv (sources_number, Sources);
alDeleteSources (sources_number, Sources);
free(Sources);
context = alcGetCurrentContext();
device = alcGetContextsDevice(context);
alcMakeContextCurrent(NULL);
alcDestroyContext(context);
alcCloseDevice(device);
fprintf(stderr,"(Bridge Info) - closed\n");
return;
}
ALboolean openal_ready (void) {
if (instances_number >= 1)
return AL_TRUE;
else
return AL_FALSE;
}
// Open a file, load into memory and allocate the Source buffer for playing
int openal_loadfile (const char *filename){
ALenum format, error;
ALsizei bitsize, freq;
uint32_t fileformat;
al_sound_t sound_data;
int len, i, index = -1;
char *data;
FILE *fp;
if (openal_ready() == AL_FALSE) {
fprintf(stderr,"(Bridge Warning) - not initialized\n");
return -1;
}
// if this sound is already loaded return the index from the_sounds
len = strlen(filename);
for (i = 0; i < cache_size; i++) {
if (the_sounds[i].filename != NULL && strncmp(the_sounds[i].filename, filename, len) == 0) {
#ifdef DEBUG
fprintf(stderr,"(Bridge Debug) - sound %d is already loaded\n", i);
#endif
return i;
}
// if we don't have memory available search for a free element
if (cache_pointer >= cache_size)
if (the_sounds[i].is_used == AL_FALSE)
index = i;
}
if (index == -1 && cache_pointer >= cache_size) {
fprintf(stderr,"(Bridge Info) - No free spots found; doubling cache size\n", filename);
cache_size *= 2;
the_sounds = (al_sound_t *)Realloc (the_sounds, sizeof(al_sound_t) * cache_size);
for (i = cache_size - 50; i < cache_size; i++)
the_sounds[i] = new_sound_el();
} else
index = ++cache_pointer;
// detect the file format, as written in the first 4 bytes of the header
fp = Fopen (filename, "rb");
if (fp == NULL) {
fprintf(stderr,"(Bridge ERROR) - File %s not loaded\n", filename);
return -3;
}
error = fread (&fileformat, sizeof(uint32_t), 1, fp);
fclose (fp);
if (error < 0) {
fprintf(stderr,"(Bridge ERROR) - File %s is too short\n", filename);
return -4;
}
switch (ENDIAN_BIG_32(fileformat)) {
case OGG_FILE_FORMAT:
error = load_oggvorbis (filename, &format, &data, &bitsize, &freq);
break;
case WAV_FILE_FORMAT:
error = load_wavpcm (filename, &format, &data, &bitsize, &freq);
break;
default:
fprintf(stderr,"(Bridge ERROR) - File format (%08X) not supported\n", ENDIAN_BIG_32(fileformat));
return -5;
break;
}
if (error != 0) {
fprintf(stderr,"(Bridge ERROR) - error loading file %s\n", filename);
if(data)
free(data);
return -6;
}
// alGenBuffers happens here
sound_data = init_sound_el(filename);
if (AL_NO_ERROR != alGetError()) {
fprintf(stderr,"(Bridge ERROR) - Failed to allocate memory for buffer %d\n", index);
free(data);
return -5;
}
// copy pcm data in one buffer and free it
alBufferData(sound_data.buffer, format, data, bitsize, freq);
free(data);
if (AL_NO_ERROR != alGetError()) {
fprintf(stderr,"(Bridge ERROR) - Failed to write data to buffer %d\n", index);
return -8;
}
// clear any AL errors beforehand
alGetError();
fprintf(stderr,"(Bridge Info) - successfully loaded %s\n", filename);
// returns the index of the source you just loaded, increments it and exits
the_sounds[index] = sound_data;
return index;
}
void openal_unloadfile (uint32_t index) {
ALint state;
if (openal_ready() == AL_TRUE && index < cache_size && the_sounds[index].is_used == AL_TRUE) {
alGetSourcei (Sources[the_sounds[index].source_index], AL_SOURCE_STATE, &state);
if (state == AL_PLAYING || state == AL_PAUSED)
openal_stopsound(index);
// free memory and
alDeleteBuffers (1, &the_sounds[index].buffer);
the_sounds[index] = new_sound_el();
}
}