1 /* --------------------------------------------------------------------
2 EXTREME TUXRACER
3 
4 Copyright (C) 2010 Extreme Tux Racer Team
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 ---------------------------------------------------------------------*/
16 
17 #ifdef HAVE_CONFIG_H
18 #include <etr_config.h>
19 #endif
20 
21 #include <SFML/Audio.hpp>
22 
23 #include "audio.h"
24 #include "spx.h"
25 
26 // the global instances of the 2 audio classes
27 CSound Sound;
28 CMusic Music;
29 #define MIX_MAX_VOLUME 100
30 
31 struct TSound {
32 	sf::SoundBuffer data;
33 	sf::Sound player;
TSoundTSound34 	explicit TSound(int volume) {
35 		setVolume(volume);
36 	}
setVolumeTSound37 	void setVolume(int volume) {
38 		player.setVolume(volume);
39 	}
40 
PlayTSound41 	void Play(bool loop) {
42 		if (player.getStatus() == sf::Sound::Playing) return;
43 		player.setLoop(loop);
44 		player.play();
45 	}
46 };
47 
48 // --------------------------------------------------------------------
49 //				class CSound
50 // --------------------------------------------------------------------
51 
~CSound()52 CSound::~CSound() {
53 	FreeSounds();
54 }
55 
LoadChunk(const std::string & name,const std::string & filename)56 bool CSound::LoadChunk(const std::string& name, const std::string& filename) {
57 	sounds.emplace_back(new TSound(param.sound_volume));
58 	if (!sounds.back()->data.loadFromFile(filename)) // Try loading sound buffer
59 		return false;
60 	sounds.back()->player.setBuffer(sounds.back()->data);
61 	SoundIndex[name] = sounds.size()-1;
62 	return true;
63 }
64 
65 // Load all soundfiles listed in "/sounds/sounds.lst"
LoadSoundList()66 void CSound::LoadSoundList() {
67 	CSPList list;
68 	if (list.Load(param.sounds_dir, "sounds.lst")) {
69 		for (CSPList::const_iterator line = list.cbegin(); line != list.cend(); ++line) {
70 			std::string name = SPStrN(*line, "name");
71 			std::string soundfile = SPStrN(*line, "file");
72 			std::string path = MakePathStr(param.sounds_dir, soundfile);
73 			LoadChunk(name, path);
74 		}
75 	}
76 }
77 
FreeSounds()78 void CSound::FreeSounds() {
79 	HaltAll();
80 	for (std::size_t i = 0; i < sounds.size(); i++)
81 		delete sounds[i];
82 	sounds.clear();
83 	SoundIndex.clear();
84 }
85 
GetSoundIdx(const std::string & name) const86 std::size_t CSound::GetSoundIdx(const std::string& name) const {
87 	try {
88 		return SoundIndex.at(name);
89 	} catch (...) {
90 		return -1;
91 	}
92 }
93 
SetVolume(std::size_t soundid,int volume)94 void CSound::SetVolume(std::size_t soundid, int volume) {
95 	if (soundid >= sounds.size()) return;
96 
97 	volume = clamp(0, volume, MIX_MAX_VOLUME);
98 	sounds[soundid]->setVolume(volume);
99 }
100 
SetVolume(const std::string & name,int volume)101 void CSound::SetVolume(const std::string& name, int volume) {
102 	SetVolume(GetSoundIdx(name), volume);
103 }
104 
105 // ------------------- play -------------------------------------------
106 
Play(std::size_t soundid,bool loop)107 void CSound::Play(std::size_t soundid, bool loop) {
108 	if (soundid >= sounds.size()) return;
109 
110 	sounds[soundid]->Play(loop);
111 }
112 
Play(const std::string & name,bool loop)113 void CSound::Play(const std::string& name, bool loop) {
114 	Play(GetSoundIdx(name), loop);
115 }
116 
Play(std::size_t soundid,bool loop,int volume)117 void CSound::Play(std::size_t soundid, bool loop, int volume) {
118 	if (soundid >= sounds.size()) return;
119 
120 	volume = clamp(0, volume, MIX_MAX_VOLUME);
121 	sounds[soundid]->setVolume(volume);
122 	sounds[soundid]->Play(loop);
123 }
124 
Play(const std::string & name,bool loop,int volume)125 void CSound::Play(const std::string& name, bool loop, int volume) {
126 	Play(GetSoundIdx(name), loop, volume);
127 }
128 
Halt(std::size_t soundid)129 void CSound::Halt(std::size_t soundid) {
130 	if (soundid >= sounds.size()) return;
131 
132 	// loop_count must be -1 (endless loop) for halt
133 	if (sounds[soundid]->player.getLoop())
134 		sounds[soundid]->player.stop();
135 }
136 
Halt(const std::string & name)137 void CSound::Halt(const std::string& name) {
138 	Halt(GetSoundIdx(name));
139 }
140 
HaltAll()141 void CSound::HaltAll() {
142 	for (std::size_t i = 0; i < sounds.size(); i++) {
143 		sounds[i]->player.stop();
144 	}
145 }
146 
147 // --------------------------------------------------------------------
148 //				class CMusic
149 // --------------------------------------------------------------------
150 
CMusic()151 CMusic::CMusic() {
152 	curr_music = 0;
153 	curr_volume = 10;
154 }
~CMusic()155 CMusic::~CMusic() {
156 	FreeMusics();
157 }
158 
LoadPiece(const std::string & name,const std::string & filename)159 bool CMusic::LoadPiece(const std::string& name, const std::string& filename) {
160 	sf::Music* m = new sf::Music();
161 	if (!m->openFromFile(filename)) {
162 		Message("could not load music", filename);
163 		return false;
164 	}
165 	MusicIndex[name] = musics.size();
166 	musics.push_back(m);
167 	return true;
168 }
169 
LoadMusicList()170 void CMusic::LoadMusicList() {
171 	// --- music ---
172 	CSPList list;
173 	if (list.Load(param.music_dir, "music.lst")) {
174 		musics.reserve(list.size());
175 		for (CSPList::const_iterator line = list.cbegin(); line != list.cend(); ++line) {
176 			std::string name = SPStrN(*line, "name");
177 			std::string musicfile = SPStrN(*line, "file");
178 			std::string path = MakePathStr(param.music_dir, musicfile);
179 			LoadPiece(name, path);
180 		}
181 	} else {
182 		Message("could not load music.lst");
183 		return;
184 	}
185 
186 	// --- racing themes ---
187 	list.clear();
188 	ThemesIndex.clear();
189 	if (list.Load(param.music_dir, "racing_themes.lst")) {
190 		themes.resize(list.size());
191 		std::size_t i = 0;
192 		for (CSPList::const_iterator line = list.cbegin(); line != list.cend(); ++line, i++) {
193 			std::string name = SPStrN(*line, "name");
194 			ThemesIndex[name] = i;
195 			std::string item = SPStrN(*line, "race", "race_1");
196 			themes[i].situation[0] = musics[MusicIndex[item]];
197 			item = SPStrN(*line, "wonrace", "wonrace_1");
198 			themes[i].situation[1] = musics[MusicIndex[item]];
199 			item = SPStrN(*line, "lostrace", "lostrace_1");
200 			themes[i].situation[2] = musics[MusicIndex[item]];
201 		}
202 	} else Message("could not load racing_themes.lst");
203 }
204 
FreeMusics()205 void CMusic::FreeMusics() {
206 	Halt();
207 	for (std::size_t i = 0; i < musics.size(); i++)
208 		delete musics[i];
209 	musics.clear();
210 	MusicIndex.clear();
211 
212 	themes.clear();
213 	ThemesIndex.clear();
214 
215 	curr_music = nullptr;
216 }
217 
GetMusicIdx(const std::string & name) const218 std::size_t CMusic::GetMusicIdx(const std::string& name) const {
219 	try {
220 		return MusicIndex.at(name);
221 	} catch (...) {
222 		return -1;
223 	}
224 }
225 
GetThemeIdx(const std::string & theme) const226 std::size_t CMusic::GetThemeIdx(const std::string& theme) const {
227 	try {
228 		return ThemesIndex.at(theme);
229 	} catch (...) {
230 		return -1;
231 	}
232 }
233 
SetVolume(int volume)234 void CMusic::SetVolume(int volume) {
235 	volume = clamp(0, volume, MIX_MAX_VOLUME);
236 	if (curr_music)
237 		curr_music->setVolume(volume);
238 	curr_volume = volume;
239 }
240 
Play(sf::Music * music,bool loop,int volume)241 bool CMusic::Play(sf::Music* music, bool loop, int volume) {
242 	if (!music)
243 		return false;
244 
245 	volume = clamp(0, volume, MIX_MAX_VOLUME);
246 	if (music != curr_music) {
247 		music->setVolume(volume);
248 		music->setLoop(loop);
249 		if (curr_music)
250 			curr_music->stop();
251 		curr_music = music;
252 		music->play();
253 	}
254 	return true;
255 }
256 
Play(std::size_t musid,bool loop)257 bool CMusic::Play(std::size_t musid, bool loop) {
258 	if (musid >= musics.size()) return false;
259 	sf::Music* music = musics[musid];
260 	return Play(music, loop, curr_volume);
261 }
262 
Play(const std::string & name,bool loop)263 bool CMusic::Play(const std::string& name, bool loop) {
264 	return Play(GetMusicIdx(name), loop);
265 }
266 
Play(std::size_t musid,bool loop,int volume)267 bool CMusic::Play(std::size_t musid, bool loop, int volume) {
268 	if (musid >= musics.size()) return false;
269 	sf::Music* music = musics[musid];
270 	return Play(music, loop, volume);
271 }
272 
Play(const std::string & name,bool loop,int volume)273 bool CMusic::Play(const std::string& name, bool loop, int volume) {
274 	return Play(GetMusicIdx(name), loop, volume);
275 }
276 
PlayTheme(std::size_t theme,ESituation situation)277 bool CMusic::PlayTheme(std::size_t theme, ESituation situation) {
278 	if (theme >= themes.size()) return false;
279 	if (situation >= SITUATION_COUNT) return false;
280 	sf::Music* music = themes[theme].situation[situation];
281 	return Play(music, true, curr_volume);
282 }
283 
Halt()284 void CMusic::Halt() {
285 	if (curr_music) {
286 		curr_music->stop();
287 		curr_music = nullptr;
288 	}
289 }
290