/** * @file sound.c * * @brief Handle all the sound details. */ #include #include #include #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); }