1 /*
2 *  Copyright (C) 2000-2013  The Exult Team
3 *
4 *  This program is free software; you can redistribute it and/or modify
5 *  it under the terms of the GNU General Public License as published by
6 *  the Free Software Foundation; either version 2 of the License, or
7 *  (at your option) any later version.
8 *
9 *  This program is distributed in the hope that it will be useful,
10 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 *  GNU General Public License for more details.
13 *
14 *  You should have received a copy of the GNU General Public License
15 *  along with this program; if not, write to the Free Software
16 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18 
19 #include "pent_include.h"
20 #include "ignore_unused_variable_warning.h"
21 
22 #include <SDL_audio.h>
23 #include <SDL_timer.h>
24 
25 #include <fstream>
26 #include <set>
27 //#include "SDL_mapping.h"
28 
29 #include "Audio.h"
30 #include "Configuration.h"
31 #include "Flex.h"
32 #include "conv.h"
33 #include "exult.h"
34 #include "fnames.h"
35 #include "game.h"
36 #include "utils.h"
37 
38 #include "AudioMixer.h"
39 #include "AudioSample.h"
40 #include "databuf.h"
41 #include "gamewin.h"
42 #include "actors.h"
43 
44 #include <cstdio>
45 #include <cstdlib>
46 #include <cstring>
47 #include <iostream>
48 #include <climits>
49 
50 #include <csignal>
51 #include <fcntl.h>
52 #include <unistd.h>
53 #include <sys/stat.h>
54 #include <sys/types.h>
55 
56 //#include <crtdbg.h>
57 
58 using std::cerr;
59 using std::cout;
60 using std::endl;
61 using std::memcpy;
62 using std::string;
63 using std::ifstream;
64 using std::ios;
65 
66 using namespace Pentagram;
67 
68 #define MIXER_CHANNELS 32
69 
70 struct	Chunk
71 {
72 	size_t	length;
73 	uint8	*data;
ChunkChunk74 	Chunk(size_t l, uint8 *d) : length(l),data(d) {}
75 };
76 
77 
78 Audio *Audio::self = nullptr;
79 int const *Audio::bg2si_sfxs = nullptr;
80 int const *Audio::bg2si_songs = nullptr;
81 
82 //----- Utilities ----------------------------------------------------
83 
84 //----- SFX ----------------------------------------------------------
85 
86 // Tries to locate a sfx in the cache based on sfx num.
find_sfx(int id)87 SFX_cache_manager::SFX_cached *SFX_cache_manager::find_sfx(int id)
88 {
89 	auto found = cache.find(id);
90 	if (found  == cache.end()) return nullptr;
91 	return &(found->second);
92 }
93 
~SFX_cache_manager()94 SFX_cache_manager::~SFX_cache_manager() {
95 	flush();
96 }
97 
98 // For SFX played through 'play_wave_sfx'. Searched cache for
99 // the sfx first, then loads from the sfx file if needed.
request(Flex * sfx_file,int id)100 AudioSample *SFX_cache_manager::request(Flex *sfx_file, int id)
101 {
102 	SFX_cached *loaded = find_sfx(id);
103 	if (!loaded) {
104 		SFX_cached new_sfx;
105 		new_sfx.first = 0;
106 		new_sfx.second = nullptr;
107 		loaded = &(cache[id] = new_sfx);
108 	}
109 
110 	if (!loaded->second)
111 	{
112 		garbage_collect();
113 
114 		size_t wavlen;			// Read .wav file.
115 		auto wavbuf = sfx_file->retrieve(id, wavlen);
116 		loaded->second = AudioSample::createAudioSample(std::move(wavbuf), wavlen);
117 	}
118 
119 	if (!loaded->second) return nullptr;
120 
121 	// Increment counter
122 	++loaded->first;
123 
124 	return loaded->second;
125 }
126 
127 // Empties the cache.
flush(AudioMixer * mixer)128 void SFX_cache_manager::flush(AudioMixer *mixer)
129 {
130 	for (auto it = cache.begin() ; it != cache.end(); it = cache.begin())
131 	{
132 		if (it->second.second)
133 		{
134 			if (it->second.second->getRefCount() != 1 && mixer)
135 				mixer->stopSample(it->second.second);
136 			it->second.second->Release();
137 		}
138 		it->second.second = nullptr;
139 		cache.erase(it);
140 	}
141 }
142 
143 // Remove unused sounds from the cache.
garbage_collect()144 void SFX_cache_manager::garbage_collect()
145 {
146 	// Maximum 'stable' number of sounds we will cache (actual
147 	// count may be higher if all of the cached sounds are
148 	// being played).
149 	const int max_fixed = 6;
150 
151 	std::multiset <int> sorted;
152 
153 	for (auto& it : cache)
154 	{
155 		if (it.second.second && it.second.second->getRefCount() == 1)
156 			sorted.insert(it.second.first);
157 	}
158 
159 	if (sorted.empty()) return;
160 
161 	int threshold = INT_MAX;
162 	int count = 0;
163 
164 	for ( auto it = sorted.rbegin( ) ; it != sorted.rend( ); ++it )
165 	{
166 		if (count < max_fixed)
167 		{
168 			threshold = *it;
169 			count++;
170 		}
171 		else if (*it == threshold)
172 		{
173 			count++;
174 		}
175 		else
176 		{
177 			break;
178 		}
179 	}
180 
181 	if (count <= max_fixed)
182 		return;
183 
184 	for (auto& it : cache)
185 	{
186 		if (it.second.second)
187 		{
188 			if (it.second.first < threshold)
189 			{
190 				it.second.second->Release();
191 				it.second.second = nullptr;
192 			}
193 			else if (it.second.first == threshold && count > max_fixed)
194 			{
195 				it.second.second->Release();
196 				it.second.second = nullptr;
197 				count--;
198 			}
199 		}
200 	}
201 
202 }
203 
204 //---- Audio ---------------------------------------------------------
Init()205 void Audio::Init()
206 {
207 	// Crate the Audio singleton object
208 	if (!self)
209 	{
210 		int sample_rate = 22050;
211 		bool stereo = true;
212 
213 		config->value("config/audio/sample_rate", sample_rate, sample_rate);
214 		config->value("config/audio/stereo", stereo, stereo);
215 
216 		self = new Audio();
217 		self->Init(sample_rate,stereo?2:1);
218 	}
219 }
220 
Destroy()221 void Audio::Destroy()
222 {
223 	delete self;
224 	self = nullptr;
225 }
226 
get_ptr()227 Audio	*Audio::get_ptr()
228 {
229 	// The following assert here might be too harsh, maybe we should leave
230 	// it to the caller to handle non-inited audio-system?
231 	assert(self != nullptr);
232 
233 	return self;
234 }
235 
236 
Audio()237 Audio::Audio()
238 {
239 	assert(self == nullptr);
240 
241 	string s;
242 
243 	config->value("config/audio/enabled",s,"yes");
244 	audio_enabled = (s!="no");
245 	config->set("config/audio/enabled", audio_enabled?"yes":"no",true);
246 
247 	config->value("config/audio/speech/enabled",s,"yes");
248 	speech_enabled = (s!="no");
249 	config->value("config/audio/speech/with_subs",s,"no");
250 	speech_with_subs = (s!="no");
251 	config->value("config/audio/midi/enabled",s,"---");
252 	music_enabled = (s!="no");
253 	config->value("config/audio/effects/enabled",s,"---");
254 	effects_enabled = (s!="no");
255 	config->value("config/audio/midi/looping",s,"yes");
256 	allow_music_looping = (s!="no");
257 
258 	mixer.reset();
259 	sfxs = std::make_unique<SFX_cache_manager>();
260 }
261 
Init(int _samplerate,int _channels)262 void Audio::Init(int _samplerate,int _channels)
263 {
264 	if (!audio_enabled) return;
265 
266 	mixer = std::make_unique<AudioMixer>(_samplerate,_channels==2,MIXER_CHANNELS);
267 
268 	COUT("Audio initialisation OK");
269 
270 	mixer->openMidiOutput();
271 	initialized = true;
272 }
273 
can_sfx(const std::string & file,std::string * out)274 bool	Audio::can_sfx(const std::string &file, std::string *out)
275 {
276 	if (file.empty())
277 		return false;
278 	string options[] = {"", "<BUNDLE>", "<DATA>"};
279 	for (auto& d : options) {
280 		string f;
281 		if (!d.empty()) {
282 			if (!is_system_path_defined(d)) {
283 				continue;
284 			}
285 			f = d;
286 			f += '/';
287 			f += file;
288 		} else {
289 			f = file;
290 		}
291 		if (U7exists(f.c_str())) {
292 			if (out)
293 				*out = f;
294 			return true;
295 		}
296 	}
297 	return false;
298 }
299 
have_roland_sfx(Exult_Game game,std::string * out)300 bool Audio::have_roland_sfx(Exult_Game game, std::string *out)
301 	{
302 	if (game == BLACK_GATE)
303 		return can_sfx(SFX_ROLAND_BG, out);
304 	else if (game == SERPENT_ISLE)
305 		return can_sfx(SFX_ROLAND_SI, out);
306 	return false;
307 	}
308 
have_sblaster_sfx(Exult_Game game,std::string * out)309 bool Audio::have_sblaster_sfx(Exult_Game game, std::string *out)
310 	{
311 	if (game == BLACK_GATE)
312 		return can_sfx(SFX_BLASTER_BG, out);
313 	else if (game == SERPENT_ISLE)
314 		return can_sfx(SFX_BLASTER_SI, out);
315 	return false;
316 	}
317 
have_midi_sfx(std::string * out)318 bool Audio::have_midi_sfx(std::string *out)
319 	{
320 #ifdef ENABLE_MIDISFX
321 	return can_sfx(SFX_MIDIFILE, out);
322 #else
323 	ignore_unused_variable_warning(out);
324 	return false;
325 #endif
326 	}
327 
have_config_sfx(const std::string & game,std::string * out)328 bool Audio::have_config_sfx(const std::string &game, std::string *out)
329 	{
330 	string s;
331 	string d = "config/disk/game/" + game + "/waves";
332 	config->value(d.c_str(), s, "---");
333 	return (s != "---") && can_sfx(s, out);
334 	}
335 
Init_sfx()336 void	Audio::Init_sfx()
337 {
338 	sfx_file.reset();
339 
340 	if (sfxs)
341 		{
342 		sfxs->flush(mixer.get());
343 		sfxs->garbage_collect();
344 		}
345 
346 	Exult_Game game = Game::get_game_type();
347 	if (game == SERPENT_ISLE)
348 	{
349 		bg2si_sfxs = bgconv;
350 		bg2si_songs = bgconvsong;
351 	}
352 	else
353 	{
354 		bg2si_sfxs = nullptr;
355 		bg2si_songs = nullptr;
356 	}
357 	// Collection of .wav's?
358 	string flex;
359 #ifdef ENABLE_MIDISFX
360 	string v;
361 	config->value("config/audio/effects/midi", v, "no");
362 	if (have_midi_sfx(&flex) && v != "no")
363 		{
364 		cout << "Opening midi SFX's file: \"" << flex << "\"" << endl;
365 		sfx_file = std::make_unique<FlexFile>(std::move(flex));
366 		return;
367 		}
368 	else if (!have_midi_sfx(&flex))
369 		config->set("config/audio/effects/midi", "no", true);
370 #endif
371 	if (!have_config_sfx(Game::get_gametitle(), &flex))
372 		{
373 		if (have_roland_sfx(game, &flex) || have_sblaster_sfx(game, &flex))
374 			{
375 			string d = "config/disk/game/" + Game::get_gametitle() + "/waves";
376 			size_t sep = flex.rfind('/');
377 			std::string pflex;
378 			if (sep != string::npos)
379 				{
380 				sep++;
381 				pflex = flex.substr(sep);
382 				}
383 			else
384 				pflex = flex;
385 			config->set(d.c_str(), pflex, true);
386 			}
387 		else
388 			{
389 			cerr << "Digital SFX's file specified: " << flex
390 				<< "... but file not found, and fallbacks are missing" << endl;
391 			return;
392 			}
393 		}
394 	cout << "Opening digital SFX's file: \"" << flex << "\"" << endl;
395 	sfx_file = std::make_unique<FlexFile>(std::move(flex));
396 }
397 
~Audio()398 Audio::~Audio()
399 {
400 	if (!initialized)
401 	{
402 		//SDL_open = false;
403 		return;
404 	}
405 
406 	CERR("~Audio:  about to stop_music()");
407 	stop_music();
408 
409 	CERR("~Audio:  about to quit subsystem");
410 }
411 
copy_and_play(const uint8 * sound_data,uint32 len,bool wait)412 void Audio::copy_and_play(const uint8 *sound_data, uint32 len, bool wait)
413 {
414 	auto new_sound_data = std::make_unique<uint8[]>(len);
415 	std::memcpy(new_sound_data.get(), sound_data, len);
416 	play(std::move(new_sound_data), len, wait);
417 }
418 
play(std::unique_ptr<uint8[]> sound_data,uint32 len,bool wait)419 void Audio::play(std::unique_ptr<uint8[]> sound_data, uint32 len, bool wait)
420 {
421 	ignore_unused_variable_warning(wait);
422 	if (!audio_enabled || !speech_enabled || !len) {
423 		return;
424 	}
425 
426 	AudioSample *audio_sample = AudioSample::createAudioSample(std::move(sound_data), len);
427 
428 	if (audio_sample) {
429 		mixer->playSample(audio_sample,0,128);
430 		audio_sample->Release();
431 	}
432 
433 }
434 
cancel_streams()435 void Audio::cancel_streams()
436 {
437 	if (!audio_enabled)
438 		return;
439 
440 	//Mix_HaltChannel(-1);
441 	mixer->reset();
442 
443 }
444 
pause_audio()445 void Audio::pause_audio()
446 {
447 	if (!audio_enabled)
448 		return;
449 
450 	mixer->setPausedAll(true);
451 }
452 
resume_audio()453 void 	Audio::resume_audio()
454 {
455 	if (!audio_enabled)
456 		return;
457 
458 	mixer->setPausedAll(false);
459 }
460 
461 
playfile(const char * fname,const char * fpatch,bool wait)462 void Audio::playfile(const char *fname, const char *fpatch, bool wait)
463 {
464 	if (!audio_enabled)
465 		return;
466 
467 	U7multiobject sample(fname, fpatch, 0);
468 
469 	size_t len;
470 	auto buf = sample.retrieve(len);
471 	if (!buf || len == 0) {
472 		// Failed to find file in patch or static dirs.
473 		CERR("Audio::playfile: Error reading file '" << fname << "'");
474 		return;
475 	}
476 
477 	play(std::move(buf), len, wait);
478 }
479 
480 
playing()481 bool	Audio::playing()
482 {
483 	return false;
484 }
485 
486 
start_music(int num,bool continuous,const std::string & flex)487 void	Audio::start_music(int num, bool continuous,const std::string& flex)
488 {
489 	if(audio_enabled && music_enabled && mixer && mixer->getMidiPlayer())
490 		mixer->getMidiPlayer()->start_music(num,continuous && allow_music_looping,flex);
491 }
492 
start_music(const std::string & fname,int num,bool continuous)493 void	Audio::start_music(const std::string& fname, int num, bool continuous)
494 {
495 	if(audio_enabled && music_enabled && mixer && mixer->getMidiPlayer())
496 		mixer->getMidiPlayer()->start_music(fname,num,continuous && allow_music_looping);
497 }
498 
start_music_combat(Combat_song song,bool continuous)499 void	Audio::start_music_combat (Combat_song song, bool continuous)
500 {
501 	if(!audio_enabled || !music_enabled || !mixer || !mixer->getMidiPlayer())
502 		return;
503 
504 	int num = -1;
505 
506 	switch (song)
507 	{
508 	case CSBattle_Over:
509 		num = Audio::game_music(9);
510 		break;
511 
512 	case CSAttacked1:
513 		num = Audio::game_music(11);
514 		break;
515 
516 	case CSAttacked2:
517 		num = Audio::game_music(12);
518 		break;
519 
520 	case CSVictory:
521 		num = Audio::game_music(15);
522 		break;
523 
524 	case CSRun_Away:
525 		num = Audio::game_music(16);
526 		break;
527 
528 	case CSDanger:
529 		num = Audio::game_music(10);
530 		break;
531 
532 	case CSHidden_Danger:
533 		num = Audio::game_music(18);
534 		break;
535 
536 	default:
537 		CERR("Error: Unable to Find combat track for song " << song << ".");
538 		break;
539 	}
540 
541 	mixer->getMidiPlayer()->start_music(num, continuous && allow_music_looping);
542 }
543 
stop_music()544 void	Audio::stop_music()
545 {
546 	if (!audio_enabled) return;
547 
548 	if(mixer && mixer->getMidiPlayer())
549 		mixer->getMidiPlayer()->stop_music(true);
550 }
551 
start_speech(int num,bool wait)552 bool Audio::start_speech(int num, bool wait)
553 {
554 	if (!audio_enabled || !speech_enabled)
555 		return false;
556 
557 	const char *filename;
558 	const char *patchfile;
559 
560 	if (Game::get_game_type() == SERPENT_ISLE)
561 	{
562 		filename = SISPEECH;
563 		patchfile = PATCH_SISPEECH;
564 	}
565 	else
566 	{
567 		filename = U7SPEECH;
568 		patchfile = PATCH_U7SPEECH;
569 	}
570 
571 	U7multiobject sample(filename, patchfile, num);
572 
573 	size_t len;
574 	auto buf = sample.retrieve(len);
575 	if (!buf || len == 0) {
576 		return false;
577 	}
578 
579 	play(std::move(buf), len, wait);
580 	return true;
581 }
582 
stop_speech()583 void Audio::stop_speech()
584 {
585 	if (!audio_enabled || !speech_enabled)
586 		return;
587 
588 	mixer->reset();
589 }
590 
591 /*
592 *	This returns a 'unique' ID, but only for .wav SFX's (for now).
593 */
play_sound_effect(int num,int volume,int balance,int repeat,int distance)594 int	Audio::play_sound_effect (int num, int volume, int balance, int repeat, int distance)
595 {
596 	if (!audio_enabled || !effects_enabled) return -1;
597 
598 #ifdef ENABLE_MIDISFX
599 	string v; // TODO: should make this check faster
600 	config->value("config/audio/effects/midi", v, "no");
601 	if (v != "no" && mixer && mixer->getMidiPlayer()) {
602 		mixer->getMidiPlayer()->start_sound_effect(num);
603 		return -1;
604 	}
605 #endif
606 	// Where sort of sfx are we using????
607 	if (sfx_file != nullptr)		// Digital .wav's?
608 		return play_wave_sfx(num, volume, balance, repeat, distance);
609 	return -1;
610 }
611 
612 /*
613 *	Play a .wav format sound effect,
614 *  return the channel number playing on or -1 if not playing, (0 is a valid channel in SDL_Mixer!)
615 */
play_wave_sfx(int num,int volume,int balance,int repeat,int distance)616 int Audio::play_wave_sfx
617 (
618 	int num,
619 	int volume,		// 0-256.
620 	int balance,		// balance, -256 (left) - +256 (right)
621 	int repeat,		// Keep playing.
622 	int distance
623 )
624 {
625 	if (!effects_enabled || !sfx_file || !mixer)
626 		return -1;  // no .wav sfx available
627 
628 	if (num < 0 || static_cast<unsigned>(num) >= sfx_file->number_of_objects())
629 	{
630 		cerr << "SFX " << num << " is out of range" << endl;
631 		return -1;
632 	}
633 	AudioSample *wave = sfxs->request(sfx_file.get(), num);
634 	if (!wave)
635 	{
636 		cerr << "Couldn't play sfx '" << num << "'" << endl;
637 		return -1;
638 	}
639 
640 	int instance_id = mixer->playSample(wave,repeat,0,true,AUDIO_DEF_PITCH,volume,volume);
641 	if (instance_id < 0)
642 	{
643 		CERR("No channel was available to play sfx '" << num << "'");
644 		return -1;
645 	}
646 
647 	CERR("Playing SFX: " << num);
648 
649 	mixer->set2DPosition(instance_id,distance,balance);
650 	mixer->setPaused(instance_id,false);
651 
652 	return instance_id;
653 }
654 
655 /*
656 *	This returns a 'unique' ID, but only for .wav SFX's (for now).
657 */
play_sound_effect(const File_spec & sfxfile,int num,int volume,int balance,int repeat,int distance)658 int	Audio::play_sound_effect (const File_spec& sfxfile, int num, int volume, int balance, int repeat, int distance)
659 {
660 	if (!audio_enabled || !effects_enabled) return -1;
661 	// TODO: No support for MIDI SFX at this time here.
662 	return play_wave_sfx(sfxfile, num, volume, balance, repeat, distance);
663 }
664 
665 /*
666 *	Play a .wav format sound effect,
667 *  return the channel number playing on or -1 if not playing, (0 is a valid channel in SDL_Mixer!)
668 */
play_wave_sfx(const File_spec & sfxfile,int num,int volume,int balance,int repeat,int distance)669 int Audio::play_wave_sfx
670 (
671 	const File_spec& sfxfile,
672 	int num,
673 	int volume,		// 0-256.
674 	int balance,		// balance, -256 (left) - +256 (right)
675 	int repeat,		// Keep playing.
676 	int distance
677 )
678 {
679 	if (!effects_enabled || !mixer || !U7exists(sfxfile.name)) {
680 		return -1;  // no .wav sfx available
681 	}
682 	IExultDataSource ds(sfxfile, num);
683 	if (!ds.good()) {
684 		cerr << "SFX " << num << " from {" << sfxfile.name << ", " << sfxfile.index << "} is out of range" << endl;
685 		return -1;
686 	}
687 
688 	size_t wavlen;			// Read .wav file.
689 	auto wavbuf = ds.steal_data(wavlen);
690 	auto *wave = AudioSample::createAudioSample(std::move(wavbuf), wavlen);
691 
692 	int instance_id = mixer->playSample(wave, repeat, 0, true, AUDIO_DEF_PITCH, volume,volume);
693 	// Either AudioMixer::playSample called IncRef through AudioChannel::playSample and the sample
694 	// will be played, of the sample was not queued for playback.
695 	// In either case we need to Release the sample to avoid a memory leak.
696 	wave->Release();
697 	if (instance_id < 0)
698 	{
699 		CERR("No channel was available to play sfx '" << num << "' from {" << sfxfile.name << ", " << sfxfile.index << "}");
700 		return -1;
701 	}
702 
703 	CERR("Playing SFX: " << num << " from {" << sfxfile.name << ", " << sfxfile.index << "}");
704 
705 	mixer->set2DPosition(instance_id, distance, balance);
706 	mixer->setPaused(instance_id, false);
707 
708 	return instance_id;
709 }
710 
711 /*
712 static int slow_sqrt(int i)
713 {
714 	for (int r = i/2; r != 0; r--)
715 	{
716 		if (r*r <= i) return r;
717 	}
718 
719 	return 0;
720 }
721 */
722 
get_2d_position_for_tile(const Tile_coord & tile,int & distance,int & balance)723 void Audio::get_2d_position_for_tile(const Tile_coord &tile, int &distance, int &balance)
724 {
725 	distance = 0;
726 	balance = 0;
727 
728 	Game_window *gwin = Game_window::get_instance();
729 	TileRect size = gwin->get_win_tile_rect();
730 	Tile_coord apos(size.x+size.w/2,size.y+size.h/2,gwin->get_camera_actor()->get_lift());
731 
732 	int sqr_dist = apos.square_distance_screen_space(tile);
733 	if (sqr_dist > MAX_SOUND_FALLOFF*MAX_SOUND_FALLOFF) {
734 		distance = 257;
735 		return;
736 	}
737 
738 	//distance = sqrt((double) sqr_dist) * 256 / MAX_SOUND_FALLOFF;
739 	//distance = slow_sqrt(sqr_dist) * 256 / MAX_SOUND_FALLOFF;
740 	distance = sqr_dist * 256 / (MAX_SOUND_FALLOFF*MAX_SOUND_FALLOFF);
741 
742 	balance = (Tile_coord::delta(apos.tx,tile.tx)*2-tile.tz-apos.tz)*32 / 5;
743 
744 }
745 
play_sound_effect(int num,const Game_object * obj,int volume,int repeat)746 int Audio::play_sound_effect (int num, const Game_object *obj, int volume, int repeat)
747 {
748 	Tile_coord tile = obj->get_center_tile();
749 	return play_sound_effect(num, tile, volume, repeat);
750 }
751 
play_sound_effect(int num,const Tile_coord & tile,int volume,int repeat)752 int Audio::play_sound_effect (int num, const Tile_coord &tile, int volume, int repeat)
753 {
754 	int distance;
755 	int balance;
756 	get_2d_position_for_tile(tile,distance,balance);
757 	if (distance > 256) distance = 256;
758 	return play_sound_effect(num,volume,balance,repeat,distance);
759 }
760 
update_sound_effect(int chan,const Game_object * obj)761 int Audio::update_sound_effect(int chan, const Game_object *obj)
762 {
763 	Tile_coord tile = obj->get_center_tile();
764 	return update_sound_effect(chan,tile);
765 }
766 
update_sound_effect(int chan,const Tile_coord & tile)767 int Audio::update_sound_effect(int chan, const Tile_coord &tile)
768 {
769 	if (!mixer) return -1;
770 
771 	int distance;
772 	int balance;
773 	get_2d_position_for_tile(tile,distance,balance);
774 	if (distance > 256) {
775 		mixer->stopSample(chan);
776 		return -1;
777 	} else if (mixer->set2DPosition(chan,distance,balance)) {
778 		return chan;
779 	} else {
780 		return -1;
781 	}
782 }
783 
stop_sound_effect(int chan)784 void Audio::stop_sound_effect(int chan)
785 {
786 	if (!mixer) return;
787 	mixer->stopSample(chan);
788 }
789 
790 /*
791 *	Halt sound effects.
792 */
793 
stop_sound_effects()794 void Audio::stop_sound_effects()
795 {
796 	if (sfxs) sfxs->flush(mixer.get());
797 
798 #ifdef ENABLE_MIDISFX
799 	if (mixer && mixer->getMidiPlayer())
800 		mixer->getMidiPlayer()->stop_sound_effects();
801 #endif
802 }
803 
804 
set_audio_enabled(bool ena)805 void Audio::set_audio_enabled(bool ena)
806 {
807 	if (ena && audio_enabled && initialized)
808 	{
809 
810 	}
811 	else if (!ena && audio_enabled && initialized)
812 	{
813 		stop_sound_effects();
814 		stop_music();
815 		audio_enabled = false;
816 	}
817 	else if (ena && !audio_enabled && initialized)
818 	{
819 		audio_enabled = true;
820 	}
821 	else if (!ena && !audio_enabled && initialized)
822 	{
823 
824 	}
825 	else if (ena && !audio_enabled && !initialized)
826 	{
827 		audio_enabled = true;
828 
829 		int sample_rate = 22050;
830 		bool stereo = true;
831 
832 		config->value("config/audio/sample_rate", sample_rate, sample_rate);
833 		config->value("config/audio/stereo", stereo, stereo);
834 
835 		Init(sample_rate,stereo?2:1);
836 	}
837 	else if (!ena && !audio_enabled && !initialized)
838 	{
839 
840 	}
841 }
842 
is_track_playing(int num) const843 bool Audio::is_track_playing(int num) const
844 {
845 	MyMidiPlayer *midi = get_midi();
846 	return midi && midi->is_track_playing(num);
847 }
848 
is_voice_playing() const849 bool Audio::is_voice_playing() const
850 {
851 	return mixer && mixer->isPlayingVoice();
852 }
853 
get_midi() const854 MyMidiPlayer *Audio::get_midi() const
855 {
856 	return mixer ? mixer->getMidiPlayer() : nullptr;
857 }
858