1 /*
2 * Alizarin Tetris
3 * The sound interface definition file.
4 *
5 * Copyright 2000, Kiri Wagstaff & Westley Weimer
6 */
7
8 #include <config.h>
9 #include <ctype.h>
10 #include <unistd.h>
11
12 /* configure magic for dirent */
13 #if HAVE_DIRENT_H
14 # include <dirent.h>
15 # define NAMLEN(dirent) strlen((dirent)->d_name)
16 #else
17 # define dirent direct
18 # define NAMLEN(dirent) (dirent)->d_namlen
19 # if HAVE_SYS_NDIR_H
20 # include <sys/ndir.h>
21 # endif
22 # if HAVE_SYS_DIR_H
23 # include <sys/dir.h>
24 # endif
25 # if HAVE_NDIR_H
26 # include <ndir.h>
27 # endif
28 #endif
29
30 #include "atris.h"
31 #include "sound.h"
32
33 samples_to_be_played current; /* what should we play now? */
34
35 char *sound_name[NUM_SOUND] = { /* english names */
36 "thud", "clear1", "clear4", "levelup", "leveldown" , "garbage1", "clock"
37 };
38
39 /***************************************************************************
40 * fill_audio()
41 ***************************************************************************/
42 static void
fill_audio(void * udata,Uint8 * stream,int len)43 fill_audio(void *udata, Uint8 *stream, int len)
44 {
45 int i;
46
47 for (i=0; i<MAX_MIXED_SAMPLES; i++) /* for each possible sample to be mixed */
48 if (current.sample[i].in_use) {
49 if (current.sample[i].delay >= len) /* keep pausing! */
50 current.sample[i].delay -= len;
51 else if (current.sample[i].delay > 0 &&
52 current.sample[i].delay < len) {
53 /* pause a bit! */
54 int diff = len - current.sample[i].delay;
55 if (diff + current.sample[i].pos >= current.sample[i].len) {
56 diff = current.sample[i].len - current.sample[i].pos;
57 current.sample[i].in_use = 0;
58 }
59 current.sample[i].delay = 0;
60 SDL_MixAudio(stream+current.sample[i].delay,
61 current.sample[i].audio_data, diff, SDL_MIX_MAXVOLUME);
62 current.sample[i].audio_data += diff;
63 current.sample[i].pos += diff;
64 } else {
65 /* we can play it now */
66 int to_play = len;
67 Assert(current.sample[i].delay == 0);
68 if (to_play + current.sample[i].pos >= current.sample[i].len) {
69 to_play = current.sample[i].len - current.sample[i].pos;
70 current.sample[i].in_use = 0;
71 }
72 SDL_MixAudio(stream, current.sample[i].audio_data,
73 to_play, SDL_MIX_MAXVOLUME);
74 current.sample[i].audio_data += to_play;
75 current.sample[i].pos += to_play;
76 }
77 } /* end: if in_use */
78 return;
79 }
80
81 /***************************************************************************
82 * play_sound_unless_already_playing()
83 * Schedules a sound to be played (unless it is already playing). "delay"
84 * is in bytes (sigh!) at 11025 Hz, unsigned 8-bit samples, mono.
85 *********************************************************************PROTO*/
86 void
play_sound_unless_already_playing(sound_style * ss,int which,int delay)87 play_sound_unless_already_playing(sound_style *ss, int which, int delay)
88 {
89 int i;
90
91 if (ss->WAV[which].audio_len == 0) {
92 if (strcmp(ss->name,"No Sound"))
93 Debug("No [%s] sound in Sound Style [%s]\n",
94 sound_name[which], ss->name);
95 return;
96 }
97 /* are we already playing? */
98 for (i=0; i< MAX_MIXED_SAMPLES; i++)
99 if (current.sample[i].in_use != 0 &&
100 !strcmp(current.sample[i].filename, ss->WAV[which].filename))
101 /* alreadying playing */
102 return;
103
104 /* find an empty slot */
105 for (i=0; i< MAX_MIXED_SAMPLES; i++) {
106 if (current.sample[i].in_use == 0) {
107 /* found it! */
108 SDL_LockAudio();
109 current.sample[i].in_use = 1;
110 current.sample[i].delay = delay;
111 current.sample[i].len = ss->WAV[which].audio_len;
112 current.sample[i].pos = 0;
113 current.sample[i].audio_data = ss->WAV[which].audio_buf;
114 current.sample[i].filename = ss->WAV[which].filename;
115 SDL_UnlockAudio();
116 /*
117 Debug("Scheduled sound %d with delay %d\n",which,delay);
118 */
119 return;
120 }
121 }
122 Debug("No room in the mixer for sound %d\n",which);
123 return;
124 }
125
126 /***************************************************************************
127 * stop_playing_sound()
128 * Stops all occurences of the given sound from playing.
129 *********************************************************************PROTO*/
130 void
stop_playing_sound(sound_style * ss,int which)131 stop_playing_sound(sound_style *ss, int which)
132 {
133 int i;
134 /* are we already playing? */
135 for (i=0; i< MAX_MIXED_SAMPLES; i++)
136 if (current.sample[i].in_use != 0 &&
137 !strcmp(current.sample[i].filename, ss->WAV[which].filename)) {
138 /* alreadying playing that sound */
139 current.sample[i].in_use = 0; /* turn it off */
140 }
141 return;
142
143 }
144
145 /***************************************************************************
146 * play_sound()
147 * Schedules a sound to be played. "delay" is in bytes (sigh!) at 11025 Hz,
148 * unsigned 8-bit samples, mono.
149 *********************************************************************PROTO*/
150 void
play_sound(sound_style * ss,int which,int delay)151 play_sound(sound_style *ss, int which, int delay)
152 {
153 int i;
154
155 if (ss->WAV[which].audio_len == 0) {
156 if (strcmp(ss->name,"No Sound"))
157 Debug("No [%s] sound in Sound Style [%s]\n",
158 sound_name[which], ss->name);
159 return;
160 }
161
162 /* find an empty slot */
163 for (i=0; i< MAX_MIXED_SAMPLES; i++) {
164 if (current.sample[i].in_use == 0) {
165 /* found it! */
166 SDL_LockAudio();
167 current.sample[i].in_use = 1;
168 current.sample[i].delay = delay;
169 current.sample[i].len = ss->WAV[which].audio_len;
170 current.sample[i].pos = 0;
171 current.sample[i].audio_data = ss->WAV[which].audio_buf;
172 current.sample[i].filename = ss->WAV[which].filename;
173 SDL_UnlockAudio();
174 /*
175 Debug("Scheduled sound %d with delay %d\n",which,delay);
176 */
177 return;
178 }
179 }
180 Debug("No room in the mixer for sound %d\n",which);
181 return;
182 }
183
184 /***************************************************************************
185 * stop_all_playing()
186 * Schedule all of the sounds associated with a given style to be played.
187 *********************************************************************PROTO*/
188 void
stop_all_playing(void)189 stop_all_playing(void)
190 {
191 int i;
192 for (i=0; i<MAX_MIXED_SAMPLES; i++) /* for each possible sample to be mixed */
193 current.sample[i].in_use = 0;
194 return;
195 }
196
197 /***************************************************************************
198 * play_all_sounds()
199 * Schedule all of the sounds associated with a given style to be played.
200 *********************************************************************PROTO*/
201 void
play_all_sounds(sound_style * ss)202 play_all_sounds(sound_style *ss)
203 {
204 int i;
205 int delay = 0;
206
207 for (i=0; i<NUM_SOUND; i++) {
208 if (i == SOUND_CLOCK) continue;
209 play_sound(ss, i, delay);
210 delay += ss->WAV[i].audio_len + 6144;
211 }
212 }
213
214 /***************************************************************************
215 * load_sound_style()
216 * Parse a sound config file.
217 ***************************************************************************/
218 static sound_style *
load_sound_style(const char * filename)219 load_sound_style(const char *filename)
220 {
221 sound_style *retval;
222 char buf[2048];
223
224 FILE *fin = fopen(filename,"rt");
225 int ok;
226 int count = 0;
227
228 if (!fin) {
229 Debug("fopen(%s)\n",filename);
230 return NULL;
231 }
232 Calloc(retval,sound_style *,sizeof(sound_style));
233
234 /* find the name */
235 fgets(buf,sizeof(buf),fin);
236 if (strchr(buf,'\n'))
237 *(strchr(buf,'\n')) = 0;
238 retval->name = strdup(buf);
239
240 while (!(feof(fin))) {
241 int i;
242 do {
243 fgets(buf,sizeof(buf),fin);
244 } while (!feof(fin) &&
245 (buf[0] == '\n' || buf[0] == '#'));
246 if (feof(fin)) break;
247 if (strchr(buf,'\n'))
248 *(strchr(buf,'\n')) = 0;
249
250 ok = 0;
251
252 for (i=0; i<NUM_SOUND; i++)
253 if (!strncasecmp(buf,sound_name[i],strlen(sound_name[i]))) {
254 char *p = strchr(buf,' ');
255 if (!p) {
256 Debug("malformed input line [%s] in [%s]\n",
257 buf,filename);
258 return NULL;
259 }
260 p++;
261 if (!(SDL_LoadWAV(p,&(retval->WAV[i].spec),
262 &(retval->WAV[i].audio_buf),
263 &(retval->WAV[i].audio_len)))) {
264 PANIC("Couldn't open %s [%s] in [%s]: %s",
265 sound_name[i], p, filename, SDL_GetError());
266 }
267 retval->WAV[i].filename = strdup(filename);
268 count++;
269 ok = 1;
270 }
271 if (!ok) {
272 Debug("unknown sound name [%s] in [%s]\nvalid names:",
273 buf, filename);
274 for (i=0;i<NUM_SOUND;i++)
275 printf(" %s",sound_name[i]);
276 printf("\n");
277 return NULL;
278 }
279 }
280
281 Debug("Sound Style [%s] loaded (%d/%d sounds).\n",retval->name,
282 count, NUM_SOUND);
283
284 return retval;
285 }
286
287 /***************************************************************************
288 * sound_Select()
289 * Returns 1 if the file pointed to ends with ".Sound"
290 * Used by scandir() to grab all the *.Sound files from a directory.
291 ***************************************************************************/
292 static int
sound_Select(const struct dirent * d)293 sound_Select(const struct dirent *d)
294 {
295 if (strstr(d->d_name,".Sound") &&
296 (signed)strlen(d->d_name) ==
297 (strstr(d->d_name,".Sound") - d->d_name + 6))
298 return 1;
299 else
300 return 0;
301 }
302
303 /***************************************************************************
304 * load_sound_styles()
305 * Load all sound styles and start the sound driver. If sound is not
306 * wanted, return a dummy style.
307 *********************************************************************PROTO*/
308 sound_styles
load_sound_styles(int sound_wanted)309 load_sound_styles(int sound_wanted)
310 {
311 sound_styles retval;
312 SDL_AudioSpec wanted;
313 DIR *my_dir;
314 int i = 0;
315
316 memset(&retval,0,sizeof(retval));
317
318 /* do we even want sound? */
319 if (!sound_wanted) goto nosound;
320
321 if (SDL_InitSubSystem(SDL_INIT_AUDIO)) {
322 Debug("Couldn't initialize audio subsystem: %s\n",SDL_GetError());
323 goto nosound;
324 }
325
326 memset(¤t,0,sizeof(current)); /* clear memory */
327
328 wanted.freq = 11025;
329 wanted.format = AUDIO_U8;
330 wanted.channels = 1;
331 wanted.samples = 512; /* good low-latency value for callback */
332 wanted.callback = fill_audio;
333 wanted.userdata = NULL;
334 /* Open the audio device, forcing the desired format */
335 if (SDL_OpenAudio(&wanted, NULL) < 0) {
336 Debug("Couldn't open audio: %s\n",SDL_GetError());
337 goto nosound;
338 }
339
340 my_dir = opendir("sounds");
341 if (my_dir) {
342 while (1) {
343 struct dirent *this_file = readdir(my_dir);
344 if (!this_file) break;
345 if (sound_Select(this_file))
346 i++;
347 }
348 closedir(my_dir);
349 } else {
350 Debug("Cannot read directory [sounds/]: atris-sounds not found!\n");
351 goto nosound;
352 }
353 my_dir = opendir("sounds");
354 if (my_dir) {
355 if (i >= 0) {
356 int j;
357 Calloc(retval.style,sound_style **,sizeof(*(retval.style))*i+1);
358 retval.num_style = i+1;
359 j = 0;
360 while (j<i) {
361 char filespec[1024];
362 struct dirent *this_file = readdir(my_dir);
363 if (!sound_Select(this_file)) continue;
364 SPRINTF(filespec,"sounds/%s",this_file->d_name);
365 retval.style[j] = load_sound_style(filespec);
366 if (strstr(retval.style[j]->name,"Default"))
367 retval.choice = j;
368 j++;
369 }
370 closedir(my_dir);
371 Calloc(retval.style[i],sound_style *,sizeof(sound_style));
372 retval.style[i]->name = "No Sound";
373 SDL_PauseAudio(0); /* start playing sound! */
374 return retval;
375 } else {
376 PANIC("No sound styles [sounds/*.Sound] found.\n");
377 }
378 } else {
379 PANIC("Cannot read directory [sounds/]");
380 }
381 Debug("No sound styles [sounds/*.Sound] found.\n");
382
383 /* for whatever reason, you don't get sound */
384 nosound:
385 retval.num_style = 1;
386 Malloc(retval.style,sound_style **,sizeof(*(retval.style))*1);
387 Calloc(retval.style[0],sound_style *,sizeof(sound_style));
388 retval.style[0]->name = "No Sound";
389 return retval;
390 }
391
392 /*
393 * $Log: sound.c,v $
394 * Revision 1.25 2000/11/10 18:16:48 weimer
395 * changes for Atris 1.0.4 - three new special options
396 *
397 * Revision 1.24 2000/11/01 16:28:53 weimer
398 * sounds removed ...
399 *
400 * Revision 1.23 2000/10/29 21:23:28 weimer
401 * One last round of header-file changes to reflect my newest and greatest
402 * knowledge of autoconf/automake. Now if you fail to have some bizarro
403 * function, we try to go on anyway unless it is vastly needed.
404 *
405 * Revision 1.22 2000/10/29 00:06:27 weimer
406 * networking fixes, change "styles/" to "styles" so that it works on Windows
407 *
408 * Revision 1.21 2000/10/27 00:07:36 weimer
409 * some sound fixes ...
410 *
411 * Revision 1.20 2000/10/22 22:05:51 weimer
412 * Added a few new sounds ...
413 *
414 * Revision 1.19 2000/10/21 01:14:43 weimer
415 * massic autoconf/automake restructure ...
416 *
417 * Revision 1.18 2000/10/20 01:32:09 weimer
418 * Minor play issue problems -- time is now truly a hard limit!
419 *
420 * Revision 1.17 2000/10/19 00:20:27 weimer
421 * sound directory changes, added a ticking clock ...
422 *
423 * Revision 1.16 2000/10/12 01:38:07 weimer
424 * added initial support for persistent player identities
425 *
426 * Revision 1.15 2000/09/09 17:05:35 wkiri
427 * Hideous log changes (Wes: how dare you include a comment character!)
428 *
429 * Revision 1.14 2000/09/09 16:58:27 weimer
430 * Sweeping Change of Ultimate Mastery. Main loop restructuring to clean up
431 * main(), isolate the behavior of the three game types. Move graphic files
432 * into graphics/-, style files into styles/-, remove some unused files,
433 * add game flow support (breaks between games, remembering your level within
434 * this game), multiplayer support for the event loop, some global variable
435 * cleanup. All that and a bag of chips!
436 *
437 * Revision 1.13 2000/09/04 19:48:02 weimer
438 * interim menu for choosing among game styles, button changes (two states)
439 *
440 * Revision 1.12 2000/09/04 14:08:30 weimer
441 * Added another color scheme and color/piece scheme scanning code.
442 *
443 * Revision 1.11 2000/09/03 21:08:23 weimer
444 * Added alternate sounds, we now actually load *.Sound ...
445 *
446 * Revision 1.10 2000/09/03 20:57:38 weimer
447 * quick fix for sound.c
448 *
449 * Revision 1.9 2000/09/03 20:57:02 weimer
450 * changes the time variable to be passed by reference in the event loop
451 *
452 * Revision 1.8 2000/09/03 18:26:11 weimer
453 * major header file and automatic prototype generation changes, restructuring
454 *
455 * Revision 1.7 2000/08/26 02:35:23 weimer
456 * add sounds when you are given garbage
457 *
458 * Revision 1.6 2000/08/26 00:56:47 weimer
459 * client-server mods, plus you can now disable sound
460 *
461 * Revision 1.5 2000/08/20 16:14:10 weimer
462 * changed the piece generation so that pieces and colors cluster together
463 *
464 * Revision 1.4 2000/08/15 00:59:41 weimer
465 * a few more sound things
466 *
467 * Revision 1.3 2000/08/15 00:48:28 weimer
468 * Massive sound rewrite, fixed a few memory problems. We now have
469 * sound_styles that work like piece_styles and color_styles.
470 *
471 * Revision 1.2 2000/08/13 21:53:08 weimer
472 * preliminary sound support (mixing multiple samples, etc.)
473 *
474 * Revision 1.1 2000/08/13 21:24:03 weimer
475 * preliminary sound
476 *
477 */
478