Crossfire Client, Trunk  R20693
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 "common.h"
26 #include "snd.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  SoundInfo* si = g_hash_table_lookup(sounds, sound);
134  if (si == NULL) {
135  fprintf(stderr, "play_sound: sound not defined: %s\n", sound);
136  return;
137  }
138 
139  Mix_Chunk* chunk = load_chunk(si->file);
140  if (chunk == NULL) {
141  return;
142  }
143  Mix_VolumeChunk(chunk, si->vol * MIX_MAX_VOLUME / 100);
144 
145  int channel = Mix_GroupAvailable(-1);
146  if (channel == -1) {
147  g_warning("No free channels available to play sound");
148  return;
149  }
150  Mix_Volume(channel, vol * MIX_MAX_VOLUME / 100);
151  Mix_PlayChannel(channel, chunk, 0);
152 }
153 
154 static bool music_is_different(char const music[static 1]) {
155  static char last_played[MAXSOCKBUF] = "";
156  if (strcmp(music, last_played) != 0) {
157  g_strlcpy(last_played, music, MAXSOCKBUF);
158  return true;
159  }
160  return false;
161 }
162 
168 void cf_play_music(const char* music_name) {
169  if (!music_is_different(music_name)) {
170  return;
171  }
172 
173  Mix_FadeOutMusic(500);
174  if (music != NULL) {
175  Mix_FreeMusic(music);
176  music = NULL;
177  }
178 
179  if (strcmp(music_name, "NONE") == 0) {
180  return;
181  }
182  char path[MAXSOCKBUF];
183  snprintf(path, sizeof(path), "%s/music/%s.ogg", g_getenv("CF_SOUND_DIR"),
184  music_name);
185  music = Mix_LoadMUS(path);
186  if (!music) {
187  fprintf(stderr, "Could not load music: %s\n", Mix_GetError());
188  return;
189  }
190  Mix_VolumeMusic(MIX_MAX_VOLUME * 3/4);
191  Mix_FadeInMusic(music, -1, 500);
192 }
193 
194 void cf_snd_exit() {
195  Mix_HaltMusic();
196  Mix_FreeMusic(music);
197 
198  /* Halt all channels that are playing and free remaining samples. */
199  Mix_HaltChannel(-1);
200  g_hash_table_destroy(chunk_cache);
201  g_hash_table_destroy(sounds);
202 
203  /* Call Mix_Quit() for each time Mix_Init() was called. */
204  while(Mix_Init(0)) {
205  Mix_Quit();
206  }
207 }
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
static GHashTable * sounds
Definition: cfsndserv.c:37
void cf_snd_exit()
Definition: cfsndserv.c:194
static GHashTable * chunk_cache
Definition: cfsndserv.c:36
static int init_audio()
Definition: cfsndserv.c:46
static bool music_is_different(char const music[static 1])
Definition: cfsndserv.c:154
static Mix_Chunk * load_chunk(char const name[static 1])
Definition: cfsndserv.c:103
int cf_snd_init()
Definition: cfsndserv.c:80
void cf_play_music(const char *music_name)
Definition: cfsndserv.c:168
#define MAXSOCKBUF
Definition: newclient.h:25
static Mix_Music * music
Definition: cfsndserv.c:33
char * name
Definition: image.c:39
static sound_settings settings
Definition: cfsndserv.c:34
struct sound_settings sound_settings