diff --git a/bin/Makefile b/bin/Makefile index 23f7d1e..f344cc3 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -1,6 +1,6 @@ # OPTIONS. DEBUG = 1 -#DEBUG_PARANOID = 0 +#DEBUG_PARANOID = 1 OS := LINUX #OS := WIN32 @@ -22,10 +22,8 @@ CLUA = -I../lib/lua CSDL = $(shell sdl-config --cflags) CXML = $(shell xml2-config --cflags) CTTF = $(shell freetype-config --cflags) -CAL = -lopenal $(shell freealut-config --cflags) -CVORBIS = CGL = -CFLAGS = $(CLUA) $(CPLUTO) $(CSDL) $(CXML) $(CTTF) $(CGL) $(CAL) $(CVORBIS) $(VERSION) -D$(OS) +CFLAGS = $(CLUA) $(CSDL) $(CXML) $(CTTF) $(CGL) $(VERSION) -D$(OS) ifeq ($(OS),LINUX) CFLAGS += -D_POSIX_SOURCE endif @@ -42,14 +40,12 @@ endif # LDFLAGS. LDLUA = ../lib/lua/liblua.a -LDSDL = $(shell sdl-config --libs) -lSDL_image +LDSDL = $(shell sdl-config --libs) -lSDL_image -lSDL_mixer LDXML = $(shell xml2-config --libs) LDTTF = $(shell freetype-config --libs) LDGL = -lGL -LDAL = -lopenal $(shell freealut-config --libs) -LDVORBIS = -lvorbisfile LDPNG = -lpng -LDFLAGS = -lm $(LDLUA) $(LDPLUTO) $(LDSDL) $(LDXML) $(LDTTF) $(LDGL) $(LDPNG) $(LDAL) $(LDVORBIS) +LDFLAGS = -lm $(LDLUA) $(LDSDL) $(LDXML) $(LDTTF) $(LDGL) $(LDPNG) # This is just for gstat to run some analysis on performance. ifdef DEBUG diff --git a/src/conf.c b/src/conf.c index 8cf3117..60e232d 100644 --- a/src/conf.c +++ b/src/conf.c @@ -261,7 +261,7 @@ void conf_parseCLI(int argc, char** argv) { music_volume(atof(optarg)); break; case 's': - sound_volume(atof(optarg)); + /*sound_volume(atof(optarg));*/ break; case 'G': nebu_forceGenerate(); diff --git a/src/lephisto.c b/src/lephisto.c index 95865f2..f43435d 100644 --- a/src/lephisto.c +++ b/src/lephisto.c @@ -129,12 +129,13 @@ int main(int argc, char** argv) { gtime = SDL_GetTicks(); /* OpenAL sound. */ - if(nosound) + if(nosound) { LOG("Sound is disabled!"); - else { - if(sound_init()) WARN("Problem setting up sound!"); - music_choose("load"); + sound_disabled = 1; + music_disabled = 1; } + if(sound_init()) WARN("Problem setting up sound!"); + music_choose("load"); /* Input. */ if((indjoystick >= 0) || (namjoystick != NULL)) { @@ -301,8 +302,6 @@ void unload_all(void) { * @brief Split main loop from main() for secondary loop hack in toolkit.c. */ void main_loop(void) { - sound_update(); /* Do sound stuff. */ - glClear(GL_COLOR_BUFFER_BIT); fps_control(); /* Everyone loves fps control.. */ diff --git a/src/music.c b/src/music.c index 99828d6..b2296b9 100644 --- a/src/music.c +++ b/src/music.c @@ -1,6 +1,4 @@ -#include -#include -#include +#include #include #include "llua.h" @@ -12,28 +10,12 @@ #include "pack.h" #include "music.h" -#define MUSIC_STOPPED (1<<1) -#define MUSIC_PLAYING (1<<2) -#define MUSIC_KILL (1<<9) -#define music_is(f) (music_state & f) -#define music_set(f) (music_state |= f) -#define music_rm(f) (music_state ^= f) - #define MUSIC_PREFIX "../snd/music/" #define MUSIC_SUFFIX ".ogg" -#define BUFFER_SIZE (4096 * 8) - -#define soundLock() SDL_mutexP(sound_lock) -#define soundUnlock() SDL_mutexV(sound_lock) - -#define musicLock() SDL_mutexP(music_vorbis_lock) -#define musicUnlock() SDL_mutexV(music_vorbis_lock) - #define MUSIC_LUA_PATH "../snd/music.lua" -/* Gobal sound mutex. */ -extern SDL_mutex* sound_lock; +int music_disabled = 0; /* Global music lua. */ static lua_State* music_lua = NULL; @@ -41,237 +23,67 @@ static lua_State* music_lua = NULL; static int musicL_load(lua_State* L); static int musicL_play(lua_State* L); static int musicL_stop(lua_State* L); -static int musicL_get(lua_State* L); static const luaL_reg music_methods[] = { { "load", musicL_load }, { "play", musicL_play }, { "stop", musicL_stop }, - { "get", musicL_get }, {0, 0} }; -/* Saves the music to ram in this structure. */ -typedef struct alMusic_ { - char name[64]; /* Name. */ - Packfile file; - OggVorbis_File stream; - vorbis_info* info; - ALenum format; -} alMusic; - -/* Song currently playing. */ -static SDL_mutex* music_vorbis_lock; -static alMusic music_vorbis; -static ALuint music_buffer[2]; /* Front and back buffer. */ -static ALuint music_source = 0; - /* What is available? */ static char** music_selection = NULL; static int nmusic_selection = 0; -/* Volume */ -static ALfloat mvolume = 1.; - -/* Vorbis suff. */ -static size_t ovpack_read(void* ptr, size_t size, - size_t nmemb, void* datasource) { - return (ssize_t) pack_read(datasource, ptr, size*nmemb); -} - -static int ovpack_retneg(void) { return -1; } /* Must return -1. */ -static int ovpack_retzero(void) { return 0; } /* Must return 0. */ -ov_callbacks ovcall = { - .read_func = ovpack_read, - .seek_func = (int(*)(void*, ogg_int64_t, int)) ovpack_retneg, - .close_func = (int(*)(void*))ovpack_retzero, - .tell_func = (long(*)(void*))ovpack_retneg -}; +static void* music_data = NULL; +static SDL_RWops* music_rw = NULL; +static Mix_Music* music_music = NULL; /* Music stuff. */ -static int stream_loadBuffer(ALuint buffer); static int music_find(void); -static int music_loadOGG(const char* filename); static void music_free(void); /* Lua stuff. */ +static void music_rechoose(void); static int music_luaInit(void); static void music_luaQuit(void); -/* The thread. */ -static unsigned int music_state = 0; -int music_thread(void* unused) { - (void)unused; - - int active; /* Active buffer. */ - ALint state; - - /* Main loop. */ - while(!music_is(MUSIC_KILL)) { - if(music_is(MUSIC_PLAYING)) { - if(music_vorbis.file.end == 0) - music_rm(MUSIC_PLAYING); - else { - music_rm(MUSIC_STOPPED); - - musicLock(); /* Lock the mutex. */ - soundLock(); - - /* Start playing current song. */ - active = 0; /* Load first buffer. */ - if(stream_loadBuffer(music_buffer[active])) music_rm(MUSIC_PLAYING); - alSourceQueueBuffers(music_source, 1, &music_buffer[active]); - - /* Start playing with buffer laoaded. */ - alSourcePlay(music_source); - - active = 1; /* Load the second buffer. */ - if(stream_loadBuffer(music_buffer[active])) music_rm(MUSIC_PLAYING); - alSourceQueueBuffers(music_source, 1, &music_buffer[active]); - - soundUnlock(); - - active = 0; - } - while(music_is(MUSIC_PLAYING)) { - soundLock(); - - alGetSourcei(music_source, AL_BUFFERS_PROCESSED, &state); - - if(state > 0) { - /* Refill active buffer. */ - alSourceUnqueueBuffers(music_source, 1, &music_buffer[active]); - if(stream_loadBuffer(music_buffer[active])) music_rm(MUSIC_PLAYING); - alSourceQueueBuffers(music_source, 1, &music_buffer[active]); - - active = 1 - active; - } - soundUnlock(); - - SDL_Delay(0); - } - soundLock(); - - alSourceStop(music_source); - alSourceUnqueueBuffers(music_source, 2, music_buffer); - - soundUnlock(); - musicUnlock(); - } - music_set(MUSIC_STOPPED); - SDL_Delay(0); /* We must not kill resources. */ - } - return 0; -} - -static int stream_loadBuffer(ALuint buffer) { - int size, section, result; - char dat[BUFFER_SIZE]; /* Buffer to hold the data. */ - - size = 0; - while(size < BUFFER_SIZE) { - /* Fill up the entire data buffer. */ - result = ov_read( - &music_vorbis.stream, /* Stream. */ - dat + size, /* Data. */ - BUFFER_SIZE - size, /* Amount to read. */ - 0, /* Big endian?. */ - 2, /* 16 bit. */ - 1, /* Signed. */ - §ion); /* Current bitstream. */ - - if(result == 0) return 1; - else if(result == OV_HOLE) { - WARN("OGG: Vorbis hole detected in music!"); - return 0; - } - else if(result == OV_EBADLINK) { - WARN("OGG: Invalid stream section or corrupt link in music!"); - return -1; - } - - size += result; - if(size == BUFFER_SIZE) break; /* Buffer is full. */ - } - /* Load the buffer. */ - alBufferData(buffer, music_vorbis.format, dat, BUFFER_SIZE, - music_vorbis.info->rate); - - return 0; -} - /* Init/Exit. */ int music_init(void) { - music_vorbis_lock = SDL_CreateMutex(); - music_vorbis.file.end = 0; /* Indication that it's not loaded.. */ + if(music_disabled) return 0; - soundLock(); - - alGenBuffers(2, music_buffer); - alGenSources(1, &music_source); - alSourcef(music_source, AL_GAIN, mvolume); - alSourcef(music_source, AL_ROLLOFF_FACTOR, 0.); - alSourcei(music_source, AL_SOURCE_RELATIVE, AL_FALSE); - - /* Start the lua music stuff. */ - music_luaInit(); - - soundUnlock(); + if(music_find() < 0) return -1; + if(music_luaInit() < 0) return -1; + music_volume(0.7); return 0; } -int music_makeList(void) { - return music_find(); -} - void music_exit(void) { - int i; - - /* Free the music. */ - alDeleteBuffers(2, music_buffer); - alDeleteSources(1, &music_source); music_free(); - - /* Free selection */ - for(i = 0; i < nmusic_selection; i++) - free(music_selection[i]); - free(music_selection); - - /* Bye bye Lua. */ - music_luaQuit(); - - SDL_DestroyMutex(music_vorbis_lock); } -/* Internal music loading ruitines. */ -static int music_loadOGG(const char* filename) { - /* Free currently loaded ogg. */ - music_free(); - - musicLock(); - - /* Set the new name. */ - strncpy(music_vorbis.name, filename, 64); - music_vorbis.name[64-1] = '\0'; - - /* Load the new ogg. */ - pack_open(&music_vorbis.file, DATA, filename); - ov_open_callbacks(&music_vorbis.file, &music_vorbis.stream, NULL, 0, ovcall); - music_vorbis.info = ov_info(&music_vorbis.stream, -1); - - if(music_vorbis.info->channels == 1) music_vorbis.format = AL_FORMAT_MONO16; - else music_vorbis.format = AL_FORMAT_STEREO16; - - musicUnlock(); - - return 0; +/* Free the current playing music. */ +static void music_free(void) { + if(music_music != NULL) { + Mix_HookMusicFinished(NULL); + Mix_HaltMusic(); + Mix_FreeMusic(music_music); + /*SDL_FreeRW(music_rw);*/ /*FreeMusic frees it itself. */ + free(music_data); + music_music = NULL; + music_rw = NULL; + music_data = NULL; + } } +/* Internal music loading routines. */ static int music_find(void) { char** files; uint32_t nfiles, i; char tmp[64]; int len; + if(music_disabled) return 0; + /* Get the file list. */ files = pack_listfiles(data, &nfiles); @@ -301,64 +113,45 @@ static int music_find(void) { return 0; } -static void music_free(void) { - musicLock(); +/* Music control functions. */ +int music_volume(const double vol) { + if(music_disabled) return 0; - if(music_vorbis.file.end != 0) { - ov_clear(&music_vorbis.stream); - pack_close(&music_vorbis.file); - music_vorbis.file.end = 0; /* Somewhat ended. */ - } - - musicUnlock(); -} - -void music_volume(const double vol) { - if(sound_lock == NULL) return; - - /* Sanity check! */ - ALfloat fvol = ABS(vol); - if(fvol > 1.) fvol = 1.; - - mvolume = fvol; - - /* Only needed if playing! */ - if(music_set(MUSIC_PLAYING)) { - soundLock(); - alSourcef(music_source, AL_GAIN, fvol); - soundUnlock(); - } + return Mix_VolumeMusic(MIX_MAX_VOLUME*vol); } /* Music control functions. */ void music_load(const char* name) { - if(sound_lock == NULL) return; + unsigned int size; + char filename[PATH_MAX]; - int i; - char tmp[64]; + if(music_disabled) return; - music_stop(); - while(!music_is(MUSIC_STOPPED)) SDL_Delay(0); + music_free(); - for(i = 0; i < nmusic_selection; i++) - if(strcmp(music_selection[i], name)==0) { - snprintf(tmp, 64, MUSIC_PREFIX"%s"MUSIC_SUFFIX, name); - music_loadOGG(tmp); - return; - } - WARN("Requested load song '%s' but it can't be found in the music stack",name); + /* Load the data. */ + snprintf(filename, PATH_MAX, MUSIC_PREFIX"%s"MUSIC_SUFFIX, name); + music_data = pack_readfile(DATA, filename, &size); + music_rw = SDL_RWFromMem(music_data, size); + music_music = Mix_LoadMUS_RW(music_rw); + if(music_music == NULL) + WARN("SDL_Mixer: %s", Mix_GetError()); + + Mix_HookMusicFinished(music_rechoose); } void music_play(void) { - if(!music_is(MUSIC_PLAYING)) music_set(MUSIC_PLAYING); + if(music_music == NULL) return; + + if(Mix_FadeInMusic(music_music, 0, 500) < 0) + WARN("SDL_Mixer: %s", Mix_GetError()); } void music_stop(void) { - if(music_is(MUSIC_PLAYING)) music_rm(MUSIC_PLAYING); -} + if(music_music == NULL) return; -void music_kill(void) { - if(!music_is(MUSIC_KILL)) music_set(MUSIC_KILL); + if(Mix_FadeOutMusic(500) < 0) + WARN("SDL_Mixer: %s", Mix_GetError()); } /* Music lua stuff. */ @@ -411,7 +204,7 @@ int lua_loadMusic(lua_State* L, int read_only) { } int music_choose(char* situation) { - if(sound_lock == NULL) return 0; + if(music_disabled) return 0; lua_getglobal(music_lua, "choose"); lua_pushstring(music_lua, situation); @@ -423,6 +216,11 @@ int music_choose(char* situation) { return 0; } +/* Attempt to rechoose the music. */ +static void music_rechoose(void) { + music_choose("idle"); +} + /* The music lua functions. */ static int musicL_load(lua_State* L) { char* str; @@ -448,11 +246,3 @@ static int musicL_stop(lua_State* L) { return 0; } -static int musicL_get(lua_State* L) { - musicLock(); - lua_pushstring(L, music_vorbis.name); - musicUnlock(); - - return 1; -} - diff --git a/src/music.h b/src/music.h index 8d071d0..b932e69 100644 --- a/src/music.h +++ b/src/music.h @@ -1,17 +1,18 @@ #pragma once #include "lua.h" +extern int music_disabled; + /* Thread. */ int music_thread(void* unused); void music_kill(void); /* Init/Exit. */ int music_init(void); -int music_makeList(void); void music_exit(void); /* Music control. */ -void music_volume(const double vol); +int music_volume(const double vol); void music_load(const char* name); void music_play(void); void music_stop(void); diff --git a/src/outfit.c b/src/outfit.c index aa9cceb..26e871a 100644 --- a/src/outfit.c +++ b/src/outfit.c @@ -377,6 +377,9 @@ static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent) { xmlNodePtr node; char str[PATH_MAX] = "\0"; + /* Defaults. */ + tmp->u.blt.sound = -1; + node = parent->xmlChildrenNode; do { /* Load all the things. */ @@ -401,13 +404,13 @@ static void outfit_parseSWeapon(Outfit* tmp, const xmlNodePtr parent) { #define MELEMENT(o,s) \ if (o) WARN("Outfit '%s' missing/invalid '"s"' element", tmp->name) - MELEMENT(tmp->u.blt.gfx_space==NULL, "gfx"); - MELEMENT((sound_lock!=NULL) && (tmp->u.blt.sound==0), "sound"); - MELEMENT(tmp->u.blt.delay==0, "delay"); - MELEMENT(tmp->u.blt.speed==0, "speed"); - MELEMENT(tmp->u.blt.range==0, "range"); - MELEMENT(tmp->u.blt.accuracy==0, "accuracy"); - MELEMENT(tmp->u.blt.damage==0, "damage"); + MELEMENT(tmp->u.blt.gfx_space==NULL, "gfx"); + MELEMENT((sound_disabled!=NULL) && (tmp->u.blt.sound==0), "sound"); + MELEMENT(tmp->u.blt.delay==0, "delay"); + MELEMENT(tmp->u.blt.speed==0, "speed"); + MELEMENT(tmp->u.blt.range==0, "range"); + MELEMENT(tmp->u.blt.accuracy==0, "accuracy"); + MELEMENT(tmp->u.blt.damage==0, "damage"); #undef MELEMENT } @@ -464,7 +467,7 @@ static void outfit_parseSAmmo(Outfit* tmp, const xmlNodePtr parent) { #define MELEMENT(o,s) if(o) WARN("Outfit '%s' missing '"s"' element", tmp->name) MELEMENT(tmp->u.amm.gfx_space == NULL, "gfx"); - MELEMENT((sound_lock != NULL) && (tmp->u.amm.sound == 0), "sound"); + MELEMENT((sound_disabled != NULL) && (tmp->u.amm.sound == 0), "sound"); MELEMENT(tmp->u.amm.thrust==0, "thrust"); MELEMENT(tmp->u.amm.turn==0, "turn"); MELEMENT(tmp->u.amm.speed==0, "speed"); diff --git a/src/outfit.h b/src/outfit.h index f7a6207..f6e090f 100644 --- a/src/outfit.h +++ b/src/outfit.h @@ -68,8 +68,8 @@ typedef struct Outfit_ { double damage; /* Damage. */ glTexture* gfx_space; - ALuint sound; /* Sound to play. */ - int spfx; /* Special effect on hit. */ + int sound; /* Sound to play. */ + int spfx; /* Special effect on hit. */ } blt; struct { /* Beam. */ double range; /* Distance it travels. */ @@ -94,8 +94,8 @@ typedef struct Outfit_ { double damage; /* Damage. */ glTexture* gfx_space; - ALuint sound; /* Sound to play. */ - int spfx; /* Special effect on hit. */ + int sound; /* Sound to play. */ + int spfx; /* Special effect on hit. */ } amm; struct { /* Modification. */ /* Movement. */ @@ -112,7 +112,7 @@ typedef struct Outfit_ { } mod; struct { /* Afterburner. */ double rumble; /* Percent of rumble. */ - ALuint sound; /* Sound of the afterburner. */ + int sound; /* Sound of the afterburner. */ double thrust_perc, thrust_abs; /* Percent and absolute thrust bonus. */ double speed_perc, speed_abs; /* Percent and absolute speed bonus. */ double energy; /* Energy used while active. */ diff --git a/src/player.c b/src/player.c index 825686a..c4b3daf 100644 --- a/src/player.c +++ b/src/player.c @@ -37,10 +37,12 @@ #define TARGET_WIDTH 128 #define TARGET_HEIGHT 96 +#define PLAYER_RESERVED_CHANNELS 4 +#define PLAYER_CHANNEL 0 + /* Player stuff. */ Pilot* player = NULL; /* extern in pilot.h */ static Ship* player_ship = NULL; /* Temp ship to hold when naming it. */ -static alVoice* player_voice = NULL; /* Player's voice. */ static double player_px, player_py, player_vx, player_vy, player_dir; static int player_credits = 0; /* Temp hack. */ @@ -147,7 +149,7 @@ static void player_newMake(void); static void player_newShipMake(char* name); /* Sound. */ static void player_initSound(void); -static void player_playSound(ALuint sound, int once); +static void player_playSound(int sound, int once); static void player_stopSound(void); /* Gui. */ static void rect_parse(const xmlNodePtr parent, @@ -429,23 +431,17 @@ void player_cleanup(void) { /* Initializes the player sound. */ static void player_initSound(void) { - if(player_voice == NULL) { - player_voice = sound_addVoice(0, /* Max priority. */ - 0., 0., 0., 0., 0., /* No properties. */ - VOICE_LOOPING | VOICE_STATIC); - } + sound_reserve(PLAYER_RESERVED_CHANNELS); + sound_createGroup(PLAYER_CHANNEL, 0, PLAYER_RESERVED_CHANNELS); } /* Play a sound. */ -static void player_playSound(ALuint sound, int once) { - unsigned int flags = VOICE_STATIC; - - if(once == 0) flags |= VOICE_LOOPING; - voice_buffer(player_voice, sound, flags); +static void player_playSound(int sound, int once) { + sound_playGroup(PLAYER_CHANNEL, sound, once); } static void player_stopSound(void) { - voice_stop(player_voice); + sound_stopGroup(PLAYER_CHANNEL); } void player_message(const char* fmt, ...) { @@ -1239,10 +1235,6 @@ void player_think(Pilot* pplayer) { vect_pset(&pplayer->solid->force, pplayer->thrust * player_acc, pplayer->solid->dir); - /* Set the listener stuff. */ - sound_listener(pplayer->solid->dir, - pplayer->solid->pos.x, pplayer->solid->pos.y, - pplayer->solid->vel.x, pplayer->solid->vel.y); } /* Modify the radar resolution. */ diff --git a/src/ship.h b/src/ship.h index 33ce395..f934c58 100644 --- a/src/ship.h +++ b/src/ship.h @@ -52,7 +52,7 @@ typedef struct Ship_ { char* gui; /* Sound. */ - ALuint sound; + int sound; /* Characteristics. */ int crew; diff --git a/src/sound.c b/src/sound.c index 99c425f..c8efb90 100644 --- a/src/sound.c +++ b/src/sound.c @@ -1,7 +1,7 @@ #include -#include -#include + #include +#include #include #include "lephisto.h" @@ -10,278 +10,121 @@ #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_CHANNEL_MAX 256 /* Overkill. */ -/* ============================================== */ -/* Sound Overview: */ -/* --------------- */ -/* */ -/* We use a priority virtual voice system with */ -/* pre-allocated buffers. */ -/* */ -/* Nameing: */ -/* -- buffer - Sound sample. */ -/* -- source - openal object that plays sound. */ -/* -- voice - Virtual object that wants to play sound. */ -/* */ -/* First we allocate all the buffers based on what */ -/* we find inside the datafile. */ -/* Then we allocate all the possible sources (giving */ -/* the music system what it needs). */ -/* Now we allow the user to dynamically create */ -/* voices, these voices will always try to grab */ -/* a source from the source pool. If they can't, */ -/* they will pretend to play the buffer. */ -/* Every so often we'll check to see if the important */ -/* voices are being played and take away the sources */ -/* from the lesser ones. */ -/* ============================================== */ +#define SOUND_PREFIX "../snd/sounds/" +#define SOUND_SUFFIX ".wav" -/* Sound parameters - TODO: make it variable per source. */ -#define SOUND_ROLLOFF_FACTOR 1. -#define SOUND_REFERENCE_DIST 500. -#define SOUND_MAX_DIST 1000. - - -#define SOUND_PREFIX "../snd/sounds/" -#define SOUND_SUFFIX ".wav" - -#define soundLock() SDL_mutexP(sound_lock) -#define soundUnlock() SDL_mutexV(sound_lock) +int sound_disabled = 0; /* Whether sound is disabled. */ +static int sound_reserved = 0; /* Amount of reserved channels. */ /* Give the buffers a name. */ typedef struct alSound_ { - char* name; /* Buffers name. */ - ALuint buffer; /* Associated OpenAL buffer. */ + char* name; /* Buffers name. */ + Mix_Chunk* buffer; } alSound; -/* Voice private flags (public in sound.h). */ -#define VOICE_PLAYING (1<<0) /* Voice is playing. */ -#define VOICE_DONE (1<<1) /* Voice is done - must remove. */ -#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; - -/* Threads. */ -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; - -/* Struct to hold all the sources and currently attached voice. */ -static ALuint* source_stack = NULL; /* And it's stack. */ -static int source_nstack = 0; - -/* Virtual voice. */ -struct alVoice { - alVoice* next; /* Yes it's a linked list. */ - - /*ALuint id; // Unique id for the voice. */ - - ALuint source; /* Source itself, 0 if not set. */ - ALuint buffer; /* Buffer. */ - - int priority; /* Base priority. */ - - double px, py; /* Position. */ - /*double vx, vy; // Velocity. */ - - unsigned int start; /* time started in ms. */ - unsigned int flags; /* Flags to set properties. */ -}; -static alVoice* voice_start = NULL; -static alVoice* voice_end = NULL; - -/* Volume. */ -static ALfloat svolume = 0.3; +static alSound* sound_list = NULL; +static int sound_nlist = 0; static int sound_makeList(void); -static int sound_load(ALuint* buffer, char* filename); +static Mix_Chunk* sound_load(char* filename); static void sound_free(alSound* snd); -static int voice_getSource(alVoice* voc); -static void voice_init(alVoice* voice); -static int voice_play(alVoice* voice); -static void voice_rm(alVoice* prev, alVoice* voice); -static void voice_parseFlags(alVoice* voice, const unsigned int flags); +/* Init the sound subsystem. */ int sound_init(void) { - int mem, ret; - ALenum err; + int frequency; + Uint16 format; + int channels; + SDL_version compile_version; + const SDL_version* link_version; - ret = 0; - - /* We'll need a mutex. */ - sound_lock = SDL_CreateMutex(); - soundLock(); + if(sound_disabled) return 0; - /* Initialize alut - I think it's worth it. */ - alutInitWithoutContext(NULL, NULL); - - const ALchar* device = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); - - /* Open the default device. */ - al_device = alcOpenDevice(NULL); - if(al_device == NULL) { - WARN("Unable to open default sound device"); - ret = -1; - goto snderr_dev; + SDL_InitSubSystem(SDL_INIT_AUDIO); + if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) < 0) { + WARN("SDL_Mixer: %s", Mix_GetError()); + return -1; } - - /* 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); - - /* We can unlock now. */ - soundUnlock(); - - /* Start the music server. */ - music_init(); - - /* Start allocating the sources - music has already taken this. */ - alGetError(); /* Another error clear. */ - mem = 0; - while(((err = alGetError()) == AL_NO_ERROR) && (source_nstack < 128)) { - if(mem < source_nstack+1) { - /* Allocate more memory. */ - mem += 32; - source_stack = realloc(source_stack, sizeof(ALuint) * mem); - } - alGenSources(1, &source_stack[source_nstack]); - source_nstack++; - } - - /* Use minimal ram. */ - source_stack = realloc(source_stack, sizeof(ALuint) * source_nstack); + + Mix_AllocateChannels(SOUND_CHANNEL_MAX); /* Debug magic. */ - DEBUG("OpenAL: %s", device); - DEBUG("Sources: %d", source_nstack); - DEBUG("Renderer: %s", alGetString(AL_RENDERER)); - DEBUG("Version: %s", alGetString(AL_VERSION)); + Mix_QuerySpec(&frequency, &format, &channels); + MIX_VERSION(&compile_version); + link_version = Mix_Linked_Version(); + DEBUG("SDL_Mixer: %d.%d.%d [compiled: %d.%d.%d]", + compile_version.major, compile_version.minor, compile_version.patch, + link_version->major, link_version->minor, link_version->patch); + DEBUG("Format: %d HZ %s", frequency, (channels == 2) ? "Sterio" : "Mono"); + DEBUG(); /* Load up all the sounds. */ sound_makeList(); - music_makeList(); /* And music. */ + sound_volume(0.3); - /* Now start the music thread. */ - music_player = SDL_CreateThread(music_thread, NULL); + /* Init the music. */ + music_init(); return 0; - -snderr_act: - alcDestroyContext(al_context); -snderr_ctx: - al_context = NULL; - alcCloseDevice(al_device); -snderr_dev: - al_device = NULL; - soundUnlock(); - SDL_DestroyMutex(sound_lock); - sound_lock = NULL; - ERR("Sound failed to initialize."); - 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]); + 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(); - } + sound_nlist = 0; - /* Clean up the voices. */ - while(voice_start != NULL) - voice_rm(NULL, voice_start); - - /* Clean up the sources. */ - if(source_stack) - alDeleteSources(source_nstack, source_stack); - - if(sound_lock) { - soundLock(); - - if(al_context) { - alcMakeContextCurrent(NULL); - alcDestroyContext(al_context); - } - if(al_device) alcCloseDevice(al_device); - - soundUnlock(); - SDL_DestroyMutex(sound_lock); - } - /* Cya alut! */ - alutExit(); + music_exit(); } -/* Get the buffer to sound of [name]. */ -ALuint sound_get(char* name) { - if(sound_lock == NULL) return 0; - +/* Get the buffer to sound of name. */ +int sound_get(char* name) { int i; - for(i = 0; i < nsound_list; i++) + + if(sound_disabled) return 0; + + for(i = 0; i < sound_nlist; i++) if(strcmp(name, sound_list[i].name)==0) - return sound_list[i].buffer; + return i; + WARN("Sound '%s' not found in sound list", name); + return -1; +} + +/* Play the sound. */ +int sound_play(int sound) { + int channel; + + if(sound_disabled) return 0; + + if((sound < 0) || (sound > sound_nlist)) + return -1; + + channel = Mix_PlayChannel(-1, sound_list[sound].buffer, 0); + + if(channel < 0) + DEBUG("SDL_Mixer: %s", Mix_GetError()); + 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; int mem; + if(sound_disabled) return 0; + /* Get the file list. */ files = pack_listfiles(data, &nfiles); @@ -293,9 +136,8 @@ static int sound_makeList(void) { SOUND_SUFFIX, strlen(SOUND_SUFFIX))==0)) { /* Expand the selection size. */ - nsound_list++; - if(nsound_list > mem) { - /* We must grow. */ + sound_nlist++; + if(sound_nlist > mem) { /* We must grow. */ mem += 32; /* We'll overallocate most likely. */ sound_list = realloc(sound_list, mem*sizeof(alSound)); } @@ -306,325 +148,122 @@ static int sound_makeList(void) { 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]); + sound_list[sound_nlist-1].name = strdup(tmp); + sound_list[sound_nlist-1].buffer = sound_load(files[i]); } /* Shrink to minimum ram usage. */ - sound_list = realloc(sound_list, nsound_list*sizeof(alSound)); + sound_list = realloc(sound_list, sound_nlist*sizeof(alSound)); /* Free the char* allocated by pack. */ for(i = 0; i < nfiles; i++) free(files[i]); free(files); - DEBUG("Loaded %d sound%s", nsound_list, (nsound_list==1)?"":"s"); + DEBUG("Loaded %d sound%s", sound_nlist, (sound_nlist==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; +/* Set the volume. */ +int sound_volume(const double vol) { + if(sound_disabled) return 0; + return Mix_Volume(-1, MIX_MAX_VOLUME*vol); +} +/* Loads a sound into the sound_list. */ +static Mix_Chunk* sound_load(char* filename) { void* wavdata; unsigned int size; - ALenum err; + SDL_RWops* rw; + Mix_Chunk* buffer; + + if(sound_disabled) return NULL; /* Get the file data buffer from the packfile. */ wavdata = pack_readfile(DATA, filename, &size); - soundLock(); + rw = SDL_RWFromMem(wavdata, size); /* Bind to OpenAL buffer. */ - (*buffer) = alutCreateBufferFromFileImage(wavdata, size); - if((*buffer) == AL_NONE) WARN("FAILURE: %s", alutGetErrorString(alutGetError())); - /*alGenBuffers(1, buffer); */ - /*alBufferData(*buffer, AL_FORMAT_MONO16, wavdata, size, 22050); */ + buffer = Mix_LoadWAV_RW(rw, 1); - /* Errors? */ - if((err = alGetError()) != AL_NO_ERROR) { - WARN("OpenAL erro '%d' loading sound '%s'.", err, filename); - return 0; - } - - soundUnlock(); + if(buffer == NULL) + DEBUG("SDL_Mixer: %s", Mix_GetError()); /* Finish up. */ free(wavdata); - return 0; + return buffer; } static void sound_free(alSound* snd) { - if(sound_lock) return; - - soundLock(); - /* Free the stuff. */ - if(snd->name) free(snd->name); - alDeleteBuffers(1, &snd->buffer); - - soundUnlock(); -} - -/* Update the sounds and prioritize them. */ -void sound_update(void) { - ALint state; - alVoice* voice, *prev, *next; - - if(sound_lock == NULL) return; /* Sound system is off. */ - if(voice_start == NULL) return; /* No voices. */ - - soundLock(); - - /* Update sound. */ - prev = NULL; - voice = voice_start; - do { - next = voice->next; - - /* Get status. */ - state = -1; - if(voice->source != 0) - alGetSourcei(voice->source, AL_SOURCE_STATE, &state); - - if(!voice_is(voice, VOICE_DONE)) { /* Still working. */ - /* Voice has a source. */ - if(voice->source != 0) { - - /* Update position. */ - alSource3f(voice->source, AL_POSITION, - voice->px, voice->py, 0.); - /*alSource3f(voice->source, AL_VELOCITY, - voice->vx, voice->vy, 0.);*/ - } - - prev = voice; - }else { - /* Delete them. */ - if(state != AL_PLAYING) - voice_rm(prev, voice); /* Do not set prev to voice. */ - else - prev = voice; - } - voice = next; - } while(voice != NULL); - - soundUnlock(); -} - -/* Remove a voice. */ -static void voice_rm(alVoice* prev, alVoice* voice) { - ALint state; - - if(voice->source != 0) { /* Source must exist. */ - /* Stop it if playing. */ - alGetSourcei(voice->source, AL_SOURCE_STATE, &state); - if(state == AL_PLAYING) alSourceStop(voice->source); - - /* Clear it and get rid of it. */ - source_stack[source_nstack++] = voice->source; /* Throw it back. */ + if(snd->name) { + free(snd->name); + snd->name = NULL; } - /* Delete from linked list. */ - if(prev == NULL) /* Was the first member. */ - voice_start = voice->next; - else /* Not first memmber. */ - prev->next = voice->next; - if(voice_end == voice) /* Last voice in linked list. */ - voice_end = prev; - free(voice); + Mix_FreeChunk(snd->buffer); + snd->buffer = NULL; } -/* Set all the sounds volume to vol. */ -void sound_volume(const double vol) { - if(sound_lock == NULL) return; - - svolume = (ALfloat) vol; -} - -/* Attempt to alloc a source for a voice. */ -static int voice_getSource(alVoice* voc) { +/* Reserve num channels. */ +int sound_reserve(int num) { int ret; - /* Sound system isn't on. */ - if(sound_lock == NULL) return -1; + if(sound_disabled) return 0; - ret = 0; /* Default return. */ + sound_reserved += num; + ret = Mix_ReserveChannels(num); - soundLock(); - - /* Try and grab a source. */ - if(source_nstack > 0) { /* We have the source. */ - /* We must pull it from the free source vector. */ - voc->source = source_stack[--source_nstack]; - - /* Initialize and play. */ - voice_init(voc); - ret = voice_play(voc); - } else - voc->source = 0; - - soundUnlock(); - - return ret; -} - -/* Must lock becore calling. */ -static void voice_init(alVoice* voice) { - /* Distance model. */ - alSourcef(voice->source, AL_ROLLOFF_FACTOR, SOUND_ROLLOFF_FACTOR); - alSourcef(voice->source, AL_MAX_DISTANCE, SOUND_MAX_DIST); - alSourcef(voice->source, AL_REFERENCE_DISTANCE, SOUND_REFERENCE_DIST); - - alSourcef(voice->source, AL_GAIN, svolume); - alSource3f(voice->source, AL_POSITION, voice->px, voice->py, 0.); - /*alSource3f(voice->source, AL_VELOCITY, voice->vx, voice->vy, 0.); */ - if(voice_is(voice, VOICE_LOOPING)) - alSourcei(voice->source, AL_LOOPING, AL_TRUE); - else - alSourcei(voice->source, AL_LOOPING, AL_FALSE); -} - -/* Create a dynamic moving piece. */ -alVoice* sound_addVoice(int priority, double px, double py, - double vx, double vy, const ALuint buffer, const unsigned int flags) { - - (void)vx; - (void)vy; - alVoice* voc; - - if(sound_lock == NULL) return NULL; - - /* Allocate the voice. */ - voc = malloc(sizeof(alVoice)); - - /* Set the data. */ - voc->next = NULL; - voc->priority = priority; - voc->start = SDL_GetTicks(); - voc->buffer = buffer; - - /* Handle positions. */ - voc->px = px; - voc->py = py; - /*voc->vx = vx; */ - /*voc->vy = vy; */ - - /* Handle the flags. */ - voice_parseFlags(voc, flags); - - /* Get the source. */ - voice_getSource(voc); - - if(voice_start == NULL) { - voice_start = voc; - voice_end = voc; - } else { - if(voice_end != NULL) - voice_end->next = voc; - voice_end = voc; - } - - return voc; -} - -/* Delete the voice. */ -void sound_delVoice(alVoice* voice) { - if(sound_lock == NULL) return; - - voice_set(voice, VOICE_DONE); -} - -/* Update voice position, should be run once per frame. */ -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; */ -} - -/* Changes the voice's buffer. */ -void voice_buffer(alVoice* voice, const ALuint buffer, const unsigned int flags) { - if(voice == NULL) return; - - voice->buffer = buffer; - voice_parseFlags(voice, flags); - - /* Start playing. */ - soundLock(); - voice_play(voice); - soundUnlock(); -} - -/* Stop playing sound. */ -void voice_stop(alVoice* voice) { - if(voice == NULL) return; - - soundLock(); - if(voice->source != 0) - alSourceStop(voice->source); - soundUnlock(); -} - -/* Handle flags. */ -static void voice_parseFlags(alVoice* voice, const unsigned int flags) { - voice->flags = 0; /* Defaults. */ - - /* Looping. */ - if(flags & VOICE_LOOPING) - voice_set(voice, VOICE_LOOPING); - - if(flags & VOICE_STATIC) - alSourcei(voice->source, AL_SOURCE_RELATIVE, AL_TRUE); - else - alSourcei(voice->source, AL_SOURCE_RELATIVE, AL_FALSE); -} - -/* Make a voice play. Must lock before calling. */ -static int voice_play(alVoice* voice) { - ALenum err; - ALint state; - - /* Must have buffer. */ - if(voice->buffer != 0) { - alGetSourcei(voice->source, AL_SOURCE_STATE, &state); - if(state == AL_PLAYING) - alSourceStop(voice->source); - /* Set buffer. */ - alSourcei(voice->source, AL_BUFFER, voice->buffer); - - /* Try to play the source. */ - alSourcePlay(voice->source); - err = alGetError(); - if(err == AL_NO_ERROR) voice_set(voice, VOICE_PLAYING); - else return 2; + if(ret != sound_reserved) { + WARN("Unable to reserve %d channels: %s", sound_reserved, Mix_GetError()); + return -1; } return 0; } -void sound_listener(double dir, double px, double py, double vx, double vy) { - (void)vx; - (void)vy; +/* Create a sound group. */ +int sound_createGroup(int tag, int start, int size) { + int ret; - if(sound_lock == NULL) return; + if(sound_disabled) return 0; - soundLock(); + ret = Mix_GroupChannels(start, start+size-1, tag); - /* Set orientation. */ - 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, 1.); - /*alListener3f(AL_VELOCITY, vx, vy, 0.); */ + if(ret != size) { + WARN("Unable to create sound group: %s", Mix_GetError()); + return -1; + } - soundUnlock(); + return 0; +} + +/* Play a sound in a group. */ +int sound_playGroup(int group, int sound, int once) { + int ret, channel; + + if(sound_disabled) return 0; + + channel = Mix_GroupAvailable(group); + + ret = Mix_PlayChannel(channel, sound_list[sound].buffer, + (once == 0) ? -1 : 0); + + if(ret < 0) { + WARN("Unable to play sound %d for group %d: %s", + sound, group, Mix_GetError()); + return -1; + } + + return 0; +} + +/* Stop all the sounds in a group. */ +void sound_stopGroup(int group) { + if(sound_disabled) return; + + Mix_FadeOutGroup(group, 100); } diff --git a/src/sound.h b/src/sound.h index 14c2e82..57aab91 100644 --- a/src/sound.h +++ b/src/sound.h @@ -1,32 +1,19 @@ #pragma once -#include -#include "physics.h" -#define VOICE_LOOPING (1<<10) /* Voice loops. */ -#define VOICE_STATIC (1<<11) /* Voice isn't relative. */ - -struct alVoice; -typedef struct alVoice alVoice; +extern int sound_disabled; /* Sound subsystem. */ int sound_init(void); void sound_exit(void); -void sound_update(void); /* Sound manupulation functions. */ -ALuint sound_get(char* name); -void sound_volume(const double vol); +int sound_get(char* name); +int sound_volume(const double vol); +int sound_play(int sound); -/* Voice manipulation function. */ -alVoice* sound_addVoice(int priority, double px, double py, - double vx, double vy, const ALuint buffer, const unsigned int flags); - -void sound_delVoice(alVoice* voice); /* Delete voice. */ - -void voice_update(alVoice* voice, double px, double py, double vx, double vy); -void voice_buffer(alVoice* voice, const ALuint buffer, const unsigned int flags); -void voice_stop(alVoice* voice); - -/* Listener manipulation. */ -void sound_listener(double dir, double px, double py, double vx, double vy); +/* Group functions. */ +int sound_reserve(int num); +int sound_createGroup(int tag, int start, int size); +int sound_playGroup(int group, int sound, int once); +void sound_stopGroup(int group); diff --git a/src/weapon.c b/src/weapon.c index d9f32fe..7e701d3 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -15,10 +15,6 @@ #define weapon_isSmart(w) (w->think != NULL) -#define VOICE_PRIORITY_BOLT 10 /* Default. */ -#define VOICE_PRIORITY_AMMO 8 /* Higher. */ -#define VOICE_PRIORITY_BEAM 6 /* Even higher. */ - #define WEAPON_CHUNK 128 /* Size to increment array with. */ /* Weapon status. */ @@ -47,8 +43,6 @@ typedef struct Weapon_ { double lockon; /* Some weapons have a lockon delay. */ double timer; /* Mainly used to see when the weapon was fired. */ - alVoice* voice; /* Virtual voise. */ - /* Update position and render. */ void(*update)(struct Weapon_*, const double, WeaponLayer); /* Position update and render. */ void(*think)(struct Weapon_*, const double); /* Some missiles need to be inteligent.a */ @@ -350,11 +344,6 @@ static void weapon_update(Weapon* w, const double dt, WeaponLayer layer) { if(weapon_isSmart(w)) (*w->think)(w,dt); (*w->solid->update)(w->solid, dt); - - /* Update the sound. */ - if(w->voice) - voice_update(w->voice, w->solid->pos.x, w->solid->pos.y, - w->solid->vel.x, w->solid->vel.y); } /* Good shot. */ @@ -439,10 +428,7 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* vect_cadd(&v, outfit->u.blt.speed*cos(rdir), outfit->u.blt.speed*sin(rdir)); w->timer += outfit->u.blt.range/outfit->u.blt.speed; w->solid = solid_create(mass, rdir, pos, &v); - w->voice = sound_addVoice(VOICE_PRIORITY_BOLT, - w->solid->pos.x, w->solid->pos.y, - w->solid->vel.x, w->solid->vel.y, - w->outfit->u.blt.sound, 0); + sound_play(w->outfit->u.blt.sound); break; /* Treat seekers togther. */ @@ -452,9 +438,6 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* w->lockon = outfit->u.amm.lockon; w->timer = outfit->u.amm.duration; w->solid = solid_create(mass, dir, pos, vel); - w->voice = sound_addVoice(VOICE_PRIORITY_AMMO, - w->solid->pos.x, w->solid->pos.y, - w->solid->vel.x, w->solid->vel.y, w->outfit->u.amm.sound, 0); /* If they are seeking a pilot, increment lockon counter. */ pilot_target = pilot_get(target); @@ -467,11 +450,11 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* w->think = think_seeker; else if(outfit->type == OUTFIT_TYPE_MISSILE_SEEK_SMART_AMMO) w->think = think_smart;*/ + sound_play(w->outfit->u.amm.sound); break; /* Just dump it where the player is. */ default: - w->voice = NULL; w->solid = solid_create(mass, dir, pos, vel); break; } @@ -562,7 +545,6 @@ static void weapon_destroy(Weapon* w, WeaponLayer layer) { /* Clear the weapon. */ static void weapon_free(Weapon* w) { - sound_delVoice(w->voice); solid_free(w->solid); free(w); }