X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/66e4690ffa2438bc74a082d614206a877f4a97e2..5150eeecd86d45af4e4107a33b4a0dd8752e2cba:/src/sound.c?ds=sidebyside diff --git a/src/sound.c b/src/sound.c index 52fe1c18bc..eaa70204da 100644 --- a/src/sound.c +++ b/src/sound.c @@ -1,5 +1,5 @@ /* sound.c -- sound support. - Copyright (C) 1998, 1999 Free Software Foundation. + Copyright (C) 1998, 1999, 2001 Free Software Foundation. This file is part of GNU Emacs. @@ -25,12 +25,19 @@ Boston, MA 02111-1307, USA. */ #if defined HAVE_SOUND -#include #include #include #include -#include #include +#include "lisp.h" +#include "dispextern.h" +#include "atimer.h" +#include +#include "syssignal.h" + +#ifndef MSDOS +#include +#endif /* FreeBSD has machine/soundcard.h. Voxware sound driver docs mention sys/soundcard.h. So, let's try whatever's there. */ @@ -41,9 +48,14 @@ Boston, MA 02111-1307, USA. */ #ifdef HAVE_SYS_SOUNDCARD_H #include #endif +#ifdef HAVE_SOUNDCARD_H +#include +#endif + +#ifndef DEFAULT_SOUND_DEVICE +#define DEFAULT_SOUND_DEVICE "/dev/dsp" +#endif -#define max(X, Y) ((X) > (Y) ? (X) : (Y)) -#define min(X, Y) ((X) < (Y) ? (X) : (Y)) #define abs(X) ((X) < 0 ? -(X) : (X)) /* Structure forward declarations. */ @@ -78,10 +90,10 @@ struct au_header { /* ASCII ".snd" */ u_int32_t magic_number; - + /* Offset of data part from start of file. Minimum value is 24. */ u_int32_t data_offset; - + /* Size of data part, 0xffffffff if unknown. */ u_int32_t data_size; @@ -136,7 +148,7 @@ struct sound_device /* 1 = mono, 2 = stereo, 0 = don't set. */ int channels; - + /* Open device SD. */ void (* open) P_ ((struct sound_device *sd)); @@ -145,7 +157,7 @@ struct sound_device /* Configure SD accoring to device-dependent parameters. */ void (* configure) P_ ((struct sound_device *device)); - + /* Choose a device-dependent format for outputting sound S. */ void (* choose_format) P_ ((struct sound_device *sd, struct sound *s)); @@ -187,7 +199,7 @@ struct sound Lisp_Object data; /* Play sound file S on device SD. */ - void (* play) P_ ((struct sound *s, struct sound_device *sd)); + void (* play) P_ ((struct sound *s, struct sound_device *sd)); }; /* Indices of attributes in a sound attributes vector. */ @@ -223,6 +235,7 @@ static void vox_choose_format P_ ((struct sound_device *, struct sound *)); static void vox_init P_ ((struct sound_device *)); static void vox_write P_ ((struct sound_device *, char *, int)); static void sound_perror P_ ((char *)); +static void sound_warning P_ ((char *)); static int parse_sound P_ ((Lisp_Object, Lisp_Object *)); static void find_sound_type P_ ((struct sound *)); static u_int32_t le2hl P_ ((u_int32_t)); @@ -249,7 +262,26 @@ static void sound_perror (msg) char *msg; { - error ("%s: %s", msg, strerror (errno)); + int saved_errno = errno; + + turn_on_atimers (1); +#ifdef SIGIO + sigunblock (sigmask (SIGIO)); +#endif + if (saved_errno != 0) + error ("%s: %s", msg, strerror (saved_errno)); + else + error ("%s", msg); +} + + +/* Display a warning message. */ + +static void +sound_warning (msg) + char *msg; +{ + message (msg); } @@ -352,28 +384,30 @@ sound_cleanup (arg) if (current_sound->fd > 0) emacs_close (current_sound->fd); } + + return Qnil; } DEFUN ("play-sound", Fplay_sound, Splay_sound, 1, 1, 0, - "Play sound SOUND.\n\ -SOUND is a list of the form `(sound KEYWORD VALUE...)'.\n\ -The following keywords are recognized:\n\ -\n\ - :file FILE.- read sound data from FILE. If FILE Isn't an\n\ -absolute file name, it is searched in `data-directory'.\n\ -\n\ - :data DATA - read sound data from string DATA.\n\ -\n\ -Exactly one of :file or :data must be present.\n\ -\n\ - :volume VOL - set volume to VOL. VOL must an integer in the\n\ -range 0..100 or a float in the range 0..1.0. If not specified,\n\ -don't change the volume setting of the sound device.\n\ -\n\ - :device DEVICE - play sound on DEVICE. If not specified,\n\ -a system-dependent default device name is used.") - (sound) + doc: /* Play sound SOUND. +SOUND is a list of the form `(sound KEYWORD VALUE...)'. +The following keywords are recognized: + + :file FILE - read sound data from FILE. If FILE isn't an +absolute file name, it is searched in `data-directory'. + + :data DATA - read sound data from string DATA. + +Exactly one of :file or :data must be present. + + :volume VOL - set volume to VOL. VOL must an integer in the +range 0..100 or a float in the range 0..1.0. If not specified, +don't change the volume setting of the sound device. + + :device DEVICE - play sound on DEVICE. If not specified, +a system-dependent default device name is used. */) + (sound) Lisp_Object sound; { Lisp_Object attrs[SOUND_ATTR_SENTINEL]; @@ -401,20 +435,20 @@ a system-dependent default device name is used.") { /* Open the sound file. */ s.fd = openp (Fcons (Vdata_directory, Qnil), - attrs[SOUND_FILE], "", &file, 0); + attrs[SOUND_FILE], Qnil, &file, 0); if (s.fd < 0) - sound_perror ("Open sound file"); + sound_perror ("Could not open sound file"); /* Read the first bytes from the file. */ s.header_size = emacs_read (s.fd, s.header, MAX_SOUND_HEADER_BYTES); if (s.header_size < 0) - sound_perror ("Reading sound file header"); + sound_perror ("Invalid sound file header"); } else { s.data = attrs[SOUND_DATA]; - bcopy (XSTRING (s.data)->data, s.header, - min (MAX_SOUND_HEADER_BYTES, STRING_BYTES (XSTRING (s.data)))); + s.header_size = min (MAX_SOUND_HEADER_BYTES, STRING_BYTES (XSTRING (s.data))); + bcopy (XSTRING (s.data)->data, s.header, s.header_size); } /* Find out the type of sound. Give up if we can't tell. */ @@ -427,7 +461,7 @@ a system-dependent default device name is used.") sd.file = (char *) alloca (len + 1); strcpy (sd.file, XSTRING (attrs[SOUND_DEVICE])->data); } - + if (INTEGERP (attrs[SOUND_VOLUME])) sd.volume = XFASTINT (attrs[SOUND_VOLUME]); else if (FLOATP (attrs[SOUND_VOLUME])) @@ -435,7 +469,7 @@ a system-dependent default device name is used.") args[0] = Qplay_sound_functions; args[1] = sound; - Frun_hook_with_args (make_number (2), args); + Frun_hook_with_args (2, args); /* There is only one type of device we currently support, the VOX sound driver. Set up the device interface functions for that @@ -576,7 +610,7 @@ wav_init (s) s->play = wav_play; return 1; -} +} /* Play RIFF-WAVE audio file S on sound device SD. */ @@ -591,7 +625,7 @@ wav_play (s, sd) /* Let the device choose a suitable device-dependent format for the file. */ sd->choose_format (sd, s); - + /* Configure the device. */ sd->sample_size = header->sample_size; sd->sample_rate = header->sample_rate; @@ -611,15 +645,15 @@ wav_play (s, sd) char *buffer; int nbytes; int blksize = 2048; - + buffer = (char *) alloca (blksize); lseek (s->fd, sizeof *header, SEEK_SET); - + while ((nbytes = emacs_read (s->fd, buffer, blksize)) > 0) sd->write (sd, buffer, nbytes); if (nbytes < 0) - sound_perror ("Reading sound file"); + sound_perror ("Error reading sound file"); } } @@ -629,7 +663,7 @@ wav_play (s, sd) Sun Audio (*.au) ***********************************************************************/ -/* Sun audio file encodings. */ +/* Sun audio file encodings. */ enum au_encoding { @@ -655,18 +689,18 @@ au_init (s) struct sound *s; { struct au_header *header = (struct au_header *) s->header; - + if (s->header_size < sizeof *header || bcmp (s->header, ".snd", 4) != 0) return 0; - + header->magic_number = be2hl (header->magic_number); header->data_offset = be2hl (header->data_offset); header->data_size = be2hl (header->data_size); header->encoding = be2hl (header->encoding); header->sample_rate = be2hl (header->sample_rate); header->channels = be2hl (header->channels); - + /* Set up the interface functions for AU. */ s->type = SUN_AUDIO; s->play = au_play; @@ -699,17 +733,17 @@ au_play (s, sd) int blksize = 2048; char *buffer; int nbytes; - + /* Seek */ lseek (s->fd, header->data_offset, SEEK_SET); - + /* Copy sound data to the device. */ buffer = (char *) alloca (blksize); while ((nbytes = emacs_read (s->fd, buffer, blksize)) > 0) sd->write (sd, buffer, nbytes); - + if (nbytes < 0) - sound_perror ("Reading sound file"); + sound_perror ("Error reading sound file"); } } @@ -731,13 +765,13 @@ vox_open (sd) struct sound_device *sd; { char *file; - + /* Open the sound device. Default is /dev/dsp. */ if (sd->file) file = sd->file; else - file = "/dev/dsp"; - + file = DEFAULT_SOUND_DEVICE; + sd->fd = emacs_open (file, O_WRONLY, 0); if (sd->fd < 0) sound_perror (file); @@ -750,38 +784,51 @@ static void vox_configure (sd) struct sound_device *sd; { - int requested; - + int val; + xassert (sd->fd >= 0); - /* Device parameters apparently depend on each other in undocumented - ways (not to imply that there is any real documentation). Be - careful when reordering the calls below. */ - if (sd->sample_size > 0 - && ioctl (sd->fd, SNDCTL_DSP_SAMPLESIZE, &sd->sample_size) < 0) - sound_perror ("Setting sample size"); - - if (sd->bps > 0 - && ioctl (sd->fd, SNDCTL_DSP_SPEED, &sd->bps) < 0) - sound_perror ("Setting speed"); - - if (sd->sample_rate > 0 - && ioctl (sd->fd, SOUND_PCM_WRITE_RATE, &sd->sample_rate) < 0) - sound_perror ("Setting sample rate"); - - requested = sd->format; - if (ioctl (sd->fd, SNDCTL_DSP_SETFMT, &sd->format) < 0) - sound_perror ("Setting format"); - else if (requested != sd->format) - error ("Setting format"); - - if (sd->channels > 1 - && ioctl (sd->fd, SNDCTL_DSP_STEREO, &sd->channels) < 0) - sound_perror ("Setting channels"); - - if (sd->volume > 0 - && ioctl (sd->fd, SOUND_MIXER_WRITE_PCM, &sd->volume) < 0) - sound_perror ("Setting volume"); + /* On GNU/Linux, it seems that the device driver doesn't like to be + interrupted by a signal. Block the ones we know to cause + troubles. */ + turn_on_atimers (0); +#ifdef SIGIO + sigblock (sigmask (SIGIO)); +#endif + + val = sd->format; + if (ioctl (sd->fd, SNDCTL_DSP_SETFMT, &sd->format) < 0 + || val != sd->format) + sound_perror ("Could not set sound format"); + + val = sd->channels != 1; + if (ioctl (sd->fd, SNDCTL_DSP_STEREO, &val) < 0 + || val != (sd->channels != 1)) + sound_perror ("Could not set stereo/mono"); + + /* I think bps and sampling_rate are the same, but who knows. + Check this. and use SND_DSP_SPEED for both. */ + if (sd->sample_rate > 0) + { + val = sd->sample_rate; + if (ioctl (sd->fd, SNDCTL_DSP_SPEED, &sd->sample_rate) < 0) + sound_perror ("Could not set sound speed"); + else if (val != sd->sample_rate) + sound_warning ("Could not set sample rate"); + } + + if (sd->volume > 0) + { + int volume = sd->volume & 0xff; + volume |= volume << 8; + /* This may fail if there is no mixer. Ignore the failure. */ + ioctl (sd->fd, SOUND_MIXER_WRITE_PCM, &volume); + } + + turn_on_atimers (1); +#ifdef SIGIO + sigunblock (sigmask (SIGIO)); +#endif } @@ -793,9 +840,21 @@ vox_close (sd) { if (sd->fd >= 0) { + /* On GNU/Linux, it seems that the device driver doesn't like to + be interrupted by a signal. Block the ones we know to cause + troubles. */ +#ifdef SIGIO + sigblock (sigmask (SIGIO)); +#endif + turn_on_atimers (0); + /* Flush sound data, and reset the device. */ ioctl (sd->fd, SNDCTL_DSP_SYNC, NULL); - ioctl (sd->fd, SNDCTL_DSP_RESET, NULL); + + turn_on_atimers (1); +#ifdef SIGIO + sigunblock (sigmask (SIGIO)); +#endif /* Close the device. */ emacs_close (sd->fd); @@ -831,7 +890,7 @@ vox_choose_format (sd, s) case AU_ENCODING_IEEE64: sd->format = AFMT_MU_LAW; break; - + case AU_ENCODING_8: case AU_ENCODING_16: case AU_ENCODING_24: @@ -874,7 +933,7 @@ vox_write (sd, buffer, nbytes) { int nwritten = emacs_write (sd->fd, buffer, nbytes); if (nwritten < 0) - sound_perror ("Writing to sound device"); + sound_perror ("Error writing to sound device"); }