webperimental: killstack decides stack protects.
[freeciv.git] / client / audio_sdl.c
blob667145e397426470bf5b9384ab8d9f942404e19e
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
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 <string.h>
20 #ifdef SDL2_PLAIN_INCLUDE
21 #include <SDL.h>
22 #include <SDL_mixer.h>
23 #else /* SDL2_PLAIN_INCLUDE */
24 #include <SDL2/SDL.h>
25 #include <SDL2/SDL_mixer.h>
26 #endif /* SDL2_PLAIN_INCLUDE */
28 /* utility */
29 #include "log.h"
30 #include "support.h"
32 /* client */
33 #include "audio.h"
35 #include "audio_sdl.h"
37 struct sample {
38 Mix_Chunk *wave;
39 const char *tag;
42 /* Sounds don't sound good on Windows unless the buffer size is 4k,
43 * but this seems to cause strange behaviour on other systems,
44 * such as a delay before playing the sound. */
45 #ifdef FREECIV_MSWINDOWS
46 const size_t buf_size = 4096;
47 #else
48 const size_t buf_size = 1024;
49 #endif
51 static Mix_Music *mus = NULL;
52 static struct sample samples[MIX_CHANNELS];
53 static double sdl_audio_volume;
55 /**************************************************************************
56 Set the volume.
57 **************************************************************************/
58 static void sdl_audio_set_volume(double volume)
60 Mix_VolumeMusic(volume * MIX_MAX_VOLUME);
61 Mix_Volume(-1, volume * MIX_MAX_VOLUME);
62 sdl_audio_volume = volume;
65 /**************************************************************************
66 Get the volume.
67 **************************************************************************/
68 static double sdl_audio_get_volume(void)
70 return sdl_audio_volume;
73 /**************************************************************************
74 Play sound
75 **************************************************************************/
76 static bool sdl_audio_play(const char *const tag, const char *const fullpath,
77 bool repeat, audio_finished_callback cb)
79 int i, j;
80 Mix_Chunk *wave = NULL;
82 if (!fullpath) {
83 return FALSE;
86 if (repeat) {
87 /* unload previous */
88 Mix_HaltMusic();
89 Mix_FreeMusic(mus);
91 /* load music file */
92 mus = Mix_LoadMUS(fullpath);
93 if (mus == NULL) {
94 log_error("Can't open file \"%s\"", fullpath);
97 if (cb == NULL) {
98 Mix_PlayMusic(mus, -1); /* -1 means loop forever */
99 } else {
100 Mix_PlayMusic(mus, 0);
101 Mix_HookMusicFinished(cb);
103 log_verbose("Playing file \"%s\" on music channel", fullpath);
104 /* in case we did a sdl_audio_stop() recently; add volume controls later */
105 Mix_VolumeMusic(MIX_MAX_VOLUME);
107 } else {
109 /* see if we can cache on this one */
110 for (j = 0; j < MIX_CHANNELS; j++) {
111 if (samples[j].tag && (strcmp(samples[j].tag, tag) == 0)) {
112 log_debug("Playing file \"%s\" from cache (slot %d)", fullpath, j);
113 i = Mix_PlayChannel(-1, samples[j].wave, 0);
114 return TRUE;
116 } /* guess not */
118 /* load wave */
119 wave = Mix_LoadWAV(fullpath);
120 if (wave == NULL) {
121 log_error("Can't open file \"%s\"", fullpath);
124 /* play sound sample on first available channel, returns -1 if no
125 channel found */
126 i = Mix_PlayChannel(-1, wave, 0);
127 if (i < 0) {
128 log_verbose("No available sound channel to play %s.", tag);
129 Mix_FreeChunk(wave);
130 return FALSE;
132 log_verbose("Playing file \"%s\" on channel %d", fullpath, i);
133 /* free previous sample on this channel. it will by definition no
134 longer be playing by the time we get here */
135 if (samples[i].wave) {
136 Mix_FreeChunk(samples[i].wave);
137 samples[i].wave = NULL;
139 /* remember for cacheing */
140 samples[i].wave = wave;
141 samples[i].tag = tag;
144 return TRUE;
147 /**************************************************************************
148 Stop music
149 **************************************************************************/
150 static void sdl_audio_stop(void)
152 /* fade out over 2 sec */
153 Mix_FadeOutMusic(2000);
156 /**************************************************************************
157 Wait for audio to die on all channels.
158 WARNING: If a channel is looping, it will NEVER exit! Always call
159 music_stop() first!
160 **************************************************************************/
161 static void sdl_audio_wait(void)
163 while (Mix_Playing(-1) != 0) {
164 SDL_Delay(100);
168 /**************************************************************************
169 Quit SDL. If the video is still in use (by gui-sdl), just quit the
170 subsystem.
172 This will need to be changed if SDL is used elsewhere.
173 **************************************************************************/
174 static void quit_sdl_audio(void)
176 if (SDL_WasInit(SDL_INIT_VIDEO)) {
177 SDL_QuitSubSystem(SDL_INIT_AUDIO);
178 } else {
179 SDL_Quit();
183 /**************************************************************************
184 Init SDL. If the video is already in use (by gui-sdl), just init the
185 subsystem.
187 This will need to be changed if SDL is used elsewhere.
188 **************************************************************************/
189 static int init_sdl_audio(void)
191 if (SDL_WasInit(SDL_INIT_VIDEO)) {
192 return SDL_InitSubSystem(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE);
193 } else {
194 return SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE);
198 /**************************************************************************
199 Clean up.
200 **************************************************************************/
201 static void sdl_audio_shutdown(void)
203 int i;
205 sdl_audio_stop();
206 sdl_audio_wait();
208 /* remove all buffers */
209 for (i = 0; i < MIX_CHANNELS; i++) {
210 if (samples[i].wave) {
211 Mix_FreeChunk(samples[i].wave);
214 Mix_HaltMusic();
215 Mix_FreeMusic(mus);
217 Mix_CloseAudio();
218 quit_sdl_audio();
221 /**************************************************************************
222 Initialize.
223 **************************************************************************/
224 static bool sdl_audio_init(void)
226 /* Initialize variables */
227 const int audio_rate = MIX_DEFAULT_FREQUENCY;
228 const int audio_format = MIX_DEFAULT_FORMAT;
229 const int audio_channels = 2;
230 int i;
232 if (init_sdl_audio() < 0) {
233 return FALSE;
236 if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, buf_size) < 0) {
237 log_error("Error calling Mix_OpenAudio");
238 /* try something else */
239 quit_sdl_audio();
240 return FALSE;
243 Mix_AllocateChannels(MIX_CHANNELS);
244 for (i = 0; i < MIX_CHANNELS; i++) {
245 samples[i].wave = NULL;
247 /* sanity check, for now; add volume controls later */
248 sdl_audio_set_volume(sdl_audio_volume);
249 return TRUE;
252 /**************************************************************************
253 Initialize. Note that this function is called very early at the
254 client startup. So for example logging isn't available.
255 **************************************************************************/
256 void audio_sdl_init(void)
258 struct audio_plugin self;
260 sz_strlcpy(self.name, "sdl");
261 sz_strlcpy(self.descr, "Simple DirectMedia Library (SDL) mixer plugin");
262 self.init = sdl_audio_init;
263 self.shutdown = sdl_audio_shutdown;
264 self.stop = sdl_audio_stop;
265 self.wait = sdl_audio_wait;
266 self.play = sdl_audio_play;
267 self.set_volume = sdl_audio_set_volume;
268 self.get_volume = sdl_audio_get_volume;
269 audio_add_plugin(&self);
270 sdl_audio_volume = 1.0;