Partially sync files.el from XEmacs 21.5 for wildcard support.
[sxemacs] / src / media / sound-sunplay.c
1 /* play.c - play a sound file on the speaker
2  **
3  ** Copyright (C) 1989 by Jef Poskanzer.
4  **
5  ** Modified 24-May-91 by Jamie Zawinski (for Lucid Emacs).
6  ** Modified 17-Dec-92 by Jamie Zawinski (largely rewritten for SunOS 4.1.3).
7  **
8  ** Permission to use, copy, modify, and distribute this software and its
9  ** documentation for any purpose and without fee is hereby granted, provided
10  ** that the above copyright notice appear in all copies and that both that
11  ** copyright notice and this permission notice appear in supporting
12  ** documentation.  This software is provided "as is" without express or
13  ** implied warranty.
14  */
15
16 /* Synched up with: Not in FSF. */
17
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21
22 #if __STDC__ || defined(STDC_HEADERS)
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <fcntl.h>              /* for open() */
26 #endif
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/fcntl.h>
31 #include <sys/file.h>
32
33 #include <multimedia/libaudio.h>
34 #include <multimedia/audio_device.h>
35
36 # include <config.h>
37 # include "lisp.h"
38 # include "sysdep.h"
39 # include <errno.h>
40 #include "syssignal.h"
41 # define perror(string) \
42     message("audio: %s, %s ", string, strerror (errno))
43 # define warn(str) message ("audio: %s ", GETTEXT (str))
44
45 #include "nativesound.h"
46 #include "media-native.h"
47 #include "sound-sunplay.h"
48
49 Lisp_Object Qsunplay;           /* cannot be Qnative */
50
51 #define MYSELF ADRIVER_NATIVE
52
53 #if 0
54 static SIGTYPE(*sighup_handler) (int sig);
55 static SIGTYPE(*sigint_handler) (int sig);
56 static SIGTYPE sighandler(int sig);
57 #endif
58
59 #if 0
60 static int audio_fd;
61 #endif
62
63 #define audio_open()    open ("/dev/audio", (O_WRONLY | O_NONBLOCK), 0)
64
65 static int initialized_device_p;
66 static int reset_volume_p, reset_device_p;
67 static double old_volume;
68 static Audio_hdr dev_hdr;
69
70 #if 0
71 static int
72 init_device(int volume, unsigned char *data, int fd,
73             unsigned int *header_length)
74 {
75 #ifdef SUNOS4_0_3
76         if (header_length)
77                 *header_length = 0;
78         return 0;
79 #else
80         Audio_hdr file_hdr;
81
82         reset_volume_p = 0;
83         reset_device_p = 0;
84
85         if (data && fd)
86                 abort();        /* one or the other */
87
88         if (AUDIO_SUCCESS != audio_get_play_config(audio_fd, &dev_hdr)) {
89                 perror("Not a valid audio device");
90                 return 1;
91         }
92
93         if (AUDIO_SUCCESS != (data
94                               ? audio_decode_filehdr(data, &file_hdr,
95                                                      header_length)
96                               : audio_read_filehdr(fd, &file_hdr, 0, 0))) {
97                 if (data)
98                         perror("invalid audio data");
99                 else
100                         perror("invalid audio file");
101                 return 1;
102         }
103
104         audio_flush_play(audio_fd);
105
106         if (!initialized_device_p || (0 != audio_cmp_hdr(&dev_hdr, &file_hdr))) {
107                 Audio_hdr new_hdr;
108                 new_hdr = file_hdr;
109                 reset_device_p = 1;
110                 initialized_device_p = 1;
111                 if (AUDIO_SUCCESS != audio_set_play_config(audio_fd, &new_hdr)) {
112                         char buf1[100], buf2[100], buf3[250];
113                         audio_enc_to_str(&file_hdr, buf1);
114                         audio_enc_to_str(&new_hdr, buf2);
115                         (void)snprintf(buf3, sizeof(buf3), "wanted %s, got %s", buf1, buf2);
116                         warn(buf3);
117                         return 1;
118                 }
119         }
120
121         if (volume < 0 || volume > 100) {
122                 char buf[255];
123                 int sz = sprintf(buf, sizeof(buf), "volume must be between 0 and 100 (not %d)",
124                                  volume);
125                 assert(sz>=0 && sz<sizeof(buf));
126                 warn(buf);
127                 return 1;
128         }
129         {
130                 /* set the volume; scale it to 0.0 - 1.0 */
131                 double V = (volume / 100.0);
132                 audio_get_play_gain(audio_fd, &old_volume);
133                 reset_volume_p = 1;
134                 audio_set_play_gain(audio_fd, &V);
135         }
136
137         return 0;
138 #endif
139 }
140
141 static void reset_device(int wait_p)
142 {
143         if (wait_p)
144                 audio_drain(audio_fd, 1);
145         else
146                 audio_flush_play(audio_fd);
147         if (reset_device_p)
148                 audio_set_play_config(audio_fd, &dev_hdr);
149         if (reset_volume_p)
150                 audio_set_play_gain(audio_fd, &old_volume);
151 }
152
153 void play_sound_file(char *sound_file, int volume)
154 {
155         int rrtn, wrtn;
156         unsigned char buf[255];
157         int file_fd;
158
159         audio_fd = audio_open();
160
161         if (audio_fd < 0) {
162                 perror("open /dev/audio");
163                 return;
164         }
165
166         /* where to find the proto for signal()... */
167         sighup_handler = (SIGTYPE(*)(int))signal(SIGHUP, sighandler);
168         sigint_handler = (SIGTYPE(*)(int))signal(SIGINT, sighandler);
169
170         file_fd = open(sound_file, O_RDONLY, 0);
171         if (file_fd < 0) {
172                 perror(sound_file);
173                 goto END_OF_PLAY;
174         }
175
176         if (init_device(volume, (unsigned char *)0, file_fd, (unsigned int *)0))
177                 goto END_OF_PLAY;
178
179         while (1) {
180                 rrtn = read(file_fd, (char *)buf, sizeof(buf));
181                 if (rrtn < 0) {
182                         perror("read");
183                         goto END_OF_PLAY;
184                 }
185                 if (rrtn == 0)
186                         break;
187
188                 while (1) {
189                         wrtn = write(audio_fd, (char *)buf, rrtn);
190                         if (wrtn < 0) {
191                                 perror("write");
192                                 goto END_OF_PLAY;
193                         }
194                         if (wrtn != 0)
195                                 break;
196
197                         if (AUDIO_ERR_INTERRUPTED == audio_drain(audio_fd, 1))
198                                 goto END_OF_PLAY;
199                 }
200                 if (wrtn != rrtn) {
201                         char warn_buf[255];
202                         int sz = sprintf(warn_buf, sizeof(warn_buf), "play: rrtn = %d, wrtn = %d", rrtn,
203                                          wrtn);
204                         assert(warn_buf>=0 && warn_buf<sizeof(warn_buf));
205                         warn(warn_buf);
206                         goto END_OF_PLAY;
207                 }
208         }
209
210       END_OF_PLAY:
211
212         if (file_fd > 0)
213                 close(file_fd);
214
215         if (audio_fd > 0) {
216                 reset_device(1);
217                 close(audio_fd);
218         }
219
220         signal(SIGHUP, sighup_handler);
221         signal(SIGINT, sigint_handler);
222 }
223
224 int play_sound_data(unsigned char *data, int length, int volume)
225 {
226         int wrtn, start = 0;
227         unsigned int ilen;
228         int result = 0;
229
230         audio_fd = -1;
231
232         if (length == 0)
233                 return 0;
234
235         /* this is just to get a better error message */
236         if (strncmp(".snd\0", (char *)data, 4)) {
237                 warn("Not valid audio data (bad magic number)");
238                 goto END_OF_PLAY;
239         }
240         if (length <= sizeof(Audio_hdr)) {
241                 warn("Not valid audio data (too short)");
242                 goto END_OF_PLAY;
243         }
244
245         audio_fd = audio_open();
246         if (audio_fd < 0)
247                 return 0;
248
249         /* where to find the proto for signal()... */
250         sighup_handler = (SIGTYPE(*)(int))signal(SIGHUP, sighandler);
251         sigint_handler = (SIGTYPE(*)(int))signal(SIGINT, sighandler);
252
253         if (init_device(volume, data, 0, &ilen))
254                 goto END_OF_PLAY;
255
256         data += (ilen << 2);
257         length -= (ilen << 2);
258         if (length <= 1)
259                 goto END_OF_PLAY;
260
261         while (1) {
262                 wrtn = write(audio_fd, (char *)(data + start), length - start);
263                 if (wrtn < 0) {
264                         perror("write");
265                         goto END_OF_PLAY;
266                 }
267                 if (wrtn != 0) {
268                         start += wrtn;
269                         break;
270                 }
271                 if (AUDIO_ERR_INTERRUPTED == audio_drain(audio_fd, 1))
272                         goto END_OF_PLAY;
273         }
274         if (wrtn != length) {
275                 char buf[255];
276                 int sz = snprintf(buf, sizeof(buf),
277                                   "play: rrtn = %d, wrtn = %d", length, wrtn);
278                 assert(sz>=0 && sz < sizeof(buf));
279                 warn(buf);
280                 goto END_OF_PLAY;
281         }
282
283         result = 1;
284
285       END_OF_PLAY:
286
287         if (audio_fd > 0) {
288                 reset_device(1);
289                 close(audio_fd);
290         }
291
292         signal(SIGHUP, sighup_handler);
293         signal(SIGINT, sigint_handler);
294
295         return result;
296 }
297 #endif
298
299 #if 0
300 /* #### sigcontext doesn't exist in Solaris.  This should be updated
301    to be correct for Solaris. */
302 static SIGTYPE sighandler(int sig)
303 {
304         if (audio_fd > 0) {
305                 reset_device(0);
306                 close(audio_fd);
307         }
308         if (sig == SIGHUP && sighup_handler)
309                 sighup_handler(sig);
310         else if (sig == SIGINT && sigint_handler)
311                 sigint_handler(sig);
312         else
313                 exit(1);
314 }
315 #endif
316
317 \f
318 static int
319 sound_native_audio_init(int audio_fd)
320 {
321 #ifdef SUNOS4_0_3
322         return 1;
323 #else
324         Audio_hdr file_hdr;
325
326         reset_volume_p = 0;
327         reset_device_p = 0;
328
329         if (AUDIO_SUCCESS != audio_get_play_config(audio_fd, &dev_hdr)) {
330                 perror("Not a valid audio device");
331                 return 0;
332         }
333
334 #if 0                           /* eerks */
335         if (AUDIO_SUCCESS != (data
336                               ? audio_decode_filehdr(data, &file_hdr,
337                                                      header_length)
338                               : audio_read_filehdr(fd, &file_hdr, 0, 0))) {
339                 if (data)
340                         perror("invalid audio data");
341                 else
342                         perror("invalid audio file");
343                 return 0;
344         }
345 #endif
346
347         audio_flush_play(audio_fd);
348
349 #if 0
350         if (!initialized_device_p || (0 != audio_cmp_hdr(&dev_hdr, &file_hdr))) {
351                 Audio_hdr new_hdr;
352                 new_hdr = file_hdr;
353                 reset_device_p = 1;
354                 initialized_device_p = 1;
355                 if (AUDIO_SUCCESS != audio_set_play_config(audio_fd, &new_hdr)) {
356                         char buf1[100], buf2[100], buf3[250];
357                         audio_enc_to_str(&file_hdr, buf1);
358                         audio_enc_to_str(&new_hdr, buf2);
359                         (void)snprintf(buf3, sizeof(buf3), "wanted %s, got %s", buf1, buf2);
360                         warn(buf3);
361                         return 0;
362                 }
363         }
364 #endif
365
366 #if 0
367         if (volume < 0 || volume > 100) {
368                 char buf[255];
369                 int sz = snprintf(buf, sizeof(buf),
370                                   "volume must be between 0 and 100 (not %d)",
371                                   volume);
372                 assert(sz>=0 && sz<sizeof(buf));
373                 warn(buf);
374                 return 0;
375         }
376 #endif
377         {
378                 /* set the volume; scale it to 0.0 - 1.0 */
379                 double V = 1.0; /* (volume / 100.0); */
380                 audio_get_play_gain(audio_fd, &old_volume);
381                 reset_volume_p = 1;
382                 audio_set_play_gain(audio_fd, &V);
383         }
384
385         return 1;
386 #endif
387 }
388
389 int sound_native_play_stream(Lisp_Object mt)
390 {
391         /* stream stuff */
392         mtype_audio_properties *mtap;
393         Lisp_Media_Stream *ms;
394         /* device stuff */
395         Lisp_Object device;
396         Lisp_Audio_Device *lad = NULL;
397         sound_native_data *saod = NULL;
398         /* native stuff */
399         int audio_fd = -1;
400         /* buffering */
401         char *buffer = NULL;
402         char *bptr;
403         uint32_t len;
404         int32_t natlen;
405         int32_t written;
406
407         int wrtn, start = 0;
408         unsigned int ilen;
409         int result = 0;
410
411         /* unpack the media thread */
412         device = XMEDIA_THREAD(mt)->device;
413         ms = XMEDIA_STREAM(XMEDIA_THREAD(mt)->stream);
414
415         /* unpack device */
416         lad = get_audio_device(device);
417         saod = get_audio_device_data(device);
418
419         /* cannot use AO on incomplete or corrupt audio devices */
420         if (lad == NULL ||
421             XAUDIO_DEVICE_DRIVER(device) != MYSELF)
422                 return 0;
423
424         /* refuse to play non-audio */
425         if (media_stream_type(ms) != MTYPE_AUDIO)
426                 return 0;
427         mtap = media_stream_type_properties(ms).aprops;
428
429         audio_fd = -1;
430         audio_fd = audio_open();
431         if (audio_fd < 0)
432                 return 0;
433
434 #if 0
435         /* where to find the proto for signal()... */
436         sighup_handler = (SIGTYPE(*)(int))signal(SIGHUP, sighandler);
437         sigint_handler = (SIGTYPE(*)(int))signal(SIGINT, sighandler);
438 #endif
439
440         if (!sound_native_audio_init(audio_fd))
441                 goto END_OF_PLAY;
442
443         /* rewind the stream */
444         media_stream_srewind(ms)(ms);
445
446         /* play chunks of the stream */
447         buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE+1);
448         while ((len = media_stream_sread(ms)(
449                         ms, buffer, mtap->samplerate)) > 0) {
450
451                 natlen = len * mtap->framesize;
452                 bptr = buffer;
453
454                 while (natlen > 0) {
455                         if ((written = write(audio_fd, bptr, natlen)) < 0) {
456                                 perror("error in write");
457                                 natlen = 0;
458                         } else if (written) {
459                                 natlen -= written;
460                                 bptr += written;
461                         }
462                         if (AUDIO_ERR_INTERRUPTED == audio_drain(audio_fd, 1))
463                                 goto END_OF_PLAY;
464                 }
465         }
466
467 END_OF_PLAY:
468
469         if (audio_fd > 0) {
470                 reset_device(1);
471                 close(audio_fd);
472         }
473
474 #if 0
475         signal(SIGHUP, sighup_handler);
476         signal(SIGINT, sigint_handler);
477 #endif
478
479         return 1;
480 }
481
482 #undef MYSELF