webperimental: killstack decides stack protects.
[freeciv.git] / client / audio.c
blobd9bc56b883cb1d000700f00eab850dc9162300f9
1 /***********************************************************************
2 Freeciv - Copyright (C) 2005 - The Freeciv Team
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
22 /* utility */
23 #include "capability.h"
24 #include "fcintl.h"
25 #include "log.h"
26 #include "mem.h"
27 #include "rand.h"
28 #include "registry.h"
29 #include "shared.h"
30 #include "string_vector.h"
31 #include "support.h"
33 /* client */
34 #include "audio_none.h"
35 #ifdef AUDIO_SDL
36 #include "audio_sdl.h"
37 #endif
38 #include "client_main.h"
39 #include "options.h"
41 #include "audio.h"
43 #define MAX_NUM_PLUGINS 2
44 #define SNDSPEC_SUFFIX ".soundspec"
45 #define MUSICSPEC_SUFFIX ".musicspec"
47 #define SOUNDSPEC_CAPSTR "+Freeciv-2.6-soundset"
48 #define MUSICSPEC_CAPSTR "+Freeciv-2.6-musicset"
50 /* keep it open throughout */
51 static struct section_file *ss_tagfile = NULL;
52 static struct section_file *ms_tagfile = NULL;
54 static struct audio_plugin plugins[MAX_NUM_PLUGINS];
55 static int num_plugins_used = 0;
56 static int selected_plugin = -1;
57 static int current_track = -1;
58 static enum music_usage current_usage;
60 static struct mfcb_data
62 struct section_file *sfile;
63 const char *tag;
64 } mfcb;
66 static int audio_play_tag(struct section_file *sfile,
67 const char *tag, bool repeat,
68 int exclude, bool keepstyle);
70 /**********************************************************************
71 Returns a static string vector of all sound plugins
72 available on the system. This function is unfortunately similar to
73 audio_get_all_plugin_names().
74 ***********************************************************************/
75 const struct strvec *get_soundplugin_list(const struct option *poption)
77 static struct strvec *plugin_list = NULL;
79 if (NULL == plugin_list) {
80 int i;
82 plugin_list = strvec_new();
83 strvec_reserve(plugin_list, num_plugins_used);
84 for (i = 0; i < num_plugins_used; i++) {
85 strvec_set(plugin_list, i, plugins[i].name);
89 return plugin_list;
92 /**********************************************************************
93 Returns a static string vector of audiosets of given type available
94 on the system by searching all data directories for files matching
95 suffix.
96 The list is NULL-terminated.
97 ***********************************************************************/
98 static const struct strvec *get_audio_speclist(const char *suffix,
99 struct strvec **audio_list)
101 if (NULL == *audio_list) {
102 *audio_list = fileinfolist(get_data_dirs(), suffix);
105 return *audio_list;
108 /**********************************************************************
109 Returns a static string vector of soundsets available on the system.
110 ***********************************************************************/
111 const struct strvec *get_soundset_list(const struct option *poption)
113 static struct strvec *sound_list = NULL;
115 return get_audio_speclist(SNDSPEC_SUFFIX, &sound_list);
118 /**********************************************************************
119 Returns a static string vector of musicsets available on the system.
120 ***********************************************************************/
121 const struct strvec *get_musicset_list(const struct option *poption)
123 static struct strvec *music_list = NULL;
125 return get_audio_speclist(MUSICSPEC_SUFFIX, &music_list);
128 /**************************************************************************
129 Add a plugin.
130 **************************************************************************/
131 void audio_add_plugin(struct audio_plugin *p)
133 fc_assert_ret(num_plugins_used < MAX_NUM_PLUGINS);
134 memcpy(&plugins[num_plugins_used], p, sizeof(struct audio_plugin));
135 num_plugins_used++;
138 /**************************************************************************
139 Choose plugin. Returns TRUE on success, FALSE if not
140 **************************************************************************/
141 bool audio_select_plugin(const char *const name)
143 int i;
144 bool found = FALSE;
146 for (i = 0; i < num_plugins_used; i++) {
147 if (strcmp(plugins[i].name, name) == 0) {
148 found = TRUE;
149 break;
153 if (found && i != selected_plugin) {
154 log_debug("Shutting down %s", plugins[selected_plugin].name);
155 plugins[selected_plugin].stop();
156 plugins[selected_plugin].wait();
157 plugins[selected_plugin].shutdown();
160 if (!found) {
161 log_fatal(_("Plugin '%s' isn't available. Available are %s"),
162 name, audio_get_all_plugin_names());
163 exit(EXIT_FAILURE);
166 if (!plugins[i].init()) {
167 log_error("Plugin %s found, but can't be initialized.", name);
168 return FALSE;
171 selected_plugin = i;
172 log_verbose("Plugin '%s' is now selected", plugins[selected_plugin].name);
173 return TRUE;
176 /**************************************************************************
177 Initialize base audio system. Note that this function is called very
178 early at the client startup. So for example logging isn't available.
179 **************************************************************************/
180 void audio_init(void)
182 #ifdef AUDIO_SDL
183 audio_sdl_init();
184 #endif
186 /* Initialize dummy plugin last, as lowest priority plugin. This
187 * affects which plugin gets selected as default in new installations. */
188 audio_none_init();
189 selected_plugin = 0;
192 /**************************************************************************
193 Returns the filename for the given audio set. Returns NULL if
194 set couldn't be found. Caller has to free the return value.
195 **************************************************************************/
196 static const char *audiospec_fullname(const char *audioset_name,
197 bool music)
199 const char *suffix = music ? MUSICSPEC_SUFFIX : SNDSPEC_SUFFIX;
200 const char *audioset_default = music ? "stdmusic" : "stdsounds";/* Do not i18n! */
201 char *fname = fc_malloc(strlen(audioset_name) + strlen(suffix) + 1);
202 const char *dname;
204 sprintf(fname, "%s%s", audioset_name, suffix);
206 dname = fileinfoname(get_data_dirs(), fname);
207 free(fname);
209 if (dname) {
210 return fc_strdup(dname);
213 if (strcmp(audioset_name, audioset_default) == 0) {
214 /* avoid endless recursion */
215 return NULL;
218 log_error("Couldn't find audioset \"%s\", trying \"%s\".",
219 audioset_name, audioset_default);
221 return audiospec_fullname(audioset_default, music);
224 /**************************************************************************
225 Check capabilities of the audio specfile
226 **************************************************************************/
227 static bool check_audiofile_capstr(struct section_file *sfile,
228 const char *filename,
229 const char *our_cap,
230 const char *opt_path)
232 const char *file_capstr;
234 file_capstr = secfile_lookup_str(sfile, "%s", opt_path);
235 if (NULL == file_capstr) {
236 log_fatal("Audio spec-file \"%s\" doesn't have capability string.",
237 filename);
238 exit(EXIT_FAILURE);
240 if (!has_capabilities(our_cap, file_capstr)) {
241 log_fatal("Audio spec-file appears incompatible:");
242 log_fatal(" file: \"%s\"", filename);
243 log_fatal(" file options: %s", file_capstr);
244 log_fatal(" supported options: %s", our_cap);
245 exit(EXIT_FAILURE);
247 if (!has_capabilities(file_capstr, our_cap)) {
248 log_fatal("Audio spec-file claims required option(s) "
249 "which we don't support:");
250 log_fatal(" file: \"%s\"", filename);
251 log_fatal(" file options: %s", file_capstr);
252 log_fatal(" supported options: %s", our_cap);
253 exit(EXIT_FAILURE);
256 return TRUE;
259 /**************************************************************************
260 Initialize audio system and autoselect a plugin
261 **************************************************************************/
262 void audio_real_init(const char *const soundset_name,
263 const char *const musicset_name,
264 const char *const preferred_plugin_name)
266 const char *ss_filename;
267 const char *ms_filename;
268 char us_ss_capstr[] = SOUNDSPEC_CAPSTR;
269 char us_ms_capstr[] = MUSICSPEC_CAPSTR;
271 if (strcmp(preferred_plugin_name, "none") == 0) {
272 /* We explicitly choose none plugin, silently skip the code below */
273 log_verbose("Proceeding with sound support disabled.");
274 ss_tagfile = NULL;
275 ms_tagfile = NULL;
276 return;
278 if (num_plugins_used == 1) {
279 /* We only have the dummy plugin, skip the code but issue an advertise */
280 log_normal(_("No real audio plugin present."));
281 log_normal(_("Proceeding with sound support disabled."));
282 log_normal(_("For sound support, install SDL2_mixer"));
283 log_normal("http://www.libsdl.org/projects/SDL_mixer/index.html");
284 ss_tagfile = NULL;
285 ms_tagfile = NULL;
286 return;
288 if (!soundset_name) {
289 log_fatal("No sound spec-file given!");
290 exit(EXIT_FAILURE);
292 if (!musicset_name) {
293 log_fatal("No music spec-file given!");
294 exit(EXIT_FAILURE);
296 log_verbose("Initializing sound using %s and %s...",
297 soundset_name, musicset_name);
298 ss_filename = audiospec_fullname(soundset_name, FALSE);
299 ms_filename = audiospec_fullname(musicset_name, TRUE);
300 if (!ss_filename || !ms_filename) {
301 log_error("Cannot find audio spec-file \"%s\" or \"%s\"",
302 soundset_name, musicset_name);
303 log_normal(_("To get sound you need to download a sound set!"));
304 log_normal(_("Get sound sets from <%s>."),
305 "http://www.freeciv.org/wiki/Sounds");
306 log_normal(_("Proceeding with sound support disabled."));
307 ss_tagfile = NULL;
308 ms_tagfile = NULL;
309 return;
311 if (!(ss_tagfile = secfile_load(ss_filename, TRUE))) {
312 log_fatal(_("Could not load sound spec-file '%s':\n%s"), ss_filename,
313 secfile_error());
314 exit(EXIT_FAILURE);
316 if (!(ms_tagfile = secfile_load(ms_filename, TRUE))) {
317 log_fatal(_("Could not load music spec-file '%s':\n%s"), ms_filename,
318 secfile_error());
319 exit(EXIT_FAILURE);
322 check_audiofile_capstr(ss_tagfile, ss_filename, us_ss_capstr,
323 "soundspec.options");
325 free((void *) ss_filename);
327 check_audiofile_capstr(ms_tagfile, ms_filename, us_ms_capstr,
328 "musicspec.options");
330 free((void *) ms_filename);
333 static bool atexit_set = FALSE;
335 if (!atexit_set) {
336 atexit(audio_shutdown);
337 atexit_set = TRUE;
341 if (preferred_plugin_name[0] != '\0') {
342 if (!audio_select_plugin(preferred_plugin_name))
343 log_normal(_("Proceeding with sound support disabled."));
344 return;
347 #ifdef AUDIO_SDL
348 if (audio_select_plugin("sdl")) return;
349 #endif
350 log_normal(_("No real audio subsystem managed to initialize!"));
351 log_normal(_("Perhaps there is some misconfiguration or bad permissions."));
352 log_normal(_("Proceeding with sound support disabled."));
355 /**************************************************************************
356 Switch soundset
357 **************************************************************************/
358 void audio_restart(const char *soundset_name, const char *musicset_name)
360 audio_stop(); /* Fade down old one */
362 sz_strlcpy(sound_set_name, soundset_name);
363 sz_strlcpy(music_set_name, musicset_name);
364 audio_real_init(sound_set_name, music_set_name, sound_plugin_name);
367 /**************************************************************************
368 Callback to start new track
369 **************************************************************************/
370 static void music_finished_callback(void)
372 bool usage_enabled = TRUE;
374 switch (current_usage) {
375 case MU_SINGLE:
376 usage_enabled = FALSE;
377 break;
378 case MU_MENU:
379 usage_enabled = gui_options.sound_enable_menu_music;
380 break;
381 case MU_INGAME:
382 usage_enabled = gui_options.sound_enable_game_music;
383 break;
386 if (usage_enabled) {
387 current_track = audio_play_tag(mfcb.sfile, mfcb.tag, TRUE, current_track,
388 FALSE);
392 /**************************************************************************
393 INTERNAL. Returns id (>= 0) of the tag selected for playing, 0 when
394 there's no alternative tags, or negative value in case of error.
395 **************************************************************************/
396 static int audio_play_tag(struct section_file *sfile,
397 const char *tag, bool repeat, int exclude,
398 bool keepstyle)
400 const char *soundfile;
401 const char *fullpath = NULL;
402 audio_finished_callback cb = NULL;
403 int ret = 0;
405 if (!tag || strcmp(tag, "-") == 0) {
406 return -1;
409 if (sfile) {
410 soundfile = secfile_lookup_str(sfile, "files.%s", tag);
411 if (soundfile == NULL) {
412 const char *files[MAX_ALT_AUDIO_FILES];
413 bool excluded = FALSE;
414 int i;
415 int j;
417 j = 0;
418 for (i = 0; i < MAX_ALT_AUDIO_FILES; i++) {
419 files[j] = secfile_lookup_str(sfile, "files.%s_%d", tag, i);
420 if (files[j] == NULL) {
421 break;
423 if (i != exclude) {
424 j++;
425 } else {
426 excluded = TRUE;
430 if (j == 0 && excluded) {
431 /* Cannot exclude the only track */
432 excluded = FALSE;
433 j++;
436 if (j > 0) {
437 ret = fc_rand(j);
439 soundfile = files[ret];
440 if (excluded) {
441 /* Exclude track was skipped earlier, include it to track number to return */
442 ret++;
444 if (repeat) {
445 if (!keepstyle) {
446 mfcb.sfile = sfile;
447 mfcb.tag = tag;
449 cb = music_finished_callback;
453 if (NULL == soundfile) {
454 log_verbose("No sound file for tag %s (file %s)", tag, soundfile);
455 } else {
456 fullpath = fileinfoname(get_data_dirs(), soundfile);
457 if (!fullpath) {
458 log_error("Cannot find audio file %s", soundfile);
463 if (!plugins[selected_plugin].play(tag, fullpath, repeat, cb)) {
464 return -1;
467 return ret;
470 /**************************************************************************
471 Play tag from sound set
472 **************************************************************************/
473 static bool audio_play_sound_tag(const char *tag, bool repeat)
475 return (audio_play_tag(ss_tagfile, tag, repeat, -1, FALSE) >= 0);
478 /**************************************************************************
479 Play tag from music set
480 **************************************************************************/
481 static int audio_play_music_tag(const char *tag, bool repeat,
482 bool keepstyle)
484 return audio_play_tag(ms_tagfile, tag, repeat, -1, keepstyle);
487 /**************************************************************************
488 Play an audio sample as suggested by sound tags
489 **************************************************************************/
490 void audio_play_sound(const char *const tag, char *const alt_tag)
492 char *pretty_alt_tag = alt_tag ? alt_tag : "(null)";
494 if (gui_options.sound_enable_effects) {
495 fc_assert_ret(tag != NULL);
497 log_debug("audio_play_sound('%s', '%s')", tag, pretty_alt_tag);
499 /* try playing primary tag first, if not go to alternative tag */
500 if (!audio_play_sound_tag(tag, FALSE)
501 && !audio_play_sound_tag(alt_tag, FALSE)) {
502 log_verbose( "Neither of tags %s or %s found", tag, pretty_alt_tag);
507 /**************************************************************************
508 Play music, either in loop or just one track in the middle of the style
509 music.
510 **************************************************************************/
511 static void real_audio_play_music(const char *const tag, char *const alt_tag,
512 bool keepstyle)
514 char *pretty_alt_tag = alt_tag ? alt_tag : "(null)";
516 fc_assert_ret(tag != NULL);
518 log_debug("audio_play_music('%s', '%s')", tag, pretty_alt_tag);
520 /* try playing primary tag first, if not go to alternative tag */
521 current_track = audio_play_music_tag(tag, TRUE, keepstyle);
523 if (current_track < 0) {
524 current_track = audio_play_music_tag(alt_tag, TRUE, keepstyle);
526 if (current_track < 0) {
527 log_verbose("Neither of tags %s or %s found", tag, pretty_alt_tag);
532 /**************************************************************************
533 Loop music as suggested by sound tags
534 **************************************************************************/
535 void audio_play_music(const char *const tag, char *const alt_tag,
536 enum music_usage usage)
538 current_usage = usage;
540 real_audio_play_music(tag, alt_tag, FALSE);
543 /**************************************************************************
544 Play single track as suggested by sound tags
545 **************************************************************************/
546 void audio_play_track(const char *const tag, char *const alt_tag)
548 current_usage = MU_SINGLE;
550 real_audio_play_music(tag, alt_tag, TRUE);
553 /**************************************************************************
554 Stop looping sound. Music should die down in a few seconds.
555 **************************************************************************/
556 void audio_stop()
558 plugins[selected_plugin].stop();
561 /**************************************************************************
562 Stop looping sound. Music should die down in a few seconds.
563 **************************************************************************/
564 double audio_get_volume()
566 return plugins[selected_plugin].get_volume();
569 /**************************************************************************
570 Stop looping sound. Music should die down in a few seconds.
571 **************************************************************************/
572 void audio_set_volume(double volume)
574 plugins[selected_plugin].set_volume(volume);
577 /**************************************************************************
578 Call this at end of program only.
579 **************************************************************************/
580 void audio_shutdown()
582 /* avoid infinite loop at end of game */
583 audio_stop();
585 audio_play_sound("e_game_quit", NULL);
586 plugins[selected_plugin].wait();
587 plugins[selected_plugin].shutdown();
589 if (NULL != ss_tagfile) {
590 secfile_destroy(ss_tagfile);
591 ss_tagfile = NULL;
593 if (NULL != ms_tagfile) {
594 secfile_destroy(ms_tagfile);
595 ms_tagfile = NULL;
599 /**************************************************************************
600 Returns a string which list all available plugins. You don't have to
601 free the string.
602 **************************************************************************/
603 const char *audio_get_all_plugin_names()
605 static char buffer[100];
606 int i;
608 sz_strlcpy(buffer, "[");
610 for (i = 0; i < num_plugins_used; i++) {
611 sz_strlcat(buffer, plugins[i].name);
612 if (i != num_plugins_used - 1) {
613 sz_strlcat(buffer, ", ");
616 sz_strlcat(buffer, "]");
617 return buffer;