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