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)
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 ***********************************************************************/
15 #include <fc_config.h>
23 #include "capability.h"
30 #include "string_vector.h"
34 #include "audio_none.h"
36 #include "audio_sdl.h"
38 #include "client_main.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
;
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
) {
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
);
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
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
);
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 /**************************************************************************
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
));
138 /**************************************************************************
139 Choose plugin. Returns TRUE on success, FALSE if not
140 **************************************************************************/
141 bool audio_select_plugin(const char *const name
)
146 for (i
= 0; i
< num_plugins_used
; i
++) {
147 if (strcmp(plugins
[i
].name
, name
) == 0) {
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();
161 log_fatal(_("Plugin '%s' isn't available. Available are %s"),
162 name
, audio_get_all_plugin_names());
166 if (!plugins
[i
].init()) {
167 log_error("Plugin %s found, but can't be initialized.", name
);
172 log_verbose("Plugin '%s' is now selected", plugins
[selected_plugin
].name
);
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)
186 /* Initialize dummy plugin last, as lowest priority plugin. This
187 * affects which plugin gets selected as default in new installations. */
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
,
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);
204 sprintf(fname
, "%s%s", audioset_name
, suffix
);
206 dname
= fileinfoname(get_data_dirs(), fname
);
210 return fc_strdup(dname
);
213 if (strcmp(audioset_name
, audioset_default
) == 0) {
214 /* avoid endless recursion */
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
,
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.",
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
);
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
);
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.");
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");
288 if (!soundset_name
) {
289 log_fatal("No sound spec-file given!");
292 if (!musicset_name
) {
293 log_fatal("No music spec-file given!");
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."));
311 if (!(ss_tagfile
= secfile_load(ss_filename
, TRUE
))) {
312 log_fatal(_("Could not load sound spec-file '%s':\n%s"), ss_filename
,
316 if (!(ms_tagfile
= secfile_load(ms_filename
, TRUE
))) {
317 log_fatal(_("Could not load music spec-file '%s':\n%s"), ms_filename
,
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
;
336 atexit(audio_shutdown
);
341 if (preferred_plugin_name
[0] != '\0') {
342 if (!audio_select_plugin(preferred_plugin_name
))
343 log_normal(_("Proceeding with sound support disabled."));
348 if (audio_select_plugin("sdl")) return;
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 /**************************************************************************
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
) {
376 usage_enabled
= FALSE
;
379 usage_enabled
= gui_options
.sound_enable_menu_music
;
382 usage_enabled
= gui_options
.sound_enable_game_music
;
387 current_track
= audio_play_tag(mfcb
.sfile
, mfcb
.tag
, TRUE
, current_track
,
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
,
400 const char *soundfile
;
401 const char *fullpath
= NULL
;
402 audio_finished_callback cb
= NULL
;
405 if (!tag
|| strcmp(tag
, "-") == 0) {
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
;
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
) {
430 if (j
== 0 && excluded
) {
431 /* Cannot exclude the only track */
439 soundfile
= files
[ret
];
441 /* Exclude track was skipped earlier, include it to track number to return */
449 cb
= music_finished_callback
;
453 if (NULL
== soundfile
) {
454 log_verbose("No sound file for tag %s (file %s)", tag
, soundfile
);
456 fullpath
= fileinfoname(get_data_dirs(), soundfile
);
458 log_error("Cannot find audio file %s", soundfile
);
463 if (!plugins
[selected_plugin
].play(tag
, fullpath
, repeat
, cb
)) {
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
,
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
510 **************************************************************************/
511 static void real_audio_play_music(const char *const tag
, char *const alt_tag
,
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 **************************************************************************/
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 */
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
);
593 if (NULL
!= ms_tagfile
) {
594 secfile_destroy(ms_tagfile
);
599 /**************************************************************************
600 Returns a string which list all available plugins. You don't have to
602 **************************************************************************/
603 const char *audio_get_all_plugin_names()
605 static char buffer
[100];
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
, "]");