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