Crossfire Client, Trunk
cfsndserv.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
19 #include "client.h"
20 
21 #include <SDL.h>
22 #include <SDL_mixer.h>
23 #include <glib-object.h>
24 
25 #include "client-vala.h"
26 #include "sound.h"
27 
28 typedef struct sound_settings {
29  int buflen; //< how big the buffers should be
30  int max_chunk; //< number of sounds that can be played at the same time
32 
33 static Mix_Music *music = NULL; //< current music sample
34 static char *music_playing; //< current or next music name
35 
36 static sound_settings settings = { 512, 4 };
37 static const int fade_time_ms = 1000;
38 
39 static GHashTable* chunk_cache;
40 static GHashTable* sounds;
41 
49 static int init_audio() {
50  if (SDL_Init(SDL_INIT_AUDIO) == -1) {
51  fprintf(stderr, "SDL_Init: %s\n", SDL_GetError());
52  return 1;
53  }
54 
55  if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2,
56  settings.buflen)) {
57  fprintf(stderr, "Mix_OpenAudio: %s\n", SDL_GetError());
58  return 2;
59  }
60 
61  /* Determine if OGG is supported. */
62  const int mix_flags = MIX_INIT_OGG;
63  int mix_init = Mix_Init(mix_flags);
64  if ((mix_init & mix_flags) != mix_flags) {
65  fprintf(stderr,
66  "OGG support in SDL_mixer is required for sound; aborting!\n");
67  return 3;
68  }
69 
70  /* Allocate channels and resize buffers accordingly. */
71  Mix_AllocateChannels(settings.max_chunk);
72  return 0;
73 }
74 
83 int cf_snd_init() {
84  /* Set $CF_SOUND_DIR to something reasonable, if not already set. */
85  if (!g_setenv("CF_SOUND_DIR", CF_SOUND_DIR, FALSE)) {
86  perror("Couldn't set $CF_SOUND_DIR");
87  return -1;
88  }
89 
90  /* Initialize sound definitions. */
91  chunk_cache = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
92  (void *)Mix_FreeChunk);
93  sounds = load_snd_config();
94  if (!sounds) {
95  return -1;
96  }
97 
98  /* Initialize audio library. */
99  if (init_audio()) {
100  return -1;
101  }
102 
103  return 0;
104 }
105 
106 static Mix_Chunk* load_chunk(char const name[static 1]) {
107  Mix_Chunk* chunk = g_hash_table_lookup(chunk_cache, name);
108  if (chunk != NULL) {
109  return chunk;
110  }
111 
112  char path[MAXSOCKBUF];
113  snprintf(path, sizeof(path), "%s/%s", g_getenv("CF_SOUND_DIR"), name);
114  chunk = Mix_LoadWAV(path);
115  if (!chunk) {
116  fprintf(stderr, "Could not load sound from '%s': %s\n", path,
117  SDL_GetError());
118  return NULL;
119  }
120  g_hash_table_insert(chunk_cache, &name, chunk);
121  return chunk;
122 }
123 
134 void cf_play_sound(gint8 x, gint8 y, guint8 dir, guint8 vol, guint8 type,
135  char const sound[static 1], char const source[static 1]) {
136  LOG(LOG_DEBUG, "cf_play_sound",
137  "Playing sound2 x=%hhd y=%hhd dir=%hhd volume=%hhd type=%hhd sound=%s "
138  "source=%s", x, y, dir, vol, type, sound, source);
139 
140  SoundInfo* si = g_hash_table_lookup(sounds, sound);
141  if (si == NULL) {
142  LOG(LOG_WARNING, "cf_play_sound", "sound not defined: %s", sound);
143  return;
144  }
145 
146  Mix_Chunk* chunk = load_chunk(si->file);
147  if (chunk == NULL) {
148  return;
149  }
150  Mix_VolumeChunk(chunk, si->vol * MIX_MAX_VOLUME / 100);
151 
152  int channel = Mix_GroupAvailable(-1);
153  if (channel == -1) {
154  g_warning("No free channels available to play sound");
155  return;
156  }
157  Mix_Volume(channel, vol * MIX_MAX_VOLUME / 100);
158  Mix_PlayChannel(channel, chunk, 0);
159 }
160 
161 static bool music_is_different(char const music[static 1]) {
162  if (music_playing == NULL)
163  return true;
164  if (strcmp(music, music_playing) != 0) {
165  return true;
166  }
167  return false;
168 }
169 
170 static bool find_music_path(const char *name, char *path, size_t len) {
171  snprintf(path, len, "%s/music/%s.ogg", g_getenv("CF_SOUND_DIR"), name);
172  if (g_file_test(path, G_FILE_TEST_EXISTS))
173  return true;
174  snprintf(path, len, "%s/music/%s.mp3", g_getenv("CF_SOUND_DIR"), name);
175  if (g_file_test(path, G_FILE_TEST_EXISTS))
176  return true;
177  return false;
178 }
179 
181  Mix_VolumeMusic(MIX_MAX_VOLUME * 3/4 * MIN(use_config[CONFIG_MUSIC_VOL], 100) / 100);
182 }
183 
185  Mix_HookMusicFinished(NULL);
186  if (music != NULL) {
187  Mix_FreeMusic(music);
188  music = NULL;
189  }
190 
191  if (strcmp(music_playing, "NONE") == 0) {
192  return;
193  }
194  char path[MAXSOCKBUF];
195  if (!find_music_path(music_playing, path, sizeof(path))) {
196  fprintf(stderr, "Could not find a music file (ogg or mp3) for %s in %s\n", music_playing, path);
197  return;
198  }
199  music = Mix_LoadMUS(path);
200  if (!music) {
201  fprintf(stderr, "Could not load music: %s\n", Mix_GetError());
202  return;
203  }
205  Mix_FadeInMusic(music, -1, fade_time_ms);
206 }
207 
213 void cf_play_music(const char* music_name) {
214  LOG(LOG_DEBUG, "cf_play_music", "\"%s\"", music_name);
215  if (!music_is_different(music_name)) {
216  return;
217  }
218 
219  if (music_playing != NULL)
220  g_free(music_playing);
221  music_playing = g_strdup(music_name);
222  if (Mix_FadeOutMusic(fade_time_ms)) {
223  Mix_HookMusicFinished(cf_play_music_cb); // handle in callback to avoid blocking during fade-out
224  } else {
225  cf_play_music_cb(); // nothing playing, just do it
226  }
227 }
228 
229 void cf_snd_exit() {
230  Mix_HaltMusic();
231  Mix_FreeMusic(music);
232 
233  /* Halt all channels that are playing and free remaining samples. */
234  Mix_HaltChannel(-1);
235  g_hash_table_destroy(chunk_cache);
236  g_hash_table_destroy(sounds);
237 
238  /* Call Mix_Quit() for each time Mix_Init() was called. */
239  while(Mix_Init(0)) {
240  Mix_Quit();
241  }
242 }
music
static Mix_Music * music
Definition: cfsndserv.c:33
music_is_different
static bool music_is_different(char const music[static 1])
Definition: cfsndserv.c:161
LOG_WARNING
@ LOG_WARNING
Warning that something might not work.
Definition: client.h:438
chunk_cache
static GHashTable * chunk_cache
Definition: cfsndserv.c:39
find_music_path
static bool find_music_path(const char *name, char *path, size_t len)
Definition: cfsndserv.c:170
CONFIG_MUSIC_VOL
#define CONFIG_MUSIC_VOL
Definition: client.h:217
MIN
#define MIN(X__, Y__)
Definition: client.h:618
set_music_volume
void set_music_volume()
Definition: cfsndserv.c:180
MAXSOCKBUF
#define MAXSOCKBUF
Maximum size of a packet the client expects to get and that the server can send.
Definition: newclient.h:25
sounds
static GHashTable * sounds
Definition: cfsndserv.c:40
settings
static sound_settings settings
Definition: cfsndserv.c:36
load_chunk
static Mix_Chunk * load_chunk(char const name[static 1])
Definition: cfsndserv.c:106
cf_snd_init
int cf_snd_init()
Initialize sound server.
Definition: cfsndserv.c:83
LOG
void LOG(LogLevel level, const char *origin, const char *format,...)
Log messages of a certain importance to stderr.
Definition: misc.c:111
sound_settings::buflen
int buflen
Definition: cfsndserv.c:29
cf_play_music_cb
void cf_play_music_cb()
Definition: cfsndserv.c:184
sound_settings
Definition: cfsndserv.c:28
sound_settings
struct sound_settings sound_settings
cf_snd_exit
void cf_snd_exit()
Definition: cfsndserv.c:229
music_playing
static char * music_playing
Definition: cfsndserv.c:34
init_audio
static int init_audio()
Initialize the sound subsystem.
Definition: cfsndserv.c:49
sound_settings::max_chunk
int max_chunk
Definition: cfsndserv.c:30
cf_play_sound
void cf_play_sound(gint8 x, gint8 y, guint8 dir, guint8 vol, guint8 type, char const sound[static 1], char const source[static 1])
Play a sound effect using the SDL_mixer sound system.
Definition: cfsndserv.c:134
fade_time_ms
static const int fade_time_ms
Definition: cfsndserv.c:37
use_config
gint16 use_config[CONFIG_NUMS]
Definition: client.h:245
sound.h
LOG_DEBUG
@ LOG_DEBUG
Useful debugging information.
Definition: client.h:436
client.h
cf_play_music
void cf_play_music(const char *music_name)
Play a music file.
Definition: cfsndserv.c:213