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