1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name sound_server.cpp - The sound server (hardware layer and so on) */
12 //
13 // (c) Copyright 1998-2006 by Lutz Sammer, Fabrice Rossi, and
14 // Jimmy Salmon
15 //
16 // This program is free software; you can redistribute it and/or modify
17 // it under the terms of the GNU General Public License as published by
18 // the Free Software Foundation; only version 2 of the License.
19 //
20 // This program is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 // GNU General Public License for more details.
24 //
25 // You should have received a copy of the GNU General Public License
26 // along with this program; if not, write to the Free Software
27 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 // 02111-1307, USA.
29 //
30
31
32 //@{
33
34 /*----------------------------------------------------------------------------
35 -- Includes
36 ----------------------------------------------------------------------------*/
37
38 #include "stratagus.h"
39
40 #include "sound_server.h"
41
42 #ifdef USE_FLUIDSYNTH
43 #include "fluidsynth.h"
44 #endif
45
46 #include "iocompat.h"
47 #include "iolib.h"
48 #include "unit.h"
49
50 #include "SDL.h"
51 #include "SDL_mixer.h"
52
53 /*----------------------------------------------------------------------------
54 -- Variables
55 ----------------------------------------------------------------------------*/
56
57 static bool SoundInitialized; /// is sound initialized
58 static bool MusicEnabled = true;
59 static bool EffectsEnabled = true;
60 static double VolumeScale = 1.0;
61 static int MusicVolume = 0;
62
63 extern volatile bool MusicFinished;
64
65 /// Channels for sound effects and unit speech
66 struct SoundChannel {
67 Origin *Unit; /// pointer to unit, who plays the sound, if any
68 void (*FinishedCallback)(int channel); /// Callback for when a sample finishes playing
69 };
70
71 #define MaxChannels 64 /// How many channels are supported
72
73 static SoundChannel Channels[MaxChannels];
74
75 static void ChannelFinished(int channel);
76
77 /**
78 ** Check if this sound is already playing
79 */
SampleIsPlaying(Mix_Chunk * sample)80 bool SampleIsPlaying(Mix_Chunk *sample)
81 {
82 for (int i = 0; i < MaxChannels; ++i) {
83 if (Mix_GetChunk(i) == sample && Mix_Playing(i)) {
84 return true;
85 }
86 }
87 return false;
88 }
89
UnitSoundIsPlaying(Origin * origin)90 bool UnitSoundIsPlaying(Origin *origin)
91 {
92 for (int i = 0; i < MaxChannels; ++i) {
93 if (origin && Channels[i].Unit && origin->Id && Channels[i].Unit->Id
94 && origin->Id == Channels[i].Unit->Id && Mix_Playing(i)) {
95 return true;
96 }
97 }
98 return false;
99 }
100
101 /**
102 ** A channel is finished playing
103 */
ChannelFinished(int channel)104 static void ChannelFinished(int channel)
105 {
106 if (Channels[channel].FinishedCallback) {
107 Channels[channel].FinishedCallback(channel);
108 }
109 delete Channels[channel].Unit;
110 Channels[channel].Unit = NULL;
111 }
112
113 /**
114 ** Set the channel volume
115 **
116 ** @param channel Channel to set
117 ** @param volume New volume 0-255, <0 will not set the volume
118 **
119 ** @return Current volume of the channel, -1 for error
120 */
SetChannelVolume(int channel,int volume)121 int SetChannelVolume(int channel, int volume)
122 {
123 return Mix_Volume(channel, volume * VolumeScale);
124 }
125
126 /**
127 ** Set the channel stereo
128 **
129 ** @param channel Channel to set
130 ** @param stereo -128 to 127, out of range will not set the stereo
131 **
132 ** @return Current stereo of the channel, -1 for error
133 */
SetChannelStereo(int channel,int stereo)134 void SetChannelStereo(int channel, int stereo)
135 {
136 if (Preference.StereoSound == false) {
137 Mix_SetPanning(channel, 255, 255);
138 } else {
139 int left, right;
140 if (stereo > 0) {
141 left = 255 - stereo;
142 right = 255;
143 } else {
144 left = 255;
145 right = 255 + stereo;
146 }
147 Mix_SetPanning(channel, left, right);
148 }
149 }
150
151 /**
152 ** Set the channel's callback for when a sound finishes playing
153 **
154 ** @param channel Channel to set
155 ** @param callback Callback to call when the sound finishes
156 */
SetChannelFinishedCallback(int channel,void (* callback)(int channel))157 void SetChannelFinishedCallback(int channel, void (*callback)(int channel))
158 {
159 if (channel < 0 || channel >= MaxChannels) {
160 return;
161 }
162 Channels[channel].FinishedCallback = callback;
163 }
164
165 /**
166 ** Get the sample playing on a channel
167 */
GetChannelSample(int channel)168 Mix_Chunk *GetChannelSample(int channel)
169 {
170 if (Mix_Playing(channel)) {
171 return Mix_GetChunk(channel);
172 }
173 return NULL;
174 }
175
176 /**
177 ** Stop a channel
178 **
179 ** @param channel Channel to stop
180 */
StopChannel(int channel)181 void StopChannel(int channel)
182 {
183 Mix_HaltChannel(channel);
184 }
185
186 /**
187 ** Stop all channels
188 */
StopAllChannels()189 void StopAllChannels()
190 {
191 Mix_HaltChannel(-1);
192 }
193
194 static Mix_Music *currentMusic = NULL;
195
LoadMusic(const char * name)196 static Mix_Music *LoadMusic(const char *name)
197 {
198 if (currentMusic) {
199 Mix_HaltMusic();
200 Mix_FreeMusic(currentMusic);
201 }
202 currentMusic = Mix_LoadMUS(name);
203 if (currentMusic) {
204 return currentMusic;
205 }
206
207 CFile *f = new CFile;
208 if (f->open(name, CL_OPEN_READ) == -1) {
209 printf("Can't open file '%s'\n", name);
210 delete f;
211 return NULL;
212 }
213 currentMusic = Mix_LoadMUS_RW(f->as_SDL_RWops(), 0);
214 return currentMusic;
215 }
216
LoadSample(const char * name)217 static Mix_Chunk *LoadSample(const char *name)
218 {
219 Mix_Chunk *r = Mix_LoadWAV(name);
220 if (r) {
221 return r;
222 }
223 CFile *f = new CFile;
224 if (f->open(name, CL_OPEN_READ) == -1) {
225 printf("Can't open file '%s'\n", name);
226 delete f;
227 return NULL;
228 }
229 return Mix_LoadWAV_RW(f->as_SDL_RWops(), 0);
230 }
231
232 /**
233 ** Load a music file
234 **
235 ** @param name File name
236 **
237 ** @return Mix_Music pointer
238 */
LoadMusic(const std::string & name)239 Mix_Music *LoadMusic(const std::string &name)
240 {
241 const std::string filename = LibraryFileName(name.c_str());
242 Mix_Music *music = LoadMusic(filename.c_str());
243
244 if (music == NULL) {
245 fprintf(stderr, "Can't load the music '%s'\n", name.c_str());
246 }
247 return music;
248 }
249
250 /**
251 ** Load a sample
252 **
253 ** @param name File name of sample (short version).
254 **
255 ** @return General sample loaded from file into memory.
256 **
257 ** @todo Add streaming, caching support.
258 */
LoadSample(const std::string & name)259 Mix_Chunk *LoadSample(const std::string &name)
260 {
261 const std::string filename = LibraryFileName(name.c_str());
262 Mix_Chunk *sample = LoadSample(filename.c_str());
263
264 if (sample == NULL) {
265 fprintf(stderr, "Can't load the sound '%s': %s\n", name.c_str(), Mix_GetError());
266 }
267 return sample;
268 }
269
270 /**
271 ** Play a sound sample
272 **
273 ** @param sample Sample to play
274 **
275 ** @return Channel number, -1 for error
276 */
PlaySample(Mix_Chunk * sample,Origin * origin)277 int PlaySample(Mix_Chunk *sample, Origin *origin)
278 {
279 int channel = -1;
280 DebugPrint("play sample %d\n" _C_ sample->volume);
281 if (SoundEnabled() && EffectsEnabled && sample) {
282 channel = Mix_PlayChannel(-1, sample, 0);
283 Channels[channel].FinishedCallback = NULL;
284 if (origin && origin->Base) {
285 Origin *source = new Origin;
286 source->Base = origin->Base;
287 source->Id = origin->Id;
288 Channels[channel].Unit = source;
289 }
290 }
291 return channel;
292 }
293
294 /**
295 ** Set the global sound volume.
296 **
297 ** @param volume the sound volume 0-255
298 */
SetEffectsVolume(int volume)299 void SetEffectsVolume(int volume)
300 {
301 VolumeScale = (volume * 1.0) / 255.0;
302 }
303
304 /**
305 ** Get effects volume
306 */
GetEffectsVolume()307 int GetEffectsVolume()
308 {
309 return VolumeScale * 255;
310 }
311
312 /**
313 ** Set effects enabled
314 */
SetEffectsEnabled(bool enabled)315 void SetEffectsEnabled(bool enabled)
316 {
317 EffectsEnabled = enabled;
318 }
319
320 /**
321 ** Check if effects are enabled
322 */
IsEffectsEnabled()323 bool IsEffectsEnabled()
324 {
325 return EffectsEnabled;
326 }
327
328 /*----------------------------------------------------------------------------
329 -- Music
330 ----------------------------------------------------------------------------*/
331
332 /**
333 ** Set the music finished callback
334 */
SetMusicFinishedCallback(void (* callback)())335 void SetMusicFinishedCallback(void (*callback)())
336 {
337 Mix_HookMusicFinished(callback);
338 }
339
340 /**
341 ** Play a music file.
342 **
343 ** @param sample Music sample.
344 **
345 ** @return 0 if music is playing, -1 if not.
346 */
PlayMusic(Mix_Music * sample)347 int PlayMusic(Mix_Music *sample)
348 {
349 if (sample) {
350 Mix_VolumeMusic(MusicVolume);
351 MusicFinished = false;
352 Mix_PlayMusic(sample, 0);
353 Mix_VolumeMusic(MusicVolume / 4.0);
354 return 0;
355 } else {
356 DebugPrint("Could not play sample\n");
357 return -1;
358 }
359 }
360
361 /**
362 ** Play a music file.
363 **
364 ** @param file Name of music file, format is automatically detected.
365 **
366 ** @return 0 if music is playing, -1 if not.
367 */
PlayMusic(const std::string & file)368 int PlayMusic(const std::string &file)
369 {
370 if (!SoundEnabled() || !IsMusicEnabled()) {
371 return -1;
372 }
373 DebugPrint("play music %s\n" _C_ file.c_str());
374 Mix_Music *music = LoadMusic(file);
375
376 if (music) {
377 MusicFinished = false;
378 Mix_FadeInMusic(music, 0, 200);
379 return 0;
380 } else {
381 DebugPrint("Could not play %s\n" _C_ file.c_str());
382 return -1;
383 }
384 }
385
386 /**
387 ** Stop the current playing music.
388 */
StopMusic()389 void StopMusic()
390 {
391 Mix_FadeOutMusic(200);
392 }
393
394 /**
395 ** Set the music volume.
396 **
397 ** @param volume the music volume 0-255
398 */
SetMusicVolume(int volume)399 void SetMusicVolume(int volume)
400 {
401 // due to left-right separation, sound effect volume is effectively halfed,
402 // so we adjust the music
403 MusicVolume = volume;
404 Mix_VolumeMusic(volume / 4.0);
405 }
406
407 /**
408 ** Get music volume
409 */
GetMusicVolume()410 int GetMusicVolume()
411 {
412 return MusicVolume;
413 }
414
415 /**
416 ** Set music enabled
417 */
SetMusicEnabled(bool enabled)418 void SetMusicEnabled(bool enabled)
419 {
420 if (enabled) {
421 MusicEnabled = true;
422 } else {
423 MusicEnabled = false;
424 StopMusic();
425 }
426 }
427
428 /**
429 ** Check if music is enabled
430 */
IsMusicEnabled()431 bool IsMusicEnabled()
432 {
433 return MusicEnabled;
434 }
435
436 /**
437 ** Check if music is playing
438 */
IsMusicPlaying()439 bool IsMusicPlaying()
440 {
441 return Mix_PlayingMusic();
442 }
443
444 /*----------------------------------------------------------------------------
445 -- Init
446 ----------------------------------------------------------------------------*/
447
448 /**
449 ** Check if sound is enabled
450 */
SoundEnabled()451 bool SoundEnabled()
452 {
453 return SoundInitialized;
454 }
455
456 /**
457 ** Initialize sound card hardware part with SDL.
458 **
459 ** @param freq Sample frequency (44100,22050,11025 hz).
460 ** @param size Sample size (8bit, 16bit)
461 **
462 ** @return True if failure, false if everything ok.
463 */
InitSdlSound()464 static int InitSdlSound()
465 {
466 // just activate everything we can by setting all bits
467 Mix_Init(std::numeric_limits<unsigned int>::max());
468 if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 1024)) {
469 fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
470 return -1;
471 } else {
472 printf("Supported sound decoders:");
473 for (int i = 0; i < Mix_GetNumChunkDecoders(); i++) {
474 printf(" %s", Mix_GetChunkDecoder(i));
475 }
476 printf("\nSupported music decoders:");
477 for (int i = 0; i < Mix_GetNumMusicDecoders(); i++) {
478 printf(" %s", Mix_GetMusicDecoder(i));
479 }
480 printf("\n");
481 }
482 return 0;
483 }
484
485 /**
486 ** Initialize sound card.
487 **
488 ** @return True if failure, false if everything ok.
489 */
InitSound()490 int InitSound()
491 {
492 //
493 // Open sound device, 8bit samples, stereo.
494 //
495 if (InitSdlSound()) {
496 SoundInitialized = false;
497 return 1;
498 }
499 SoundInitialized = true;
500 Mix_AllocateChannels(MaxChannels);
501 Mix_ChannelFinished(ChannelFinished);
502
503 // Now we're ready for the callback to run
504 Mix_ResumeMusic();
505 Mix_Resume(-1);
506 return 0;
507 }
508
509 /**
510 ** Cleanup sound server.
511 */
QuitSound()512 void QuitSound()
513 {
514 Mix_CloseAudio();
515 Mix_Quit();
516 SoundInitialized = false;
517 }
518
519 //@}
520