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>
34 #include <netinet/in.h> /* for ntohl() etc. */
36 /* Configuration options */
38 /* ability to parse Sun/NeXT (.au or .snd) audio file headers. The
39 .snd format supports all sampling rates and sample widths that are
40 commonly used, as well as stereo. It is also easy to parse. */
41 #ifndef HAVE_SND_FILES
42 #define HAVE_SND_FILES 1
45 /* support for eight-but mu-law encoding. This is a useful compaction
46 technique, and most sounds from the Sun universe are in this
49 #define HAVE_MULAW_8 1
52 /* if your machine is very slow, you have to use a table lookup to
53 convert mulaw samples to linear. This makes Emacs bigger so try to
55 #ifndef USE_MULAW_DECODE_TABLE
56 #define USE_MULAW_DECODE_TABLE 0
59 /* support for linear encoding -- useful if you want better quality.
60 This enables 8, 16 and 24 bit wide samples. */
65 /* support for 32 bit wide samples. If you notice the difference
66 between 32 and 24 bit samples, you must have very good ears. Since
67 the SGI audio library only supports 24 bit samples, each sample has
68 to be shifted right by 8 bits anyway. So you should probably just
69 convert all your 32 bit audio files to 24 bit. */
70 #ifndef HAVE_LINEAR_32
71 #define HAVE_LINEAR_32 0
74 /* support for stereo sound. Imagine the cool applications of this:
75 finally you don't just hear a beep -- you also know immediately
76 *where* something went wrong! Unfortunately the programming
77 interface only takes a single volume argument so far. */
82 /* the play routine can be interrupted between chunks, so we choose a
83 small chunksize to keep the system responsive (2000 samples
84 correspond to a quarter of a second for .au files. If you
85 HAVE_STEREO, the chunksize should probably be even. */
86 #define CHUNKSIZE 8000
88 /* the format assumed for header-less audio data. The following
89 assumes ".au" format (8000 samples/sec mono 8-bit mulaw). */
90 #define DEFAULT_SAMPLING_RATE 8000
91 #define DEFAULT_CHANNEL_COUNT 1
92 #define DEFAULT_FORMAT AFmulaw8
96 /* all compilers on machines that have the SGI audio library
97 understand prototypes, right? */
99 extern void play_sound_file (char *, int);
100 extern void play_sound_data (unsigned char *, int, int);
102 /* Data structures */
104 /* an AudioContext describes everything we want to know about how a
105 particular sound snippet should be played. It is split into three
106 parts (device, port and buffer) for implementation reasons. The
107 device part corresponds to the state of the output device and must
108 be reverted after playing the samples. The port part corresponds
109 to an ALport; we want to allocate a minimal number of these since
110 there are only four of them system-wide, but on the other hand we
111 can't use the same port for mono and stereo. The buffer part
112 corresponds to the sound data itself. */
114 typedef struct _AudioContextRec * AudioContext;
119 int left_speaker_gain;
120 int right_speaker_gain;
123 AudioDeviceRec, * AudioDevice;
125 /* supported sound data formats */
152 AudioPortRec, * AudioPort;
158 void (* write_chunk_function) (void *, void *, AudioContext);
160 AudioBufferRec, * AudioBuffer;
162 typedef struct _AudioContextRec
164 AudioDeviceRec device;
166 AudioBufferRec buffer;
170 #define ac_device device.device
171 #define ac_left_speaker_gain device.left_speaker_gain
172 #define ac_right_speaker_gain device.right_speaker_gain
173 #define ac_output_rate device.output_rate
174 #define ac_port port.port
175 #define ac_format port.format
176 #define ac_nchan port.nchan
177 #define ac_queue_size port.queue_size
178 #define ac_data buffer.data
179 #define ac_size buffer.size
180 #define ac_write_chunk_function buffer.write_chunk_function
182 /* Forward declarations */
184 static Lisp_Object close_sound_file (Lisp_Object);
185 static AudioContext audio_initialize (unsigned char *, int, int);
186 static void play_internal (unsigned char *, int, AudioContext);
187 static void drain_audio_port (AudioContext);
188 static void write_mulaw_8_chunk (void *, void *, AudioContext);
189 static void write_linear_chunk (void *, void *, AudioContext);
190 static void write_linear_32_chunk (void *, void *, AudioContext);
191 static Lisp_Object restore_audio_port (Lisp_Object);
192 static AudioContext initialize_audio_port (AudioContext);
193 static int open_audio_port (AudioContext, AudioContext);
194 static void adjust_audio_volume (AudioDevice);
195 static void get_current_volumes (AudioDevice);
196 static int set_channels (ALconfig, unsigned);
197 static int set_output_format (ALconfig, AudioFormat);
198 static int parse_snd_header (void*, long, AudioContext);
200 /* are we looking at an NeXT/Sun audio header? */
201 #define LOOKING_AT_SND_HEADER_P(address) \
202 (!strncmp(".snd", (char *)(address), 4))
205 close_sound_file (Lisp_Object closure)
207 close (XINT (closure));
212 play_sound_file (char *sound_file, int volume)
214 int count = specpdl_depth ();
216 unsigned char buffer[CHUNKSIZE];
218 AudioContext ac = (AudioContext) 0;
220 input_fd = open (sound_file, O_RDONLY);
222 /* no error message -- this can't happen
223 because Fplay_sound_file has checked the
227 record_unwind_protect (close_sound_file, make_int (input_fd));
229 while ((bytes_read = read (input_fd, buffer, CHUNKSIZE)) > 0)
231 if (ac == (AudioContext) 0)
233 ac = audio_initialize (buffer, bytes_read, volume);
239 ac->ac_data = buffer;
240 ac->ac_size = bytes_read;
242 play_internal (buffer, bytes_read, ac);
244 drain_audio_port (ac);
245 unbind_to (count, Qnil);
249 saved_device_state[] = {
251 AL_LEFT_SPEAKER_GAIN, 0,
252 AL_RIGHT_SPEAKER_GAIN, 0,
256 restore_audio_port (Lisp_Object closure)
258 Lisp_Object * contents = XVECTOR_DATA (closure);
259 saved_device_state[1] = XINT (contents[0]);
260 saved_device_state[3] = XINT (contents[1]);
261 saved_device_state[5] = XINT (contents[2]);
262 ALsetparams (AL_DEFAULT_DEVICE, saved_device_state, 6);
267 play_sound_data (unsigned char *data, int length, int volume)
269 int count = specpdl_depth ();
272 ac = audio_initialize (data, length, volume);
273 if (ac == (AudioContext) 0)
275 play_internal (data, length, ac);
276 drain_audio_port (ac);
277 unbind_to (count, Qnil);
281 audio_initialize (unsigned char *data, int length, int volume)
283 Lisp_Object audio_port_state[3];
284 static AudioContextRec desc;
287 desc.ac_right_speaker_gain
288 = desc.ac_left_speaker_gain
289 = volume * 256 / 100;
290 desc.ac_device = AL_DEFAULT_DEVICE;
293 if (LOOKING_AT_SND_HEADER_P (data))
295 if (parse_snd_header (data, length, & desc)==-1)
296 report_file_error ("decoding .snd header", Qnil);
302 desc.ac_size = length;
303 desc.ac_output_rate = DEFAULT_SAMPLING_RATE;
304 desc.ac_nchan = DEFAULT_CHANNEL_COUNT;
305 desc.ac_format = DEFAULT_FORMAT;
306 desc.ac_write_chunk_function = write_mulaw_8_chunk;
309 /* Make sure that the audio port is reset to
310 its initial characteristics after exit */
311 ALgetparams (desc.ac_device, saved_device_state,
312 sizeof (saved_device_state) / sizeof (long));
313 audio_port_state[0] = make_int (saved_device_state[1]);
314 audio_port_state[1] = make_int (saved_device_state[3]);
315 audio_port_state[2] = make_int (saved_device_state[5]);
316 record_unwind_protect (restore_audio_port,
317 Fvector (3, &audio_port_state[0]));
319 ac = initialize_audio_port (& desc);
325 play_internal (unsigned char *data, int length, AudioContext ac)
327 unsigned char * limit;
328 if (ac == (AudioContext) 0)
331 data = (unsigned char *) ac->ac_data;
332 limit = data + ac->ac_size;
335 unsigned char * chunklimit = data + CHUNKSIZE;
337 if (chunklimit > limit)
342 (* ac->ac_write_chunk_function) (data, chunklimit, ac);
348 drain_audio_port (AudioContext ac)
350 while (ALgetfilled (ac->ac_port) > 0)
354 /* Methods to write a "chunk" from a buffer containing audio data to
355 an audio port. This may involve some conversion if the output
356 device doesn't directly support the format the audio data is in. */
360 #if USE_MULAW_DECODE_TABLE
362 #else /* not USE_MULAW_DECODE_TABLE */
364 st_ulaw_to_linear (int u)
366 static const short table[] = {0,132,396,924,1980,4092,8316,16764};
368 short exponent = (u1 >> 4) & 0x07;
369 int mantissa = u1 & 0x0f;
370 int unsigned_result = table[exponent]+(mantissa << (exponent+3));
371 return u1 & 0x80 ? -unsigned_result : unsigned_result;
373 #endif /* not USE_MULAW_DECODE_TABLE */
376 write_mulaw_8_chunk (void *buffer, void *chunklimit, AudioContext ac)
378 unsigned char * data = (unsigned char *) buffer;
379 unsigned char * limit = (unsigned char *) chunklimit;
380 short * obuf, * bufp;
381 long n_samples = limit - data;
383 obuf = alloca_array (short, n_samples);
387 *bufp++ = st_ulaw_to_linear (*data++);
388 ALwritesamps (ac->ac_port, obuf, n_samples);
390 #endif /* HAVE_MULAW_8 */
394 write_linear_chunk (void *data, void *limit, AudioContext ac)
398 switch (ac->ac_format)
400 case AFlinear16: n_samples = (short *) limit - (short *) data; break;
401 case AFlinear8: n_samples = (char *) limit - (char *) data; break;
402 default: n_samples = (long *) limit - (long *) data; break;
404 ALwritesamps (ac->ac_port, data, (long) n_samples);
409 write_linear_32_chunk (void *buffer, void *chunklimit, AudioContext ac)
411 long * data = (long *) buffer;
412 long * limit = (long *) chunklimit;
414 long n_samples = limit-data;
416 obuf = alloca_array (long, n_samples);
420 *bufp++ = *data++ >> 8;
421 ALwritesamps (ac->ac_port, obuf, n_samples);
423 #endif /* HAVE_LINEAR_32 */
424 #endif /* HAVE_LINEAR */
427 initialize_audio_port (AudioContext desc)
429 /* we can't use the same port for mono and stereo */
430 static AudioContextRec mono_port_state
432 { (ALport) 0, AFunknown, 1, 0 },
433 { (void *) 0, (unsigned long) 0 } };
435 static AudioContextRec stereo_port_state
437 { (ALport) 0, AFunknown, 2, 0 },
438 { (void *) 0, (unsigned long) 0 } };
439 static AudioContext return_ac;
441 switch (desc->ac_nchan)
443 case 1: return_ac = & mono_port_state; break;
444 case 2: return_ac = & stereo_port_state; break;
445 default: return (AudioContext) 0;
447 #else /* not HAVE_STEREO */
448 static AudioContext return_ac = & mono_port_state;
449 #endif /* not HAVE_STEREO */
451 return_ac->device = desc->device;
452 return_ac->buffer = desc->buffer;
453 return_ac->ac_format = desc->ac_format;
454 return_ac->ac_queue_size = desc->ac_queue_size;
456 if (return_ac->ac_port==(ALport) 0)
458 if ((open_audio_port (return_ac, desc))==-1)
460 report_file_error ("Open audio port", Qnil);
461 return (AudioContext) 0;
466 ALconfig config = ALgetconfig (return_ac->ac_port);
470 params[0] = AL_OUTPUT_RATE;
471 ALgetparams (return_ac->ac_device, params, 2);
472 return_ac->ac_output_rate = params[1];
474 if (return_ac->ac_output_rate != desc->ac_output_rate)
476 return_ac->ac_output_rate = params[1] = desc->ac_output_rate;
477 ALsetparams (return_ac->ac_device, params, 2);
479 if ((changed = set_output_format (config, return_ac->ac_format))==-1)
480 return (AudioContext) 0;
481 return_ac->ac_format = desc->ac_format;
483 ALsetconfig (return_ac->ac_port, config);
485 return_ac->ac_write_chunk_function = desc->ac_write_chunk_function;
486 get_current_volumes (& return_ac->device);
487 if (return_ac->ac_left_speaker_gain != desc->ac_left_speaker_gain
488 || return_ac->ac_right_speaker_gain != desc->ac_right_speaker_gain)
489 adjust_audio_volume (& desc->device);
494 open_audio_port (AudioContext return_ac, AudioContext desc)
496 ALconfig config = ALnewconfig();
499 adjust_audio_volume (& desc->device);
500 return_ac->ac_left_speaker_gain = desc->ac_left_speaker_gain;
501 return_ac->ac_right_speaker_gain = desc->ac_right_speaker_gain;
502 params[0] = AL_OUTPUT_RATE;
503 params[1] = desc->ac_output_rate;
504 ALsetparams (desc->ac_device, params, 2);
505 return_ac->ac_output_rate = desc->ac_output_rate;
506 if (set_channels (config, desc->ac_nchan)==-1)
508 return_ac->ac_nchan = desc->ac_nchan;
509 if (set_output_format (config, desc->ac_format)==-1)
511 return_ac->ac_format = desc->ac_format;
512 ALsetqueuesize (config, (long) CHUNKSIZE);
513 return_ac->ac_port = ALopenport("XEmacs audio output", "w", config);
514 ALfreeconfig (config);
515 if (return_ac->ac_port==0)
517 report_file_error ("Opening audio output port", Qnil);
524 set_channels (ALconfig config, unsigned int nchan)
528 case 1: ALsetchannels (config, AL_MONO); break;
530 case 2: ALsetchannels (config, AL_STEREO); break;
531 #endif /* HAVE_STEREO */
533 report_file_error ("Unsupported channel count",
534 Fcons (make_int (nchan), Qnil));
541 set_output_format (ALconfig config, AudioFormat format)
554 #if HAVE_MULAW_8 || HAVE_LINEAR
555 samplesize = AL_SAMPLE_16;
560 samplesize = AL_SAMPLE_8;
565 samplesize = AL_SAMPLE_24;
570 report_file_error ("Unsupported audio format",
571 Fcons (make_int (format), Qnil));
574 old_samplesize = ALgetwidth (config);
575 if (old_samplesize==samplesize)
577 ALsetwidth (config, samplesize);
582 adjust_audio_volume (AudioDevice device)
585 params[0] = AL_LEFT_SPEAKER_GAIN;
586 params[1] = device->left_speaker_gain;
587 params[2] = AL_RIGHT_SPEAKER_GAIN;
588 params[3] = device->right_speaker_gain;
589 ALsetparams (device->device, params, 4);
593 get_current_volumes (AudioDevice device)
596 params[0] = AL_LEFT_SPEAKER_GAIN;
597 params[2] = AL_RIGHT_SPEAKER_GAIN;
598 ALgetparams (device->device, params, 4);
599 device->left_speaker_gain = params[1];
600 device->right_speaker_gain = params[3];
605 /* Parsing .snd (NeXT/Sun) headers */
618 #define SOUND_TO_HOST_INT(x) ntohl(x)
622 SND_FORMAT_FORMAT_UNSPECIFIED,
625 SND_FORMAT_LINEAR_16,
626 SND_FORMAT_LINEAR_24,
627 SND_FORMAT_LINEAR_32,
633 SND_FORMAT_DSP_DATA_8,
634 SND_FORMAT_DSP_DATA_16,
635 SND_FORMAT_DSP_DATA_24,
636 SND_FORMAT_DSP_DATA_32,
637 SND_FORMAT_DSP_unknown_15,
639 SND_FORMAT_MULAW_SQUELCH,
640 SND_FORMAT_EMPHASIZED,
641 SND_FORMAT_COMPRESSED,
642 SND_FORMAT_COMPRESSED_EMPHASIZED,
643 SND_FORMAT_DSP_COMMANDS,
644 SND_FORMAT_DSP_COMMANDS_SAMPLES
649 parse_snd_header (void *header, long length, AudioContext desc)
651 #define hp ((SNDSoundStruct *) (header))
655 desc->ac_write_chunk_function = write_linear_chunk;
657 switch ((SNDFormatCode) SOUND_TO_HOST_INT (hp->dataFormat))
660 case SND_FORMAT_MULAW_8:
661 desc->ac_format = AFmulaw8;
662 desc->ac_write_chunk_function = write_mulaw_8_chunk;
666 case SND_FORMAT_LINEAR_8:
667 desc->ac_format = AFlinear8;
669 case SND_FORMAT_LINEAR_16:
670 desc->ac_format = AFlinear16;
672 case SND_FORMAT_LINEAR_24:
673 desc->ac_format = AFlinear24;
677 case SND_FORMAT_LINEAR_32:
678 desc->ac_format = AFlinear32;
679 desc->ac_write_chunk_function = write_linear_32_chunk;
683 desc->ac_format = AFunknown;
685 desc->ac_output_rate = SOUND_TO_HOST_INT (hp->samplingRate);
686 desc->ac_nchan = SOUND_TO_HOST_INT (hp->channelCount);
687 desc->ac_data = (char *) header + SOUND_TO_HOST_INT (hp->dataLocation);
688 limit = (char *) header + length - (char *) desc->ac_data;
689 desc->ac_size = SOUND_TO_HOST_INT (hp->dataSize);
690 if (desc->ac_size > limit) desc->ac_size = limit;
694 #endif /* HAVE_SND_FILES */