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 #ifndef AUDIO_H
20 #define AUDIO_H
21 
22 #include <map>
23 #include <memory>
24 #include <vector>
25 #include <SDL.h>
26 #include <SDL_audio.h>
27 #include "AudioMixer.h"
28 #include "exceptions.h"
29 #include "exult_constants.h"
30 #include "Flex.h"
31 #include "Midi.h"
32 
33 namespace Pentagram {
34 	class AudioSample;
35 }
36 struct File_spec;
37 class Game_object;
38 class MyMidiPlayer;
39 class SFX_cached;
40 class Tile_coord;
41 
42 #define MAX_SOUND_FALLOFF	24
43 /*
44  *	Music:
45  */
46 enum Combat_song
47 {
48 	CSBattle_Over,
49 	CSAttacked1,
50 	CSAttacked2,
51 	CSVictory,
52 	CSRun_Away,
53 	CSDanger,
54 	CSHidden_Danger
55 };
56 
57 /*
58  *	This is a resource-management class for SFX. Maybe make it a
59  *	template class and use for other resources also?
60  *	Based on code by Sam Lantinga et al on:
61  *	http://www.ibm.com/developerworks/library/l-pirates2/
62  */
63 class SFX_cache_manager {
64 	using SFX_cached = std::pair<int,Pentagram::AudioSample*>;
65 
66 	std::map<int, SFX_cached> cache;
67 
68 	// Tries to locate a sfx in the cache based on sfx num.
69 	SFX_cached *find_sfx(int id);
70 
71 public:
72 	SFX_cache_manager() = default;
73 	~SFX_cache_manager();
74 	SFX_cache_manager(const SFX_cache_manager&) = default;
75 	SFX_cache_manager(SFX_cache_manager&&) = default;
76 	SFX_cache_manager& operator=(const SFX_cache_manager&) = default;
77 	SFX_cache_manager& operator=(SFX_cache_manager&&) = default;
78 	// For SFX played through 'play_wave_sfx'. Searched cache for
79 	// the sfx first, then loads from the sfx file if needed.
80 	Pentagram::AudioSample *request(Flex *sfx_file, int id);
81 	// Empties the cache.
82 	void flush(Pentagram::AudioMixer *mixer = nullptr);
83 	// Remove unused sounds from the cache.
84 	void garbage_collect();
85 };
86 
87 
88 //---- Audio -----------------------------------------------------------
89 
90 class Audio : nonreplicatable
91 {
92 private:
93 	static	Audio	*self;
94 	static	const int *bg2si_songs;	// Converts BG songs to SI songs.
95 	static	const int *bg2si_sfxs;	// Converts BG sfx's to SI sfx's.
96 	bool truthful_ = false;
97 	bool speech_enabled = true, music_enabled = true, effects_enabled = true, speech_with_subs = false;
98 	bool allow_music_looping;
99 	std::unique_ptr<SFX_cache_manager> sfxs;		// SFX and voice cache manager
100 	bool initialized = false;
101 	SDL_AudioSpec wanted;
102 	std::unique_ptr<Pentagram::AudioMixer> mixer;
103 	bool audio_enabled;
104 	std::unique_ptr<Flex> sfx_file;			// Holds .wav sound effects.
105 	// You never allocate an Audio object directly, you rather access it using get_ptr()
106 	Audio();
107 	~Audio();
108 	void	Init(int _samplerate,int _channels);
109 
110 public:
111 	friend class Tired_of_compiler_warnings;
112 	static void		Init();
113 	static void		Destroy();
114 	static Audio*	get_ptr();
115 
116 	// Given BG sfx, get SI if playing SI.
game_sfx(int sfx)117 	static	int game_sfx(int sfx)
118 		{ return bg2si_sfxs ? bg2si_sfxs[sfx] : sfx; }
119 
120 	// Given BG song, get SI if playing SI.
game_music(int mus)121 	static	int game_music(int mus)
122 		{ return bg2si_songs ? bg2si_songs[mus] : mus; }
123 
124 	void	Init_sfx();
125 
honest_sample_rates()126 	void	honest_sample_rates() { truthful_=true; }
127 	void	cancel_streams();	// Dump any audio streams
128 
129 	void	pause_audio();
130 	void    resume_audio();
131 
132 	void	copy_and_play(const uint8 *sound_data,uint32 len,bool);
133 	void	play(std::unique_ptr<uint8[]> sound_data,uint32 len,bool);
134 	void	playfile(const char *,const char *,bool);
135 	bool	playing();
136 	void	start_music(int num,bool continuous=false,const std::string& flex=MAINMUS);
137 	void	start_music(const std::string& fname,int num,bool continuous=false);
138 	void	start_music_combat(Combat_song song,bool continuous);
139 	void	stop_music();
140 	int		play_sound_effect(int num, int volume = AUDIO_MAX_VOLUME,
141 					int balance = 0, int repeat = 0, int distance=0);
142 	int		play_wave_sfx(int num, int volume = AUDIO_MAX_VOLUME,
143 					int balance = 0, int repeat = 0, int distance=0);
144 	int		play_sound_effect(int num, const Game_object *obj, int volume = AUDIO_MAX_VOLUME, int repeat = 0);
145 	int		play_sound_effect(int num, const Tile_coord &tile, int volume = AUDIO_MAX_VOLUME, int repeat = 0);
146 	// These two do not cache the SFX, and play it directly from the file.
147 	int		play_sound_effect(const File_spec& sfxfile, int num, int volume = AUDIO_MAX_VOLUME,
148 					int balance = 0, int repeat = 0, int distance=0);
149 	int		play_wave_sfx(const File_spec& sfxfile, int num, int volume = AUDIO_MAX_VOLUME,
150 					int balance = 0, int repeat = 0, int distance=0);
151 
152 	static void get_2d_position_for_tile(const Tile_coord &tile, int &distance, int &balance);
153 
154 	int		update_sound_effect(int chan, const Game_object *obj);
155 	int		update_sound_effect(int chan, const Tile_coord &tile);
156 
157 	void	stop_sound_effect(int chan);
158 
159 	void	stop_sound_effects();
160 	bool	start_speech(int num,bool wait=false);
161 	void	stop_speech();
is_speech_enabled()162 	bool	is_speech_enabled() const { return speech_enabled; }
set_speech_enabled(bool ena)163 	void	set_speech_enabled(bool ena) { speech_enabled = ena; }
is_speech_with_subs()164 	bool	is_speech_with_subs() const { return speech_with_subs; }
is_music_enabled()165 	bool	is_music_enabled() const { return music_enabled; }
set_music_enabled(bool ena)166 	void	set_music_enabled(bool ena) { music_enabled = ena; }
are_effects_enabled()167 	bool	are_effects_enabled() const { return effects_enabled; }
set_effects_enabled(bool ena)168 	void	set_effects_enabled(bool ena) { effects_enabled = ena; }
is_audio_enabled()169 	bool	is_audio_enabled() const { return audio_enabled; }
170 	void	set_audio_enabled(bool ena);
is_music_looping_allowed()171 	bool	is_music_looping_allowed() const { return allow_music_looping; }
set_allow_music_looping(bool ena)172 	void	set_allow_music_looping(bool ena) { allow_music_looping = ena; }
173 	static bool	can_sfx(const std::string &file, std::string *out = nullptr);
174 	static bool have_roland_sfx(Exult_Game game, std::string *out = nullptr);
175 	static bool have_sblaster_sfx(Exult_Game game, std::string *out = nullptr);
176 	static bool have_midi_sfx(std::string *out = nullptr);
177 	static bool have_config_sfx(const std::string &game, std::string *out = nullptr);
178 	static void	channel_complete_callback(int chan);
179 
180 	bool	is_track_playing(int num) const;
181 	bool	is_voice_playing() const;
182 
get_sfx_file()183 	Flex *get_sfx_file()
184 		{ return sfx_file.get(); }
get_sfx_cache()185 	SFX_cache_manager *get_sfx_cache() const
186 		{ return sfxs.get(); }
187 
188 	MyMidiPlayer *get_midi() const;
189 };
190 
191 #endif
192