Freeciv-3.2
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 /* AUDIO_SDL */
38#include "client_main.h"
39#include "music.h"
40#include "options.h"
41
42#include "audio.h"
43
44#define MAX_NUM_PLUGINS 2
45#define SNDSPEC_SUFFIX ".soundspec"
46#define MUSICSPEC_SUFFIX ".musicspec"
47
48#define SOUNDSPEC_CAPSTR "+Freeciv-3.2-soundset"
49#define MUSICSPEC_CAPSTR "+Freeciv-3.2-musicspec"
50
51/* Keep them open throughout */
52static struct section_file *ss_tagfile = NULL;
53static struct section_file *ms_tagfile = NULL;
54
56static int num_plugins_used = 0;
57static int selected_plugin = -1;
58static int current_track = -1;
60static bool switching_usage = FALSE;
62
63static struct mfcb_data
64{
66 const char *tag;
68
69static int audio_play_tag(struct section_file *sfile,
70 const char *tag, bool repeat,
71 int exclude, bool keep_old_style);
72
73static void audio_shutdown_atexit(void);
74
75/**********************************************************************/
80const struct strvec *get_soundplugin_list(const struct option *poption)
81{
82 static struct strvec *plugin_list = NULL;
83
84 if (NULL == plugin_list) {
85 int i;
86
89 for (i = 0; i < num_plugins_used; i++) {
91 }
92 }
93
94 return plugin_list;
95}
96
97/**********************************************************************/
103static const struct strvec *get_audio_speclist(const char *suffix,
104 struct strvec **audio_list)
105{
106 if (NULL == *audio_list) {
108 }
109
110 return *audio_list;
111}
112
113/**********************************************************************/
116const struct strvec *get_soundset_list(const struct option *poption)
117{
118 static struct strvec *sound_list = NULL;
119
121}
122
123/**********************************************************************/
126const struct strvec *get_musicset_list(const struct option *poption)
127{
128 static struct strvec *music_list = NULL;
129
131}
132
133/**********************************************************************/
142
143/**********************************************************************/
146bool audio_select_plugin(const char *const name)
147{
148 int i;
149 bool found = FALSE;
150
151 for (i = 0; i < num_plugins_used; i++) {
152 if (strcmp(plugins[i].name, name) == 0) {
153 found = TRUE;
154 break;
155 }
156 }
157
158 if (found && i != selected_plugin) {
159 log_debug("Shutting down %s", plugins[selected_plugin].name);
163 }
164
165 if (!found) {
166 log_fatal(_("Plugin '%s' isn't available. Available are %s"),
169 }
170
171 if (!plugins[i].init(&(plugins[i]))) {
172 log_error("Plugin %s found, but can't be initialized.", name);
173 return FALSE;
174 }
175
177 log_verbose("Plugin '%s' is now selected", plugins[selected_plugin].name);
178
180
181 return TRUE;
182}
183
184/**********************************************************************/
188void audio_init(void)
189{
190#ifdef AUDIO_SDL
192#endif
193
194 /* Initialize dummy plugin last, as lowest priority plugin. This
195 * affects which plugin gets selected as default in new installations. */
197 selected_plugin = 0;
198}
199
200/**********************************************************************/
204static const char *audiospec_fullname(const char *audioset_name,
205 bool music)
206{
208 const char *audioset_default = music ? "stdmusic" : "stdsounds";/* Do not i18n! */
210 const char *dname;
211
213
215 free(fname);
216
217 if (dname) {
218 return fc_strdup(dname);
219 }
220
222 /* avoid endless recursion */
223 return NULL;
224 }
225
226 /* Marked for translation, as user may see this when
227 * their client configuration from older version has
228 * a musicset that they have not yet installed for the
229 * new version. */
230 log_error(_("Couldn't find audioset \"%s\", trying \"%s\"."),
232
234}
235
236/**********************************************************************/
239static bool check_audiofile_capstr(struct section_file *sfile,
240 const char *filename,
241 const char *our_cap,
242 const char *opt_path)
243{
244 const char *file_capstr;
245
247 if (NULL == file_capstr) {
248 log_fatal("Audio spec-file \"%s\" doesn't have capability string.",
249 filename);
251 }
253 log_fatal("Audio spec-file appears incompatible:");
254 log_fatal(" file: \"%s\"", filename);
255 log_fatal(" file options: %s", file_capstr);
256 log_fatal(" supported options: %s", our_cap);
258 }
260 log_fatal("Audio spec-file claims required option(s) "
261 "which we don't support:");
262 log_fatal(" file: \"%s\"", filename);
263 log_fatal(" file options: %s", file_capstr);
264 log_fatal(" supported options: %s", our_cap);
266 }
267
268 return TRUE;
269}
270
271/**********************************************************************/
274void audio_real_init(const char *const soundset_name,
275 const char *const musicset_name,
276 const char *const preferred_plugin_name)
277{
278 const char *ss_filename;
279 const char *ms_filename;
282
283 if (strcmp(preferred_plugin_name, "none") == 0) {
284 /* We explicitly choose none plugin, silently skip the code below */
285 log_verbose("Proceeding with sound support disabled.");
289
290 return;
291 }
292 if (num_plugins_used == 1) {
293 /* We only have the dummy plugin, skip the code but issue an advertise */
294 log_normal(_("No real audio plugin present."));
295 log_normal(_("Proceeding with sound support disabled."));
296 log_normal(_("For sound support, install SDL2_mixer"));
297 log_normal("https://github.com/libsdl-org/SDL_mixer");
301
302 return;
303 }
304 if (!soundset_name) {
305 log_fatal("No sound spec-file given!");
307 }
308 if (!musicset_name) {
309 log_fatal("No music spec-file given!");
311 }
312 log_verbose("Initializing sound using %s and %s...",
316 if (!ss_filename || !ms_filename) {
317 log_error("Cannot find audio spec-file \"%s\" or \"%s\"",
319 log_normal(_("To get sound you need to download a sound set!"));
320 log_normal(_("Get sound sets from <%s>."),
321 "https://www.freeciv.org/wiki/Sounds");
322 log_normal(_("Proceeding with sound support disabled."));
326
327 return;
328 }
330 log_fatal(_("Could not load sound spec-file '%s':\n%s"), ss_filename,
331 secfile_error());
333 }
335 log_fatal(_("Could not load music spec-file '%s':\n%s"), ms_filename,
336 secfile_error());
338 }
339
341 "soundspec.options");
342
343 free((void *) ss_filename);
344
346 "musicspec.options");
347
348 free((void *) ms_filename);
349
350 {
351 static bool atexit_set = FALSE;
352
353 if (!atexit_set) {
356 }
357 }
358
359 if (preferred_plugin_name[0] != '\0') {
361 log_normal(_("Proceeding with sound support disabled."));
362 return;
363 }
364
365#ifdef AUDIO_SDL
366 if (audio_select_plugin("sdl")) return;
367#endif
368 log_normal(_("No real audio subsystem managed to initialize!"));
369 log_normal(_("Perhaps there is some misconfiguration or bad permissions."));
370 log_normal(_("Proceeding with sound support disabled."));
371}
372
373/**********************************************************************/
384
385/**********************************************************************/
388static void music_finished_callback(void)
389{
390 bool usage_enabled = TRUE;
391
392 if (switching_usage) {
394
395 return;
396 }
397
399 /* This call is style music ending before single track plays.
400 * Do not restart style music now.
401 * Make sure style music restarts when single track itself finishes. */
403
404 return;
405 }
406
407 switch (current_usage) {
408 case MU_MENU:
410 break;
411 case MU_INGAME:
413 break;
414 }
415
416 if (usage_enabled) {
418 FALSE);
419 }
420}
421
422/**********************************************************************/
426static int audio_play_tag(struct section_file *sfile,
427 const char *tag, bool repeat, int exclude,
428 bool keep_old_style)
429{
430 const char *soundfile;
431 const char *fullpath = NULL;
433 int ret = 0;
434
435 if (!tag || strcmp(tag, "-") == 0) {
436 return -1;
437 }
438
439 if (sfile) {
440 soundfile = secfile_lookup_str(sfile, "files.%s", tag);
441 if (soundfile == NULL) {
442 const char *files[MAX_ALT_AUDIO_FILES];
443 int excluded = -1;
444 int i;
445 int j;
446
447 j = 0;
448 for (i = 0; i < MAX_ALT_AUDIO_FILES; i++) {
449 const char *ftmp = secfile_lookup_str(sfile, "files.%s_%d", tag, i);
450
451 if (ftmp == NULL) {
452 if (excluded != -1 && j == 0) {
453 /* Cannot exclude the only track */
454 excluded = -1;
455 j++;
456 }
457 files[j] = NULL;
458 break;
459 }
460 files[j] = ftmp;
461 if (i != exclude) {
462 j++;
463 } else {
464 excluded = j;
465 }
466 }
467
468 if (j > 0) {
469 ret = fc_rand(j);
470
471 soundfile = files[ret];
472 if (excluded != -1 && excluded < ret) {
473 /* Exclude track was skipped earlier, include it to track number to return */
474 ret++;
475 }
476 }
477 }
478
479 if (repeat) {
480 if (!keep_old_style) {
481 mfcb.sfile = sfile;
482 mfcb.tag = tag;
483 }
484
485 /* Callback is needed even when there's no alternative tracks -
486 * we may be running single track now, and want to switch
487 * (by the callback) back to style music when it ends. */
489 }
490
491 if (NULL == soundfile) {
492 log_verbose("No sound file for tag %s", tag);
493 } else {
495 if (!fullpath) {
496 log_error("Cannot find audio file %s for tag %s", soundfile, tag);
497 }
498 }
499 }
500
501 if (!plugins[selected_plugin].play(tag, fullpath, repeat, cb)) {
502 return -1;
503 }
504
505 return ret;
506}
507
508/**********************************************************************/
511static bool audio_play_sound_tag(const char *tag, bool repeat)
512{
513 return (audio_play_tag(ss_tagfile, tag, repeat, -1, FALSE) >= 0);
514}
515
516/**********************************************************************/
519static int audio_play_music_tag(const char *tag, bool repeat,
520 bool keep_old_style)
521{
522 return audio_play_tag(ms_tagfile, tag, repeat, -1, keep_old_style);
523}
524
525/**********************************************************************/
528void audio_play_sound(const char *const tag, const char *const alt_tag,
529 const char *const alt_tag2)
530{
531 const char *pretty_alt_tag = alt_tag ? alt_tag : "(null)";
532 const char *pretty_alt2_tag = alt_tag2 ? alt_tag2 : "(null)";
533
535 fc_assert_ret(tag != NULL);
536
537 log_debug("audio_play_sound('%s', '%s', '%s')",
539
540 /* Try playing primary tag first, if not go to alternative tags */
541 if (!audio_play_sound_tag(tag, FALSE)
544 log_verbose( "None of tags %s, %s, or %s found",
546 }
547 }
548}
549
550/**********************************************************************/
554static void real_audio_play_music(const char *const tag, char *const alt_tag,
555 bool keep_old_style)
556{
557 char *pretty_alt_tag = alt_tag ? alt_tag : "(null)";
558
559 fc_assert_ret(tag != NULL);
560
561 log_debug("audio_play_music('%s', '%s')", tag, pretty_alt_tag);
562
563 /* try playing primary tag first, if not go to alternative tag */
565
566 if (current_track < 0) {
568
569 if (current_track < 0) {
570 log_verbose("Neither of tags %s or %s found", tag, pretty_alt_tag);
571 }
572 }
573}
574
575/**********************************************************************/
578void audio_play_music(const char *const tag, char *const alt_tag,
579 enum music_usage usage)
580{
581 current_usage = usage;
582
584}
585
586/**********************************************************************/
589void audio_play_track(const char *const tag, char *const alt_tag)
590{
591 if (current_track >= 0) {
592 /* Only set let_single_track_play when there's music playing that will
593 * result in calling the music_finished_callback */
595
596 /* Stop old music. */
597 audio_stop();
598 }
599
601}
602
603/**********************************************************************/
606void audio_pause(void)
607{
609}
610
611/**********************************************************************/
614void audio_resume(void)
615{
617}
618
619/**********************************************************************/
622void audio_stop(void)
623{
625}
626
627/**********************************************************************/
635
636/**********************************************************************/
640{
642}
643
644/**********************************************************************/
651
652/**********************************************************************/
658{
659 /* Avoid infinite loop at end of game */
660 audio_stop();
661
662 if (play_quit_tag) {
663 audio_play_sound("e_client_quit", NULL, NULL);
664 }
665
669 }
670
671 if (NULL != ss_tagfile) {
674 }
675 if (NULL != ms_tagfile) {
678 }
679}
680
681/**********************************************************************/
684static void audio_shutdown_atexit(void)
685{
686 /* If support has already been shut down, can't handle audio tags. */
688}
689
690/**********************************************************************/
695{
696 static char buffer[100];
697 int i;
698
699 sz_strlcpy(buffer, "[");
700
701 for (i = 0; i < num_plugins_used; i++) {
702 sz_strlcat(buffer, plugins[i].name);
703 if (i != num_plugins_used - 1) {
704 sz_strlcat(buffer, ", ");
705 }
706 }
707 sz_strlcat(buffer, "]");
708 return buffer;
709}
void audio_restart(const char *soundset_name, const char *musicset_name)
Definition audio.c:376
static void music_finished_callback(void)
Definition audio.c:388
void audio_pause(void)
Definition audio.c:606
static struct audio_plugin plugins[MAX_NUM_PLUGINS]
Definition audio.c:55
void audio_play_track(const char *const tag, char *const alt_tag)
Definition audio.c:589
static void real_audio_play_music(const char *const tag, char *const alt_tag, bool keep_old_style)
Definition audio.c:554
#define SNDSPEC_SUFFIX
Definition audio.c:45
static bool audio_play_sound_tag(const char *tag, bool repeat)
Definition audio.c:511
static enum music_usage current_usage
Definition audio.c:59
void audio_resume(void)
Definition audio.c:614
#define MUSICSPEC_CAPSTR
Definition audio.c:49
static int selected_plugin
Definition audio.c:57
void audio_play_music(const char *const tag, char *const alt_tag, enum music_usage usage)
Definition audio.c:578
static const char * audiospec_fullname(const char *audioset_name, bool music)
Definition audio.c:204
void audio_shutdown(bool play_quit_tag)
Definition audio.c:657
const char * audio_get_all_plugin_names(void)
Definition audio.c:694
void audio_stop(void)
Definition audio.c:622
void audio_stop_usage(void)
Definition audio.c:630
void audio_init(void)
Definition audio.c:188
static int current_track
Definition audio.c:58
bool audio_select_plugin(const char *const name)
Definition audio.c:146
#define SOUNDSPEC_CAPSTR
Definition audio.c:48
static struct section_file * ss_tagfile
Definition audio.c:52
static int audio_play_music_tag(const char *tag, bool repeat, bool keep_old_style)
Definition audio.c:519
static const struct strvec * get_audio_speclist(const char *suffix, struct strvec **audio_list)
Definition audio.c:103
static bool check_audiofile_capstr(struct section_file *sfile, const char *filename, const char *our_cap, const char *opt_path)
Definition audio.c:239
void audio_set_volume(double volume)
Definition audio.c:647
const struct strvec * get_musicset_list(const struct option *poption)
Definition audio.c:126
void audio_play_sound(const char *const tag, const char *const alt_tag, const char *const alt_tag2)
Definition audio.c:528
static int audio_play_tag(struct section_file *sfile, const char *tag, bool repeat, int exclude, bool keep_old_style)
Definition audio.c:426
double audio_get_volume(void)
Definition audio.c:639
void audio_add_plugin(struct audio_plugin *p)
Definition audio.c:136
const struct strvec * get_soundset_list(const struct option *poption)
Definition audio.c:116
static bool let_single_track_play
Definition audio.c:61
static struct section_file * ms_tagfile
Definition audio.c:53
static void audio_shutdown_atexit(void)
Definition audio.c:684
static struct mfcb_data mfcb
static int num_plugins_used
Definition audio.c:56
const struct strvec * get_soundplugin_list(const struct option *poption)
Definition audio.c:80
#define MUSICSPEC_SUFFIX
Definition audio.c:46
#define MAX_NUM_PLUGINS
Definition audio.c:44
static bool switching_usage
Definition audio.c:60
void audio_real_init(const char *const soundset_name, const char *const musicset_name, const char *const preferred_plugin_name)
Definition audio.c:274
music_usage
Definition audio.h:46
@ MU_INGAME
Definition audio.h:46
@ MU_MENU
Definition audio.h:46
void(* audio_finished_callback)(void)
Definition audio.h:28
#define MAX_ALT_AUDIO_FILES
Definition audio.h:26
void audio_none_init(void)
Definition audio_none.c:91
void audio_sdl_init(void)
Definition audio_sdl.c:295
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]
char * incite_cost
Definition comments.c:75
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:483
#define fc_strdup(str)
Definition mem.h:43
#define fc_malloc(sz)
Definition mem.h:34
struct section_file * musicspec_load(const char *ms_filename)
Definition music.c:148
void musicspec_close(struct section_file *tagfile)
Definition music.c:219
struct client_options gui_options
Definition options.c:71
#define fc_rand(_size)
Definition rand.h:56
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:1101
struct strvec * fileinfolist(const struct strvec *dirs, const char *suffix)
Definition shared.c:1027
const struct strvec * get_data_dirs(void)
Definition shared.c:893
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(* resume)(void)
Definition audio.h:43
void(* stop)(void)
Definition audio.h:36
void(* wait)(void)
Definition audio.h:37
void(* set_volume)(double volume)
Definition audio.h:39
double(* get_volume)(void)
Definition audio.h:38
void(* shutdown)(struct audio_plugin *self)
Definition audio.h:35
void(* pause)(void)
Definition audio.h:42
bool sound_enable_menu_music
Definition options.h:188
int sound_effects_volume
Definition options.h:190
bool sound_enable_effects
Definition options.h:187
bool sound_enable_game_music
Definition options.h:189
struct section_file * sfile
Definition audio.c:65
const char * tag
Definition audio.c:66
bool are_support_services_available(void)
Definition support.c:1383
#define sz_strlcpy(dest, src)
Definition support.h:195
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define sz_strlcat(dest, src)
Definition support.h:196