Lephisto/src/sound.c
Allanis 9178376c28 [Change] Split engine sounds and GUI sounds for player.
[Fix] Some issues in sound.c
[Add] More documentation.
2013-09-04 15:19:04 +01:00

413 lines
9.2 KiB
C

/**
* @file sound.c
*
* @brief Handle all the sound details.
*/
#include <sys/stat.h>
#include <SDL.h>
#include <SDL_mixer.h>
#include "lephisto.h"
#include "log.h"
#include "pack.h"
#include "music.h"
#include "physics.h"
#include "sound.h"
#define SOUND_CHANNEL_MAX 256 /**< Number of sound channels to allocate. Overkill. */
#define SOUND_PREFIX "../snd/sounds/" /**< Prefix of where to find sounds. */
#define SOUND_SUFFIX ".wav" /**< Suffix of sounds. */
/* Global sound properties. */
int sound_disabled = 0; /**< Whether sound is disabled. */
static int sound_reserved = 0; /**< Amount of reserved channels. */
static double sound_pos[3]; /**< Position of listener. */
/* Give the buffers a name. */
typedef struct alSound_ {
char* name; /**< Buffers name. */
Mix_Chunk* buffer; /**< Buffer data. */
} alSound;
/* List of sounds available (All preloaded into a buffer). */
static alSound* sound_list = NULL; /**< List of available sounds. */
static int sound_nlist = 0; /**< Number of available sounds. */
static int sound_makeList(void);
static Mix_Chunk* sound_load(char* filename);
static void sound_free(alSound* snd);
/**
* @fn int sound_init(void)
*
* @brief Initializes the sound subsystem.
* @return 0 on success.
*/
int sound_init(void) {
int frequency;
Uint16 format;
int channels;
SDL_version compile_version;
const SDL_version* link_version;
if(sound_disabled) return 0;
SDL_InitSubSystem(SDL_INIT_AUDIO);
if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) < 0) {
WARN("Opening Audio: %s", Mix_GetError());
return -1;
}
Mix_AllocateChannels(SOUND_CHANNEL_MAX);
/* Debug magic. */
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();
sound_volume(0.4);
/* Init the music. */
music_init();
return 0;
}
/**
* @fn void sound_exit(void)
*
* @brief Clean up after the sound subsystem.
*/
void sound_exit(void) {
int i;
/* Free the sounds. */
for(i = 0; i < sound_nlist; i++)
sound_free(&sound_list[i]);
free(sound_list);
sound_list = NULL;
sound_nlist = 0;
music_exit();
}
/**
* @fn void sound_get(char* name)
*
* @brief Get the buffer to sound of name.
* @param name Name of the sound to get id of.
* @return ID of the sound matching name.
*/
int sound_get(char* name) {
int i;
if(sound_disabled) return 0;
for(i = 0; i < sound_nlist; i++)
if(strcmp(name, sound_list[i].name)==0) {
return i;
}
WARN("Sound '%s' not found in sound list", name);
return -1;
}
/**
* @fn int sound_play(int sound)
*
* @brief Play the sound in the first available channel.
* @param sound Sound to play.
* @return 0 on success.
*/
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)
WARN("Unable to play sound: %s", Mix_GetError());
return 0;
}
/**
* @fn int sound_playPos(int sound, double x, double y)
*
* @brief Play a sound based on position.
* @param sound Sound to play.
* @param x X position of sound.
* @param y Y position of sound.
* @return 0 on success.
*/
int sound_playPos(int sound, double x, double y) {
int channel;
double angle, dist;
double px, py;
if(sound_disabled) return 0;
if((sound < 0) || (sound > sound_nlist))
return -1;
px = x - sound_pos[0];
py = y - sound_pos[1];
angle = sound_pos[2] - ANGLE(px, py)/M_PI*180.;
dist = MOD(px, py);
channel = Mix_PlayChannel(-1, sound_list[sound].buffer, 0);
if(channel < 0) {
WARN("Unable to play sound: %s", Mix_GetError());
return -1;
}
if(Mix_SetPosition(channel, (int)angle, (int)dist/10) < 0) {
WARN("Unable to set sound position: %s", Mix_GetError());
return -1;
}
return 0;
}
/**
* @fn int sound_updateListener(double dir, double x, double y)
*
* @brief Update the sound listener.
* @param dir Direction listener if facing.
* @param x X position of the listener.
* @param y Y position of the listener.
*
* @sa sound_playPos
*/
int sound_updateListener(double dir, double x, double y) {
sound_pos[0] = x;
sound_pos[1] = y;
sound_pos[2] = dir/M_PI*180.;
return 0;
}
/**
* @fn static int sound_makeList(void)
*
* @brief Make the list of available sounds.
*/
static int sound_makeList(void) {
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);
/* Load the profiles. */
mem = 0;
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_nlist++;
if(sound_nlist > mem) { /* We must grow. */
mem += 32; /* We'll overallocate most likely. */
sound_list = realloc(sound_list, mem*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[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, 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", sound_nlist, (sound_nlist==1)?"":"s");
return 0;
}
/**
* @fn int sound_volume(const double vol)
*
* @brief Set the volume.
* @param vol Volume to set to.
* @return 0 on success.
*/
int sound_volume(const double vol) {
if(sound_disabled) return 0;
return Mix_Volume(-1, MIX_MAX_VOLUME*vol);
}
/**
* @fn stastic Mix_Chunk* sound_load(char* filename)
*
* @brief Load a sound into the sound_list.
* @paramfilename Name for the file to load.
* @return The SDL_Mixer of the loaded chunk.
*
* @sa sound_makeList
*/
static Mix_Chunk* sound_load(char* filename) {
void* wavdata;
unsigned int size;
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);
rw = SDL_RWFromMem(wavdata, size);
/* Bind to OpenAL buffer. */
buffer = Mix_LoadWAV_RW(rw, 1);
if(buffer == NULL)
DEBUG("Unable to load sound '%s' : %s", filename, Mix_GetError());
/* Finish up. */
free(wavdata);
return buffer;
}
/**
* @fn static void sound_free(alSound* snd)
*
* @brief Frees the sound.
* @param snd Sound to free.
*/
static void sound_free(alSound* snd) {
/* Free the stuff. */
if(snd->name) {
free(snd->name);
snd->name = NULL;
}
Mix_FreeChunk(snd->buffer);
snd->buffer = NULL;
}
/**
* @fn int sound_reverse(int num)
*
* @brief Reserve num channels.
* @param num Number of channels to reserve.
* @return 0 on success.
*/
int sound_reserve(int num) {
int ret;
if(sound_disabled) return 0;
sound_reserved += num;
ret = Mix_ReserveChannels(num);
if(ret != sound_reserved) {
WARN("Unable to reserve %d channels: %s", sound_reserved, Mix_GetError());
return -1;
}
return 0;
}
/**
* @fn int sound_createGroup(int tag, int start, int size)
*
* @brief Create a sound group.
* @param tag Identifier of the group to create.
* @param start Where to start creating the group.
* @param size Size of the group.
* @return 0 on success.
*/
int sound_createGroup(int tag, int start, int size) {
int ret;
if(sound_disabled) return 0;
ret = Mix_GroupChannels(start, start+size-1, tag);
if(ret != size) {
WARN("Unable to create sound group: %s", Mix_GetError());
return -1;
}
return 0;
}
/**
* @fn int sound_playGroup(int group, int sound, int once)
*
* @brief Play a sound in a group.
* @param group Group to play sound in.
* @param sound Sound to play.
* @param once Whether to play only once.
* @param 0 on success.
*/
int sound_playGroup(int group, int sound, int once) {
int ret, channel;
if(sound_disabled) return 0;
channel = Mix_GroupAvailable(group);
if(channel == -1) {
WARN("Group '%d' has no free channels!", group);
return -1;
}
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;
}
/**
* @fn void sound_stopGroup(int group)
*
* @brief Stop all the sounds in a group.
* @param group Group to stop all it's sounds.
*/
void sound_stopGroup(int group) {
if(sound_disabled) return;
Mix_HaltGroup(group);
}