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 "civilization.h"
47 #include "iocompat.h"
48 #include "iolib.h"
49 //Wyrmgus start
50 #include "grand_strategy.h" //for playing faction music
51 #include "player.h" //for playing faction music
52 #include "ui/interface.h" //for player faction music
53 //Wyrmgus end
54 #include "unit/unit.h"
55 //Wyrmgus start
56 #include "unit/unit_manager.h"
57 //Wyrmgus end
58
59 #include "SDL.h"
60
61 #ifdef USE_OAML
62 #include <oaml.h>
63 #endif
64
65 /*----------------------------------------------------------------------------
66 -- Variables
67 ----------------------------------------------------------------------------*/
68
69 static bool SoundInitialized; /// is sound initialized
70 static bool MusicPlaying; /// flag true if playing music
71
72 static int EffectsVolume = 128; /// effects sound volume
73 static int MusicVolume = 128; /// music volume
74
75 static bool MusicEnabled = true;
76 static bool EffectsEnabled = true;
77
78 /// Channels for sound effects and unit speech
79 struct SoundChannel {
80 CSample *Sample; /// sample to play
81 Origin *Unit; /// pointer to unit, who plays the sound, if any
82 unsigned char Volume; /// Volume of this channel
83 signed char Stereo; /// stereo location of sound (-128 left, 0 center, 127 right)
84 //Wyrmgus start
85 int Voice; /// Voice group of this channel (for identifying voice types)
86 //Wyrmgus end
87
88 bool Playing; /// channel is currently playing
89 int Point; /// point in sample if playing or next free channel
90
91 void (*FinishedCallback)(int channel); /// Callback for when a sample finishes playing
92 };
93
94 #define MaxChannels 64 /// How many channels are supported
95
96 static SoundChannel Channels[MaxChannels];
97 static int NextFreeChannel;
98
99 static struct {
100 CSample *Sample; /// Music sample
101 void (*FinishedCallback)(); /// Callback for when music finishes playing
102 } MusicChannel;
103
104 static void ChannelFinished(int channel);
105
106 static struct {
107 SDL_AudioSpec Format;
108 SDL_mutex *Lock;
109 SDL_cond *Cond;
110 SDL_Thread *Thread;
111
112 int *MixerBuffer;
113 Uint8 *Buffer;
114 bool Running;
115 } Audio;
116
117 #ifdef USE_OAML
118 #ifndef SDL_AUDIO_BITSIZE
119 #define SDL_AUDIO_BITSIZE(x) (x&0xFF)
120 #endif
121
122 extern oamlApi *oaml;
123 extern bool enableOAML;
124 #endif
125
126 /*----------------------------------------------------------------------------
127 -- Mixers
128 ----------------------------------------------------------------------------*/
129
130 /**
131 ** Convert RAW sound data to 44100 hz, Stereo, 16 bits per channel
132 **
133 ** @param src Source buffer
134 ** @param dest Destination buffer
135 ** @param frequency Frequency of source
136 ** @param chansize Bitrate in bytes per channel of source
137 ** @param channels Number of channels of source
138 ** @param bytes Number of compressed bytes to read
139 **
140 ** @return Number of bytes written in 'dest'
141 */
ConvertToStereo32(const char * src,char * dest,int frequency,int chansize,int channels,int bytes)142 static int ConvertToStereo32(const char *src, char *dest, int frequency,
143 int chansize, int channels, int bytes)
144 {
145 SDL_AudioCVT acvt;
146 Uint16 format;
147
148 if (chansize == 1) {
149 format = AUDIO_U8;
150 } else {
151 format = AUDIO_S16SYS;
152 }
153 SDL_BuildAudioCVT(&acvt, format, channels, frequency, AUDIO_S16SYS, 2, 44100);
154
155 acvt.buf = (Uint8 *)dest;
156 memcpy(dest, src, bytes);
157 acvt.len = bytes;
158
159 SDL_ConvertAudio(&acvt);
160
161 return acvt.len_mult * bytes;
162 }
163
164 /**
165 ** Mix music to stereo 32 bit.
166 **
167 ** @param buffer Buffer for mixed samples.
168 ** @param size Number of samples that fits into buffer.
169 **
170 ** @todo this functions can be called from inside the SDL audio callback,
171 ** which is bad, the buffer should be precalculated.
172 */
MixMusicToStereo32(int * buffer,int size)173 static void MixMusicToStereo32(int *buffer, int size)
174 {
175 if (MusicPlaying) {
176 Assert(MusicChannel.Sample);
177
178 short *buf = new short[size];
179 int len = size * sizeof(short);
180 char *tmp = new char[len];
181
182 int div = 176400 / (MusicChannel.Sample->Frequency * (MusicChannel.Sample->SampleSize / 8) * MusicChannel.Sample->Channels);
183
184 size = MusicChannel.Sample->Read(tmp, len / div);
185
186 int n = ConvertToStereo32(tmp, (char *)buf, MusicChannel.Sample->Frequency,
187 MusicChannel.Sample->SampleSize / 8, MusicChannel.Sample->Channels, size);
188
189 for (int i = 0; i < n / (int)sizeof(*buf); ++i) {
190 // Add to our samples
191 // FIXME: why taking out '/ 2' leads to distortion
192 buffer[i] += buf[i] * MusicVolume / MaxVolume / 2;
193 }
194
195 delete[] tmp;
196 delete[] buf;
197
198 if (n < len) { // End reached
199 MusicPlaying = false;
200 delete MusicChannel.Sample;
201 MusicChannel.Sample = nullptr;
202
203 if (MusicChannel.FinishedCallback) {
204 MusicChannel.FinishedCallback();
205 }
206 }
207 }
208 }
209
210 /**
211 ** Mix sample to buffer.
212 **
213 ** The input samples are adjusted by the local volume and resampled
214 ** to the output frequence.
215 **
216 ** @param sample Input sample
217 ** @param index Position into input sample
218 ** @param volume Volume of the input sample
219 ** @param stereo Stereo (left/right) position of sample
220 ** @param buffer Output buffer
221 ** @param size Size of output buffer (in samples per channel)
222 **
223 ** @return The number of bytes used to fill buffer
224 **
225 ** @todo Can mix faster if signed 8 bit buffers are used.
226 */
MixSampleToStereo32(CSample * sample,int index,unsigned char volume,char stereo,int * buffer,int size)227 static int MixSampleToStereo32(CSample *sample, int index, unsigned char volume,
228 char stereo, int *buffer, int size)
229 {
230 static int buf[SOUND_BUFFER_SIZE / 2];
231 unsigned char left;
232 unsigned char right;
233
234 int div = 176400 / (sample->Frequency * (sample->SampleSize / 8) * sample->Channels);
235 int local_volume = (int)volume * EffectsVolume / MaxVolume;
236
237 if (stereo < 0) {
238 left = 128;
239 right = 128 + stereo;
240 } else {
241 left = 128 - stereo;
242 right = 128;
243 }
244
245 Assert(!(index & 1));
246
247 size = std::min((sample->Len - index) * div / 2, size);
248
249 size = ConvertToStereo32((char *)(sample->Buffer + index), (char *)buf, sample->Frequency,
250 sample->SampleSize / 8, sample->Channels,
251 size * 2 / div);
252
253 size /= 2;
254 for (int i = 0; i < size; i += 2) {
255 // FIXME: why taking out '/ 2' leads to distortion
256 buffer[i] += ((short *)buf)[i] * local_volume * left / 128 / MaxVolume / 2;
257 buffer[i + 1] += ((short *)buf)[i + 1] * local_volume * right / 128 / MaxVolume / 2;
258 }
259
260 return 2 * size / div;
261 }
262
263 /**
264 ** Mix channels to stereo 32 bit.
265 **
266 ** @param buffer Buffer for mixed samples.
267 ** @param size Number of samples that fits into buffer.
268 **
269 ** @return How many channels become free after mixing them.
270 */
MixChannelsToStereo32(int * buffer,int size)271 static int MixChannelsToStereo32(int *buffer, int size)
272 {
273 int new_free_channels = 0;
274
275 for (int channel = 0; channel < MaxChannels; ++channel) {
276 if (Channels[channel].Playing && Channels[channel].Sample) {
277 //Wyrmgus start
278 if ((Channels[channel].Point & 1)) {
279 fprintf(stderr, "Sound effect error; Index: %d, Voice: %d, Origin: \"%s\", Sample Length: %d, Sample Filename: \"%s\"\n", Channels[channel].Point, Channels[channel].Voice, (Channels[channel].Unit && Channels[channel].Unit->Base) ? UnitManager.GetSlotUnit(Channels[channel].Unit->Id).Type->Ident.c_str() : "", Channels[channel].Sample->Len, Channels[channel].Sample->File.c_str());
280 }
281 //Wyrmgus end
282 int i = MixSampleToStereo32(Channels[channel].Sample,
283 Channels[channel].Point, Channels[channel].Volume,
284 Channels[channel].Stereo, buffer, size);
285 Channels[channel].Point += i;
286 Assert(Channels[channel].Point <= Channels[channel].Sample->Len);
287
288 if (Channels[channel].Point == Channels[channel].Sample->Len) {
289 ChannelFinished(channel);
290 ++new_free_channels;
291 }
292 }
293 }
294 return new_free_channels;
295 }
296
297 /**
298 ** Clip mix to output stereo 16 signed bit.
299 **
300 ** @param mix signed 32 bit input.
301 ** @param size number of samples in input.
302 ** @param output clipped 16 signed bit output buffer.
303 */
ClipMixToStereo16(const int * mix,int size,short * output)304 static void ClipMixToStereo16(const int *mix, int size, short *output)
305 {
306 const int *end = mix + size;
307
308 while (mix < end) {
309 int s = (*mix++);
310 clamp(&s, SHRT_MIN, SHRT_MAX);
311 *output++ = s;
312 }
313 }
314
315 /**
316 ** Mix into buffer.
317 **
318 ** @param buffer Buffer to be filled with samples. Buffer must be big enough.
319 ** @param samples Number of samples.
320 */
MixIntoBuffer(void * buffer,int samples)321 static void MixIntoBuffer(void *buffer, int samples)
322 {
323 // FIXME: can save the memset here, if first channel sets the values
324 memset(Audio.MixerBuffer, 0, samples * sizeof(*Audio.MixerBuffer));
325
326 if (EffectsEnabled) {
327 // Add channels to mixer buffer
328 MixChannelsToStereo32(Audio.MixerBuffer, samples);
329 }
330 if (MusicEnabled) {
331 // Add music to mixer buffer
332 MixMusicToStereo32(Audio.MixerBuffer, samples);
333 }
334 ClipMixToStereo16(Audio.MixerBuffer, samples, (short *)buffer);
335
336 #ifdef USE_OAML
337 if (enableOAML && oaml) {
338 oaml->SetAudioFormat(Audio.Format.freq, Audio.Format.channels, SDL_AUDIO_BITSIZE(Audio.Format.format) / 8);
339 oaml->MixToBuffer(buffer, samples);
340 }
341 #endif
342 }
343
344 /**
345 ** Fill buffer for the sound card.
346 **
347 ** @see SDL_OpenAudio
348 **
349 ** @param udata the pointer stored in userdata field of SDL_AudioSpec.
350 ** @param stream pointer to buffer you want to fill with information.
351 ** @param len is length of audio buffer in bytes.
352 */
FillAudio(void *,Uint8 * stream,int len)353 static void FillAudio(void *, Uint8 *stream, int len)
354 {
355 Assert((len/2) != Audio.Format.size);
356
357 if (Audio.Running == false)
358 return;
359
360 SDL_memset(stream, 0, len);
361
362 SDL_LockMutex(Audio.Lock);
363 SDL_MixAudio(stream, Audio.Buffer, len, SDL_MIX_MAXVOLUME);
364
365 // Signal our FillThread, we can fill the Audio.Buffer again
366 SDL_CondSignal(Audio.Cond);
367 SDL_UnlockMutex(Audio.Lock);
368 }
369
370 /**
371 ** Fill audio thread.
372 */
FillThread(void *)373 static int FillThread(void *)
374 {
375 while (Audio.Running == true) {
376 int status = SDL_LockMutex(Audio.Lock);
377 #ifdef USE_WIN32
378 if (SDL_CondWaitTimeout(Audio.Cond, Audio.Lock, 1000) == 0) {
379 #else
380 if (SDL_CondWaitTimeout(Audio.Cond, Audio.Lock, 100) == 0) {
381 #endif
382 MixIntoBuffer(Audio.Buffer, Audio.Format.samples * Audio.Format.channels);
383 }
384 SDL_UnlockMutex(Audio.Lock);
385
386 #ifdef USE_OAML
387 if (enableOAML && oaml)
388 oaml->Update();
389 #endif
390 }
391
392 return 0;
393 }
394
395 /*----------------------------------------------------------------------------
396 -- Effects
397 ----------------------------------------------------------------------------*/
398
399 /**
400 ** Check if this sound is already playing
401 */
402 bool SampleIsPlaying(CSample *sample)
403 {
404 for (int i = 0; i < MaxChannels; ++i) {
405 if (Channels[i].Sample == sample && Channels[i].Playing) {
406 return true;
407 }
408 }
409 return false;
410 }
411
412 bool UnitSoundIsPlaying(Origin *origin)
413 {
414 for (int i = 0; i < MaxChannels; ++i) {
415 //Wyrmgus start
416 // if (origin && Channels[i].Unit && origin->Id && Channels[i].Unit->Id
417 // && origin->Id == Channels[i].Unit->Id && Channels[i].Playing) {
418 if (
419 origin && Channels[i].Playing
420 && Channels[i].Voice != -1
421 && Channels[i].Voice != VoiceHit && Channels[i].Voice != VoiceMiss && Channels[i].Voice != VoiceFireMissile && Channels[i].Voice != VoiceStep
422 && Channels[i].Unit && origin->Id && Channels[i].Unit->Id
423 && origin->Id == Channels[i].Unit->Id
424 ) {
425 //Wyrmgus end
426 return true;
427 }
428 }
429 return false;
430 }
431
432 /**
433 ** A channel is finished playing
434 */
435 static void ChannelFinished(int channel)
436 {
437 if (Channels[channel].FinishedCallback) {
438 Channels[channel].FinishedCallback(channel);
439 }
440
441 //Wyrmgus start
442 // delete Channels[channel].Unit;
443 if (Channels[channel].Unit) {
444 delete Channels[channel].Unit;
445 }
446 //Wyrmgus end
447 Channels[channel].Unit = nullptr;
448
449 //Wyrmgus start
450 Channels[channel].Voice = -1;
451 //Wyrmgus end
452 Channels[channel].Playing = false;
453 Channels[channel].Point = NextFreeChannel;
454 NextFreeChannel = channel;
455 }
456
457 /**
458 ** Put a sound request in the next free channel.
459 */
460 static int FillChannel(CSample *sample, unsigned char volume, char stereo, Origin *origin)
461 {
462 Assert(NextFreeChannel < MaxChannels);
463
464 int old_free = NextFreeChannel;
465 int next_free = Channels[NextFreeChannel].Point;
466
467 Channels[NextFreeChannel].Volume = volume;
468 Channels[NextFreeChannel].Point = 0;
469 //Wyrmgus start
470 Channels[NextFreeChannel].Voice = -1;
471 //Wyrmgus end
472 Channels[NextFreeChannel].Playing = true;
473 Channels[NextFreeChannel].Sample = sample;
474 Channels[NextFreeChannel].Stereo = stereo;
475 Channels[NextFreeChannel].FinishedCallback = nullptr;
476 //Wyrmgus start
477 Channels[NextFreeChannel].Unit = nullptr;
478 //Wyrmgus end
479 if (origin && origin->Base) {
480 Origin *source = new Origin;
481 source->Base = origin->Base;
482 source->Id = origin->Id;
483 Channels[NextFreeChannel].Unit = source;
484 }
485 NextFreeChannel = next_free;
486
487 return old_free;
488 }
489
490 /**
491 ** Set the channel volume
492 **
493 ** @param channel Channel to set
494 ** @param volume New volume, <0 will not set the volume
495 **
496 ** @return Current volume of the channel, -1 for error
497 */
498 int SetChannelVolume(int channel, int volume)
499 {
500 if (channel < 0 || channel >= MaxChannels) {
501 return -1;
502 }
503
504 if (volume < 0) {
505 volume = Channels[channel].Volume;
506 } else {
507 SDL_LockMutex(Audio.Lock);
508
509 volume = std::min(MaxVolume, volume);
510 Channels[channel].Volume = volume;
511
512 SDL_UnlockMutex(Audio.Lock);
513 }
514 return volume;
515 }
516
517 /**
518 ** Set the channel stereo
519 **
520 ** @param channel Channel to set
521 ** @param stereo -128 to 127, out of range will not set the stereo
522 **
523 ** @return Current stereo of the channel, -1 for error
524 */
525 int SetChannelStereo(int channel, int stereo)
526 {
527 if (Preference.StereoSound == false) {
528 stereo = 0;
529 }
530 if (channel < 0 || channel >= MaxChannels) {
531 return -1;
532 }
533
534 if (stereo < -128 || stereo > 127) {
535 stereo = Channels[channel].Stereo;
536 } else {
537 SDL_LockMutex(Audio.Lock);
538 Channels[channel].Stereo = stereo;
539 SDL_UnlockMutex(Audio.Lock);
540 }
541 return stereo;
542 }
543
544 //Wyrmgus start
545 /**
546 ** Set the channel voice group
547 **
548 ** @param channel Channel to set
549 **
550 ** @return Current stereo of the channel, -1 for error
551 */
552 int SetChannelVoiceGroup(int channel, UnitVoiceGroup voice)
553 {
554 if (channel < 0 || channel >= MaxChannels) {
555 return -1;
556 }
557
558 SDL_LockMutex(Audio.Lock);
559 Channels[channel].Voice = voice;
560 SDL_UnlockMutex(Audio.Lock);
561
562 return voice;
563 }
564 //Wyrmgus end
565
566 /**
567 ** Set the channel's callback for when a sound finishes playing
568 **
569 ** @param channel Channel to set
570 ** @param callback Callback to call when the sound finishes
571 */
572 void SetChannelFinishedCallback(int channel, void (*callback)(int channel))
573 {
574 if (channel < 0 || channel >= MaxChannels) {
575 return;
576 }
577 Channels[channel].FinishedCallback = callback;
578 }
579
580 /**
581 ** Get the sample playing on a channel
582 */
583 CSample *GetChannelSample(int channel)
584 {
585 if (channel < 0 || channel >= MaxChannels) {
586 return nullptr;
587 }
588 return Channels[channel].Sample;
589 }
590
591 /**
592 ** Stop a channel
593 **
594 ** @param channel Channel to stop
595 */
596 void StopChannel(int channel)
597 {
598 SDL_LockMutex(Audio.Lock);
599 if (channel >= 0 && channel < MaxChannels) {
600 if (Channels[channel].Playing) {
601 ChannelFinished(channel);
602 }
603 }
604 SDL_UnlockMutex(Audio.Lock);
605 }
606
607 /**
608 ** Stop all channels
609 */
610 void StopAllChannels()
611 {
612 SDL_LockMutex(Audio.Lock);
613 for (int i = 0; i < MaxChannels; ++i) {
614 if (Channels[i].Playing) {
615 ChannelFinished(i);
616 }
617 }
618 SDL_UnlockMutex(Audio.Lock);
619 }
620
621 static CSample *LoadSample(const char *name, enum _play_audio_flags_ flag)
622 {
623 CSample *sampleWav = LoadWav(name, flag);
624
625 if (sampleWav) {
626 return sampleWav;
627 }
628 #ifdef USE_VORBIS
629 CSample *sampleVorbis = LoadVorbis(name, flag);
630 if (sampleVorbis) {
631 return sampleVorbis;
632 }
633 #endif
634 #ifdef USE_MIKMOD
635 CSample *sampleMikMod = LoadMikMod(name, flag);
636 if (sampleMikMod) {
637 return sampleMikMod;
638 }
639 #endif
640 #ifdef USE_FLUIDSYNTH
641 CSample *sampleFluidSynth = LoadFluidSynth(name, flag);
642 if (sampleFluidSynth) {
643 return sampleFluidSynth;
644 }
645 #endif
646 return nullptr;
647 }
648
649
650 /**
651 ** Load a sample
652 **
653 ** @param name File name of sample (short version).
654 **
655 ** @return General sample loaded from file into memory.
656 **
657 ** @todo Add streaming, caching support.
658 */
659 CSample *LoadSample(const std::string &name)
660 {
661 const std::string filename = LibraryFileName(name.c_str());
662 CSample *sample = LoadSample(filename.c_str(), PlayAudioLoadInMemory);
663
664 if (sample == nullptr) {
665 fprintf(stderr, "Can't load the sound '%s'\n", name.c_str());
666 }
667 return sample;
668 }
669
670 /**
671 ** Play a sound sample
672 **
673 ** @param sample Sample to play
674 **
675 ** @return Channel number, -1 for error
676 */
677 int PlaySample(CSample *sample, Origin *origin)
678 {
679 int channel = -1;
680
681 SDL_LockMutex(Audio.Lock);
682 if (SoundEnabled() && EffectsEnabled && sample && NextFreeChannel != MaxChannels) {
683 channel = FillChannel(sample, EffectsVolume, 0, origin);
684 }
685 SDL_UnlockMutex(Audio.Lock);
686 return channel;
687 }
688
689 /**
690 ** Play a sound file
691 **
692 ** @param name Filename of a sound to play
693 **
694 ** @return Channel number the sound is playing on, -1 for error
695 */
696 int PlaySoundFile(const std::string &name)
697 {
698 CSample *sample = LoadSample(name);
699 if (sample) {
700 return PlaySample(sample);
701 }
702 return -1;
703 }
704
705 /**
706 ** Set the global sound volume.
707 **
708 ** @param volume the sound volume 0-255
709 */
710 void SetEffectsVolume(int volume)
711 {
712 clamp(&volume, 0, MaxVolume);
713 EffectsVolume = volume;
714 }
715
716 /**
717 ** Get effects volume
718 */
719 int GetEffectsVolume()
720 {
721 return EffectsVolume;
722 }
723
724 /**
725 ** Set effects enabled
726 */
727 void SetEffectsEnabled(bool enabled)
728 {
729 EffectsEnabled = enabled;
730 }
731
732 /**
733 ** Check if effects are enabled
734 */
735 bool IsEffectsEnabled()
736 {
737 return EffectsEnabled;
738 }
739
740 /*----------------------------------------------------------------------------
741 -- Music
742 ----------------------------------------------------------------------------*/
743
744 /**
745 ** Set the music finished callback
746 */
747 void SetMusicFinishedCallback(void (*callback)())
748 {
749 MusicChannel.FinishedCallback = callback;
750 }
751
752 /**
753 ** Play a music file.
754 **
755 ** @param sample Music sample.
756 **
757 ** @return 0 if music is playing, -1 if not.
758 */
759 int PlayMusic(CSample *sample)
760 {
761 if (sample) {
762 StopMusic();
763 MusicChannel.Sample = sample;
764 MusicPlaying = true;
765 return 0;
766 } else {
767 DebugPrint("Could not play sample\n");
768 return -1;
769 }
770 }
771
772 /**
773 ** Play a music file.
774 **
775 ** @param file Name of music file, format is automatically detected.
776 **
777 ** @return 0 if music is playing, -1 if not.
778 */
779 int PlayMusic(const std::string &file)
780 {
781 if (!SoundEnabled() || !IsMusicEnabled()) {
782 return -1;
783 }
784 const std::string name = LibraryFileName(file.c_str());
785 DebugPrint("play music %s\n" _C_ name.c_str());
786 CSample *sample = LoadSample(name.c_str(), PlayAudioStream);
787
788 if (sample) {
789 StopMusic();
790 MusicChannel.Sample = sample;
791 MusicPlaying = true;
792 return 0;
793 } else {
794 DebugPrint("Could not play %s\n" _C_ file.c_str());
795 return -1;
796 }
797 }
798
799 void PlayMusicName(const std::string &name) {
800 if (!IsMusicEnabled()) {
801 return;
802 }
803
804 #ifdef USE_OAML
805 if (enableOAML == false || oaml == nullptr)
806 return;
807
808 SDL_LockMutex(Audio.Lock);
809 oaml->PlayTrack(name.c_str());
810 SDL_UnlockMutex(Audio.Lock);
811 #endif
812 }
813
814 void PlayMusicByGroupRandom(const std::string &group) {
815 if (!IsMusicEnabled()) {
816 return;
817 }
818
819 #ifdef USE_OAML
820 if (enableOAML == false || oaml == nullptr)
821 return;
822
823 SDL_LockMutex(Audio.Lock);
824 oaml->PlayTrackByGroupRandom(group.c_str());
825 SDL_UnlockMutex(Audio.Lock);
826 #endif
827 }
828
829 void PlayMusicByGroupAndSubgroupRandom(const std::string &group, const std::string &subgroup) {
830 if (!IsMusicEnabled()) {
831 return;
832 }
833
834 #ifdef USE_OAML
835 if (enableOAML == false || oaml == nullptr)
836 return;
837
838 SDL_LockMutex(Audio.Lock);
839 if (oaml->PlayTrackByGroupAndSubgroupRandom(group.c_str(), subgroup.c_str()) != OAML_OK) {
840 oaml->PlayTrackByGroupRandom(group.c_str());
841 }
842 SDL_UnlockMutex(Audio.Lock);
843 #endif
844 }
845
846 void PlayMusicByGroupAndFactionRandom(const std::string &group, const std::string &civilization_name, const std::string &faction_name) {
847 if (!IsMusicEnabled()) {
848 return;
849 }
850
851 #ifdef USE_OAML
852 if (enableOAML == false || oaml == nullptr)
853 return;
854
855 SDL_LockMutex(Audio.Lock);
856 if (oaml->PlayTrackByGroupAndSubgroupRandom(group.c_str(), faction_name.c_str()) != OAML_OK) {
857 CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
858 int faction = PlayerRaces.GetFactionIndexByName(faction_name);
859 int parent_faction = -1;
860 bool found_music = false;
861 if (faction != -1) {
862 while (true) {
863 parent_faction = PlayerRaces.Factions[faction]->ParentFaction;
864 if (parent_faction == -1) {
865 break;
866 }
867 faction = parent_faction;
868
869 if (oaml->PlayTrackByGroupAndSubgroupRandom(group.c_str(), PlayerRaces.Factions[faction]->Ident.c_str()) == OAML_OK) {
870 found_music = true;
871 break;
872 }
873 }
874 }
875 if (!found_music && oaml->PlayTrackByGroupAndSubgroupRandom(group.c_str(), civilization_name.c_str()) != OAML_OK) {
876 CCivilization *parent_civilization = nullptr;
877 if (civilization) {
878 while (true) {
879 parent_civilization = civilization->ParentCivilization;
880 if (!parent_civilization) {
881 break;
882 }
883 civilization = parent_civilization;
884
885 if (oaml->PlayTrackByGroupAndSubgroupRandom(group.c_str(), PlayerRaces.Name[civilization->ID].c_str()) == OAML_OK) {
886 found_music = true;
887 break;
888 }
889 }
890 }
891 if (!found_music) {
892 oaml->PlayTrackByGroupRandom(group.c_str());
893 }
894 }
895 }
896 SDL_UnlockMutex(Audio.Lock);
897 #endif
898 }
899
900 void SetMusicCondition(int id, int value) {
901 #ifdef USE_OAML
902 if (enableOAML == false || oaml == nullptr)
903 return;
904
905 SDL_LockMutex(Audio.Lock);
906 oaml->SetCondition(id, value);
907 SDL_UnlockMutex(Audio.Lock);
908 #endif
909 }
910
911 void SetMusicLayerGain(const std::string &layer, float gain) {
912 #ifdef USE_OAML
913 if (enableOAML == false || oaml == nullptr)
914 return;
915
916 SDL_LockMutex(Audio.Lock);
917 oaml->SetLayerGain(layer.c_str(), gain);
918 SDL_UnlockMutex(Audio.Lock);
919 #endif
920 }
921
922 /**
923 ** Stop the current playing music.
924 */
925 void StopMusic()
926 {
927 #ifdef USE_OAML
928 if (enableOAML && oaml) {
929 SDL_LockMutex(Audio.Lock);
930 oaml->StopPlaying();
931 SDL_UnlockMutex(Audio.Lock);
932 }
933 #endif
934
935 if (MusicPlaying) {
936 MusicPlaying = false;
937 if (MusicChannel.Sample) {
938 SDL_LockMutex(Audio.Lock);
939 delete MusicChannel.Sample;
940 MusicChannel.Sample = nullptr;
941 SDL_UnlockMutex(Audio.Lock);
942 }
943 }
944 }
945
946 /**
947 ** Set the music volume.
948 **
949 ** @param volume the music volume 0-255
950 */
951 void SetMusicVolume(int volume)
952 {
953 clamp(&volume, 0, MaxVolume);
954 MusicVolume = volume;
955
956 #ifdef USE_OAML
957 if (enableOAML && oaml) {
958 SDL_LockMutex(Audio.Lock);
959 oaml->SetVolume(MusicVolume / 255.f);
960 SDL_UnlockMutex(Audio.Lock);
961 }
962 #endif
963 }
964
965 /**
966 ** Get music volume
967 */
968 int GetMusicVolume()
969 {
970 return MusicVolume;
971 }
972
973 /**
974 ** Set music enabled
975 */
976 void SetMusicEnabled(bool enabled)
977 {
978 if (enabled) {
979 MusicEnabled = true;
980 } else {
981 MusicEnabled = false;
982 StopMusic();
983 }
984 }
985
986 /**
987 ** Check if music is enabled
988 */
989 bool IsMusicEnabled()
990 {
991 return MusicEnabled;
992 }
993
994 /**
995 ** Check if music is playing
996 */
997 bool IsMusicPlaying()
998 {
999 #ifdef USE_OAML
1000 if (enableOAML && oaml) {
1001 if (oaml->IsPlaying())
1002 return true;
1003 }
1004 #endif
1005 return MusicPlaying;
1006 }
1007
1008 /**
1009 ** Add tension to music
1010 */
1011 void AddMusicTension(int value)
1012 {
1013 #ifdef USE_OAML
1014 if (enableOAML == false || oaml == nullptr)
1015 return;
1016
1017 SDL_LockMutex(Audio.Lock);
1018 oaml->AddTension(value);
1019 SDL_UnlockMutex(Audio.Lock);
1020 #endif
1021 }
1022
1023
1024 /*----------------------------------------------------------------------------
1025 -- Init
1026 ----------------------------------------------------------------------------*/
1027
1028 /**
1029 ** Check if sound is enabled
1030 */
1031 bool SoundEnabled()
1032 {
1033 return SoundInitialized;
1034 }
1035
1036 /**
1037 ** Initialize sound card hardware part with SDL.
1038 **
1039 ** @param freq Sample frequency (44100,22050,11025 hz).
1040 ** @param size Sample size (8bit, 16bit)
1041 **
1042 ** @return True if failure, false if everything ok.
1043 */
1044 static int InitSdlSound(int freq, int size)
1045 {
1046 SDL_AudioSpec wanted;
1047
1048 wanted.freq = freq;
1049 if (size == 8) {
1050 wanted.format = AUDIO_U8;
1051 } else if (size == 16) {
1052 wanted.format = AUDIO_S16SYS;
1053 } else {
1054 DebugPrint("Unexpected sample size %d\n" _C_ size);
1055 wanted.format = AUDIO_S16SYS;
1056 }
1057 wanted.channels = 2;
1058 wanted.samples = 4096;
1059 wanted.callback = FillAudio;
1060 wanted.userdata = nullptr;
1061
1062 // Open the audio device, forcing the desired format
1063 if (SDL_OpenAudio(&wanted, &Audio.Format) < 0) {
1064 fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
1065 return -1;
1066 }
1067 SDL_PauseAudio(0);
1068 return 0;
1069 }
1070
1071 /**
1072 ** Initialize sound card.
1073 **
1074 ** @return True if failure, false if everything ok.
1075 */
1076 int InitSound()
1077 {
1078 //
1079 // Open sound device, 8bit samples, stereo.
1080 //
1081 if (InitSdlSound(44100, 16)) {
1082 SoundInitialized = false;
1083 return 1;
1084 }
1085 SoundInitialized = true;
1086
1087 // ARI: The following must be done here to allow sound to work in
1088 // pre-start menus!
1089 // initialize channels
1090 for (int i = 0; i < MaxChannels; ++i) {
1091 Channels[i].Point = i + 1;
1092 //Wyrmgus start
1093 Channels[i].Sample = nullptr;
1094 Channels[i].Unit = nullptr;
1095 Channels[i].Volume = 0;
1096 Channels[i].Stereo = 0;
1097 Channels[i].Voice = -1;
1098 Channels[i].Playing = false;
1099 //Wyrmgus end
1100 }
1101
1102 // Create mutex and cond for FillThread
1103 Audio.MixerBuffer = new int[Audio.Format.samples * Audio.Format.channels];
1104 memset(Audio.MixerBuffer, 0, Audio.Format.samples * Audio.Format.channels * sizeof(int));
1105 Audio.Buffer = new Uint8[Audio.Format.size];
1106 memset(Audio.Buffer, 0, Audio.Format.size);
1107 Audio.Lock = SDL_CreateMutex();
1108 Audio.Cond = SDL_CreateCond();
1109 Audio.Running = true;
1110
1111 // Create thread to fill sdl audio buffer
1112 Audio.Thread = SDL_CreateThread(FillThread, nullptr);
1113 return 0;
1114 }
1115
1116 /**
1117 ** Cleanup sound server.
1118 */
1119 void QuitSound()
1120 {
1121 #ifdef USE_OAML
1122 if (oaml) {
1123 oaml->Shutdown();
1124 delete oaml;
1125 oaml = nullptr;
1126 }
1127 #endif
1128
1129 Audio.Running = false;
1130 SDL_WaitThread(Audio.Thread, nullptr);
1131
1132 SDL_DestroyCond(Audio.Cond);
1133 SDL_DestroyMutex(Audio.Lock);
1134
1135 // Mustn't call SDL_CloseAudio here, it'll be called again from SDL_Quit
1136 SoundInitialized = false;
1137 delete[] Audio.MixerBuffer;
1138 Audio.MixerBuffer = nullptr;
1139 delete[] Audio.Buffer;
1140 Audio.Buffer = nullptr;
1141 #ifdef USE_FLUIDSYNTH
1142 CleanFluidSynth();
1143 #endif
1144 }
1145
1146 //@}
1147