1 /* Play sound using the SGI audio library
2 written by Simon Leinen <simon@lia.di.epfl.ch>
3 Copyright (C) 1992 Free Software Foundation, Inc.
5 This file is part of XEmacs.
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
22 /* Synched up with: Not in FSF. */
29 #include <sys/types.h>
33 #include <netinet/in.h> /* for ntohl() etc. */
35 /* Configuration options */
37 /* ability to parse Sun/NeXT (.au or .snd) audio file headers. The
38 .snd format supports all sampling rates and sample widths that are
39 commonly used, as well as stereo. It is also easy to parse. */
40 #ifndef HAVE_SND_FILES
41 #define HAVE_SND_FILES 1
44 /* support for eight-but mu-law encoding. This is a useful compaction
45 technique, and most sounds from the Sun universe are in this
48 #define HAVE_MULAW_8 1
51 /* if your machine is very slow, you have to use a table lookup to
52 convert mulaw samples to linear. This makes Emacs bigger so try to
54 #ifndef USE_MULAW_DECODE_TABLE
55 #define USE_MULAW_DECODE_TABLE 0
58 /* support for linear encoding -- useful if you want better quality.
59 This enables 8, 16 and 24 bit wide samples. */
64 /* support for 32 bit wide samples. If you notice the difference
65 between 32 and 24 bit samples, you must have very good ears. Since
66 the SGI audio library only supports 24 bit samples, each sample has
67 to be shifted right by 8 bits anyway. So you should probably just
68 convert all your 32 bit audio files to 24 bit. */
69 #ifndef HAVE_LINEAR_32
70 #define HAVE_LINEAR_32 0
73 /* support for stereo sound. Imagine the cool applications of this:
74 finally you don't just hear a beep -- you also know immediately
75 *where* something went wrong! Unfortunately the programming
76 interface only takes a single volume argument so far. */
81 /* the play routine can be interrupted between chunks, so we choose a
82 small chunksize to keep the system responsive (2000 samples
83 correspond to a quarter of a second for .au files. If you
84 HAVE_STEREO, the chunksize should probably be even. */
85 #define CHUNKSIZE 8000
87 /* the format assumed for header-less audio data. The following
88 assumes ".au" format (8000 samples/sec mono 8-bit mulaw). */
89 #define DEFAULT_SAMPLING_RATE 8000
90 #define DEFAULT_CHANNEL_COUNT 1
91 #define DEFAULT_FORMAT AFmulaw8
95 /* all compilers on machines that have the SGI audio library
96 understand prototypes, right? */
98 extern void play_sound_file (char *, int);
99 extern void play_sound_data (unsigned char *, int, int);
101 /* Data structures */
103 /* an AudioContext describes everything we want to know about how a
104 particular sound snippet should be played. It is split into three
105 parts (device, port and buffer) for implementation reasons. The
106 device part corresponds to the state of the output device and must
107 be reverted after playing the samples. The port part corresponds
108 to an ALport; we want to allocate a minimal number of these since
109 there are only four of them system-wide, but on the other hand we
110 can't use the same port for mono and stereo. The buffer part
111 corresponds to the sound data itself. */
113 typedef struct _AudioContextRec * AudioContext;
118 int left_speaker_gain;
119 int right_speaker_gain;
122 AudioDeviceRec, * AudioDevice;
124 /* supported sound data formats */
151 AudioPortRec, * AudioPort;
157 void (* write_chunk_function) (void *, void *, AudioContext);
159 AudioBufferRec, * AudioBuffer;
161 typedef struct _AudioContextRec
163 AudioDeviceRec device;
165 AudioBufferRec buffer;
169 #define ac_device device.device
170 #define ac_left_speaker_gain device.left_speaker_gain
171 #define ac_right_speaker_gain device.right_speaker_gain
172 #define ac_output_rate device.output_rate
173 #define ac_port port.port
174 #define ac_format port.format
175 #define ac_nchan port.nchan
176 #define ac_queue_size port.queue_size
177 #define ac_data buffer.data
178 #define ac_size buffer.size
179 #define ac_write_chunk_function buffer.write_chunk_function
181 /* Forward declarations */
183 static Lisp_Object close_sound_file (Lisp_Object);
184 static AudioContext audio_initialize (unsigned char *, int, int);
185 static void play_internal (unsigned char *, int, AudioContext);
186 static void drain_audio_port (AudioContext);
187 static void write_mulaw_8_chunk (void *, void *, AudioContext);
188 static void write_linear_chunk (void *, void *, AudioContext);
189 static void write_linear_32_chunk (void *, void *, AudioContext);
190 static Lisp_Object restore_audio_port (Lisp_Object);
191 static AudioContext initialize_audio_port (AudioContext);
192 static int open_audio_port (AudioContext, AudioContext);
193 static void adjust_audio_volume (AudioDevice);
194 static void get_current_volumes (AudioDevice);
195 static int set_channels (ALconfig, unsigned);
196 static int set_output_format (ALconfig, AudioFormat);
197 static int parse_snd_header (void*, long, AudioContext);
199 /* are we looking at an NeXT/Sun audio header? */
200 #define LOOKING_AT_SND_HEADER_P(address) \
201 (!strncmp(".snd", (char *)(address), 4))
204 close_sound_file (closure)
207 close (XINT (closure));
212 play_sound_file (sound_file, volume)
216 int count = specpdl_depth ();
218 unsigned char buffer[CHUNKSIZE];
220 AudioContext ac = (AudioContext) 0;
222 input_fd = open (sound_file, O_RDONLY);
224 /* no error message -- this can't happen
225 because Fplay_sound_file has checked the
229 record_unwind_protect (close_sound_file, make_int (input_fd));
231 while ((bytes_read = read (input_fd, buffer, CHUNKSIZE)) > 0)
233 if (ac == (AudioContext) 0)
235 ac = audio_initialize (buffer, bytes_read, volume);
241 ac->ac_data = buffer;
242 ac->ac_size = bytes_read;
244 play_internal (buffer, bytes_read, ac);
246 drain_audio_port (ac);
247 unbind_to (count, Qnil);
251 saved_device_state[] = {
253 AL_LEFT_SPEAKER_GAIN, 0,
254 AL_RIGHT_SPEAKER_GAIN, 0,
258 restore_audio_port (closure)
261 Lisp_Object * contents = XVECTOR_DATA (closure);
262 saved_device_state[1] = XINT (contents[0]);
263 saved_device_state[3] = XINT (contents[1]);
264 saved_device_state[5] = XINT (contents[2]);
265 ALsetparams (AL_DEFAULT_DEVICE, saved_device_state, 6);
270 play_sound_data (data, length, volume)
271 unsigned char * data;
275 int count = specpdl_depth ();
278 ac = audio_initialize (data, length, volume);
279 if (ac == (AudioContext) 0)
281 play_internal (data, length, ac);
282 drain_audio_port (ac);
283 unbind_to (count, Qnil);
287 audio_initialize (data, length, volume)
288 unsigned char * data;
292 Lisp_Object audio_port_state[3];
293 static AudioContextRec desc;
296 desc.ac_right_speaker_gain
297 = desc.ac_left_speaker_gain
298 = volume * 256 / 100;
299 desc.ac_device = AL_DEFAULT_DEVICE;
302 if (LOOKING_AT_SND_HEADER_P (data))
304 if (parse_snd_header (data, length, & desc)==-1)
305 report_file_error ("decoding .snd header", Qnil);
311 desc.ac_size = length;
312 desc.ac_output_rate = DEFAULT_SAMPLING_RATE;
313 desc.ac_nchan = DEFAULT_CHANNEL_COUNT;
314 desc.ac_format = DEFAULT_FORMAT;
315 desc.ac_write_chunk_function = write_mulaw_8_chunk;
318 /* Make sure that the audio port is reset to
319 its initial characteristics after exit */
320 ALgetparams (desc.ac_device, saved_device_state,
321 sizeof (saved_device_state) / sizeof (long));
322 audio_port_state[0] = make_int (saved_device_state[1]);
323 audio_port_state[1] = make_int (saved_device_state[3]);
324 audio_port_state[2] = make_int (saved_device_state[5]);
325 record_unwind_protect (restore_audio_port,
326 Fvector (3, &audio_port_state[0]));
328 ac = initialize_audio_port (& desc);
334 play_internal (data, length, ac)
335 unsigned char * data;
339 unsigned char * limit;
340 if (ac == (AudioContext) 0)
344 limit = data + ac->ac_size;
347 unsigned char * chunklimit = data + CHUNKSIZE;
349 if (chunklimit > limit)
354 (* ac->ac_write_chunk_function) (data, chunklimit, ac);
360 drain_audio_port (ac)
363 while (ALgetfilled (ac->ac_port) > 0)
367 /* Methods to write a "chunk" from a buffer containing audio data to
368 an audio port. This may involve some conversion if the output
369 device doesn't directly support the format the audio data is in. */
373 #if USE_MULAW_DECODE_TABLE
375 #else /* not USE_MULAW_DECODE_TABLE */
377 st_ulaw_to_linear (u)
380 static CONST short table[] = {0,132,396,924,1980,4092,8316,16764};
382 short exponent = (u1 >> 4) & 0x07;
383 int mantissa = u1 & 0x0f;
384 int unsigned_result = table[exponent]+(mantissa << (exponent+3));
385 return u1 & 0x80 ? -unsigned_result : unsigned_result;
387 #endif /* not USE_MULAW_DECODE_TABLE */
390 write_mulaw_8_chunk (buffer, chunklimit, ac)
395 unsigned char * data = (unsigned char *) buffer;
396 unsigned char * limit = (unsigned char *) chunklimit;
397 short * obuf, * bufp;
398 long n_samples = limit - data;
400 obuf = alloca_array (short, n_samples);
404 *bufp++ = st_ulaw_to_linear (*data++);
405 ALwritesamps (ac->ac_port, obuf, n_samples);
407 #endif /* HAVE_MULAW_8 */
411 write_linear_chunk (data, limit, ac)
418 switch (ac->ac_format)
420 case AFlinear16: n_samples = (short *) limit - (short *) data; break;
421 case AFlinear8: n_samples = (char *) limit - (char *) data; break;
422 default: n_samples = (long *) limit - (long *) data; break;
424 ALwritesamps (ac->ac_port, data, (long) n_samples);
429 write_linear_32_chunk (buffer, chunklimit, ac)
434 long * data = (long *) buffer;
435 long * limit = (long *) chunklimit;
437 long n_samples = limit-data;
439 obuf = alloca_array (long, n_samples);
443 *bufp++ = *data++ >> 8;
444 ALwritesamps (ac->ac_port, obuf, n_samples);
446 #endif /* HAVE_LINEAR_32 */
447 #endif /* HAVE_LINEAR */
450 initialize_audio_port (desc)
453 /* we can't use the same port for mono and stereo */
454 static AudioContextRec mono_port_state
456 { (ALport) 0, AFunknown, 1, 0 },
457 { (void *) 0, (unsigned long) 0 } };
459 static AudioContextRec stereo_port_state
461 { (ALport) 0, AFunknown, 2, 0 },
462 { (void *) 0, (unsigned long) 0 } };
463 static AudioContext return_ac;
465 switch (desc->ac_nchan)
467 case 1: return_ac = & mono_port_state; break;
468 case 2: return_ac = & stereo_port_state; break;
469 default: return (AudioContext) 0;
471 #else /* not HAVE_STEREO */
472 static AudioContext return_ac = & mono_port_state;
473 #endif /* not HAVE_STEREO */
475 return_ac->device = desc->device;
476 return_ac->buffer = desc->buffer;
477 return_ac->ac_format = desc->ac_format;
478 return_ac->ac_queue_size = desc->ac_queue_size;
480 if (return_ac->ac_port==(ALport) 0)
482 if ((open_audio_port (return_ac, desc))==-1)
484 report_file_error ("Open audio port", Qnil);
485 return (AudioContext) 0;
490 ALconfig config = ALgetconfig (return_ac->ac_port);
494 params[0] = AL_OUTPUT_RATE;
495 ALgetparams (return_ac->ac_device, params, 2);
496 return_ac->ac_output_rate = params[1];
498 if (return_ac->ac_output_rate != desc->ac_output_rate)
500 return_ac->ac_output_rate = params[1] = desc->ac_output_rate;
501 ALsetparams (return_ac->ac_device, params, 2);
503 if ((changed = set_output_format (config, return_ac->ac_format))==-1)
504 return (AudioContext) 0;
505 return_ac->ac_format = desc->ac_format;
507 ALsetconfig (return_ac->ac_port, config);
509 return_ac->ac_write_chunk_function = desc->ac_write_chunk_function;
510 get_current_volumes (& return_ac->device);
511 if (return_ac->ac_left_speaker_gain != desc->ac_left_speaker_gain
512 || return_ac->ac_right_speaker_gain != desc->ac_right_speaker_gain)
513 adjust_audio_volume (& desc->device);
518 open_audio_port (return_ac, desc)
519 AudioContext return_ac;
522 ALconfig config = ALnewconfig();
525 adjust_audio_volume (& desc->device);
526 return_ac->ac_left_speaker_gain = desc->ac_left_speaker_gain;
527 return_ac->ac_right_speaker_gain = desc->ac_right_speaker_gain;
528 params[0] = AL_OUTPUT_RATE;
529 params[1] = desc->ac_output_rate;
530 ALsetparams (desc->ac_device, params, 2);
531 return_ac->ac_output_rate = desc->ac_output_rate;
532 if (set_channels (config, desc->ac_nchan)==-1)
534 return_ac->ac_nchan = desc->ac_nchan;
535 if (set_output_format (config, desc->ac_format)==-1)
537 return_ac->ac_format = desc->ac_format;
538 ALsetqueuesize (config, (long) CHUNKSIZE);
539 return_ac->ac_port = ALopenport("XEmacs audio output", "w", config);
540 ALfreeconfig (config);
541 if (return_ac->ac_port==0)
543 report_file_error ("Opening audio output port", Qnil);
550 set_channels (config, nchan)
556 case 1: ALsetchannels (config, AL_MONO); break;
558 case 2: ALsetchannels (config, AL_STEREO); break;
559 #endif /* HAVE_STEREO */
561 report_file_error ("Unsupported channel count",
562 Fcons (make_int (nchan), Qnil));
569 set_output_format (config, format)
584 #if HAVE_MULAW_8 || HAVE_LINEAR
585 samplesize = AL_SAMPLE_16;
590 samplesize = AL_SAMPLE_8;
595 samplesize = AL_SAMPLE_24;
600 report_file_error ("Unsupported audio format",
601 Fcons (make_int (format), Qnil));
604 old_samplesize = ALgetwidth (config);
605 if (old_samplesize==samplesize)
607 ALsetwidth (config, samplesize);
612 adjust_audio_volume (device)
616 params[0] = AL_LEFT_SPEAKER_GAIN;
617 params[1] = device->left_speaker_gain;
618 params[2] = AL_RIGHT_SPEAKER_GAIN;
619 params[3] = device->right_speaker_gain;
620 ALsetparams (device->device, params, 4);
624 get_current_volumes (device)
628 params[0] = AL_LEFT_SPEAKER_GAIN;
629 params[2] = AL_RIGHT_SPEAKER_GAIN;
630 ALgetparams (device->device, params, 4);
631 device->left_speaker_gain = params[1];
632 device->right_speaker_gain = params[3];
637 /* Parsing .snd (NeXT/Sun) headers */
650 #define SOUND_TO_HOST_INT(x) ntohl(x)
654 SND_FORMAT_FORMAT_UNSPECIFIED,
657 SND_FORMAT_LINEAR_16,
658 SND_FORMAT_LINEAR_24,
659 SND_FORMAT_LINEAR_32,
665 SND_FORMAT_DSP_DATA_8,
666 SND_FORMAT_DSP_DATA_16,
667 SND_FORMAT_DSP_DATA_24,
668 SND_FORMAT_DSP_DATA_32,
669 SND_FORMAT_DSP_unknown_15,
671 SND_FORMAT_MULAW_SQUELCH,
672 SND_FORMAT_EMPHASIZED,
673 SND_FORMAT_COMPRESSED,
674 SND_FORMAT_COMPRESSED_EMPHASIZED,
675 SND_FORMAT_DSP_COMMANDS,
676 SND_FORMAT_DSP_COMMANDS_SAMPLES
681 parse_snd_header (header, length, desc)
686 #define hp ((SNDSoundStruct *) (header))
690 desc->ac_write_chunk_function = write_linear_chunk;
692 switch ((SNDFormatCode) SOUND_TO_HOST_INT (hp->dataFormat))
695 case SND_FORMAT_MULAW_8:
696 desc->ac_format = AFmulaw8;
697 desc->ac_write_chunk_function = write_mulaw_8_chunk;
701 case SND_FORMAT_LINEAR_8:
702 desc->ac_format = AFlinear8;
704 case SND_FORMAT_LINEAR_16:
705 desc->ac_format = AFlinear16;
707 case SND_FORMAT_LINEAR_24:
708 desc->ac_format = AFlinear24;
712 case SND_FORMAT_LINEAR_32:
713 desc->ac_format = AFlinear32;
714 desc->ac_write_chunk_function = write_linear_32_chunk;
718 desc->ac_format = AFunknown;
720 desc->ac_output_rate = SOUND_TO_HOST_INT (hp->samplingRate);
721 desc->ac_nchan = SOUND_TO_HOST_INT (hp->channelCount);
722 desc->ac_data = (char *) header + SOUND_TO_HOST_INT (hp->dataLocation);
723 limit = (char *) header + length - (char *) desc->ac_data;
724 desc->ac_size = SOUND_TO_HOST_INT (hp->dataSize);
725 if (desc->ac_size > limit) desc->ac_size = limit;
729 #endif /* HAVE_SND_FILES */