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;
34 static sound_settings settings = { 512, 4 };
35 
36 static GHashTable* chunk_cache;
37 static GHashTable* sounds;
38 
46 static int init_audio() {
47  if (SDL_Init(SDL_INIT_AUDIO) == -1) {
48  fprintf(stderr, "SDL_Init: %s\n", SDL_GetError());
49  return 1;
50  }
51 
52  if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2,
53  settings.buflen)) {
54  fprintf(stderr, "Mix_OpenAudio: %s\n", SDL_GetError());
55  return 2;
56  }
57 
58  /* Determine if OGG is supported. */
59  const int mix_flags = MIX_INIT_OGG;
60  int mix_init = Mix_Init(mix_flags);
61  if ((mix_init & mix_flags) != mix_flags) {
62  fprintf(stderr,
63  "OGG support in SDL_mixer is required for sound; aborting!\n");
64  return 3;
65  }
66 
67  /* Allocate channels and resize buffers accordingly. */
68  Mix_AllocateChannels(settings.max_chunk);
69  return 0;
70 }
71 
80 int cf_snd_init() {
81  /* Set $CF_SOUND_DIR to something reasonable, if not already set. */
82  if (!g_setenv("CF_SOUND_DIR", CF_SOUND_DIR, FALSE)) {
83  perror("Couldn't set $CF_SOUND_DIR");
84  return -1;
85  }
86 
87  /* Initialize sound definitions. */
88  chunk_cache = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
89  (void *)Mix_FreeChunk);
90  sounds = load_snd_config();
91  if (!sounds) {
92  return -1;
93  }
94 
95  /* Initialize audio library. */
96  if (init_audio()) {
97  return -1;
98  }
99 
100  return 0;
101 }
102 
103 static Mix_Chunk* load_chunk(char const name[static 1]) {
104  Mix_Chunk* chunk = g_hash_table_lookup(chunk_cache, name);
105  if (chunk != NULL) {
106  return chunk;
107  }
108 
109  char path[MAXSOCKBUF];
110  snprintf(path, sizeof(path), "%s/%s", g_getenv("CF_SOUND_DIR"), name);
111  chunk = Mix_LoadWAV(path);
112  if (!chunk) {
113  fprintf(stderr, "Could not load sound from '%s': %s\n", path,
114  SDL_GetError());
115  return NULL;
116  }
117  g_hash_table_insert(chunk_cache, &name, chunk);
118  return chunk;
119 }
120 
131 void cf_play_sound(gint8 x, gint8 y, guint8 dir, guint8 vol, guint8 type,
132  char const sound[static 1], char const source[static 1]) {
133  LOG(LOG_DEBUG, "cf_play_sound",
134  "Playing sound2 x=%hhd y=%hhd dir=%hhd volume=%hhd type=%hhd sound=%s "
135  "source=%s", x, y, dir, vol, type, sound, source);
136 
137  SoundInfo* si = g_hash_table_lookup(sounds, sound);
138  if (si == NULL) {
139  LOG(LOG_WARNING, "cf_play_sound", "sound not defined: %s", sound);
140  return;
141  }
142 
143  Mix_Chunk* chunk = load_chunk(si->file);
144  if (chunk == NULL) {
145  return;
146  }
147  Mix_VolumeChunk(chunk, si->vol * MIX_MAX_VOLUME / 100);
148 
149  int channel = Mix_GroupAvailable(-1);
150  if (channel == -1) {
151  g_warning("No free channels available to play sound");
152  return;
153  }
154  Mix_Volume(channel, vol * MIX_MAX_VOLUME / 100);
155  Mix_PlayChannel(channel, chunk, 0);
156 }
157 
158 static bool music_is_different(char const music[static 1]) {
159  static char last_played[MAXSOCKBUF] = "";
160  if (strcmp(music, last_played) != 0) {
161  g_strlcpy(last_played, music, MAXSOCKBUF);
162  return true;
163  }
164  return false;
165 }
166 
172 void cf_play_music(const char* music_name) {
173  LOG(LOG_DEBUG, "cf_play_music", "\"%s\"", music_name);
174  if (!music_is_different(music_name)) {
175  return;
176  }
177 
178  Mix_FadeOutMusic(500);
179  if (music != NULL) {
180  Mix_FreeMusic(music);
181  music = NULL;
182  }
183 
184  if (strcmp(music_name, "NONE") == 0) {
185  return;
186  }
187  char path[MAXSOCKBUF];
188  snprintf(path, sizeof(path), "%s/music/%s.ogg", g_getenv("CF_SOUND_DIR"),
189  music_name);
190  music = Mix_LoadMUS(path);
191  if (!music) {
192  fprintf(stderr, "Could not load music: %s\n", Mix_GetError());
193  return;
194  }
195  Mix_VolumeMusic(MIX_MAX_VOLUME * 3/4);
196  Mix_FadeInMusic(music, -1, 500);
197 }
198 
199 void cf_snd_exit() {
200  Mix_HaltMusic();
201  Mix_FreeMusic(music);
202 
203  /* Halt all channels that are playing and free remaining samples. */
204  Mix_HaltChannel(-1);
205  g_hash_table_destroy(chunk_cache);
206  g_hash_table_destroy(sounds);
207 
208  /* Call Mix_Quit() for each time Mix_Init() was called. */
209  while(Mix_Init(0)) {
210  Mix_Quit();
211  }
212 }
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:158
LOG_WARNING
@ LOG_WARNING
Warning that something might not work.
Definition: client.h:435
chunk_cache
static GHashTable * chunk_cache
Definition: cfsndserv.c:36
MAXSOCKBUF
#define MAXSOCKBUF
Definition: newclient.h:25
sounds
static GHashTable * sounds
Definition: cfsndserv.c:37
settings
static sound_settings settings
Definition: cfsndserv.c:34
load_chunk
static Mix_Chunk * load_chunk(char const name[static 1])
Definition: cfsndserv.c:103
cf_snd_init
int cf_snd_init()
Definition: cfsndserv.c:80
LOG
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:111
sound_settings::buflen
int buflen
Definition: cfsndserv.c:29
sound_settings
Definition: cfsndserv.c:28
sound_settings
struct sound_settings sound_settings
cf_snd_exit
void cf_snd_exit()
Definition: cfsndserv.c:199
init_audio
static int init_audio()
Definition: cfsndserv.c:46
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])
Definition: cfsndserv.c:131
sound.h
LOG_DEBUG
@ LOG_DEBUG
Useful debugging information.
Definition: client.h:433
client.h
cf_play_music
void cf_play_music(const char *music_name)
Definition: cfsndserv.c:172