From 529ca21644e0aa555da63acd5cc432876b329df8 Mon Sep 17 00:00:00 2001 From: Allanis Date: Fri, 22 Feb 2013 04:06:47 +0000 Subject: [PATCH] [Add] Audio engine is now working, it It's fully tuned enough yet however. [Change] Refactored ship.c to comply with xml.* standards. --- src/music.c | 6 +++ src/music.h | 1 + src/outfit.c | 5 +- src/outfit.h | 2 + src/pilot.c | 9 ++++ src/pilot.h | 7 ++- src/player.c | 11 ++++- src/ship.c | 101 +++++++++++++++++++++-------------------- src/ship.h | 3 ++ src/sound.c | 126 +++++++++++++++++++++++++++++++++++++++++---------- src/sound.h | 9 ++-- src/weapon.c | 24 +++++++++- 12 files changed, 224 insertions(+), 80 deletions(-) diff --git a/src/music.c b/src/music.c index 55ae090..62f97c1 100644 --- a/src/music.c +++ b/src/music.c @@ -153,6 +153,8 @@ void music_exit(void) { for(i = 0; i < nmusic_selection; i++) free(music_selection[i]); free(music_selection); + + SDL_DestroyMutex(music_vorbis_lock); } // Internal music loading ruitines. @@ -220,6 +222,10 @@ static void music_free(void) { SDL_mutexV(music_vorbis_lock); } +void music_volume(const double vol) { + alSourcef(music_source, AL_GAIN, (ALfloat)vol); +} + // Music control functions. void music_load(const char* name) { int i; diff --git a/src/music.h b/src/music.h index 857e93c..96bb65f 100644 --- a/src/music.h +++ b/src/music.h @@ -9,6 +9,7 @@ int music_init(void); void music_exit(void); // Music control. +void 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 a9dce7c..951f890 100644 --- a/src/outfit.c +++ b/src/outfit.c @@ -159,6 +159,8 @@ static void outfit_parseSAmmo(Outfit* tmp, const xmlNodePtr parent) { OUTFIT_GFX"%s.png", xml_get(node)); tmp->gfx_space = gl_newSprite(str, 6, 6); } + else if(xml_isNode(node, "sound")) + tmp->sound = sound_get(xml_get(node)); else if(xml_isNode(node, "damage")) { cur = node->children; do { @@ -170,7 +172,8 @@ static void outfit_parseSAmmo(Outfit* tmp, const xmlNodePtr parent) { #define MELEMENT(o,s) if((o) == 0) WARN("Outfit '%s' missing '"s"' element", tmp->name) if(tmp->gfx_space == NULL) WARN("Outfit '%s' missing 'gfx' element", tmp->name); - MELEMENT(tmp->thrust, " thrust"); + MELEMENT(tmp->sound, "sound"); + MELEMENT(tmp->thrust, "thrust"); MELEMENT(tmp->turn, "turn"); MELEMENT(tmp->speed, "speed"); MELEMENT(tmp->range, "duration"); diff --git a/src/outfit.h b/src/outfit.h index 2eceb69..99921de 100644 --- a/src/outfit.h +++ b/src/outfit.h @@ -1,5 +1,6 @@ #pragma once #include "opengl.h" +#include "sound.h" #define outfit_isProp(o,p) ((o)->properties & p) // Property flags. @@ -46,6 +47,7 @@ typedef struct { double damage_armour, damage_shield; // Damage. glTexture* gfx_space; + ALuint sound; // Sound to play. }; struct { // Launcher. //unsigned int delay; // Delay between shots. diff --git a/src/pilot.c b/src/pilot.c index 5ebe951..9450e93 100644 --- a/src/pilot.c +++ b/src/pilot.c @@ -254,6 +254,10 @@ static void pilot_update(Pilot* pilot, const double dt) { // Should not go faster. vect_pset(&pilot->solid->vel, VMOD(pilot->solid->vel) - 0.3*pilot->ship->thrust*dt, VANGLE(pilot->solid->vel)); + + // Update the source. + alSource3f(pilot->source, AL_POSITION, pilot->solid->pos.x, pilot->solid->pos.y, 0.); + alSource3f(pilot->source, AL_VELOCITY, pilot->solid->vel.x, pilot->solid->vel.y, 0.); } // Pilot is getting ready or is in, hyperspace. @@ -356,7 +360,12 @@ void pilot_init(Pilot* pilot, Ship* ship, char* name, Faction* faction, AI_Profi } } + // Sound source. + // Eh.... + + // Set flags and functions. if(flags & PILOT_PLAYER) { + alSourcef(pilot->source, AL_GAIN, 0.); pilot->think = player_think; // Players don't need to thing! :P pilot->render = NULL; // Render will be called from player_think pilot_setFlag(pilot, PILOT_PLAYER); // It's a player! diff --git a/src/pilot.h b/src/pilot.h index 53b8bbd..8d1ff23 100644 --- a/src/pilot.h +++ b/src/pilot.h @@ -4,6 +4,7 @@ #include "ai.h" #include "outfit.h" #include "faction.h" +#include "sound.h" #include "ship.h" #define PLAYER_ID 1 @@ -66,7 +67,11 @@ typedef struct Pilot { PilotOutfit* secondary; // Secondary weapon. PilotOutfit* ammo; // Secondary ammo (if needed). - unsigned int flags; // Used for AI etc. + // Sound source. + ALuint source; + + // Misc. + unsigned int flags; // Used for AI etc. unsigned int ptimer; // Generic timer for internal pilot use. // AI. diff --git a/src/player.c b/src/player.c index 30852a1..01232f6 100644 --- a/src/player.c +++ b/src/player.c @@ -174,7 +174,8 @@ void player_new(void) { d = RNG(0, 359)/180.*M_PI; pilot_create(ship, "Player", faction_get("Player"), NULL, d, &v, NULL, PILOT_PLAYER); - gl_bindCamera(&player->solid->pos); + alSourcef(player->source, AL_GAIN, 0.5); + gl_bindCamera(&player->solid->pos); // Set opengl camers. space_init(system); // Welcome message. @@ -796,6 +797,14 @@ void player_think(Pilot* player) { pilot_shoot(player, player_target, 1); vect_pset(&player->solid->force, player->ship->thrust * player_acc, player->solid->dir); + + // Set the listener stuff. + ALfloat ori[] = { 0., 0., 0., 0., 0., 1. }; + ori[0] = cos(player->solid->dir); + ori[1] = sin(player->solid->dir); + alListenerfv(AL_ORIENTATION, ori); + alListener3f(AL_POSITION, player->solid->pos.x, player->solid->pos.y, 0.); + alListener3f(AL_VELOCITY, player->solid->vel.x, player->solid->vel.y, 0.); } // Modify the radar resolution. diff --git a/src/ship.c b/src/ship.c index ec9a791..ef96c31 100644 --- a/src/ship.c +++ b/src/ship.c @@ -38,81 +38,83 @@ static Ship* ship_parse(xmlNodePtr parent) { ShipOutfit* otmp, *ocur; char str[PATH_MAX] = "\0"; - xmlChar* xstr; + char* stmp; - tmp->name = (char*)xmlGetProp(parent, (xmlChar*)"name"); + tmp->name = xml_nodeProp(parent, "name"); if(tmp->name == NULL) WARN("Ship in "SHIP_DATA" has invalid or no name"); node = parent->xmlChildrenNode; do { // Load all the data. - if(strcmp((char*)node->name, "GFX")==0) { - snprintf(str, strlen((char*)node->children->content)+sizeof(SHIP_GFX)+sizeof(SHIP_EXT), - SHIP_GFX"%s"SHIP_EXT, (char*)node->children->content); + if(xml_isNode(node,"GFX")) { + snprintf(str, strlen(xml_get(node)) + sizeof(SHIP_GFX) + sizeof(SHIP_EXT), + SHIP_GFX"%s"SHIP_EXT, xml_get(node)); tmp->gfx_space = gl_newSprite(str, 6, 6); // Target. - snprintf(str, strlen((char*)node->children->content)+sizeof(SHIP_GFX)+sizeof(SHIP_TARGET)+sizeof(SHIP_EXT), - SHIP_GFX"%s"SHIP_TARGET SHIP_EXT, (char*)node->children->content); + snprintf(str, strlen(xml_get(node)) + sizeof(SHIP_GFX)+sizeof(SHIP_TARGET)+sizeof(SHIP_EXT), + SHIP_GFX"%s"SHIP_TARGET SHIP_EXT, xml_get(node)); tmp->gfx_target = gl_newImage(str); } - else if(strcmp((char*)node->name, "GUI")==0) - tmp->gui = strdup((char*)node->children->content); - else if(strcmp((char*)node->name, "class")==0) - tmp->class = atoi((char*)node->children->content); - else if(strcmp((char*)node->name, "movement")==0) { + else if(xml_isNode(node, "GUI")) + tmp->gui = strdup(xml_get(node)); + else if(xml_isNode(node, "sound")) + tmp->sound = sound_get(xml_get(node)); + else if(xml_isNode(node, "class")) + tmp->class = atoi(xml_get(node)); + else if(xml_isNode(node, "movement")) { cur = node->children; do { - if(strcmp((char*)cur->name, "thrust")==0) - tmp->thrust = atoi((char*)cur->children->content); - else if(strcmp((char*)cur->name, "turn")==0) - tmp->turn = atoi((char*)cur->children->content); - else if(strcmp((char*)cur->name, "speed")==0) - tmp->speed = atoi((char*)cur->children->content); + if(xml_isNode(cur, "thrust")) + tmp->thrust = xml_getInt(cur); + else if(xml_isNode(cur, "turn")) + tmp->turn = xml_getInt(cur); + else if(xml_isNode(cur, "speed")) + tmp->speed = xml_getInt(cur); } while((cur = cur->next)); } - else if(strcmp((char*)node->name, "health")==0) { + else if(xml_isNode(node, "health")) { cur = node->children; do { - if(strcmp((char*)cur->name, "armour")==0) - tmp->armour = (double)atoi((char*)cur->children->content); - else if(strcmp((char*)cur->name, "shield")==0) - tmp->shield = (double)atoi((char*)cur->children->content); - else if(strcmp((char*)cur->name, "energy")==0) - tmp->energy = (double)atoi((char*)cur->children->content); - else if(strcmp((char*)cur->name, "armour_regen")==0) - tmp->armour_regen = (double)(atoi((char*)cur->children->content))/60.0; - else if(strcmp((char*)cur->name, "shield_regen")==0) - tmp->shield_regen = (double)(atoi((char*)cur->children->content))/60.0; - else if(strcmp((char*)cur->name, "energy_regen")==0) - tmp->energy_regen = (double)(atoi((char*)cur->children->content))/60.0; + if(xml_isNode(cur, "armour")) + tmp->armour = (double)xml_getInt(cur); + else if(xml_isNode(cur, "shield")) + tmp->shield = (double)xml_getInt(cur); + else if(xml_isNode(cur, "energy")) + tmp->energy = (double)xml_getInt(cur); + else if(xml_isNode(cur, "armour_regen")) + tmp->armour_regen = (double)(xml_getInt(cur))/60.0; + else if(xml_isNode(cur, "shield_regen")) + tmp->shield_regen = (double)(xml_getInt(cur))/60.0; + else if(xml_isNode(cur, "energy_regen")) + tmp->energy_regen = (double)(xml_getInt(cur))/60.0; } while((cur = cur->next)); } - else if(strcmp((char*)node->name, "characteristics")==0) { + else if(xml_isNode(node, "characteristics")) { cur = node->children; do { - if(strcmp((char*)cur->name, "crew")==0) - tmp->crew = atoi((char*)cur->children->content); - else if(strcmp((char*)cur->name, "mass")==0) - tmp->mass = (double)atoi((char*)cur->children->content); - else if(strcmp((char*)cur->name, "cap_weapon")==0) - tmp->cap_weapon = atoi((char*)cur->children->content); - else if(strcmp((char*)cur->name, "cap_cargo")==0) - tmp->cap_cargo = atoi((char*)cur->children->content); + if(xml_isNode(cur, "crew")) + tmp->crew = xml_getInt(cur); + else if(xml_isNode(cur, "mass")) + tmp->mass = (double)xml_getInt(cur); + else if(xml_isNode(cur, "cap_weapon")) + tmp->cap_weapon = xml_getInt(cur); + else if(xml_isNode(cur, "cap_cargo")) + tmp->cap_cargo = xml_getInt(cur); } while((cur = cur->next)); } - else if(strcmp((char*)node->name, "outfits")==0) { + else if(xml_isNode(node, "outfits")) { cur = node->children; do { - if(strcmp((char*)cur->name, "outfit")==0) { + if(xml_isNode(cur, "outfit")) { otmp = MALLOC_L(ShipOutfit); - otmp->data = outfit_get((char*)cur->children->content); - xstr = xmlGetProp(cur, (xmlChar*)"quantity"); - if(!xstr) + otmp->data = outfit_get(xml_get(cur)); + stmp = xml_nodeProp(cur, "quantity"); + if(!stmp) WARN("Ship '%s' is missing tag 'quantity for outfit '%s'", tmp->name, otmp->data->name); - otmp->quantity = atoi((char*)xstr); - free(xstr); + otmp->quantity = atoi(stmp); + free(stmp); otmp->next = NULL; if((ocur = tmp->outfit) == NULL) tmp->outfit = otmp; @@ -130,17 +132,18 @@ static Ship* ship_parse(xmlNodePtr parent) { #define MELEMENT(o,s) if(o == 0) WARN("Ship '%s' missing '"s"' element", tmp->name) if(tmp->name == NULL) WARN("Ship '%s' missing 'name' tag", tmp->name); if(tmp->gfx_space == NULL) WARN("Ship '%s' missing 'GFX' element", tmp->name); + if(tmp->gui == NULL) WARN("Ship '%s' missing 'GUI' element", tmp->name); MELEMENT(tmp->thrust, "thrust"); MELEMENT(tmp->turn, "turn"); MELEMENT(tmp->speed, "speed"); - MELEMENT(tmp->crew, "crew"); - MELEMENT(tmp->mass, "mass"); MELEMENT(tmp->armour, "armour"); MELEMENT(tmp->armour_regen, "armour_regen"); MELEMENT(tmp->shield, "shield"); MELEMENT(tmp->shield_regen, "shield_regen"); MELEMENT(tmp->energy, "energy"); MELEMENT(tmp->energy_regen, "energt_regen"); + MELEMENT(tmp->crew, "crew"); + MELEMENT(tmp->mass, "mass"); MELEMENT(tmp->cap_cargo, "cap_cargo"); MELEMENT(tmp->cap_weapon, "cap_weapon"); #undef MELEMENT diff --git a/src/ship.h b/src/ship.h index 0555900..fbfca35 100644 --- a/src/ship.h +++ b/src/ship.h @@ -36,6 +36,9 @@ typedef struct { // GUI interface. char* gui; + + // Sound. + ALuint sound; // Characteristics. int crew; diff --git a/src/sound.c b/src/sound.c index 256f9a4..2d2e821 100644 --- a/src/sound.c +++ b/src/sound.c @@ -9,20 +9,29 @@ #include "music.h" #include "sound.h" -// This isn't always set? -#ifndef AL_FORMAT_VORBIS_EXT -#define AL_FORMAT_VORBIS_EXT 0x10003 -#endif +#define SOUND_PREFIX "snd/sounds/" +#define SOUND_SUFFIX ".wav" typedef struct { - ALfloat x, y; -} alVector; + char* name; + ALuint buffer; +} alSound; +// 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; + +static int sound_makeList(void); +static int sound_load(ALuint* buffer, char* filename); +static void sound_free(alSound* snd); + int sound_init(void) { // Open the default device. al_device = alcOpenDevice(NULL); @@ -38,18 +47,16 @@ int sound_init(void) { 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; } + // Load up all the sounds. + sound_makeList(); + + // Start the music server. music_init(); music_player = SDL_CreateThread(music_thread, NULL); @@ -59,6 +66,16 @@ int sound_init(void) { // 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. music_stop(); music_kill(); SDL_WaitThread(music_player, NULL); @@ -71,19 +88,67 @@ void sound_exit(void) { if(al_device) alcCloseDevice(al_device); } -// Loads a vorbis file. -ALuint sound_sndCreate(char* filename) { - void* ovdata; +// Get the buffer to sound of [name]. +ALuint sound_get(char* name) { + 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) { + 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_SUFFIX); + 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) { + void* wavdata; unsigned int size; ALenum err; - ALuint buf; // Get the file data buffer from the packfile. - ovdata = pack_readfile(DATA, filename, &size); + wavdata = pack_readfile(DATA, filename, &size); // Bind to OpenAL buffer. - alGenBuffers(1, &buf); - alBufferData(buf, AL_FORMAT_VORBIS_EXT, ovdata, size, 44800); + alGenBuffers(1, buffer); + alBufferData(*buffer, AL_FORMAT_MONO16, wavdata, size, 22050); // Errors? if((err = alGetError()) != AL_NO_ERROR) { @@ -92,11 +157,26 @@ ALuint sound_sndCreate(char* filename) { } // Finish up. - free(ovdata); - return buf; + free(wavdata); + return 0; } -void sound_sndFree(const ALuint snd) { - alDeleteBuffers(1, &snd); +static void sound_free(alSound* snd) { + if(snd->name) free(snd->name); + alDeleteBuffers(1, &snd->buffer); +} + +ALuint sound_dynSource(double px, double py, double vx, double vy, int looping) { + ALuint tmp; + + alGenSources(1, &tmp); + + alSourcei(tmp, AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(tmp, AL_POSITION, px, py, 0.); + alSource3f(tmp, AL_VELOCITY, vx, vy, 0.); + + if(looping) alSourcei(tmp, AL_LOOPING, AL_TRUE); + + return tmp; } diff --git a/src/sound.h b/src/sound.h index 06157b8..f66cc08 100644 --- a/src/sound.h +++ b/src/sound.h @@ -2,15 +2,16 @@ #include #include "physics.h" +#define SOUND_REFERENCE_DIST 500. +#define SOUND_MAX_DIST 1000. + // Sound subsystem. int sound_init(void); void sound_exit(void); // Sound manupulation functions. -ALuint sound_sndCreate(char* filename); -void sound_sndFree(const ALuint snd); +ALuint sound_get(char* name); // Source manipulation function. -#define sound_initSource(s) (alGenSources(1, &(s))) -#define sound_delSource(s) (alDeleteSources(1, &(s)) +ALuint sound_dynSource(double px, double py, double vx, double vy, int looping); diff --git a/src/weapon.c b/src/weapon.c index b059eff..23e096b 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -29,7 +29,9 @@ typedef struct Weapon { const Outfit* outfit; // Related outfit that fired. unsigned int timer; // Mainly used to see when the weapon was fired. - // Update position and render. + ALuint source; // Source for sound. + + // Update position and render. void(*update)(struct Weapon*, const double, WeaponLayer); // Position update and render. void(*think)(struct Weapon*); // Some missiles need to be inteligent. } Weapon; @@ -44,6 +46,7 @@ static int nwfrontLayer = 0; // Number of elements. static int mwfrontLayer = 0; // Allocated memory size. +static void weapon_sound(Weapon* w); static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* pos, const Vec2* vel, const unsigned int parent, const unsigned int target); @@ -125,6 +128,14 @@ void weapons_update(const double dt) { weapons_updateLayer(dt, WEAPON_LAYER_FG); } +// Play the weapon sound. +static void weapon_sound(Weapon* w) { + w->source = sound_dynSource(w->solid->pos.x, w->solid->pos.y, + w->solid->vel.x, w->solid->vel.y, 0); + alSourcei(w->source, AL_BUFFER, w->outfit->sound); + alSourcePlay(w->source); +} + // Update all weapons in the layer. static void weapons_updateLayer(const double dt, const WeaponLayer layer) { Weapon** wlayer; @@ -222,6 +233,10 @@ static void weapon_update(Weapon* w, const double dt, WeaponLayer layer) { } if(weapon_isSmart(w)) (*w->think)(w); (*w->solid->update)(w->solid, dt); + + // Update the sound. + alSource3f(w->source, AL_POSITION, w->solid->pos.x, w->solid->pos.y, 0.); + alSource3f(w->source, AL_VELOCITY, w->solid->vel.x, w->solid->vel.y, 0.); } // Good shot. @@ -260,14 +275,17 @@ static Weapon* weapon_create(const Outfit* outfit, const double dir, const Vec2* vectcpy(&v, vel); vect_cadd(&v, outfit->speed*cos(rdir), outfit->speed*sin(rdir)); w->solid = solid_create(mass, rdir, pos, &v); + weapon_sound(w); break; case OUTFIT_TYPE_MISSILE_SEEK_AMMO: mass = w->outfit->mass; w->solid = solid_create(mass, dir, pos, vel); w->think = think_seeker; // Eeek!!! + weapon_sound(w); break; default: // Just dump it where the player is. + w->source = 0; w->solid = solid_create(mass, dir, pos, vel); break; } @@ -336,6 +354,10 @@ static void weapon_destroy(Weapon* w, WeaponLayer layer) { break; } for(i = 0; wlayer[i] != w; i++); // Get us to the current posision. + if(w->source) { + alSourceStop(wlayer[i]->source); + alDeleteSources(1, &wlayer[i]->source); + } weapon_free(wlayer[i]); wlayer[i] = NULL; (*nlayer)--;