1 /*
2 Copyright (C) 2005-2007 Tom Beaumont
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 DISABLE_SOUND
20 #include <list>
21 #include <vector>
22 #include <ctime>
23 #include <SDL_mixer.h>
24 #include "sfx.h"
25 #include "system-directory.h"
26
27 #define SOUND_START_DELAY -0.2
28 #define MUSIC_VOLUME 0.75
29
30 static const char* const music_names[HHOP_MUSIC_MAX] =
31 {
32 "music-ending",
33 "music-game",
34 // "music-map",
35 // "music-title",
36 // "music-win"
37 };
38 static const char* const sound_names[HHOP_SOUND_MAX] =
39 {
40 "sound-builder",
41 "sound-collapse",
42 "sound-crack",
43 "sound-death",
44 "sound-disintegrate",
45 "sound-explode-big",
46 "sound-explode-small",
47 "sound-floater-enter",
48 "sound-floater-move",
49 "sound-found-antiice",
50 "sound-found-jump",
51 "sound-ice",
52 "sound-laser",
53 "sound-lift-up",
54 "sound-lift-down",
55 "sound-spinner",
56 "sound-step",
57 "sound-trampoline",
58 "sound-ui-fade",
59 "sound-ui-menu",
60 "sound-used-antiice",
61 "sound-used-jump",
62 "sound-win"
63 };
64
65 /* We store delayed sound effects in a queue and play them back when the
66 time is right. It's sort of ugly but makes creating samples easier. */
67 class SoundQueue
68 {
69 public:
SoundQueue(int ty,double ti)70 SoundQueue(int ty, double ti) : type(ty), time(ti) { }
71 public:
72 int type;
73 double time;
74 };
75
76 class SoundEngine
77 {
78 public:
SoundEngine(const char * path)79 SoundEngine(const char* path) : disable_music(0), disable_effects(0), music_curr(-1), music_next(-1)
80 {
81 int i;
82 int j;
83 char* pth;
84 const char* name;
85 lisysDir* dir;
86
87 srand (time (NULL));
88
89 // Open data directory.
90 dir = lisys_dir_open (path);
91 if (dir == NULL)
92 return;
93 lisys_dir_set_filter (dir, LISYS_DIR_FILTER_FILES);
94 if (!lisys_dir_scan (dir))
95 {
96 lisys_dir_free (dir);
97 return;
98 }
99
100 // Scan for sound and music.
101 for (i = 0 ; i < lisys_dir_get_count (dir) ; i++)
102 {
103 name = lisys_dir_get_name (dir, i);
104 pth = lisys_dir_get_path (dir, i);
105 for (j = 0 ; j < HHOP_MUSIC_MAX ; j++)
106 {
107 if (strstr (name, music_names[j]) == name)
108 {
109 Mix_Music* music = Mix_LoadMUS(pth);
110 if (music)
111 music_chunks[j].push_back(music);
112 else
113 fprintf(stderr, "Cannot load music `%s': %s\n", name, Mix_GetError());
114 }
115 }
116 for (j = 0 ; j < HHOP_SOUND_MAX ; j++)
117 {
118 if (strstr (name, sound_names[j]) == name)
119 {
120 Mix_Chunk* sound = Mix_LoadWAV(pth);
121 if (sound)
122 sound_chunks[j].push_back(sound);
123 else
124 fprintf(stderr, "Cannot load effect `%s': %s\n", name, Mix_GetError());
125 }
126 }
127 free (pth);
128 }
129
130 lisys_dir_free (dir);
131 }
~SoundEngine()132 ~SoundEngine()
133 {
134 int j;
135
136 for (j = 0 ; j < HHOP_MUSIC_MAX ; j++)
137 {
138 std::vector<Mix_Music*>::iterator i;
139 for (i = music_chunks[j].begin() ; i != music_chunks[j].end() ; i++)
140 Mix_FreeMusic (*i);
141 }
142 for (j = 0 ; j < HHOP_SOUND_MAX ; j++)
143 {
144 std::vector<Mix_Chunk*>::iterator i;
145 for (i = sound_chunks[j].begin() ; i != sound_chunks[j].end() ; i++)
146 Mix_FreeChunk (*i);
147 }
148 }
PlayMusic(int type)149 void PlayMusic(int type)
150 {
151 if (disable_music)
152 return;
153 int size = music_chunks[type].size();
154 if (size)
155 {
156 int music = rand () % size;
157 Mix_FadeInMusic(music_chunks[type][music], 1, HHOP_FADE_MUSIC_IN);
158 }
159 }
PlaySound(int type)160 void PlaySound(int type)
161 {
162 if (disable_effects)
163 return;
164 int size = sound_chunks[type].size();
165 if (size)
166 {
167 int sound = rand() % size;
168 Mix_PlayChannel(-1, sound_chunks[type][sound], 0);
169 }
170 }
QueueMusic(int type)171 void QueueMusic(int type)
172 {
173 if (disable_music)
174 return;
175 if (music_curr != type || music_next != type)
176 {
177 music_curr = -2;
178 music_next = type;
179 Mix_FadeOutMusic(HHOP_FADE_MUSIC_OUT);
180 }
181 }
QueueSound(int type,double time)182 void QueueSound(int type, double time)
183 {
184 if (disable_effects)
185 return;
186 sound_queue.push_back(SoundQueue(type, time));
187 }
ToggleEffects()188 void ToggleEffects()
189 {
190 disable_effects = !disable_effects;
191 if (disable_effects)
192 {
193 Mix_HaltChannel(-1);
194 sound_queue.clear();
195 }
196 }
ToggleMusic()197 void ToggleMusic()
198 {
199 disable_music = !disable_music;
200 if (disable_music)
201 Mix_HaltMusic();
202 }
UndoQueue()203 void UndoQueue()
204 {
205 sound_queue.clear();
206 }
Update(double time)207 void Update(double time)
208 {
209 while (true)
210 {
211 std::list<SoundQueue>::iterator i;
212
213 // Find the first effect that needs playing.
214 for (i = sound_queue.begin() ; i != sound_queue.end() ; i++)
215 {
216 if (time >= i->time)
217 {
218 PlaySound(i->type);
219 break;
220 }
221 }
222
223 // Erase the effect or stop if not found.
224 if (i != sound_queue.end())
225 sound_queue.erase(i);
226 else
227 break;
228 }
229 if (!disable_music && !Mix_PlayingMusic())
230 {
231 PlayMusic(music_next);
232 music_curr = music_next;
233 }
234 }
235 public:
236 int disable_music;
237 int disable_effects;
238 int music_curr;
239 int music_next;
240 std::vector<Mix_Music*> music_chunks[HHOP_MUSIC_MAX];
241 std::vector<Mix_Chunk*> sound_chunks[HHOP_SOUND_MAX];
242 std::list<SoundQueue> sound_queue;
243 };
244
245 static SoundEngine* sound_engine;
246 #endif
247
InitSound(const char * path)248 void InitSound(const char* path)
249 {
250 #ifndef DISABLE_SOUND
251 SDL_InitSubSystem(SDL_INIT_AUDIO);
252 if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) == -1)
253 {
254 fprintf(stderr, "Initializing audio failed: %s\n", Mix_GetError());
255 exit(1);
256 }
257 Mix_AllocateChannels(HHOP_EFFECT_CHANNELS);
258 Mix_VolumeMusic((int)(MIX_MAX_VOLUME*MUSIC_VOLUME));
259
260 sound_engine = new SoundEngine(path);
261 #endif
262 }
263
FreeSound()264 void FreeSound()
265 {
266 #ifndef DISABLE_SOUND
267 delete sound_engine;
268 Mix_CloseAudio();
269 #endif
270 }
271
PlayMusic(int type)272 void PlayMusic(int type)
273 {
274 #ifndef DISABLE_SOUND
275 sound_engine->QueueMusic(type);
276 #endif
277 }
278
PlaySound(int type)279 void PlaySound(int type)
280 {
281 #ifndef DISABLE_SOUND
282 sound_engine->PlaySound(type);
283 #endif
284 }
285
QueueSound(int type,double time)286 void QueueSound(int type, double time)
287 {
288 #ifndef DISABLE_SOUND
289 sound_engine->QueueSound(type, time + SOUND_START_DELAY);
290 #endif
291 }
292
ToggleMusic()293 void ToggleMusic()
294 {
295 #ifndef DISABLE_SOUND
296 sound_engine->ToggleMusic();
297 #endif
298 }
299
ToggleEffects()300 void ToggleEffects()
301 {
302 #ifndef DISABLE_SOUND
303 sound_engine->ToggleEffects();
304 #endif
305 }
306
UndoSound()307 void UndoSound()
308 {
309 #ifndef DISABLE_SOUND
310 sound_engine->UndoQueue();
311 #endif
312 }
313
UpdateSound(double time)314 void UpdateSound(double time)
315 {
316 #ifndef DISABLE_SOUND
317 sound_engine->Update(time);
318 #endif
319 }
320