Atrinik Client  4.0
sound.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
32 #include <global.h>
33 #include <toolkit/packet.h>
34 #include <toolkit/string.h>
35 #include <toolkit/path.h>
36 
40 static char *sound_background;
44 static uint8_t sound_map_background_disabled = 0;
48 static uint8_t enabled = 0;
53 
54 #ifdef HAVE_SDL_MIXER
55 
59 static uint32_t sound_background_started;
63 static uint32_t sound_background_duration;
67 static uint8_t sound_background_update_duration;
72 static int sound_background_loop;
76 static int sound_background_volume;
80 static sound_data_struct *sound_data;
84 static void (*sound_background_hook)(void) ;
85 
89 static void sound_background_hook_execute(void)
90 {
93  }
94 }
95 
107 static sound_data_struct *sound_new(int type, const char *filename, void *data)
108 {
109  sound_data_struct *tmp;
110 
111  tmp = emalloc(sizeof(sound_data_struct));
112  tmp->type = type;
113  tmp->filename = estrdup(filename);
114  tmp->data = data;
115  HASH_ADD_KEYPTR(hh, sound_data, tmp->filename, strlen(tmp->filename), tmp);
116 
117  return tmp;
118 }
119 
125 static void sound_free(sound_data_struct *tmp)
126 {
127  switch (tmp->type) {
128  case SOUND_TYPE_CHUNK:
129  Mix_FreeChunk(tmp->data);
130  break;
131 
132  case SOUND_TYPE_MUSIC:
133  Mix_FreeMusic(tmp->data);
134  break;
135 
136  default:
137  LOG(BUG, "Trying to free sound with unknown type: %d.", tmp->type);
138  break;
139  }
140 
141  efree(tmp->filename);
142  efree(tmp);
143 }
144 
152 static uint32_t sound_music_file_get_duration(const char *filename)
153 {
154  char path[HUGE_BUF], *contents, *cp;
155  uint32_t duration;
156 
157  snprintf(path, sizeof(path), DIRECTORY_MEDIA "/durations/%s", filename);
158  cp = file_path(path, "r");
159  contents = path_file_contents(cp);
160  efree(cp);
161 
162  if (!contents) {
163  return 0;
164  }
165 
166  duration = atoi(contents);
167  efree(contents);
168 
169  return duration;
170 }
171 
179 static void sound_music_file_set_duration(const char *filename, uint32_t duration)
180 {
181  char path[HUGE_BUF];
182  FILE *fp;
183 
184  snprintf(path, sizeof(path), DIRECTORY_MEDIA "/durations/%s", filename);
185  fp = path_fopen(path, "w");
186 
187  if (!fp) {
188  LOG(BUG, "Could not open file for writing: %s", path);
189  return;
190  }
191 
192  fprintf(fp, "%u", duration);
193  fclose(fp);
194 }
195 
199 static void sound_music_finished(void)
200 {
201  uint32_t duration;
202  char *tmp;
203  const char *bg_music;
204 
205  if (!sound_background) {
206  return;
207  }
208 
209  tmp = sound_background;
210  bg_music = sound_get_bg_music_basename();
211  duration = sound_music_get_offset();
212 
213  sound_background = NULL;
214  sound_background_hook_execute();
215 
216  if (sound_background_update_duration && (!sound_background_duration || duration != sound_background_duration)) {
217  sound_music_file_set_duration(bg_music, duration);
218  }
219 
220  if (sound_background_loop) {
221  if (sound_background_loop > 0) {
222  sound_background_loop--;
223  }
224 
225  sound_start_bg_music(bg_music, sound_background_volume, sound_background_loop);
226  }
227 
228  efree(tmp);
229 }
230 
231 #endif
232 
239 {
240 #ifdef HAVE_SDL_MIXER
241  sound_background_hook = ptr;
242 #endif
243 }
244 
248 void sound_init(void)
249 {
250  sound_background = NULL;
251 
252 #ifdef HAVE_SDL_MIXER
253  sound_background_hook = NULL;
254  sound_data = NULL;
255  enabled = 1;
256 
257  if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, AUDIO_S16, MIX_DEFAULT_CHANNELS, 1024) < 0) {
258  draw_info_format(COLOR_RED, "Could not initialize audio device; sound will not be heard. Reason: %s", Mix_GetError());
259  enabled = 0;
260  }
261 
262  Mix_HookMusicFinished(sound_music_finished);
263 #else
264  enabled = 0;
265 #endif
266 }
267 
271 static void sound_cache_free(void)
272 {
273 #ifdef HAVE_SDL_MIXER
274  sound_data_struct *curr, *tmp;
275 
276  HASH_ITER(hh, sound_data, curr, tmp)
277  {
278  HASH_DEL(sound_data, curr);
279  sound_free(curr);
280  }
281 #endif
282 }
283 
287 void sound_deinit(void)
288 {
290 #ifdef HAVE_SDL_MIXER
291  Mix_CloseAudio();
292 #endif
293 
294  enabled = 0;
295 
296  if (sound_background != NULL) {
297  efree(sound_background);
298  sound_background = NULL;
299  }
300 }
301 
306 {
308 }
309 
321 static int sound_add_effect(const char *filename, int volume, int loop)
322 {
323 #ifdef HAVE_SDL_MIXER
324  int channel;
325  sound_data_struct *tmp;
326 
327  if (!enabled) {
328  return -1;
329  }
330 
331  /* Try to find the sound first. */
332  HASH_FIND_STR(sound_data, filename, tmp);
333 
334  if (!tmp) {
335  Mix_Chunk *chunk = Mix_LoadWAV(filename);
336 
337  if (!chunk) {
338  LOG(BUG, "Could not load '%s'. Reason: %s.", filename, Mix_GetError());
339  return -1;
340  }
341 
342  /* We loaded it now, so add it to the array of loaded sounds. */
343  tmp = sound_new(SOUND_TYPE_CHUNK, filename, chunk);
344  }
345 
346  channel = Mix_PlayChannel(-1, (Mix_Chunk *) tmp->data, loop);
347 
348  if (channel == -1) {
349  return -1;
350  }
351 
352  Mix_Volume(channel, (int) ((setting_get_int(OPT_CAT_SOUND, OPT_VOLUME_SOUND) / 100.0) * ((double) volume * (MIX_MAX_VOLUME / 100.0))));
353 
354  return channel;
355 #else
356  return -1;
357 #endif
358 }
359 
367 void sound_play_effect(const char *filename, int volume)
368 {
369  char path[HUGE_BUF], *cp;
370 
371  snprintf(path, sizeof(path), DIRECTORY_SFX "/%s", filename);
372  cp = file_path(path, "r");
373  sound_add_effect(cp, volume, 0);
374  efree(cp);
375 }
376 
390 int sound_play_effect_loop(const char *filename, int volume, int loop)
391 {
392  char path[HUGE_BUF], *cp;
393  int ret;
394 
395  snprintf(path, sizeof(path), DIRECTORY_SFX "/%s", filename);
396  cp = file_path(path, "r");
397  ret = sound_add_effect(cp, volume, loop);
398  efree(cp);
399 
400  return ret;
401 }
402 
412 void sound_start_bg_music(const char *filename, int volume, int loop)
413 {
414 #ifdef HAVE_SDL_MIXER
415  char path[HUGE_BUF];
416  sound_data_struct *tmp;
417 
418  if (!enabled) {
419  return;
420  }
421 
422  if (!strcmp(filename, "no_music") || !strcmp(filename, "Disable music")) {
424  return;
425  }
426 
427  snprintf(path, sizeof(path), DIRECTORY_MEDIA "/%s", filename);
428 
429  /* Same background music, nothing to do. */
430  if (sound_background && !strcmp(sound_background, path)) {
431  return;
432  }
433 
434  /* Try to find the music. */
435  HASH_FIND_STR(sound_data, path, tmp);
436 
437  if (!tmp) {
438  char *cp;
439  Mix_Music *music;
440 
441  cp = file_path(path, "r");
442  music = Mix_LoadMUS(cp);
443  efree(cp);
444 
445  if (music == NULL) {
446  LOG(BUG, "Could not load '%s'. Reason: %s.", path, Mix_GetError());
447  return;
448  }
449 
450  /* Add the loaded music to the array. */
451  tmp = sound_new(SOUND_TYPE_MUSIC, path, music);
452  }
453 
455 
456  sound_background = estrdup(path);
457  sound_background_hook_execute();
458  sound_background_loop = loop;
459  sound_background_volume = volume;
460  sound_background_duration = sound_music_file_get_duration(filename);
461  sound_background_update_duration = 1;
462 
463  Mix_VolumeMusic(volume);
464  Mix_PlayMusic(tmp->data, 0);
465 
466  sound_background_started = SDL_GetTicks();
467 
468  /* Due to a bug in SDL_mixer, some audio types (such as XM, among
469  * others) will continue playing even when the volume has been set to
470  * 0, which means we need to manually pause the music if volume is 0,
471  * and unpause it in sound_update_volume(), if the volume changes. */
472  if (volume == 0) {
474  }
475 #endif
476 }
477 
482 {
483  if (!enabled) {
484  return;
485  }
486 
487  if (sound_background) {
488  efree(sound_background);
489  sound_background = NULL;
490 #ifdef HAVE_SDL_MIXER
491  sound_background_hook_execute();
492  Mix_HaltMusic();
493 #endif
494  }
495 }
496 
501 {
502 #ifdef HAVE_SDL_MIXER
503  Mix_PauseMusic();
504  sound_background_update_duration = 0;
505 #endif
506 }
507 
512 {
513 #ifdef HAVE_SDL_MIXER
514  Mix_ResumeMusic();
515 #endif
516 }
517 
523 void update_map_bg_music(const char *bg_music)
524 {
526  return;
527  }
528 
529  if (!strcmp(bg_music, "no_music")) {
531  } else {
532  int loop = -1, vol = 0;
533  char filename[MAX_BUF];
534 
535  if (sscanf(bg_music, "%s %d %d", filename, &loop, &vol) < 1) {
536  LOG(BUG, "Bogus background music: '%s'", bg_music);
537  return;
538  }
539 
541  }
542 }
543 
548 {
549  if (!enabled) {
550  return;
551  }
552 
553 #ifdef HAVE_SDL_MIXER
555 
556  /* If there is any background music, due to a bug in SDL_mixer, we
557  * may need to pause or unpause the music. */
558  if (sound_background) {
559  /* If the new volume is 0, pause the music. */
561  if (!Mix_PausedMusic()) {
563  }
564  } else if (Mix_PausedMusic()) {
565  /* Non-zero and already paused, so resume the music. */
567  }
568  }
569 #endif
570 }
571 
577 const char *sound_get_bg_music(void)
578 {
579  return sound_background;
580 }
581 
588 {
589  const char *bg_music = sound_background;
590  char *cp;
591 
592  if (bg_music && (cp = strrchr(bg_music, '/'))) {
593  bg_music = cp + 1;
594  }
595 
596  return bg_music;
597 }
598 
608 uint8_t sound_map_background(int val)
609 {
610  if (val == -1) {
612  } else {
614  return val;
615  }
616 }
617 
624 {
625  if (!sound_background) {
626  return 0;
627  }
628 
629 #ifdef HAVE_SDL_MIXER
630  return (SDL_GetTicks() - sound_background_started) / 1000;
631 #else
632  return 0;
633 #endif
634 }
635 
643 {
644  if (!sound_background) {
645  return 0;
646  }
647 
648 #ifdef HAVE_SDL_MIXER
649  switch (Mix_GetMusicType(NULL)) {
650  case MUS_OGG:
651  case MUS_MP3:
652  case MUS_MP3_MAD:
653  return 1;
654 
655  default:
656  break;
657  }
658 #endif
659 
660  return 0;
661 }
662 
669 void sound_music_seek(uint32_t offset)
670 {
671  if (!sound_music_can_seek()) {
672  return;
673  }
674 
675 #ifdef HAVE_SDL_MIXER
676  Mix_RewindMusic();
677 
678  if (Mix_SetMusicPosition(offset) == -1) {
679  LOG(BUG, "Mix_SetMusicPosition: %s", Mix_GetError());
680  }
681 
682  sound_background_started = SDL_GetTicks() - offset * 1000;
683 #endif
684 }
685 
692 {
693 #ifdef HAVE_SDL_MIXER
694  return sound_background_duration;
695 #else
696  return 0;
697 #endif
698 }
699 
701 void socket_command_sound(uint8_t *data, size_t len, size_t pos)
702 {
703  uint8_t type;
704  int loop, volume;
705  char filename[MAX_BUF];
706 
707  type = packet_to_uint8(data, len, &pos);
708  packet_to_string(data, len, &pos, filename, sizeof(filename));
709  loop = packet_to_int8(data, len, &pos);
710  volume = packet_to_int8(data, len, &pos);
711 
712  if (type == CMD_SOUND_EFFECT) {
713  int8_t x, y;
714  int channel;
715 
716  x = packet_to_uint8(data, len, &pos);
717  y = packet_to_uint8(data, len, &pos);
718 
719  channel = sound_play_effect_loop(filename, 100 + volume, loop);
720 
721  if (channel != -1) {
722  int angle, distance;
723 
724  angle = 0;
725  distance = (255 * isqrt(POW2(x) + POW2(y))) / MAX_SOUND_DISTANCE;
726 
727  if (setting_get_int(OPT_CAT_SOUND, OPT_3D_SOUNDS) && distance >= (255 / MAX_SOUND_DISTANCE) * 2) {
728  angle = atan2(-y, x) * (180 / M_PI);
729  angle = 90 - angle;
730  }
731 
732 #ifdef HAVE_SDL_MIXER
733  Mix_SetPosition(channel, angle, distance);
734 #endif
735  }
736  } else if (type == CMD_SOUND_BACKGROUND) {
739  }
740  } else if (type == CMD_SOUND_ABSOLUTE) {
741  sound_add_effect(filename, (uint8_t) volume, loop);
742  } else {
743  LOG(BUG, "Invalid sound type: %d", type);
744  return;
745  }
746 }
747 
756 {
757  DL_DELETE(sound_ambient_head, tmp);
758 #ifdef HAVE_SDL_MIXER
759  Mix_HaltChannel(tmp->channel);
760 #endif
761  efree(tmp);
762 }
763 
770 {
771 #ifdef HAVE_SDL_MIXER
772  int x, y, angle, distance, cx, cy;
773 
776 
777  /* The x/y positions stored in the sound effect structure are the
778  * positions on the map, so we have to convert it to coordinates
779  * relative to the player. */
780  x = tmp->x - cx;
781  y = tmp->y - cy;
782 
783  angle = 0;
784  /* Calculate the distance. */
785  distance = MIN(255, (255 * isqrt(POW2(x) + POW2(y))) / (tmp->max_range + (tmp->max_range / 2)));
786 
787  /* Calculate the angle. */
788  if (setting_get_int(OPT_CAT_SOUND, OPT_3D_SOUNDS) && distance) {
789  angle = atan2(-y, x) * (180 / M_PI);
790  angle = 90 - angle;
791  }
792 
793  Mix_SetPosition(tmp->channel, angle, distance);
794 #else
795  (void) tmp;
796 #endif
797 }
798 
809 void sound_ambient_mapcroll(int xoff, int yoff)
810 {
811  sound_ambient_struct *sound_ambient, *tmp;
812 
813  DL_FOREACH_SAFE(sound_ambient_head, sound_ambient, tmp)
814  {
815  /* Adjust the coordinates. */
816  sound_ambient->x -= xoff;
817  sound_ambient->y -= yoff;
818 
819  /* If the sound effect is now off-screen, remove it. */
820  if (sound_ambient->x < 0 || sound_ambient->x >= setting_get_int(OPT_CAT_MAP, OPT_MAP_WIDTH) || sound_ambient->y < 0 || sound_ambient->y >= setting_get_int(OPT_CAT_MAP, OPT_MAP_HEIGHT)) {
821  sound_ambient_free(sound_ambient);
822  continue;
823  }
824 
825  /* Adjust the distance and angle. */
826  sound_ambient_set_position(sound_ambient);
827  }
828 }
829 
834 {
835  sound_ambient_struct *sound_ambient, *tmp;
836 
837  DL_FOREACH_SAFE(sound_ambient_head, sound_ambient, tmp)
838  {
839  sound_ambient_free(sound_ambient);
840  }
841 }
842 
844 void socket_command_sound_ambient(uint8_t *data, size_t len, size_t pos)
845 {
846  int tag, tag_old;
847  uint8_t x, y;
848  sound_ambient_struct *sound_ambient;
849 
850  /* Loop through the data, as there may be multiple sound effects. */
851  while (pos < len) {
852  x = packet_to_uint8(data, len, &pos);
853  y = packet_to_uint8(data, len, &pos);
854  tag_old = packet_to_uint32(data, len, &pos);
855  tag = packet_to_uint32(data, len, &pos);
856 
857  /* If there is an old tag, the server is telling us to stop
858  * playing a sound effect. */
859  if (tag_old != 0) {
860  DL_FOREACH(sound_ambient_head, sound_ambient) {
861  if (sound_ambient->tag == tag_old) {
862  sound_ambient_free(sound_ambient);
863  break;
864  }
865  }
866  }
867 
868  /* Is there a new sound effect to start playing? */
869  if (tag) {
870  char filename[MAX_BUF];
871  uint8_t volume, max_range;
872  int channel;
873 
874  /* Get the sound effect filename, volume, etc. */
875  packet_to_string(data, len, &pos, filename, sizeof(filename));
876  volume = packet_to_uint8(data, len, &pos);
877  max_range = packet_to_uint8(data, len, &pos);
878 
879  /* Try to start playing the sound effect. */
880  channel = sound_play_effect_loop(filename, volume, -1);
881 
882  /* Successfully started playing the effect, add it to the
883  * list of active sound effects. */
884  if (channel != -1) {
885  sound_ambient = ecalloc(1, sizeof(*sound_ambient));
886  sound_ambient->channel = channel;
887  sound_ambient->tag = tag;
888  sound_ambient->x = x;
889  sound_ambient->y = y;
890  sound_ambient->max_range = max_range;
891  sound_ambient_set_position(sound_ambient);
892  DL_APPEND(sound_ambient_head, sound_ambient);
893  }
894  }
895  }
896 }
897 
904 {
905 #ifdef HAVE_SDL_MIXER
906  return Mix_PlayingMusic();
907 #else
908  return 0;
909 #endif
910 }
void sound_ambient_mapcroll(int xoff, int yoff)
Definition: sound.c:809
const char * sound_get_bg_music_basename(void)
Definition: sound.c:587
uint8_t sound_map_background(int val)
Definition: sound.c:608
void sound_update_volume(void)
Definition: sound.c:547
void sound_music_seek(uint32_t offset)
Definition: sound.c:669
static sound_ambient_struct * sound_ambient_head
Definition: sound.c:52
static int sound_add_effect(const char *filename, int volume, int loop)
Definition: sound.c:321
static void sound_ambient_free(sound_ambient_struct *tmp)
Definition: sound.c:755
static char * sound_background
Definition: sound.c:40
void sound_start_bg_music(const char *filename, int volume, int loop)
Definition: sound.c:412
void sound_play_effect(const char *filename, int volume)
Definition: sound.c:367
void sound_resume_music(void)
Definition: sound.c:511
int sound_playing_music(void)
Definition: sound.c:903
void sound_background_hook_register(void *ptr)
Definition: sound.c:238
static uint8_t enabled
Definition: sound.c:48
uint32_t sound_music_get_duration()
Definition: sound.c:691
#define SOUND_TYPE_CHUNK
Definition: sound.h:38
void socket_command_sound_ambient(uint8_t *data, size_t len, size_t pos)
Definition: sound.c:844
void sound_ambient_clear(void)
Definition: sound.c:833
void sound_clear_cache(void)
Definition: sound.c:305
#define COLOR_RED
Definition: text.h:295
uint8_t max_range
Definition: sound.h:88
void socket_command_sound(uint8_t *data, size_t len, size_t pos)
Definition: sound.c:701
int sound_play_effect_loop(const char *filename, int volume, int loop)
Definition: sound.c:390
static uint8_t sound_map_background_disabled
Definition: sound.c:44
int64_t setting_get_int(int cat, int setting)
Definition: settings.c:414
void sound_stop_bg_music(void)
Definition: sound.c:481
void sound_init(void)
Definition: sound.c:248
void sound_deinit(void)
Definition: sound.c:287
static void sound_background_hook(void)
Definition: main.c:421
char * filename
Definition: sound.h:54
#define SOUND_TYPE_MUSIC
Definition: sound.h:40
void update_map_bg_music(const char *bg_music)
Definition: sound.c:523
void sound_pause_music(void)
Definition: sound.c:500
void * data
Definition: sound.h:48
char * file_path(const char *path, const char *mode)
Definition: wrapper.c:369
const char * sound_get_bg_music(void)
Definition: sound.c:577
void draw_info_format(const char *color, const char *format,...)
Definition: textwin.c:429
static void sound_ambient_set_position(sound_ambient_struct *tmp)
Definition: sound.c:769
int sound_music_can_seek(void)
Definition: sound.c:642
#define MAX_SOUND_DISTANCE
Definition: sound.h:63
static void sound_cache_free(void)
Definition: sound.c:271
uint32_t sound_music_get_offset(void)
Definition: sound.c:623