Freeciv-3.1
Loading...
Searching...
No Matches
audio.c
Go to the documentation of this file.
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.
7
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***********************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21
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"
32
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"
40
41#include "audio.h"
42
43#define MAX_NUM_PLUGINS 2
44#define SNDSPEC_SUFFIX ".soundspec"
45#define MUSICSPEC_SUFFIX ".musicspec"
46
47#define SOUNDSPEC_CAPSTR "+Freeciv-3.1-soundset"
48#define MUSICSPEC_CAPSTR "+Freeciv-2.6-musicset"
49
50/* Keep them open throughout */
51static struct section_file *ss_tagfile = NULL;
52static struct section_file *ms_tagfile = NULL;
53
55static int num_plugins_used = 0;
56static int selected_plugin = -1;
57static int current_track = -1;
59static bool switching_usage = FALSE;
61
62static struct mfcb_data
63{
65 const char *tag;
67
68static int audio_play_tag(struct section_file *sfile,
69 const char *tag, bool repeat,
70 int exclude, bool keep_old_style);
71
72static void audio_shutdown_atexit(void);
73
74/**********************************************************************/
79const struct strvec *get_soundplugin_list(const struct option *poption)
80{
81 static struct strvec *plugin_list = NULL;
82
83 if (NULL == plugin_list) {
84 int i;
85
86 plugin_list = strvec_new();
87 strvec_reserve(plugin_list, num_plugins_used);
88 for (i = 0; i < num_plugins_used; i++) {
89 strvec_set(plugin_list, i, plugins[i].name);
90 }
91 }
92
93 return plugin_list;
94}
95
96/**********************************************************************/
102static const struct strvec *get_audio_speclist(const char *suffix,
103 struct strvec **audio_list)
104{
105 if (NULL == *audio_list) {
106 *audio_list = fileinfolist(get_data_dirs(), suffix);
107 }
108
109 return *audio_list;
110}
111
112/**********************************************************************/
115const struct strvec *get_soundset_list(const struct option *poption)
116{
117 static struct strvec *sound_list = NULL;
118
119 return get_audio_speclist(SNDSPEC_SUFFIX, &sound_list);
120}
121
122/**********************************************************************/
125const struct strvec *get_musicset_list(const struct option *poption)
126{
127 static struct strvec *music_list = NULL;
128
129 return get_audio_speclist(MUSICSPEC_SUFFIX, &music_list);
130}
131
132/**********************************************************************/
141
142/**********************************************************************/
145bool audio_select_plugin(const char *const name)
146{
147 int i;
148 bool found = FALSE;
149
150 for (i = 0; i < num_plugins_used; i++) {
151 if (strcmp(plugins[i].name, name) == 0) {
152 found = TRUE;
153 break;
154 }
155 }
156
157 if (found && i != selected_plugin) {
158 log_debug("Shutting down %s", plugins[selected_plugin].name);
162 }
163
164 if (!found) {
165 log_fatal(_("Plugin '%s' isn't available. Available are %s"),
167 exit(EXIT_FAILURE);
168 }
169
170 if (!plugins[i].init(&(plugins[i]))) {
171 log_error("Plugin %s found, but can't be initialized.", name);
172 return FALSE;
173 }
174
175 selected_plugin = i;
176 log_verbose("Plugin '%s' is now selected", plugins[selected_plugin].name);
177
179
180 return TRUE;
181}
182
183/**********************************************************************/
187void audio_init(void)
188{
189#ifdef AUDIO_SDL
191#endif
192
193 /* Initialize dummy plugin last, as lowest priority plugin. This
194 * affects which plugin gets selected as default in new installations. */
196 selected_plugin = 0;
197}
198
199/**********************************************************************/
203static const char *audiospec_fullname(const char *audioset_name,
204 bool music)
205{
206 const char *suffix = music ? MUSICSPEC_SUFFIX : SNDSPEC_SUFFIX;
207 const char *audioset_default = music ? "stdmusic" : "stdsounds";/* Do not i18n! */
208 char *fname = fc_malloc(strlen(audioset_name) + strlen(suffix) + 1);
209 const char *dname;
210
211 sprintf(fname, "%s%s", audioset_name, suffix);
212
213 dname = fileinfoname(get_data_dirs(), fname);
214 free(fname);
215
216 if (dname) {
217 return fc_strdup(dname);
218 }
219
220 if (strcmp(audioset_name, audioset_default) == 0) {
221 /* avoid endless recursion */
222 return NULL;
223 }
224
225 /* Marked for translation, as user may see this when
226 * their client configuration from older version has
227 * a musicset that they have not yet installed for the
228 * new version. */
229 log_error(_("Couldn't find audioset \"%s\", trying \"%s\"."),
230 audioset_name, audioset_default);
231
232 return audiospec_fullname(audioset_default, music);
233}
234
235/**********************************************************************/
238static bool check_audiofile_capstr(struct section_file *sfile,
239 const char *filename,
240 const char *our_cap,
241 const char *opt_path)
242{
243 const char *file_capstr;
244
245 file_capstr = secfile_lookup_str(sfile, "%s", opt_path);
246 if (NULL == file_capstr) {
247 log_fatal("Audio spec-file \"%s\" doesn't have capability string.",
248 filename);
249 exit(EXIT_FAILURE);
250 }
251 if (!has_capabilities(our_cap, file_capstr)) {
252 log_fatal("Audio spec-file appears incompatible:");
253 log_fatal(" file: \"%s\"", filename);
254 log_fatal(" file options: %s", file_capstr);
255 log_fatal(" supported options: %s", our_cap);
256 exit(EXIT_FAILURE);
257 }
258 if (!has_capabilities(file_capstr, our_cap)) {
259 log_fatal("Audio spec-file claims required option(s) "
260 "which we don't support:");
261 log_fatal(" file: \"%s\"", filename);
262 log_fatal(" file options: %s", file_capstr);
263 log_fatal(" supported options: %s", our_cap);
264 exit(EXIT_FAILURE);
265 }
266
267 return TRUE;
268}
269
270/**********************************************************************/
273void audio_real_init(const char *const soundset_name,
274 const char *const musicset_name,
275 const char *const preferred_plugin_name)
276{
277 const char *ss_filename;
278 const char *ms_filename;
279 char us_ss_capstr[] = SOUNDSPEC_CAPSTR;
280 char us_ms_capstr[] = MUSICSPEC_CAPSTR;
281
282 if (strcmp(preferred_plugin_name, "none") == 0) {
283 /* We explicitly choose none plugin, silently skip the code below */
284 log_verbose("Proceeding with sound support disabled.");
285 ss_tagfile = NULL;
286 ms_tagfile = NULL;
287 return;
288 }
289 if (num_plugins_used == 1) {
290 /* We only have the dummy plugin, skip the code but issue an advertise */
291 log_normal(_("No real audio plugin present."));
292 log_normal(_("Proceeding with sound support disabled."));
293 log_normal(_("For sound support, install SDL2_mixer"));
294 log_normal("https://github.com/libsdl-org/SDL_mixer");
295 ss_tagfile = NULL;
296 ms_tagfile = NULL;
297 return;
298 }
299 if (!soundset_name) {
300 log_fatal("No sound spec-file given!");
301 exit(EXIT_FAILURE);
302 }
303 if (!musicset_name) {
304 log_fatal("No music spec-file given!");
305 exit(EXIT_FAILURE);
306 }
307 log_verbose("Initializing sound using %s and %s...",
308 soundset_name, musicset_name);
309 ss_filename = audiospec_fullname(soundset_name, FALSE);
310 ms_filename = audiospec_fullname(musicset_name, TRUE);
311 if (!ss_filename || !ms_filename) {
312 log_error("Cannot find audio spec-file \"%s\" or \"%s\"",
313 soundset_name, musicset_name);
314 log_normal(_("To get sound you need to download a sound set!"));
315 log_normal(_("Get sound sets from <%s>."),
316 "https://www.freeciv.org/wiki/Sounds");
317 log_normal(_("Proceeding with sound support disabled."));
318 ss_tagfile = NULL;
319 ms_tagfile = NULL;
320 return;
321 }
322 if (!(ss_tagfile = secfile_load(ss_filename, TRUE))) {
323 log_fatal(_("Could not load sound spec-file '%s':\n%s"), ss_filename,
324 secfile_error());
325 exit(EXIT_FAILURE);
326 }
327 if (!(ms_tagfile = secfile_load(ms_filename, TRUE))) {
328 log_fatal(_("Could not load music spec-file '%s':\n%s"), ms_filename,
329 secfile_error());
330 exit(EXIT_FAILURE);
331 }
332
333 check_audiofile_capstr(ss_tagfile, ss_filename, us_ss_capstr,
334 "soundspec.options");
335
336 free((void *) ss_filename);
337
338 check_audiofile_capstr(ms_tagfile, ms_filename, us_ms_capstr,
339 "musicspec.options");
340
341 free((void *) ms_filename);
342
343 {
344 static bool atexit_set = FALSE;
345
346 if (!atexit_set) {
347 atexit(audio_shutdown_atexit);
348 atexit_set = TRUE;
349 }
350 }
351
352 if (preferred_plugin_name[0] != '\0') {
353 if (!audio_select_plugin(preferred_plugin_name))
354 log_normal(_("Proceeding with sound support disabled."));
355 return;
356 }
357
358#ifdef AUDIO_SDL
359 if (audio_select_plugin("sdl")) return;
360#endif
361 log_normal(_("No real audio subsystem managed to initialize!"));
362 log_normal(_("Perhaps there is some misconfiguration or bad permissions."));
363 log_normal(_("Proceeding with sound support disabled."));
364}
365
366/**********************************************************************/
369void audio_restart(const char *soundset_name, const char *musicset_name)
370{
371 audio_stop(); /* Fade down old one */
372
373 sz_strlcpy(sound_set_name, soundset_name);
374 sz_strlcpy(music_set_name, musicset_name);
376}
377
378/**********************************************************************/
381static void music_finished_callback(void)
382{
383 bool usage_enabled = TRUE;
384
385 if (switching_usage) {
387
388 return;
389 }
390
392 /* This call is style music ending before single track plays.
393 * Do not restart style music now.
394 * Make sure style music restarts when single track itself finishes. */
396
397 return;
398 }
399
400 switch (current_usage) {
401 case MU_MENU:
402 usage_enabled = gui_options.sound_enable_menu_music;
403 break;
404 case MU_INGAME:
405 usage_enabled = gui_options.sound_enable_game_music;
406 break;
407 }
408
409 if (usage_enabled) {
411 FALSE);
412 }
413}
414
415/**********************************************************************/
419static int audio_play_tag(struct section_file *sfile,
420 const char *tag, bool repeat, int exclude,
421 bool keep_old_style)
422{
423 const char *soundfile;
424 const char *fullpath = NULL;
425 audio_finished_callback cb = NULL;
426 int ret = 0;
427
428 if (!tag || strcmp(tag, "-") == 0) {
429 return -1;
430 }
431
432 if (sfile) {
433 soundfile = secfile_lookup_str(sfile, "files.%s", tag);
434 if (soundfile == NULL) {
435 const char *files[MAX_ALT_AUDIO_FILES];
436 int excluded = -1;
437 int i;
438 int j;
439
440 j = 0;
441 for (i = 0; i < MAX_ALT_AUDIO_FILES; i++) {
442 const char *ftmp = secfile_lookup_str(sfile, "files.%s_%d", tag, i);
443
444 if (ftmp == NULL) {
445 if (excluded != -1 && j == 0) {
446 /* Cannot exclude the only track */
447 excluded = -1;
448 j++;
449 }
450 files[j] = NULL;
451 break;
452 }
453 files[j] = ftmp;
454 if (i != exclude) {
455 j++;
456 } else {
457 excluded = j;
458 }
459 }
460
461 if (j > 0) {
462 ret = fc_rand(j);
463
464 soundfile = files[ret];
465 if (excluded != -1 && excluded < ret) {
466 /* Exclude track was skipped earlier, include it to track number to return */
467 ret++;
468 }
469 }
470 }
471
472 if (repeat) {
473 if (!keep_old_style) {
474 mfcb.sfile = sfile;
475 mfcb.tag = tag;
476 }
477
478 /* Callback is needed even when there's no alternative tracks -
479 * we may be running single track now, and want to switch
480 * (by the callback) back to style music when it ends. */
482 }
483
484 if (NULL == soundfile) {
485 log_verbose("No sound file for tag %s", tag);
486 } else {
487 fullpath = fileinfoname(get_data_dirs(), soundfile);
488 if (!fullpath) {
489 log_error("Cannot find audio file %s for tag %s", soundfile, tag);
490 }
491 }
492 }
493
494 if (!plugins[selected_plugin].play(tag, fullpath, repeat, cb)) {
495 return -1;
496 }
497
498 return ret;
499}
500
501/**********************************************************************/
504static bool audio_play_sound_tag(const char *tag, bool repeat)
505{
506 return (audio_play_tag(ss_tagfile, tag, repeat, -1, FALSE) >= 0);
507}
508
509/**********************************************************************/
512static int audio_play_music_tag(const char *tag, bool repeat,
513 bool keep_old_style)
514{
515 return audio_play_tag(ms_tagfile, tag, repeat, -1, keep_old_style);
516}
517
518/**********************************************************************/
521void audio_play_sound(const char *const tag, const char *const alt_tag)
522{
523 const char *pretty_alt_tag = alt_tag ? alt_tag : "(null)";
524
526 fc_assert_ret(tag != NULL);
527
528 log_debug("audio_play_sound('%s', '%s')", tag, pretty_alt_tag);
529
530 /* try playing primary tag first, if not go to alternative tag */
531 if (!audio_play_sound_tag(tag, FALSE)
532 && !audio_play_sound_tag(alt_tag, FALSE)) {
533 log_verbose( "Neither of tags %s or %s found", tag, pretty_alt_tag);
534 }
535 }
536}
537
538/**********************************************************************/
542static void real_audio_play_music(const char *const tag, char *const alt_tag,
543 bool keep_old_style)
544{
545 char *pretty_alt_tag = alt_tag ? alt_tag : "(null)";
546
547 fc_assert_ret(tag != NULL);
548
549 log_debug("audio_play_music('%s', '%s')", tag, pretty_alt_tag);
550
551 /* try playing primary tag first, if not go to alternative tag */
552 current_track = audio_play_music_tag(tag, TRUE, keep_old_style);
553
554 if (current_track < 0) {
555 current_track = audio_play_music_tag(alt_tag, TRUE, keep_old_style);
556
557 if (current_track < 0) {
558 log_verbose("Neither of tags %s or %s found", tag, pretty_alt_tag);
559 }
560 }
561}
562
563/**********************************************************************/
566void audio_play_music(const char *const tag, char *const alt_tag,
567 enum music_usage usage)
568{
569 current_usage = usage;
570
571 real_audio_play_music(tag, alt_tag, FALSE);
572}
573
574/**********************************************************************/
577void audio_play_track(const char *const tag, char *const alt_tag)
578{
579 if (current_track >= 0) {
580 /* Only set let_single_track_play when there's music playing that will
581 * result in calling the music_finished_callback */
583
584 /* Stop old music. */
585 audio_stop();
586 }
587
588 real_audio_play_music(tag, alt_tag, TRUE);
589}
590
591/**********************************************************************/
594void audio_stop(void)
595{
597}
598
599/**********************************************************************/
607
608/**********************************************************************/
612{
614}
615
616/**********************************************************************/
619void audio_set_volume(double volume)
620{
622}
623
624/**********************************************************************/
629void audio_shutdown(bool play_quit_tag)
630{
631 /* Avoid infinite loop at end of game */
632 audio_stop();
633
634 if (play_quit_tag) {
635 audio_play_sound("e_game_quit", NULL);
636 }
637
641 }
642
643 if (NULL != ss_tagfile) {
645 ss_tagfile = NULL;
646 }
647 if (NULL != ms_tagfile) {
649 ms_tagfile = NULL;
650 }
651}
652
653/**********************************************************************/
656static void audio_shutdown_atexit(void)
657{
658 /* If support has already been shut down, can't handle audio tags. */
660}
661
662/**********************************************************************/
667{
668 static char buffer[100];
669 int i;
670
671 sz_strlcpy(buffer, "[");
672
673 for (i = 0; i < num_plugins_used; i++) {
674 sz_strlcat(buffer, plugins[i].name);
675 if (i != num_plugins_used - 1) {
676 sz_strlcat(buffer, ", ");
677 }
678 }
679 sz_strlcat(buffer, "]");
680 return buffer;
681}
void audio_restart(const char *soundset_name, const char *musicset_name)
Definition audio.c:369
static void music_finished_callback(void)
Definition audio.c:381
static struct audio_plugin plugins[MAX_NUM_PLUGINS]
Definition audio.c:54
void audio_play_track(const char *const tag, char *const alt_tag)
Definition audio.c:577
static void real_audio_play_music(const char *const tag, char *const alt_tag, bool keep_old_style)
Definition audio.c:542
#define SNDSPEC_SUFFIX
Definition audio.c:44
static bool audio_play_sound_tag(const char *tag, bool repeat)
Definition audio.c:504
static enum music_usage current_usage
Definition audio.c:58
#define MUSICSPEC_CAPSTR
Definition audio.c:48
static int selected_plugin
Definition audio.c:56
void audio_play_music(const char *const tag, char *const alt_tag, enum music_usage usage)
Definition audio.c:566
static const char * audiospec_fullname(const char *audioset_name, bool music)
Definition audio.c:203
void audio_shutdown(bool play_quit_tag)
Definition audio.c:629
const char * audio_get_all_plugin_names(void)
Definition audio.c:666
void audio_stop(void)
Definition audio.c:594
void audio_stop_usage(void)
Definition audio.c:602
void audio_init(void)
Definition audio.c:187
static int current_track
Definition audio.c:57
void audio_play_sound(const char *const tag, const char *const alt_tag)
Definition audio.c:521
bool audio_select_plugin(const char *const name)
Definition audio.c:145
#define SOUNDSPEC_CAPSTR
Definition audio.c:47
static struct section_file * ss_tagfile
Definition audio.c:51
static int audio_play_music_tag(const char *tag, bool repeat, bool keep_old_style)
Definition audio.c:512
static const struct strvec * get_audio_speclist(const char *suffix, struct strvec **audio_list)
Definition audio.c:102
static bool check_audiofile_capstr(struct section_file *sfile, const char *filename, const char *our_cap, const char *opt_path)
Definition audio.c:238
void audio_set_volume(double volume)
Definition audio.c:619
const struct strvec * get_musicset_list(const struct option *poption)
Definition audio.c:125
static int audio_play_tag(struct section_file *sfile, const char *tag, bool repeat, int exclude, bool keep_old_style)
Definition audio.c:419
double audio_get_volume(void)
Definition audio.c:611
void audio_add_plugin(struct audio_plugin *p)
Definition audio.c:135
const struct strvec * get_soundset_list(const struct option *poption)
Definition audio.c:115
static bool let_single_track_play
Definition audio.c:60
static struct section_file * ms_tagfile
Definition audio.c:52
static void audio_shutdown_atexit(void)
Definition audio.c:656
static struct mfcb_data mfcb
static int num_plugins_used
Definition audio.c:55
const struct strvec * get_soundplugin_list(const struct option *poption)
Definition audio.c:79
#define MUSICSPEC_SUFFIX
Definition audio.c:45
#define MAX_NUM_PLUGINS
Definition audio.c:43
static bool switching_usage
Definition audio.c:59
void audio_real_init(const char *const soundset_name, const char *const musicset_name, const char *const preferred_plugin_name)
Definition audio.c:273
music_usage
Definition audio.h:43
@ MU_INGAME
Definition audio.h:43
@ MU_MENU
Definition audio.h:43
void(* audio_finished_callback)(void)
Definition audio.h:27
#define MAX_ALT_AUDIO_FILES
Definition audio.h:25
void audio_none_init(void)
Definition audio_none.c:75
void audio_sdl_init(void)
Definition audio_sdl.c:267
bool has_capabilities(const char *us, const char *them)
Definition capability.c:86
char music_set_name[512]
char sound_plugin_name[512]
char sound_set_name[512]
static bool initialized
Definition effects.c:42
#define _(String)
Definition fcintl.h:67
const char * name
Definition inputfile.c:127
#define fc_assert_ret(condition)
Definition log.h:191
#define log_verbose(message,...)
Definition log.h:109
#define log_fatal(message,...)
Definition log.h:100
#define log_debug(message,...)
Definition log.h:115
#define log_normal(message,...)
Definition log.h:107
#define log_error(message,...)
Definition log.h:103
bool init
Definition mapimg.c:482
#define fc_strdup(str)
Definition mem.h:43
#define fc_malloc(sz)
Definition mem.h:34
struct client_options gui_options
Definition options.c:71
#define fc_rand(_size)
Definition rand.h:34
struct section_file * secfile_load(const char *filename, bool allow_duplicates)
Definition registry.c:50
const char * secfile_error(void)
void secfile_destroy(struct section_file *secfile)
const char * secfile_lookup_str(const struct section_file *secfile, const char *path,...)
const char * fileinfoname(const struct strvec *dirs, const char *filename)
Definition shared.c:1094
struct strvec * fileinfolist(const struct strvec *dirs, const char *suffix)
Definition shared.c:1020
const struct strvec * get_data_dirs(void)
Definition shared.c:886
bool strvec_set(struct strvec *psv, size_t svindex, const char *string)
void strvec_reserve(struct strvec *psv, size_t reserve)
struct strvec * strvec_new(void)
void(* stop)(void)
Definition audio.h:35
void(* wait)(void)
Definition audio.h:36
void(* set_volume)(double volume)
Definition audio.h:38
double(* get_volume)(void)
Definition audio.h:37
void(* shutdown)(struct audio_plugin *self)
Definition audio.h:34
bool sound_enable_menu_music
Definition options.h:181
int sound_effects_volume
Definition options.h:183
bool sound_enable_effects
Definition options.h:180
bool sound_enable_game_music
Definition options.h:182
struct section_file * sfile
Definition audio.c:64
const char * tag
Definition audio.c:65
bool are_support_services_available(void)
Definition support.c:1378
#define sz_strlcpy(dest, src)
Definition support.h:167
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define sz_strlcat(dest, src)
Definition support.h:168