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(&current,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