Initial git import
[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                         sprintf(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                 sprintf(buf, "volume must be between 0 and 100 (not %d)",
124                         volume);
125                 warn(buf);
126                 return 1;
127         }
128         {
129                 /* set the volume; scale it to 0.0 - 1.0 */
130                 double V = (volume / 100.0);
131                 audio_get_play_gain(audio_fd, &old_volume);
132                 reset_volume_p = 1;
133                 audio_set_play_gain(audio_fd, &V);
134         }
135
136         return 0;
137 #endif
138 }
139
140 static void reset_device(int wait_p)
141 {
142         if (wait_p)
143                 audio_drain(audio_fd, 1);
144         else
145                 audio_flush_play(audio_fd);
146         if (reset_device_p)
147                 audio_set_play_config(audio_fd, &dev_hdr);
148         if (reset_volume_p)
149                 audio_set_play_gain(audio_fd, &old_volume);
150 }
151
152 void play_sound_file(char *sound_file, int volume)
153 {
154         int rrtn, wrtn;
155         unsigned char buf[255];
156         int file_fd;
157
158         audio_fd = audio_open();
159
160         if (audio_fd < 0) {
161                 perror("open /dev/audio");
162                 return;
163         }
164
165         /* where to find the proto for signal()... */
166         sighup_handler = (SIGTYPE(*)(int))signal(SIGHUP, sighandler);
167         sigint_handler = (SIGTYPE(*)(int))signal(SIGINT, sighandler);
168
169         file_fd = open(sound_file, O_RDONLY, 0);
170         if (file_fd < 0) {
171                 perror(sound_file);
172                 goto END_OF_PLAY;
173         }
174
175         if (init_device(volume, (unsigned char *)0, file_fd, (unsigned int *)0))
176                 goto END_OF_PLAY;
177
178         while (1) {
179                 rrtn = read(file_fd, (char *)buf, sizeof(buf));
180                 if (rrtn < 0) {
181                         perror("read");
182                         goto END_OF_PLAY;
183                 }
184                 if (rrtn == 0)
185                         break;
186
187                 while (1) {
188                         wrtn = write(audio_fd, (char *)buf, rrtn);
189                         if (wrtn < 0) {
190                                 perror("write");
191                                 goto END_OF_PLAY;
192                         }
193                         if (wrtn != 0)
194                                 break;
195
196                         if (AUDIO_ERR_INTERRUPTED == audio_drain(audio_fd, 1))
197                                 goto END_OF_PLAY;
198                 }
199                 if (wrtn != rrtn) {
200                         char warn_buf[255];
201                         sprintf(warn_buf, "play: rrtn = %d, wrtn = %d", rrtn,
202                                 wrtn);
203                         warn(warn_buf);
204                         goto END_OF_PLAY;
205                 }
206         }
207
208       END_OF_PLAY:
209
210         if (file_fd > 0)
211                 close(file_fd);
212
213         if (audio_fd > 0) {
214                 reset_device(1);
215                 close(audio_fd);
216         }
217
218         signal(SIGHUP, sighup_handler);
219         signal(SIGINT, sigint_handler);
220 }
221
222 int play_sound_data(unsigned char *data, int length, int volume)
223 {
224         int wrtn, start = 0;
225         unsigned int ilen;
226         int result = 0;
227
228         audio_fd = -1;
229
230         if (length == 0)
231                 return 0;
232
233         /* this is just to get a better error message */
234         if (strncmp(".snd\0", (char *)data, 4)) {
235                 warn("Not valid audio data (bad magic number)");
236                 goto END_OF_PLAY;
237         }
238         if (length <= sizeof(Audio_hdr)) {
239                 warn("Not valid audio data (too short)");
240                 goto END_OF_PLAY;
241         }
242
243         audio_fd = audio_open();
244         if (audio_fd < 0)
245                 return 0;
246
247         /* where to find the proto for signal()... */
248         sighup_handler = (SIGTYPE(*)(int))signal(SIGHUP, sighandler);
249         sigint_handler = (SIGTYPE(*)(int))signal(SIGINT, sighandler);
250
251         if (init_device(volume, data, 0, &ilen))
252                 goto END_OF_PLAY;
253
254         data += (ilen << 2);
255         length -= (ilen << 2);
256         if (length <= 1)
257                 goto END_OF_PLAY;
258
259         while (1) {
260                 wrtn = write(audio_fd, (char *)(data + start), length - start);
261                 if (wrtn < 0) {
262                         perror("write");
263                         goto END_OF_PLAY;
264                 }
265                 if (wrtn != 0) {
266                         start += wrtn;
267                         break;
268                 }
269                 if (AUDIO_ERR_INTERRUPTED == audio_drain(audio_fd, 1))
270                         goto END_OF_PLAY;
271         }
272         if (wrtn != length) {
273                 char buf[255];
274                 sprintf(buf, "play: rrtn = %d, wrtn = %d", length, wrtn);
275                 warn(buf);
276                 goto END_OF_PLAY;
277         }
278
279         result = 1;
280
281       END_OF_PLAY:
282
283         if (audio_fd > 0) {
284                 reset_device(1);
285                 close(audio_fd);
286         }
287
288         signal(SIGHUP, sighup_handler);
289         signal(SIGINT, sigint_handler);
290
291         return result;
292 }
293 #endif
294
295 #if 0
296 /* #### sigcontext doesn't exist in Solaris.  This should be updated
297    to be correct for Solaris. */
298 static SIGTYPE sighandler(int sig)
299 {
300         if (audio_fd > 0) {
301                 reset_device(0);
302                 close(audio_fd);
303         }
304         if (sig == SIGHUP && sighup_handler)
305                 sighup_handler(sig);
306         else if (sig == SIGINT && sigint_handler)
307                 sigint_handler(sig);
308         else
309                 exit(1);
310 }
311 #endif
312
313 \f
314 static int
315 sound_native_audio_init(int audio_fd)
316 {
317 #ifdef SUNOS4_0_3
318         return 1;
319 #else
320         Audio_hdr file_hdr;
321
322         reset_volume_p = 0;
323         reset_device_p = 0;
324
325         if (AUDIO_SUCCESS != audio_get_play_config(audio_fd, &dev_hdr)) {
326                 perror("Not a valid audio device");
327                 return 0;
328         }
329
330 #if 0                           /* eerks */
331         if (AUDIO_SUCCESS != (data
332                               ? audio_decode_filehdr(data, &file_hdr,
333                                                      header_length)
334                               : audio_read_filehdr(fd, &file_hdr, 0, 0))) {
335                 if (data)
336                         perror("invalid audio data");
337                 else
338                         perror("invalid audio file");
339                 return 0;
340         }
341 #endif
342
343         audio_flush_play(audio_fd);
344
345 #if 0
346         if (!initialized_device_p || (0 != audio_cmp_hdr(&dev_hdr, &file_hdr))) {
347                 Audio_hdr new_hdr;
348                 new_hdr = file_hdr;
349                 reset_device_p = 1;
350                 initialized_device_p = 1;
351                 if (AUDIO_SUCCESS != audio_set_play_config(audio_fd, &new_hdr)) {
352                         char buf1[100], buf2[100], buf3[250];
353                         audio_enc_to_str(&file_hdr, buf1);
354                         audio_enc_to_str(&new_hdr, buf2);
355                         sprintf(buf3, "wanted %s, got %s", buf1, buf2);
356                         warn(buf3);
357                         return 0;
358                 }
359         }
360 #endif
361
362 #if 0
363         if (volume < 0 || volume > 100) {
364                 char buf[255];
365                 sprintf(buf, "volume must be between 0 and 100 (not %d)",
366                         volume);
367                 warn(buf);
368                 return 0;
369         }
370 #endif
371         {
372                 /* set the volume; scale it to 0.0 - 1.0 */
373                 double V = 1.0; /* (volume / 100.0); */
374                 audio_get_play_gain(audio_fd, &old_volume);
375                 reset_volume_p = 1;
376                 audio_set_play_gain(audio_fd, &V);
377         }
378
379         return 1;
380 #endif
381 }
382
383 int sound_native_play_stream(Lisp_Object mt)
384 {
385         /* stream stuff */
386         mtype_audio_properties *mtap;
387         Lisp_Media_Stream *ms;
388         /* device stuff */
389         Lisp_Object device;
390         Lisp_Audio_Device *lad = NULL;
391         sound_native_data *saod = NULL;
392         /* native stuff */
393         int audio_fd = -1;
394         /* buffering */
395         char *buffer = NULL;
396         char *bptr;
397         uint32_t len;
398         int32_t natlen;
399         int32_t written;
400
401         int wrtn, start = 0;
402         unsigned int ilen;
403         int result = 0;
404
405         /* unpack the media thread */
406         device = XMEDIA_THREAD(mt)->device;
407         ms = XMEDIA_STREAM(XMEDIA_THREAD(mt)->stream);
408
409         /* unpack device */
410         lad = get_audio_device(device);
411         saod = get_audio_device_data(device);
412
413         /* cannot use AO on incomplete or corrupt audio devices */
414         if (lad == NULL ||
415             XAUDIO_DEVICE_DRIVER(device) != MYSELF)
416                 return 0;
417
418         /* refuse to play non-audio */
419         if (media_stream_type(ms) != MTYPE_AUDIO)
420                 return 0;
421         mtap = media_stream_type_properties(ms).aprops;
422
423         audio_fd = -1;
424         audio_fd = audio_open();
425         if (audio_fd < 0)
426                 return 0;
427
428 #if 0
429         /* where to find the proto for signal()... */
430         sighup_handler = (SIGTYPE(*)(int))signal(SIGHUP, sighandler);
431         sigint_handler = (SIGTYPE(*)(int))signal(SIGINT, sighandler);
432 #endif
433
434         if (!sound_native_audio_init(audio_fd))
435                 goto END_OF_PLAY;
436
437         /* rewind the stream */
438         media_stream_srewind(ms)(ms);
439
440         /* play chunks of the stream */
441         buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE+1);
442         while ((len = media_stream_sread(ms)(
443                         ms, buffer, mtap->samplerate)) > 0) {
444
445                 natlen = len * mtap->framesize;
446                 bptr = buffer;
447
448                 while (natlen > 0) {
449                         if ((written = write(audio_fd, bptr, natlen)) < 0) {
450                                 perror("error in write");
451                                 natlen = 0;
452                         } else if (written) {
453                                 natlen -= written;
454                                 bptr += written;
455                         }
456                         if (AUDIO_ERR_INTERRUPTED == audio_drain(audio_fd, 1))
457                                 goto END_OF_PLAY;
458                 }
459         }
460
461 END_OF_PLAY:
462
463         if (audio_fd > 0) {
464                 reset_device(1);
465                 close(audio_fd);
466         }
467
468 #if 0
469         signal(SIGHUP, sighup_handler);
470         signal(SIGINT, sigint_handler);
471 #endif
472
473         return 1;
474 }
475
476 #undef MYSELF