Atrinik Client 2.5
client/sound.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 
00033 static char *sound_background;
00035 static uint8 sound_map_background_disabled = 0;
00037 static sound_data_struct *sound_data;
00039 static size_t sound_data_num;
00041 static uint8 enabled = 0;
00042 
00043 #ifdef HAVE_SDL_MIXER
00044 
00050 static int sound_compare(const void *a, const void *b)
00051 {
00052     return strcmp(((sound_data_struct *) a)->filename, ((sound_data_struct *) b)->filename);
00053 }
00054 
00057 static void sound_sort()
00058 {
00059     qsort((void *) sound_data, sound_data_num, sizeof(sound_data_struct), (void *) (int (*)()) sound_compare);
00060 }
00061 
00066 static sound_data_struct *sound_find(const char *filename)
00067 {
00068     sound_data_struct key;
00069 
00070     key.filename = (char *) filename;
00071     return (sound_data_struct *) bsearch((void *) &key, (void *) sound_data, sound_data_num, sizeof(sound_data_struct), sound_compare);
00072 }
00073 
00080 static sound_data_struct *sound_new(int type, const char *filename, void *data)
00081 {
00082     sound_data_num++;
00083     sound_data = realloc(sound_data, sizeof(sound_data_struct) * sound_data_num);
00084     sound_data[sound_data_num - 1].type = type;
00085     sound_data[sound_data_num - 1].filename = strdup(filename);
00086     sound_data[sound_data_num - 1].data = data;
00087 
00088     return &sound_data[sound_data_num - 1];
00089 }
00090 
00091 #endif
00092 
00096 static void sound_free(sound_data_struct *tmp)
00097 {
00098     switch (tmp->type)
00099     {
00100         case SOUND_TYPE_CHUNK:
00101 #ifdef HAVE_SDL_MIXER
00102             Mix_FreeChunk((Mix_Chunk *) tmp->data);
00103 #endif
00104             break;
00105 
00106         case SOUND_TYPE_MUSIC:
00107 #ifdef HAVE_SDL_MIXER
00108             Mix_FreeMusic((Mix_Music *) tmp->data);
00109 #endif
00110             break;
00111 
00112         default:
00113             LOG(llevBug, "sound_free(): Trying to free sound with unknown type: %d.\n", tmp->type);
00114             return;
00115     }
00116 
00117     free(tmp->filename);
00118 }
00119 
00122 void sound_init()
00123 {
00124     sound_background = NULL;
00125     sound_data = NULL;
00126     sound_data_num = 0;
00127     enabled = 1;
00128 
00129 #ifdef HAVE_SDL_MIXER
00130     if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, AUDIO_S16, MIX_DEFAULT_CHANNELS, 1024) < 0)
00131     {
00132         draw_info_format(COLOR_RED, "Could not initialize audio device; sound will not be heard. Reason: %s", Mix_GetError());
00133         enabled = 0;
00134     }
00135 #else
00136     enabled =0;
00137 #endif
00138 }
00139 
00142 void sound_deinit()
00143 {
00144     size_t i;
00145 
00146     for (i = 0; i < sound_data_num; i++)
00147     {
00148         sound_free(&sound_data[i]);
00149     }
00150 
00151     free(sound_data);
00152     sound_data = NULL;
00153     sound_data_num = 0;
00154 #ifdef HAVE_SDL_MIXER
00155     Mix_CloseAudio();
00156 #endif
00157     enabled = 0;
00158 }
00159 
00165 static void sound_add_effect(const char *filename, int volume, int loop)
00166 {
00167 #ifdef HAVE_SDL_MIXER
00168     int channel;
00169     sound_data_struct *tmp;
00170     Mix_Chunk *chunk = NULL;
00171 
00172     if (!enabled)
00173     {
00174         return;
00175     }
00176 
00177     /* Try to find the sound first. */
00178     tmp = sound_find(filename);
00179 
00180     if (!tmp)
00181     {
00182         chunk = Mix_LoadWAV(filename);
00183 
00184         if (!chunk)
00185         {
00186             LOG(llevBug, "sound_add_effect(): Could not load '%s'. Reason: %s.\n", filename, Mix_GetError());
00187             return;
00188         }
00189 
00190         /* We loaded it now, so add it to the array of loaded sounds. */
00191         tmp = sound_new(SOUND_TYPE_CHUNK, filename, chunk);
00192     }
00193 
00194     channel = Mix_PlayChannel(-1, (Mix_Chunk *) tmp->data, loop);
00195 
00196     if (channel == -1)
00197     {
00198         return;
00199     }
00200 
00201     Mix_Volume(channel, (int) ((setting_get_int(OPT_CAT_SOUND, OPT_VOLUME_SOUND) / 100.0) * ((double) volume * (MIX_MAX_VOLUME / 100.0))));
00202 
00203     /* Re-sort the array as needed. */
00204     if (chunk)
00205     {
00206         sound_sort();
00207     }
00208 #else
00209     (void) filename;
00210     (void) volume;
00211     (void) loop;
00212 #endif
00213 }
00214 
00219 void sound_play_effect(const char *filename, int volume)
00220 {
00221     char path[HUGE_BUF];
00222 
00223     snprintf(path, sizeof(path), DIRECTORY_SFX"/%s", filename);
00224     sound_add_effect(file_path(path, "r"), volume, 0);
00225 }
00226 
00232 void sound_start_bg_music(const char *filename, int volume, int loop)
00233 {
00234 #ifdef HAVE_SDL_MIXER
00235     char path[HUGE_BUF];
00236     sound_data_struct *tmp;
00237     Mix_Music *music = NULL;
00238 
00239     if (!enabled)
00240     {
00241         return;
00242     }
00243 
00244     if (!strcmp(filename, "no_music") || !strcmp(filename, "Disable music"))
00245     {
00246         sound_stop_bg_music();
00247         return;
00248     }
00249 
00250     snprintf(path, sizeof(path), DIRECTORY_MEDIA"/%s", filename);
00251 
00252     /* Same background music, nothing to do. */
00253     if (sound_background && !strcmp(sound_background, path))
00254     {
00255         return;
00256     }
00257 
00258     /* Try to find the music. */
00259     tmp = sound_find(path);
00260 
00261     if (!tmp)
00262     {
00263         music = Mix_LoadMUS(file_path(path, "r"));
00264 
00265         if (!music)
00266         {
00267             LOG(llevBug, "sound_start_bg_music(): Could not load '%s'. Reason: %s.\n", path, Mix_GetError());
00268             return;
00269         }
00270 
00271         /* Add the loaded music to the array. */
00272         tmp = sound_new(SOUND_TYPE_MUSIC, path, music);
00273     }
00274 
00275     sound_stop_bg_music();
00276 
00277     sound_background = strdup(path);
00278     Mix_VolumeMusic(volume);
00279     Mix_PlayMusic((Mix_Music *) tmp->data, loop);
00280 
00281     /* Due to a bug in SDL_mixer, some audio types (such as XM, among
00282      * others) will continue playing even when the volume has been set to
00283      * 0, which means we need to manually pause the music if volume is 0,
00284      * and unpause it in sound_update_volume(), if the volume changes. */
00285     if (volume == 0)
00286     {
00287         Mix_PauseMusic();
00288     }
00289 
00290     /* Re-sort the array as needed. */
00291     if (music)
00292     {
00293         sound_sort();
00294     }
00295 #else
00296     (void) filename;
00297     (void) volume;
00298     (void) loop;
00299 #endif
00300 }
00301 
00304 void sound_stop_bg_music()
00305 {
00306     if (!enabled)
00307     {
00308         return;
00309     }
00310 
00311     if (sound_background)
00312     {
00313 #ifdef HAVE_SDL_MIXER
00314         Mix_HaltMusic();
00315 #endif
00316         free(sound_background);
00317         sound_background = NULL;
00318     }
00319 }
00320 
00324 void update_map_bg_music(const char *bg_music)
00325 {
00326     if (sound_map_background_disabled)
00327     {
00328         return;
00329     }
00330 
00331     if (!strcmp(bg_music, "no_music"))
00332     {
00333         sound_stop_bg_music();
00334     }
00335     else
00336     {
00337         int loop = -1, vol = 0;
00338         char filename[MAX_BUF];
00339 
00340         if (sscanf(bg_music, "%s %d %d", filename, &loop, &vol) < 1)
00341         {
00342             LOG(llevBug, "parse_map_bg_music(): Bogus background music: '%s'\n", bg_music);
00343             return;
00344         }
00345 
00346         sound_start_bg_music(filename, setting_get_int(OPT_CAT_SOUND, OPT_VOLUME_MUSIC) + vol, loop);
00347     }
00348 }
00349 
00352 void sound_update_volume()
00353 {
00354     if (!enabled)
00355     {
00356         return;
00357     }
00358 
00359 #ifdef HAVE_SDL_MIXER
00360     Mix_VolumeMusic(setting_get_int(OPT_CAT_SOUND, OPT_VOLUME_MUSIC));
00361 
00362     /* If there is any background music, due to a bug in SDL_mixer, we
00363      * may need to pause or unpause the music. */
00364     if (sound_background)
00365     {
00366         /* If the new volume is 0, pause the music. */
00367         if (setting_get_int(OPT_CAT_SOUND, OPT_VOLUME_MUSIC) == 0)
00368         {
00369             if (!Mix_PausedMusic())
00370             {
00371                 Mix_PauseMusic();
00372             }
00373         }
00374         /* Non-zero and already paused, so resume the music. */
00375         else if (Mix_PausedMusic())
00376         {
00377             Mix_ResumeMusic();
00378         }
00379     }
00380 #endif
00381 }
00382 
00386 const char *sound_get_bg_music()
00387 {
00388     return sound_background;
00389 }
00390 
00394 const char *sound_get_bg_music_basename()
00395 {
00396     const char *bg_music = sound_background;
00397     char *cp;
00398 
00399     if (bg_music && (cp = strrchr(bg_music, '/')))
00400     {
00401         bg_music = cp + 1;
00402     }
00403 
00404     return bg_music;
00405 }
00406 
00412 uint8 sound_map_background(int new)
00413 {
00414     if (new == -1)
00415     {
00416         return sound_map_background_disabled;
00417     }
00418     else
00419     {
00420         sound_map_background_disabled = new;
00421         return new;
00422     }
00423 }
00424 
00429 void SoundCmd(uint8 *data, int len)
00430 {
00431     size_t pos = 0, i = 0;
00432     uint8 type;
00433     int loop, volume;
00434     char filename[MAX_BUF], c;
00435 
00436     (void) len;
00437     filename[0] = '\0';
00438     type = data[pos++];
00439 
00440     while ((c = (char) (data[pos++])))
00441     {
00442         filename[i++] = c;
00443     }
00444 
00445     filename[i] = '\0';
00446     loop = data[pos++];
00447     volume = data[pos++];
00448 
00449     if (type == CMD_SOUND_EFFECT)
00450     {
00451         sint8 x, y;
00452         int dist_volume;
00453         char path[HUGE_BUF];
00454 
00455         x = data[pos++];
00456         y = data[pos++];
00457         dist_volume = isqrt(POW2(0 - x) + POW2(0 - y)) - 1;
00458 
00459         if (dist_volume < 0)
00460         {
00461             dist_volume = 0;
00462         }
00463 
00464         dist_volume = 100 - dist_volume * (100 / MAX_SOUND_DISTANCE);
00465         snprintf(path, sizeof(path), DIRECTORY_SFX"/%s", filename);
00466         sound_add_effect(file_path(path, "r"), dist_volume + volume, loop);
00467     }
00468     else if (type == CMD_SOUND_BACKGROUND)
00469     {
00470         if (!sound_map_background_disabled)
00471         {
00472             sound_start_bg_music(filename, setting_get_int(OPT_CAT_SOUND, OPT_VOLUME_MUSIC) + volume, loop);
00473         }
00474     }
00475     else if (type == CMD_SOUND_ABSOLUTE)
00476     {
00477         sound_add_effect(filename, volume, loop);
00478     }
00479     else
00480     {
00481         LOG(llevBug, "SoundCmd(): Invalid sound type: %d\n", type);
00482         return;
00483     }
00484 }
00485 
00488 void sound_pause_music()
00489 {
00490 #ifdef HAVE_SDL_MIXER
00491     Mix_PauseMusic();
00492 #endif
00493 }
00494 
00497 void sound_resume_music()
00498 {
00499 #ifdef HAVE_SDL_MIXER
00500     Mix_ResumeMusic();
00501 #endif
00502 }
00503 
00507 int sound_playing_music()
00508 {
00509 #ifdef HAVE_SDL_MIXER
00510     return Mix_PlayingMusic();
00511 #else
00512     return 0;
00513 #endif
00514 }