1 /* sdlsfx.c: Creating the program's sound effects.
2 *
3 * Copyright (C) 2001-2006 by Brian Raiter, under the GNU General Public
4 * License. No warranty. See COPYING for details.
5 */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include "SDL.h"
11 #include "sdlgen.h"
12 #include "../err.h"
13 #include "../state.h"
14
15 /* Some generic default settings for the audio output.
16 */
17 #define DEFAULT_SND_FMT AUDIO_S16SYS
18 #define DEFAULT_SND_FREQ 22050
19 #define DEFAULT_SND_CHAN 1
20
21 /* The data needed for each sound effect's wave.
22 */
23 typedef struct sfxinfo {
24 Uint8 *wave; /* the actual wave data */
25 Uint32 len; /* size of the wave data */
26 int pos; /* how much has been played already */
27 int playing; /* is the wave currently playing? */
28 char const *textsfx; /* the onomatopoeia string */
29 } sfxinfo;
30
31 /* The data needed to talk to the sound output device.
32 */
33 static SDL_AudioSpec spec;
34
35 /* All of the sound effects.
36 */
37 static sfxinfo sounds[SND_COUNT];
38
39 /* TRUE if sound-playing has been enabled.
40 */
41 static int enabled = FALSE;
42
43 /* TRUE if the program is currently talking to a sound device.
44 */
45 static int hasaudio = FALSE;
46
47 /* The volume level.
48 */
49 static int volume = SDL_MIX_MAXVOLUME;
50
51 /* The sound buffer size scaling factor.
52 */
53 static int soundbufsize = 0;
54
55
56 /* Initialize the textual sound effects.
57 */
initonomatopoeia(void)58 static void initonomatopoeia(void)
59 {
60 sounds[SND_CHIP_LOSES].textsfx = "\"Bummer\"";
61 sounds[SND_CHIP_WINS].textsfx = "Tadaa!";
62 sounds[SND_TIME_OUT].textsfx = "Clang!";
63 sounds[SND_TIME_LOW].textsfx = "Ktick!";
64 sounds[SND_DEREZZ].textsfx = "Bzont!";
65 sounds[SND_CANT_MOVE].textsfx = "Mnphf!";
66 sounds[SND_IC_COLLECTED].textsfx = "Chack!";
67 sounds[SND_ITEM_COLLECTED].textsfx = "Slurp!";
68 sounds[SND_BOOTS_STOLEN].textsfx = "Flonk!";
69 sounds[SND_TELEPORTING].textsfx = "Bamff!";
70 sounds[SND_DOOR_OPENED].textsfx = "Spang!";
71 sounds[SND_SOCKET_OPENED].textsfx = "Clack!";
72 sounds[SND_BUTTON_PUSHED].textsfx = "Click!";
73 sounds[SND_BOMB_EXPLODES].textsfx = "Booom!";
74 sounds[SND_WATER_SPLASH].textsfx = "Plash!";
75 sounds[SND_TILE_EMPTIED].textsfx = "Whisk!";
76 sounds[SND_WALL_CREATED].textsfx = "Chunk!";
77 sounds[SND_TRAP_ENTERED].textsfx = "Shunk!";
78 sounds[SND_SKATING_TURN].textsfx = "Whing!";
79 sounds[SND_SKATING_FORWARD].textsfx = "Whizz ...";
80 sounds[SND_SLIDING].textsfx = "Drrrr ...";
81 sounds[SND_BLOCK_MOVING].textsfx = "Scrrr ...";
82 sounds[SND_SLIDEWALKING].textsfx = "slurp slurp ...";
83 sounds[SND_ICEWALKING].textsfx = "snick snick ...";
84 sounds[SND_WATERWALKING].textsfx = "plip plip ...";
85 sounds[SND_FIREWALKING].textsfx = "crackle crackle ...";
86 }
87
88 /* Display the onomatopoeia for the currently playing sound effect.
89 * Only the first sound is used, since we can't display multiple
90 * strings.
91 */
displaysoundeffects(unsigned long sfx,int display)92 static void displaysoundeffects(unsigned long sfx, int display)
93 {
94 unsigned long flag;
95 int i;
96
97 if (!display) {
98 setdisplaymsg(NULL, 0, 0);
99 return;
100 }
101
102 for (flag = 1, i = 0 ; flag ; flag <<= 1, ++i) {
103 if (sfx & flag) {
104 setdisplaymsg(sounds[i].textsfx, 500, 10);
105 return;
106 }
107 }
108 }
109
110 /* The callback function that is called by the sound driver to supply
111 * the latest sound effects. All the sound effects are checked, and
112 * the ones that are being played get another chunk of their sound
113 * data mixed into the output buffer. When the end of a sound effect's
114 * wave data is reached, the one-shot sounds are changed to be marked
115 * as not playing, and the continuous sounds are looped.
116 */
sfxcallback(void * data,Uint8 * wave,int len)117 static void sfxcallback(void *data, Uint8 *wave, int len)
118 {
119 int i, n;
120
121 (void)data;
122 memset(wave, spec.silence, len);
123 for (i = 0 ; i < SND_COUNT ; ++i) {
124 if (!sounds[i].wave)
125 continue;
126 if (!sounds[i].playing)
127 if (!sounds[i].pos || i >= SND_ONESHOT_COUNT)
128 continue;
129 n = sounds[i].len - sounds[i].pos;
130 if (n > len) {
131 SDL_MixAudio(wave, sounds[i].wave + sounds[i].pos, len, volume);
132 sounds[i].pos += len;
133 } else {
134 SDL_MixAudio(wave, sounds[i].wave + sounds[i].pos, n, volume);
135 sounds[i].pos = 0;
136 if (i < SND_ONESHOT_COUNT) {
137 sounds[i].playing = FALSE;
138 } else if (sounds[i].playing) {
139 while (len - n >= (int)sounds[i].len) {
140 SDL_MixAudio(wave + n, sounds[i].wave, sounds[i].len,
141 volume);
142 n += sounds[i].len;
143 }
144 sounds[i].pos = len - n;
145 SDL_MixAudio(wave + n, sounds[i].wave, sounds[i].pos, volume);
146 }
147 }
148 }
149 }
150
151 /*
152 * The exported functions.
153 */
154
155 /* Activate or deactivate the sound system. When activating for the
156 * first time, the connection to the sound device is established. When
157 * deactivating, the connection is closed.
158 */
setaudiosystem(int active)159 int setaudiosystem(int active)
160 {
161 SDL_AudioSpec des;
162 int n;
163
164 if (!enabled)
165 return !active;
166
167 if (!active) {
168 if (hasaudio) {
169 SDL_PauseAudio(TRUE);
170 SDL_CloseAudio();
171 hasaudio = FALSE;
172 }
173 return TRUE;
174 }
175
176 if (!SDL_WasInit(SDL_INIT_AUDIO)) {
177 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
178 warn("Cannot initialize audio output: %s", SDL_GetError());
179 return FALSE;
180 }
181 }
182
183 if (hasaudio)
184 return TRUE;
185
186 des.freq = DEFAULT_SND_FREQ;
187 des.format = DEFAULT_SND_FMT;
188 des.channels = DEFAULT_SND_CHAN;
189 des.callback = sfxcallback;
190 des.userdata = NULL;
191 for (n = 1 ; n <= des.freq / TICKS_PER_SECOND ; n <<= 1) ;
192 des.samples = (n << soundbufsize) >> 2;
193 if (SDL_OpenAudio(&des, &spec) < 0) {
194 warn("can't access audio output: %s", SDL_GetError());
195 return FALSE;
196 }
197 hasaudio = TRUE;
198 SDL_PauseAudio(FALSE);
199
200 return TRUE;
201 }
202
203 /* Load a single wave file into memory. The wave data is converted to
204 * the format expected by the sound device.
205 */
loadsfxfromfile(int index,char const * filename)206 int loadsfxfromfile(int index, char const *filename)
207 {
208 SDL_AudioSpec specin;
209 SDL_AudioCVT convert;
210 Uint8 *wavein;
211 Uint8 *wavecvt;
212 Uint32 lengthin;
213
214 if (!filename) {
215 freesfx(index);
216 return TRUE;
217 }
218
219 if (!enabled)
220 return FALSE;
221 if (!hasaudio)
222 if (!setaudiosystem(TRUE))
223 return FALSE;
224
225 if (!SDL_LoadWAV(filename, &specin, &wavein, &lengthin)) {
226 warn("can't load %s: %s", filename, SDL_GetError());
227 return FALSE;
228 }
229
230 if (SDL_BuildAudioCVT(&convert,
231 specin.format, specin.channels, specin.freq,
232 spec.format, spec.channels, spec.freq) < 0) {
233 warn("can't create converter for %s: %s", filename, SDL_GetError());
234 return FALSE;
235 }
236 if (!(wavecvt = malloc(lengthin * convert.len_mult)))
237 memerrexit();
238 memcpy(wavecvt, wavein, lengthin);
239 SDL_FreeWAV(wavein);
240 convert.buf = wavecvt;
241 convert.len = lengthin;
242 if (SDL_ConvertAudio(&convert) < 0) {
243 warn("can't convert %s: %s", filename, SDL_GetError());
244 return FALSE;
245 }
246
247 freesfx(index);
248 SDL_LockAudio();
249 sounds[index].wave = convert.buf;
250 sounds[index].len = convert.len * convert.len_ratio;
251 sounds[index].pos = 0;
252 sounds[index].playing = FALSE;
253 SDL_UnlockAudio();
254
255 return TRUE;
256 }
257
258 /* Select the sounds effects to be played. sfx is a bitmask of sound
259 * effect indexes. Any continuous sounds that are not included in sfx
260 * are stopped. One-shot sounds that are included in sfx are
261 * restarted.
262 */
playsoundeffects(unsigned long sfx)263 void playsoundeffects(unsigned long sfx)
264 {
265 unsigned long flag;
266 int i;
267
268 if (!hasaudio || !volume) {
269 displaysoundeffects(sfx, TRUE);
270 return;
271 }
272
273 SDL_LockAudio();
274 for (i = 0, flag = 1 ; i < SND_COUNT ; ++i, flag <<= 1) {
275 if (sfx & flag) {
276 sounds[i].playing = TRUE;
277 if (sounds[i].pos && i < SND_ONESHOT_COUNT)
278 sounds[i].pos = 0;
279 } else {
280 if (i >= SND_ONESHOT_COUNT)
281 sounds[i].playing = FALSE;
282 }
283 }
284 SDL_UnlockAudio();
285 }
286
287 /* If action is negative, stop playing all sounds immediately.
288 * Otherwise, just temporarily pause or unpause sound-playing.
289 */
setsoundeffects(int action)290 void setsoundeffects(int action)
291 {
292 int i;
293
294 if (!hasaudio || !volume)
295 return;
296
297 if (action < 0) {
298 SDL_LockAudio();
299 for (i = 0 ; i < SND_COUNT ; ++i) {
300 sounds[i].playing = FALSE;
301 sounds[i].pos = 0;
302 }
303 SDL_UnlockAudio();
304 } else {
305 SDL_PauseAudio(!action);
306 }
307 }
308
309 /* Release all memory for the given sound effect.
310 */
freesfx(int index)311 void freesfx(int index)
312 {
313 if (sounds[index].wave) {
314 SDL_LockAudio();
315 free(sounds[index].wave);
316 sounds[index].wave = NULL;
317 sounds[index].pos = 0;
318 sounds[index].playing = FALSE;
319 SDL_UnlockAudio();
320 }
321 }
322
323 /* Set the current volume level to v. If display is true, the
324 * new volume level is displayed to the user.
325 */
setvolume(int v,int display)326 int setvolume(int v, int display)
327 {
328 char buf[16];
329
330 if (!hasaudio)
331 return FALSE;
332 if (v < 0)
333 v = 0;
334 else if (v > 10)
335 v = 10;
336 if (!volume && v)
337 displaysoundeffects(0, FALSE);
338 volume = (SDL_MIX_MAXVOLUME * v + 9) / 10;
339 if (display) {
340 sprintf(buf, "Volume: %d", v);
341 setdisplaymsg(buf, 1000, 1000);
342 }
343 return TRUE;
344 }
345
346 /* Change the current volume level by delta. If display is true, the
347 * new volume level is displayed to the user.
348 */
changevolume(int delta,int display)349 int changevolume(int delta, int display)
350 {
351 return setvolume(((10 * volume) / SDL_MIX_MAXVOLUME) + delta, display);
352 }
353
354 /* Shut down the sound system.
355 */
shutdown(void)356 static void shutdown(void)
357 {
358 setaudiosystem(FALSE);
359 if (SDL_WasInit(SDL_INIT_AUDIO))
360 SDL_QuitSubSystem(SDL_INIT_AUDIO);
361 hasaudio = FALSE;
362 }
363
364 /* Initialize the module. If silence is TRUE, then the program will
365 * leave sound output disabled.
366 */
_sdlsfxinitialize(int silence,int _soundbufsize)367 int _sdlsfxinitialize(int silence, int _soundbufsize)
368 {
369 atexit(shutdown);
370 enabled = !silence;
371 soundbufsize = _soundbufsize;
372 initonomatopoeia();
373 if (enabled)
374 setaudiosystem(TRUE);
375 return TRUE;
376 }
377