#include #include #include #include #include "lephisto.h" #include "log.h" #include "pack.h" #include "music.h" #include "sound.h" // ============================================== // sound.c controls the routines for using a // virtual voice wrapper system around the openal // library to get 3D sound. // // We only use position sound and no doppler effect // right now. // ============================================== #define SOUND_PREFIX "../snd/sounds/" #define SOUND_SUFFIX ".wav" // Give the buffers a name. typedef struct alSound_ { char* name; // Buffers name. ALuint buffer; // Associated OpenAL buffer. } alSound; #define VOICE_PLAYING (1<<0) // Voice is playing. #define VOICE_LOOPING (1<<1) // Voice is looping. #define voice_set(v,f) ((v)->flags |= f) #define voice_is(v,f) ((v)->flags & f) // Global sound lock. SDL_mutex* sound_lock = NULL; // Gobal device and context. static ALCcontext* al_context = NULL; static ALCdevice* al_device = NULL; // Music player thread to assure streaming is perfect. static SDL_Thread* music_player = NULL; // List of sounds available (All preloaded into a buffer). static alSound* sound_list = NULL; static int nsound_list = 0; // Current sources playing. static alVoice** voice_stack = NULL; static int nvoice_stack = 0; static int mvoice_stack = 0; // Volume. static ALfloat svolume = 0.3; static int sound_makeList(void); static int sound_load(ALuint* buffer, char* filename); static void sound_free(alSound* snd); static int voice_getSource(alVoice* voc); int sound_init(void) { int ret = 0; sound_lock = SDL_CreateMutex(); SDL_mutexP(sound_lock); const ALchar* device = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); DEBUG("OpenAL using device '%s'", device); // Open the default device. al_device = alcOpenDevice(NULL); if(al_device == NULL) { WARN("Unable to open default sound device"); ret = -1; goto snderr_dev; } // Create the OpenAL context. al_context = alcCreateContext(al_device, NULL); if(sound_lock == NULL) { WARN("Unable to create OpenAL context"); ret = -2; goto snderr_ctx; } // Clear the errors. alGetError(); // Set active context. if(alcMakeContextCurrent(al_context)==AL_FALSE) { WARN("Failure to set default context"); ret = -4; goto snderr_act; } // Set the master gain. alListenerf(AL_GAIN, .1); // Set the distance model. alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); SDL_mutexV(sound_lock); // Load up all the sounds. sound_makeList(); // Start the music server. music_init(); music_player = SDL_CreateThread(music_thread, NULL); return 0; snderr_act: alcDestroyContext(al_context); snderr_ctx: al_context = NULL; alcCloseDevice(al_device); snderr_dev: al_device = NULL; SDL_mutexV(sound_lock); SDL_DestroyMutex(sound_lock); sound_lock = NULL; return ret; } // Clean up after the sound system. void sound_exit(void) { int i; // Free the sounds. for(i = 0; i < nsound_list; i++) sound_free(&sound_list[i]); free(sound_list); sound_list = NULL; nsound_list = 0; // Must stop the music before killing it, // then thread should commit suicide. if(music_player) { music_stop(); music_kill(); SDL_WaitThread(music_player, NULL); music_exit(); } if(sound_lock) { SDL_mutexP(sound_lock); if(al_context) { alcMakeContextCurrent(NULL); alcDestroyContext(al_context); } if(al_device) alcCloseDevice(al_device); SDL_mutexV(sound_lock); SDL_DestroyMutex(sound_lock); } } // Get the buffer to sound of [name]. ALuint sound_get(char* name) { if(sound_lock == NULL) return 0; int i; for(i = 0; i < nsound_list; i++) if(strcmp(name, sound_list[i].name)==0) return sound_list[i].buffer; WARN("Sound '%s' not found in sound list", name); return 0; } // Make list of available sounds. static int sound_makeList(void) { if(sound_lock == NULL) return 0; char** files; uint32_t nfiles, i; char tmp[64]; int len; // Get the file list. files = pack_listfiles(data, &nfiles); // Load the profiles. for(i = 0; i < nfiles; i++) if((strncmp(files[i], SOUND_PREFIX, strlen(SOUND_PREFIX))==0) && (strncmp(files[i] + strlen(files[i]) - strlen(SOUND_SUFFIX), SOUND_SUFFIX, strlen(SOUND_SUFFIX))==0)) { // Expand the selection size. sound_list = realloc(sound_list, ++nsound_list * sizeof(alSound)); // Remove the prefix and suffix. len = strlen(files[i]) - strlen(SOUND_SUFFIX SOUND_PREFIX); strncpy(tmp, files[i] + strlen(SOUND_PREFIX), len); tmp[len] = '\0'; // give it the new name. sound_list[nsound_list-1].name = strdup(tmp); sound_load(&sound_list[nsound_list-1].buffer, files[i]); } // Free the char* allocated by pack. for(i = 0; i < nfiles; i++) free(files[i]); free(files); DEBUG("Loaded %d sound%c", nsound_list, (nsound_list==1)?' ':'s'); return 0; } // Loads a sound into the sound_list. static int sound_load(ALuint* buffer, char* filename) { if(sound_lock == NULL) return 0; void* wavdata; unsigned int size; ALenum err; // Get the file data buffer from the packfile. wavdata = pack_readfile(DATA, filename, &size); SDL_mutexP(sound_lock); // Bind to OpenAL buffer. alGenBuffers(1, buffer); alBufferData(*buffer, AL_FORMAT_MONO16, wavdata, size, 22050); // Errors? if((err = alGetError()) != AL_NO_ERROR) { WARN("OpenAL erro '%d' loading sound '%s'.", err, filename); return 0; } SDL_mutexV(sound_lock); // Finish up. free(wavdata); return 0; } static void sound_free(alSound* snd) { if(sound_lock) return; SDL_mutexP(sound_lock); if(snd->name) free(snd->name); alDeleteBuffers(1, &snd->buffer); SDL_mutexV(sound_lock); } // Update the sounds and prioritize them. void sound_update(void) { if(sound_lock == NULL) return; int i; // TODO: Prioritize the things. SDL_mutexP(sound_lock); for(i = 0; i < nvoice_stack; i++) { if(voice_is(voice_stack[i], VOICE_PLAYING)) { // Update position. alSource3f(voice_stack[i]->source, AL_POSITION, voice_stack[i]->px, voice_stack[i]->py, 0.); //alSource3f(voice_stack[i]->source, AL_VELOCITY, //voice_stack[i]->vx, voice_stack[i]->vy, 0.); } } SDL_mutexV(sound_lock); } // Set all the sounds volume to vol. void sound_volume(const double vol) { if(sound_lock == NULL) return; int i; svolume = (ALfloat) vol; SDL_mutexP(sound_lock); for(i = 0; i < nvoice_stack; i++) if(voice_set(voice_stack[i], VOICE_PLAYING)) alSourcef(voice_stack[i]->source, AL_GAIN, svolume); SDL_mutexV(sound_lock); } // Attempt to alloc a source for a voice. static int voice_getSource(alVoice* voc) { if(sound_lock == NULL) return -1; int ret; ALenum err; ret = 0; // Default return. SDL_mutexP(sound_lock); // Try and grab a source. voc->source = 0; alGenSources(1, &voc->source); err = alGetError(); if(err != AL_NO_ERROR) { voc->source = 0; ret = 1; } else { // Set the properties. alSourcei(voc->source, AL_BUFFER, voc->buffer); // Distance model. alSourcef(voc->source, AL_MAX_DISTANCE, 200.); alSourcef(voc->source, AL_REFERENCE_DISTANCE, 50.); alSourcei(voc->source, AL_SOURCE_RELATIVE, AL_FALSE); alSourcef(voc->source, AL_GAIN, svolume); alSource3f(voc->source, AL_POSITION, voc->px, voc->py, 0.); //alSource3f(voc->source, AL_VELOCITY, voc->vx, voc->vy, 0.); if(voice_is(voc, VOICE_LOOPING)) alSourcei(voc->source, AL_LOOPING, AL_TRUE); else alSourcei(voc->source, AL_LOOPING, AL_FALSE); // Try to play the source. alSourcePlay(voc->source); err = alGetError(); if(err == AL_NO_ERROR) voice_set(voc, VOICE_PLAYING); else ret = 2; } SDL_mutexV(sound_lock); return ret; } alVoice* sound_addVoice(int priority, double px, double py, double vx, double vy, const ALuint buffer, const int looping) { (void)vx; (void)vy; if(sound_lock == NULL) return NULL; alVoice* voc; nvoice_stack++; if(nvoice_stack > mvoice_stack) voice_stack = realloc(voice_stack, ++mvoice_stack*sizeof(alVoice*)); voc = malloc(sizeof(alVoice)); voice_stack[nvoice_stack-1] = voc; // Set the data. voc->priority = priority; voc->start = SDL_GetTicks(); voc->buffer = buffer; if(looping) voice_set(voc, VOICE_LOOPING); voc->px = px; voc->py = py; //voc->vx = vx; //voc->vy = vy; voice_getSource(voc); return voc; } void sound_delVoice(alVoice* voice) { if(sound_lock == NULL) return; ALint stat; int i; for(i = 0; i < nvoice_stack; i++) if(voice == voice_stack[i]) break; // No match found. if(i >= nvoice_stack) { WARN("Unable to find voice to free from stack"); return; } if(voice->source) { SDL_mutexP(sound_lock); alGetSourcei(voice->source, AL_SOURCE_STATE, &stat); if(stat == AL_PLAYING) alSourceStop(voice->source); alDeleteSources(1, &voice->source); voice->source = 0; SDL_mutexV(sound_lock); } free(voice_stack[i]); nvoice_stack--; for(; i < nvoice_stack; i++) voice_stack[i] = voice_stack[i+1]; } void voice_update(alVoice* voice, double px, double py, double vx, double vy) { (void) vx; (void) vy; if(sound_lock == NULL) return; voice->px = px; voice->py = py; //voice->vx = vx; //voice->vy = vy; } void sound_listener(double dir, double px, double py, double vx, double vy) { (void)vx; (void)vy; if(sound_lock == NULL) return; SDL_mutexP(sound_lock); ALfloat ori[] = { 0., 0., 0., 0., 0., 1. }; ori[0] = cos(dir); ori[1] = sin(dir); alListenerfv(AL_ORIENTATION, ori); alListener3f(AL_POSITION, px, py, 0.); //alListener3f(AL_VELOCITY, vx, vy, 0.); SDL_mutexV(sound_lock); }