diff --git a/bin/Makefile b/bin/Makefile index 5e36169..ee45d7d 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -42,7 +42,7 @@ endif DATA_AI = $(shell find ../scripts/ai/ -name '*.lua') DATA_GFX = $(shell find ../gfx/ -name '*.png') DATA_XML = $(shell find ../dat/ -name '*.xml' -o -name '*.ttf') -DATA_SND = $(shell find ../snd/ -name '*.ogg') +DATA_SND = $(shell find ../snd/ -name '*.ogg' -o -name '*.wav') DATA = data DATAFILES = $(VERSIONFILE) $(DATA_AI) $(DATA_GFX) $(DATA_XML) $(DATA_SND) diff --git a/dat/outfit.xml b/dat/outfit.xml index a527833..29d0901 100644 --- a/dat/outfit.xml +++ b/dat/outfit.xml @@ -37,6 +37,7 @@ missile + missile 5 1200 200 diff --git a/dat/ship.xml b/dat/ship.xml index 769978b..3839e62 100644 --- a/dat/ship.xml +++ b/dat/ship.xml @@ -3,6 +3,7 @@ ship minimal + engine 1 320 @@ -30,6 +31,7 @@ leapard minimal + engine 1 400 @@ -57,6 +59,7 @@ ship1 minimal + engine 2 180 diff --git a/snd/music/Machina.ogg b/snd/music/Machina.ogg new file mode 100644 index 0000000..adda9f2 Binary files /dev/null and b/snd/music/Machina.ogg differ diff --git a/snd/music/agriculture.ogg b/snd/music/agriculture.ogg new file mode 100644 index 0000000..fe51b62 Binary files /dev/null and b/snd/music/agriculture.ogg differ diff --git a/snd/music/galacticbattle.ogg b/snd/music/galacticbattle.ogg new file mode 100644 index 0000000..af9bf9b Binary files /dev/null and b/snd/music/galacticbattle.ogg differ diff --git a/snd/music/liftoff.ogg b/snd/music/liftoff.ogg new file mode 100644 index 0000000..e357653 Binary files /dev/null and b/snd/music/liftoff.ogg differ diff --git a/snd/sounds/engine.wav b/snd/sounds/engine.wav new file mode 100644 index 0000000..7727f09 Binary files /dev/null and b/snd/sounds/engine.wav differ diff --git a/snd/sounds/laser.wav b/snd/sounds/laser.wav new file mode 100644 index 0000000..9d987db Binary files /dev/null and b/snd/sounds/laser.wav differ diff --git a/snd/sounds/missile.wav b/snd/sounds/missile.wav new file mode 100644 index 0000000..7727f09 Binary files /dev/null and b/snd/sounds/missile.wav differ diff --git a/src/land.c b/src/land.c index ffff77d..f7a2026 100644 --- a/src/land.c +++ b/src/land.c @@ -2,6 +2,7 @@ #include "pause.h" #include "player.h" #include "rng.h" +#include "music.h" #include "land.h" // Global/main window. @@ -22,6 +23,9 @@ #define COMMODITY_WIDTH 400 #define COMMODITY_HEIGHT 400 +#define MUSIC_TAKEOFF "liftoff" +#define MUSIC_LAND "agriculture" + int landed = 0; static int land_wid = 0; // Primary land window. @@ -97,6 +101,11 @@ static void news_close(char* str) { // Land the player. void land(Planet* p) { if(landed) return; + + // Change music. + music_load(MUSIC_LAND); + music_play(); + planet = p; land_wid = window_create(p->name, -1, -1, LAND_WIDTH, LAND_HEIGHT); @@ -139,6 +148,9 @@ void land(Planet* p) { void takeoff(void) { if(!landed) return; + music_load(MUSIC_TAKEOFF); + music_play(); + int sw, sh; sw = planet->gfx_space->w; sh = planet->gfx_space->h; diff --git a/src/main.c b/src/main.c index 1217531..1064d03 100644 --- a/src/main.c +++ b/src/main.c @@ -23,6 +23,8 @@ #include "pause.h" #include "toolkit.h" #include "pilot.h" +#include "sound.h" +#include "music.h" #define XML_START_ID "Start" #define START_DATA "../dat/start.xml" @@ -99,6 +101,11 @@ int main(int argc, char** argv) { window_caption(); + // OpenAL sound. + if(sound_init()) WARN("Problem setting up sound!"); + music_load("Machina"); + music_play(); + // Input. if((indjoystick >= 0) || (namjoystick != NULL)) { if(joystick_init()) @@ -193,6 +200,8 @@ int main(int argc, char** argv) { joystick_exit(); // Release joystick. input_exit(); // Clean up keybindings. gl_exit(); // Kills video output. + sound_exit(); // Kills the sound. + SDL_Quit(); // Quits SDL. // All is good. exit(EXIT_SUCCESS); diff --git a/src/music.c b/src/music.c new file mode 100644 index 0000000..55ae090 --- /dev/null +++ b/src/music.c @@ -0,0 +1,250 @@ +#include +#include +#include +#include "SDL.h" + +#include "main.h" +#include "log.h" +#include "pack.h" +#include "music.h" + +#define MUSIC_PLAYING (2<<0) +#define MUSIC_KILL (9<<0) +#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) + +// Saves the music to ram in this structure. +typedef struct { + char name[32]; // 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; + +// 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); +} + +ov_callbacks ovcall = { + .read_func = ovpack_read, + .seek_func = NULL, + .close_func = NULL, + .tell_func = NULL +}; + +static int stream_loadBuffer(ALuint buffer); +static int music_find(void); +static int music_loadOGG(const char* filename); +static void music_free(void); + +// The thread. +static unsigned int music_state = 0; +int music_thread(void* unused) { + (void)unused; + + int active; // Active buffer. + ALint stat; + + // Main loop. + while(!music_is(MUSIC_KILL)) { + if(music_is(MUSIC_PLAYING)) { + SDL_mutexP(music_vorbis_lock); // Lock the mutex. + + // 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 the buffer loaded. + alSourcePlay(music_source); + + active = 1; // Load second buffer. + if(stream_loadBuffer(music_buffer[active])) music_rm(MUSIC_PLAYING); + alSourceQueueBuffers(music_source, 1, &music_buffer[active]); + + active = 0; // dive into the loop. + while(music_is(MUSIC_PLAYING)) { + alGetSourcei(music_source, AL_BUFFERS_PROCESSED, &stat); + if(stat > 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; + } + SDL_Delay(0); + } + alSourceStop(music_source); + alSourceUnqueueBuffers(music_source, 2, music_buffer); + + SDL_mutexV(music_vorbis_lock); + } + SDL_Delay(0); // We must not kill resources. + } + return 0; +} + +static int stream_loadBuffer(ALuint buffer) { + int size, section, result; + char data[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. + data + size, // Data. + BUFFER_SIZE - size, // Amount to read. + 0, // Big endian?. + 2, // 16 bit. + 1, // Signed. + §ion); // Current bitstream. + + if(result == 0) return 1; + + size += result; + if(size == BUFFER_SIZE) break; // Buffer is full. + } + alBufferData(buffer, AL_FORMAT_STEREO16, data, size, music_vorbis.info->rate); + + return 0; +} + +// Init/Exit. +int music_init(void) { + music_vorbis_lock = SDL_CreateMutex(); + music_find(); + music_vorbis.file.fd = 0; // Indication that it's not loaded.. + alGenBuffers(2, music_buffer); + alGenSources(1, &music_source); + alSourcef(music_source, AL_ROLLOFF_FACTOR, 0.); + alSourcei(music_source, AL_SOURCE_RELATIVE, AL_TRUE); + return 0; +} + +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); +} + +// Internal music loading ruitines. +static int music_loadOGG(const char* filename) { + // Free currently loaded ogg. + music_free(); + + SDL_mutexP(music_vorbis_lock); + + // 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; + + SDL_mutexV(music_vorbis_lock); + return 0; +} + +static int music_find(void) { + 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], MUSIC_PREFIX, strlen(MUSIC_PREFIX))==0) && + (strncmp(files[i] + strlen(files[i]) - strlen(MUSIC_SUFFIX), + MUSIC_SUFFIX, strlen(MUSIC_SUFFIX))==0)) { + + // Grow the selection size. + music_selection = realloc(music_selection, ++nmusic_selection*sizeof(char*)); + + // Remove the prefix and suffix. + len = strlen(files[i]) - strlen(MUSIC_SUFFIX MUSIC_PREFIX); + strncpy(tmp, files[i] + strlen(MUSIC_PREFIX), len); + tmp[len] = '\0'; + + music_selection[nmusic_selection-1] = strdup(tmp); + } + // Free the char* allocated by pack. + for(i = 0; i < nfiles; i++) + free(files[i]); + free(files); + + DEBUG("Loaded %d song%c", nmusic_selection, (nmusic_selection==1)?' ':'s'); + + return 0; +} + +static void music_free(void) { + SDL_mutexP(music_vorbis_lock); + + if(music_vorbis.file.fd != 0) { + ov_clear(&music_vorbis.stream); + pack_close(&music_vorbis.file); + } + + SDL_mutexV(music_vorbis_lock); +} + +// Music control functions. +void music_load(const char* name) { + int i; + char tmp[64]; + + music_stop(); + + 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); +} + +void music_play(void) { + music_set(MUSIC_PLAYING); +} + +void music_stop(void) { + music_rm(MUSIC_PLAYING); +} + +void music_kill(void) { + music_set(MUSIC_KILL); +} + diff --git a/src/music.h b/src/music.h new file mode 100644 index 0000000..857e93c --- /dev/null +++ b/src/music.h @@ -0,0 +1,15 @@ +#pragma once + +// Thread. +int music_thread(void* unused); +void music_kill(void); + +// Init/Exit. +int music_init(void); +void music_exit(void); + +// Music control. +void music_load(const char* name); +void music_play(void); +void music_stop(void); + diff --git a/src/opengl.c b/src/opengl.c index 62395cb..21e73c6 100644 --- a/src/opengl.c +++ b/src/opengl.c @@ -532,8 +532,7 @@ int gl_init(void) { // Clean up our mess. void gl_exit(void) { - //SDL_ShowCursor(SDL_ENABLE); - SDL_Quit(); + } // Saves a png. diff --git a/src/player.c b/src/player.c index 46130dc..30852a1 100644 --- a/src/player.c +++ b/src/player.c @@ -11,6 +11,7 @@ #include "rng.h" #include "land.h" #include "toolkit.h" +#include "sound.h" #include "player.h" #define XML_GUI_ID "GUIs" // XML section identifier. diff --git a/src/sound.c b/src/sound.c new file mode 100644 index 0000000..256f9a4 --- /dev/null +++ b/src/sound.c @@ -0,0 +1,102 @@ +#include +#include +#include "SDL.h" +#include "SDL_thread.h" + +#include "main.h" +#include "log.h" +#include "pack.h" +#include "music.h" +#include "sound.h" + +// This isn't always set? +#ifndef AL_FORMAT_VORBIS_EXT +#define AL_FORMAT_VORBIS_EXT 0x10003 +#endif + +typedef struct { + ALfloat x, y; +} alVector; + +static ALCcontext* al_context = NULL; +static ALCdevice* al_device = NULL; + +static SDL_Thread* music_player = NULL; + +int sound_init(void) { + // Open the default device. + al_device = alcOpenDevice(NULL); + if(al_device == NULL) { + WARN("Unable to open default sound device"); + return -1; + } + + // Create the OpenAL context. + al_context = alcCreateContext(al_device, NULL); + if(al_context == NULL) { + WARN("Unable to create OpenAL context"); + return -2; + } + + // We use the vorbis extension for AL to load it easily. + if(alIsExtensionPresent("AL_EXT_vorbis") == AL_FALSE) { + WARN("AL_EXT_vorbis not available in OpenAL context!"); + return -3; + } + + // Set active context. + if(alcMakeContextCurrent(al_context)==AL_FALSE) { + WARN("Failure to set default context"); + return -4; + } + + music_init(); + music_player = SDL_CreateThread(music_thread, NULL); + + return 0; +} + + +// Clean up after the sound system. +void sound_exit(void) { + music_stop(); + music_kill(); + SDL_WaitThread(music_player, NULL); + music_exit(); + + if(al_context) { + alcMakeContextCurrent(NULL); + alcDestroyContext(al_context); + } + if(al_device) alcCloseDevice(al_device); +} + +// Loads a vorbis file. +ALuint sound_sndCreate(char* filename) { + void* ovdata; + unsigned int size; + ALenum err; + ALuint buf; + + // Get the file data buffer from the packfile. + ovdata = pack_readfile(DATA, filename, &size); + + // Bind to OpenAL buffer. + alGenBuffers(1, &buf); + alBufferData(buf, AL_FORMAT_VORBIS_EXT, ovdata, size, 44800); + + // Errors? + if((err = alGetError()) != AL_NO_ERROR) { + WARN("OpenAL erro '%d' loading sound '%s'.", err, filename); + return 0; + } + + // Finish up. + free(ovdata); + return buf; +} + +void sound_sndFree(const ALuint snd) { + alDeleteBuffers(1, &snd); +} + diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 0000000..06157b8 --- /dev/null +++ b/src/sound.h @@ -0,0 +1,16 @@ +#pragma once +#include +#include "physics.h" + +// Sound subsystem. +int sound_init(void); +void sound_exit(void); + +// Sound manupulation functions. +ALuint sound_sndCreate(char* filename); +void sound_sndFree(const ALuint snd); + +// Source manipulation function. +#define sound_initSource(s) (alGenSources(1, &(s))) +#define sound_delSource(s) (alDeleteSources(1, &(s)) +