rnd-20040820-1-src
[rocksndiamonds.git] / src / game_em / sound.c
1 /* 2000-08-10T17:39:15Z
2  *
3  * handle sounds in emerald mine
4  */
5
6 #include "../libgame/platform.h"
7
8
9 #if defined(TARGET_X11)
10
11 #if defined(PLATFORM_LINUX) || defined(PLATFORM_BSD)
12
13 #ifdef PLATFORM_LINUX
14 #include <sys/soundcard.h>
15 #endif
16
17 #ifdef PLATFORM_BSD
18 #include <soundcard.h>
19 #endif
20
21 #include <sys/time.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/ioctl.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31
32 #include "global.h"
33 #include "sample.h"
34
35 static char audioname[] = "/dev/audio";
36
37 static const int sound_priority[SAMPLE_MAX] = {
38         SAMPLE_exit, SAMPLE_die, SAMPLE_time, SAMPLE_boom, SAMPLE_tick,
39         SAMPLE_collect, SAMPLE_roll, SAMPLE_push, SAMPLE_dynamite, SAMPLE_press,
40         SAMPLE_door, SAMPLE_dirt, SAMPLE_blank, SAMPLE_android, SAMPLE_ball,
41         SAMPLE_grow, SAMPLE_squash, SAMPLE_crack, SAMPLE_slurp, SAMPLE_drip,
42         SAMPLE_wonder, SAMPLE_wheel, SAMPLE_stone, SAMPLE_spring, SAMPLE_diamond,
43         SAMPLE_nut, SAMPLE_bug, SAMPLE_tank, SAMPLE_eater, SAMPLE_alien,
44         SAMPLE_acid
45 };
46
47 int sound_thread(void)
48 {
49         int audio_fd; /* file descriptor of /dev/audio or -1 if not open */
50         int audio_format;
51         int sample_rate;
52         int fragment_size;
53         unsigned char *audio_buffer; /* actual buffer pumped to /dev/audio */
54         short *mix_buffer;
55
56         char sound_play[SAMPLE_MAX]; /* if set, we should be playing these sounds */
57         long sound_pos[SAMPLE_MAX]; /* position in the sound */
58         int mix_play[MIXER_MAX]; /* which sounds we have chosen to mix (calculated each time) */
59         int mix_count;
60
61         int i;
62
63 loop:
64         audio_fd = -1;
65         audio_format = AUDIO_ULAW; /* defaults for non-OSS /dev/audio */
66         sample_rate = 8000;
67         fragment_size = 256;
68         audio_buffer = 0;
69         mix_buffer = 0;
70         mix_count = 0;
71
72         memset(sound_play, 0, sizeof(sound_play)); /* not playing any sounds */
73
74         for(;;) {
75                 for(;;) {
76
77 /* pick sounds to play, if any */
78                         if(sound_play[SAMPLE_exit] || sound_play[SAMPLE_die]) sound_play[SAMPLE_boom] = 0; /* no explosions if player goes home */
79                         mix_count = 0;
80                         for(i = 0; i < SAMPLE_MAX; i++) {
81                                 if(sound_play[sound_priority[i]]) {
82                                         mix_play[mix_count++] = sound_priority[i];
83                                         if(mix_count == MIXER_MAX) break; /* cant mix too many sounds at once */
84                                 }
85                         }
86
87 /* check for incoming messages */
88                         if(mix_count || audio_fd != -1) { /* dont block if we are playing sounds */
89                                 fd_set rfds;
90                                 struct timeval tv;
91                                 FD_ZERO(&rfds);
92                                 FD_SET(sound_pipe[0], &rfds);
93                                 tv.tv_sec = 0;
94                                 tv.tv_usec = 0; /* (900000 * fragment_size / sample_rate) */
95                                 i = select(sound_pipe[0] + 1, &rfds, 0, 0, &tv); /* dont block */
96                                 if(i == -1) {
97                                         fprintf(stderr, "%s: %s: %s\n", progname, "select failed", strerror(errno));
98                                         goto fail;
99                                 }
100                                 if(i == 0) break; /* no messages */
101                         }
102
103 /* get a message and start a sound */
104                         i = read(sound_pipe[0], &play, sizeof(play));
105                         if(i == -1) {
106                                 fprintf(stderr, "%s: %s: %s\n", progname, "read failed", strerror(errno));
107                                 goto fail;
108                         }
109                         if(i == 0) {
110                                 fprintf(stderr, "%s: %s: %s\n", progname, "read sound", "Broken pipe");
111                                 goto fail;
112                         }
113                         if(i != sizeof(play)) {
114                                 fprintf(stderr, "%s: %s\n", progname, "bad message length");
115                                 goto fail;
116                         }
117                         for(i = 0; i < SAMPLE_MAX; i++) {
118                                 if(play[i]) {
119                                         sound_play[i] = 1; /* play this sound */
120                                         sound_pos[i] = 0; /* start it from the start */
121                                 }
122                         }
123                 }
124
125 /* open the audio device if there are sounds to play */
126                 if(mix_count && audio_fd == -1) {
127                         audio_fd = open(audioname, O_WRONLY);
128                         if(audio_fd == -1) goto reset;
129 #ifdef OPEN_SOUND_SYSTEM
130                         i = 0x00020008;
131                         if(ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &i) == -1) {
132                                 fprintf(stderr, "%s: \"%s\": %s (%d): %s\n", progname, audioname, "unable to set fragment size", 512, strerror(errno));
133                                 goto reset;
134                         }
135                         if(ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &i) == -1) {
136                                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, audioname, "unable to query audio format", strerror(errno));
137                                 goto reset;
138                         }
139                         audio_format = (i & AFMT_U8) ? AFMT_U8 : AFMT_MU_LAW; /* prefer 8 bit unsigned and fall back on mu-law */
140                         i = audio_format;
141                         if(ioctl(audio_fd, SNDCTL_DSP_SETFMT, &i) == -1) {
142                                 fprintf(stderr, "%s: \"%s\": %s (%d): %s\n", progname, audioname, "unable to set audio format", audio_format, strerror(errno));
143                                 goto reset;
144                         }
145                         if(i == AFMT_MU_LAW) {
146                                 audio_format = AUDIO_ULAW;
147                         } else if(i == AFMT_U8) {
148                                 audio_format = AUDIO_U8;
149                         } else {
150                                 fprintf(stderr, "%s: \"%s\": %s (%d)\n", progname, audioname, "audio format required by device not supported", i);
151                                 goto reset;
152                         }
153                         i = 1;
154                         if(ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &i) == -1) {
155                                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, audioname, "unable to set channels to mono", strerror(errno));
156                                 goto reset;
157                         }
158                         if(i != 1) {
159                                 fprintf(stderr, "%s: \"%s\": %s (%d)\n", progname, audioname, "channels required by device not supported", i);
160                                 goto reset;
161                         }
162                         i = 8000;
163                         if(ioctl(audio_fd, SNDCTL_DSP_SPEED, &i) == -1) {
164                                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, audioname, "unable to set sampling rate", strerror(errno));
165                                 goto reset;
166                         }
167                         sample_rate = i;
168                         if(ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &i) == -1) {
169                                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, audioname, "unable to get block size", strerror(errno));
170                                 goto reset;
171                         }
172                         fragment_size = i;
173 #else
174                         if(fcntl(audio_fd, F_SETFL, O_NONBLOCK) == -1) {
175                                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, audioname, "unable to make audio non blocking", strerror(errno));
176                                 goto reset;
177                         }
178 #endif /* OPEN_SOUND_SYSTEM */
179                         audio_buffer = malloc(fragment_size * sizeof(*audio_buffer));
180                         if(audio_buffer == 0) {
181                                 fprintf(stderr, "%s: %s (%d): %s\n", progname, "unable to malloc audio buffer", fragment_size * sizeof(*audio_buffer), strerror(errno));
182                                 goto fail;
183                         }
184                         mix_buffer = malloc(fragment_size * sizeof(*mix_buffer));
185                         if(mix_buffer == 0) {
186                                 fprintf(stderr, "%s: %s (%d): %s\n", progname, "unable to malloc mixing buffer", fragment_size * sizeof(*mix_buffer), strerror(errno));
187                                 goto fail;
188                         }
189                 }
190
191 /* close the audio device if no sounds are playing */
192                 if(mix_count == 0 && audio_fd != -1) {
193                         close(audio_fd);
194                         free(audio_buffer);
195                         free(mix_buffer);
196                         audio_fd = -1;
197                         audio_buffer = 0;
198                         mix_buffer = 0;
199                 }
200
201 /* if we are playing sounds and the audio device is open, mix them */
202                 if(mix_count && audio_fd != -1) {
203
204                         memset(mix_buffer, 0, fragment_size * sizeof(*mix_buffer)); /* prepare mix buffer */
205
206                         for(i = 0; i < mix_count; i++) {
207                                 register short *mix_ptr = mix_buffer;
208                                 register short *sound_ptr = sound_data[mix_play[i]] + sound_pos[mix_play[i]];
209                                 register long count = sound_length[mix_play[i]] - sound_pos[mix_play[i]];
210                                 if(count > fragment_size) count = fragment_size;
211                                 while(count--) *mix_ptr++ += *sound_ptr++; /* mix the sounds in */
212                         }
213                         switch(audio_format) {
214                         case AUDIO_ULAW:
215                                 for(i = 0; i < fragment_size; i++) audio_buffer[i] = linear_to_ulaw[mix_buffer[i] + 32768];
216                                 break;
217                         case AUDIO_U8:
218                                 for(i = 0; i < fragment_size; i++) audio_buffer[i] = (mix_buffer[i] + 32768) >> 8;
219                                 break;
220                         }
221
222 /* advance sound pointers */
223                         for(i = 0; i < SAMPLE_MAX; i++) {
224                                 if(sound_play[i]) {
225                                         if(sound_pos[i] + fragment_size < sound_length[i]) {
226                                                 sound_pos[i] += fragment_size;
227                                         } else {
228                                                 sound_play[i] = 0;
229                                         }
230                                 }
231                         }
232
233 /* send the data to the audio device */
234                         i = write(audio_fd, audio_buffer, fragment_size);
235                         if(i == -1) {
236                                 fprintf(stderr, "%s: %s: %s\n", progname, "write error", strerror(errno));
237                                 goto reset;
238                         }
239                         if(i != fragment_size) {
240                                 fprintf(stderr, "%s: %s\n", progname, "bad write length");
241                                 goto reset;
242                         }
243                 }
244         } /* for */
245
246 reset:
247         if(audio_fd != -1) close(audio_fd);
248         if(audio_buffer) free(audio_buffer);
249         if(mix_buffer) free(mix_buffer);
250         goto loop; /* back to top */
251
252 fail:
253         if(audio_fd != -1) close(audio_fd);
254         if(audio_buffer) free(audio_buffer);
255         if(mix_buffer) free(mix_buffer);
256         return(0);
257 }
258
259 int read_sample(char *name, short **data, long *length)
260 {
261         int result;
262         FILE *file = 0;
263         short *dataptr = 0;
264         long datalength;
265
266         int i, actual, ch;
267         unsigned char buffer[24];
268         unsigned long temp;
269
270         file = fopen(name, "rb");
271         if(file == 0) {
272                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, name, "open error", strerror(errno));
273                 result = 1; goto fail;
274         }
275         actual = fread(buffer, 1, 24, file);
276         if(actual == -1) {
277                 fprintf(stderr, "%s: \"%s\": %s: %s\n", progname, name, "read error", strerror(errno));
278                 result = 1; goto fail;
279         }
280         if(actual < 24) {
281                 fprintf(stderr, "%s: \"%s\": %s\n", progname, name, "premature eof");
282                 result = 1; goto fail;
283         }
284         temp = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]; /* magic */
285         if(temp != 0x2e736e64) {
286                 fprintf(stderr, "%s: \"%s\": %s\n", progname, name, "unrecognized file format");
287                 result = 1; goto fail;
288         }
289         temp = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7]; /* header length */
290         if(temp < 24) {
291                 fprintf(stderr, "%s: \"%s\": %s\n", progname, name, "bad header length");
292                 result = 1; goto fail;
293         }
294         actual = temp;
295         for(i = 24; i < actual; i++) { /* skip the rest of the header */
296                 ch = fgetc(file);
297                 if(ch == EOF) break;
298         }
299
300         temp = buffer[8] << 24 | buffer[9] << 16 | buffer[10] << 8 | buffer[11]; /* data length */
301         datalength = temp;
302         temp = buffer[12] << 24 | buffer[13] << 16 | buffer[14] << 8 | buffer[15]; /* encoding */
303         if(temp != 1) {
304                 fprintf(stderr, "%s: \"%s\": %s (%ld != 1)\n", progname, name, "bad encoding type", temp);
305                 result = 1; goto fail;
306         }
307         temp = buffer[16] << 24 | buffer[17] << 16 | buffer[18] << 8 | buffer[19]; /* sample rate */
308         if(temp != 8000) {
309                 fprintf(stderr, "%s: \"%s\": %s (%ld != 8000)\n", progname, name, "bad sample rate", temp);
310                 result = 1; goto fail;
311         }
312         temp = buffer[20] << 24 | buffer[21] << 16 | buffer[22] << 8 | buffer[23]; /* channels */
313         if(temp != 1) {
314                 fprintf(stderr, "%s: \"%s\": %s (%ld != 1)\n", progname, name, "unsupported channels", temp);
315                 result = 1; goto fail;
316         }
317
318         dataptr = malloc(datalength * sizeof(*dataptr));
319         if(dataptr == 0) {
320                 fprintf(stderr, "%s: \"%s\": %s (%ld): %s\n", progname, name, "unable to malloc buffer", datalength * sizeof(*dataptr), strerror(errno));
321                 result = 1; goto fail;
322         }
323
324         for(i = 0; i < datalength; i++) {
325                 ch = fgetc(file);
326                 if(ch == EOF) break;
327                 dataptr[i] = ulaw_to_linear[ch];
328         }
329         fclose(file);
330         file = 0;
331
332         *data = dataptr;
333         *length = datalength;
334         result = 0;
335 fail:
336         if(file) fclose(file);
337         return(result);
338 }
339
340 #endif /* defined(PLATFORM_LINUX) || defined(PLATFORM_BSD) */
341
342 #endif