]> code.delx.au - gnu-emacs/blobdiff - src/sound.c
'temacs -nw' should not call missing functions
[gnu-emacs] / src / sound.c
index 151bc1a08c16418ab64e0b5551f092dfaad01c23..88d86f6f84acd8a0754f22fa044d3439e80263d0 100644 (file)
@@ -1,6 +1,6 @@
 /* sound.c -- sound support.
 
-Copyright (C) 1998-1999, 2001-2012 Free Software Foundation, Inc.
+Copyright (C) 1998-1999, 2001-2015 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -31,7 +31,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
   cause an error to be generated.
 
   The Windows implementation of play-sound is implemented via the
-  Win32 API functions mciSendString, waveOutGetVolume, and
+  Windows API functions mciSendString, waveOutGetVolume, and
   waveOutSetVolume which are exported by Winmm.dll.
 */
 
@@ -44,11 +44,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <unistd.h>
 #include <sys/types.h>
 #include <errno.h>
-#include <setjmp.h>
+
 #include "lisp.h"
 #include "dispextern.h"
 #include "atimer.h"
-#include <signal.h>
 #include "syssignal.h"
 /* END: Common Includes */
 
@@ -56,6 +55,8 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 /* BEGIN: Non Windows Includes */
 #ifndef WINDOWSNT
 
+#include <byteswap.h>
+
 #include <sys/ioctl.h>
 
 /* FreeBSD has machine/soundcard.h.  Voxware sound driver docs mention
@@ -85,8 +86,13 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 /* BEGIN: Windows Specific Includes */
 #include <stdio.h>
 #include <limits.h>
+#include <mbstring.h>
 #include <windows.h>
 #include <mmsystem.h>
+
+#include "coding.h"
+#include "w32common.h"
+#include "w32.h"
 /* END: Windows Specific Includes */
 
 #endif /* WINDOWSNT */
@@ -182,8 +188,8 @@ struct au_header
 
 struct sound_device
 {
-  /* The name of the device or null meaning use a default device name.  */
-  char *file;
+  /* If a string, the name of the device; otherwise use a default.  */
+  Lisp_Object file;
 
   /* File descriptor of the device.  */
   int fd;
@@ -272,25 +278,12 @@ static struct sound *current_sound;
 
 /* Function prototypes.  */
 
-static void vox_open (struct sound_device *);
-static void vox_configure (struct sound_device *);
-static void vox_close (struct sound_device *sd);
-static void vox_choose_format (struct sound_device *, struct sound *);
-static int vox_init (struct sound_device *);
 static void vox_write (struct sound_device *, const char *, ptrdiff_t);
-static void find_sound_type (struct sound *);
-static u_int32_t le2hl (u_int32_t);
-static u_int16_t le2hs (u_int16_t);
-static u_int32_t be2hl (u_int32_t);
-static int wav_init (struct sound *);
+static bool wav_init (struct sound *);
 static void wav_play (struct sound *, struct sound_device *);
-static int au_init (struct sound *);
+static bool au_init (struct sound *);
 static void au_play (struct sound *, struct sound_device *);
 
-#if 0 /* Currently not used.  */
-static u_int16_t be2hs (u_int16_t);
-#endif
-
 /* END: Non Windows Definitions */
 #else /* WINDOWSNT */
 
@@ -315,8 +308,13 @@ sound_perror (const char *msg)
   int saved_errno = errno;
 
   turn_on_atimers (1);
-#ifdef SIGIO
-  sigunblock (sigmask (SIGIO));
+#ifdef USABLE_SIGIO
+  {
+    sigset_t unblocked;
+    sigemptyset (&unblocked);
+    sigaddset (&unblocked, SIGIO);
+    pthread_sigmask (SIG_UNBLOCK, &unblocked, 0);
+  }
 #endif
   if (saved_errno != 0)
     error ("%s: %s", msg, strerror (saved_errno));
@@ -330,7 +328,7 @@ sound_perror (const char *msg)
 static void
 sound_warning (const char *msg)
 {
-  message ("%s", msg);
+  message1 (msg);
 }
 
 
@@ -360,7 +358,7 @@ sound_warning (const char *msg)
    VOL must be an integer in the range [0, 100], or a float in the
    range [0, 1].  */
 
-static int
+static bool
 parse_sound (Lisp_Object sound, Lisp_Object *attrs)
 {
   /* SOUND must be a list starting with the symbol `sound'.  */
@@ -426,6 +424,15 @@ parse_sound (Lisp_Object sound, Lisp_Object *attrs)
 /* BEGIN: Non Windows functions */
 #ifndef WINDOWSNT
 
+/* Return S's value as a string if S is a string, otherwise DEFAULT_VALUE.  */
+
+static char const *
+string_default (Lisp_Object s, char const *default_value)
+{
+  return STRINGP (s) ? SSDATA (s) : default_value;
+}
+
+
 /* Find out the type of the sound file whose file descriptor is FD.
    S is the sound file structure to fill in.  */
 
@@ -437,10 +444,10 @@ find_sound_type (struct sound *s)
 }
 
 
-/* Function installed by play-sound-internal with record_unwind_protect.  */
+/* Function installed by play-sound-internal with record_unwind_protect_void.  */
 
-static Lisp_Object
-sound_cleanup (Lisp_Object arg)
+static void
+sound_cleanup (void)
 {
   if (current_sound_device->close)
     current_sound_device->close (current_sound_device);
@@ -448,8 +455,6 @@ sound_cleanup (Lisp_Object arg)
     emacs_close (current_sound->fd);
   xfree (current_sound_device);
   xfree (current_sound);
-
-  return Qnil;
 }
 
 /***********************************************************************
@@ -463,8 +468,7 @@ static u_int32_t
 le2hl (u_int32_t value)
 {
 #ifdef WORDS_BIGENDIAN
-  unsigned char *p = (unsigned char *) &value;
-  value = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
+  value = bswap_32 (value);
 #endif
   return value;
 }
@@ -477,8 +481,7 @@ static u_int16_t
 le2hs (u_int16_t value)
 {
 #ifdef WORDS_BIGENDIAN
-  unsigned char *p = (unsigned char *) &value;
-  value = p[0] + (p[1] << 8);
+  value = bswap_16 (value);
 #endif
   return value;
 }
@@ -491,30 +494,11 @@ static u_int32_t
 be2hl (u_int32_t value)
 {
 #ifndef WORDS_BIGENDIAN
-  unsigned char *p = (unsigned char *) &value;
-  value = p[3] + (p[2] << 8) + (p[1] << 16) + (p[0] << 24);
+  value = bswap_32 (value);
 #endif
   return value;
 }
 
-
-#if 0 /* Currently not used.  */
-
-/* Convert 16-bit value VALUE which is in big-endian byte-order
-   to host byte-order.  */
-
-static u_int16_t
-be2hs (u_int16_t value)
-{
-#ifndef WORDS_BIGENDIAN
-  unsigned char *p = (unsigned char *) &value;
-  value = p[1] + (p[0] << 8);
-#endif
-  return value;
-}
-
-#endif /* 0 */
-
 /***********************************************************************
                          RIFF-WAVE (*.wav)
  ***********************************************************************/
@@ -523,9 +507,9 @@ be2hs (u_int16_t value)
    contains the first MAX_SOUND_HEADER_BYTES number of bytes from the
    sound file.  If the file is a WAV-format file, set up interface
    functions in S and convert header fields to host byte-order.
-   Value is non-zero if the file is a WAV file.  */
+   Value is true if the file is a WAV file.  */
 
-static int
+static bool
 wav_init (struct sound *s)
 {
   struct wav_header *header = (struct wav_header *) s->header;
@@ -585,12 +569,11 @@ wav_play (struct sound *s, struct sound_device *sd)
               SBYTES (s->data) - sizeof *header);
   else
     {
-      char *buffer;
       ptrdiff_t nbytes = 0;
       ptrdiff_t blksize = sd->period_size ? sd->period_size (sd) : 2048;
       ptrdiff_t data_left = header->data_length;
-
-      buffer = alloca (blksize);
+      USE_SAFE_ALLOCA;
+      char *buffer = SAFE_ALLOCA (blksize);
       lseek (s->fd, sizeof *header, SEEK_SET);
       while (data_left > 0
              && (nbytes = emacs_read (s->fd, buffer, blksize)) > 0)
@@ -603,6 +586,7 @@ wav_play (struct sound *s, struct sound_device *sd)
 
       if (nbytes < 0)
        sound_perror ("Error reading sound file");
+      SAFE_FREE ();
     }
 }
 
@@ -631,9 +615,9 @@ enum au_encoding
    contains the first MAX_SOUND_HEADER_BYTES number of bytes from the
    sound file.  If the file is a AU-format file, set up interface
    functions in S and convert header fields to host byte-order.
-   Value is non-zero if the file is an AU file.  */
+   Value is true if the file is an AU file.  */
 
-static int
+static bool
 au_init (struct sound *s)
 {
   struct au_header *header = (struct au_header *) s->header;
@@ -677,19 +661,20 @@ au_play (struct sound *s, struct sound_device *sd)
   else
     {
       ptrdiff_t blksize = sd->period_size ? sd->period_size (sd) : 2048;
-      char *buffer;
       ptrdiff_t nbytes;
 
       /* Seek */
       lseek (s->fd, header->data_offset, SEEK_SET);
 
       /* Copy sound data to the device.  */
-      buffer = alloca (blksize);
+      USE_SAFE_ALLOCA;
+      char *buffer = SAFE_ALLOCA (blksize);
       while ((nbytes = emacs_read (s->fd, buffer, blksize)) > 0)
        sd->write (sd, buffer, nbytes);
 
       if (nbytes < 0)
        sound_perror ("Error reading sound file");
+      SAFE_FREE ();
     }
 }
 
@@ -702,20 +687,14 @@ au_play (struct sound *s, struct sound_device *sd)
    has a compatible own driver aka Luigi's driver.  */
 
 
-/* Open device SD.  If SD->file is non-null, open that device,
+/* Open device SD.  If SD->file is a string, open that device,
    otherwise use a default device name.  */
 
 static void
 vox_open (struct sound_device *sd)
 {
-  const char *file;
-
   /* Open the sound device (eg /dev/dsp).  */
-  if (sd->file)
-    file = sd->file;
-  else
-    file = DEFAULT_SOUND_DEVICE;
-
+  char const *file = string_default (sd->file, DEFAULT_SOUND_DEVICE);
   sd->fd = emacs_open (file, O_WRONLY, 0);
   if (sd->fd < 0)
     sound_perror (file);
@@ -728,6 +707,9 @@ static void
 vox_configure (struct sound_device *sd)
 {
   int val;
+#ifdef USABLE_SIGIO
+  sigset_t oldset, blocked;
+#endif
 
   eassert (sd->fd >= 0);
 
@@ -735,8 +717,10 @@ vox_configure (struct sound_device *sd)
      interrupted by a signal.  Block the ones we know to cause
      troubles.  */
   turn_on_atimers (0);
-#ifdef SIGIO
-  sigblock (sigmask (SIGIO));
+#ifdef USABLE_SIGIO
+  sigemptyset (&blocked);
+  sigaddset (&blocked, SIGIO);
+  pthread_sigmask (SIG_BLOCK, &blocked, &oldset);
 #endif
 
   val = sd->format;
@@ -769,8 +753,8 @@ vox_configure (struct sound_device *sd)
     }
 
   turn_on_atimers (1);
-#ifdef SIGIO
-  sigunblock (sigmask (SIGIO));
+#ifdef USABLE_SIGIO
+  pthread_sigmask (SIG_SETMASK, &oldset, 0);
 #endif
 }
 
@@ -785,8 +769,11 @@ vox_close (struct sound_device *sd)
       /* 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));
+#ifdef USABLE_SIGIO
+      sigset_t blocked, oldset;
+      sigemptyset (&blocked);
+      sigaddset (&blocked, SIGIO);
+      pthread_sigmask (SIG_BLOCK, &blocked, &oldset);
 #endif
       turn_on_atimers (0);
 
@@ -794,8 +781,8 @@ vox_close (struct sound_device *sd)
       ioctl (sd->fd, SNDCTL_DSP_SYNC, NULL);
 
       turn_on_atimers (1);
-#ifdef SIGIO
-      sigunblock (sigmask (SIGIO));
+#ifdef USABLE_SIGIO
+      pthread_sigmask (SIG_SETMASK, &oldset, 0);
 #endif
 
       /* Close the device.  */
@@ -843,25 +830,19 @@ vox_choose_format (struct sound_device *sd, struct sound *s)
        }
     }
   else
-    abort ();
+    emacs_abort ();
 }
 
 
 /* Initialize device SD.  Set up the interface functions in the device
    structure.  */
 
-static int
+static bool
 vox_init (struct sound_device *sd)
 {
-  const char *file;
-  int fd;
-
   /* Open the sound device (eg /dev/dsp).  */
-  if (sd->file)
-    file = sd->file;
-  else
-    file = DEFAULT_SOUND_DEVICE;
-  fd = emacs_open (file, O_WRONLY, 0);
+  char const *file = string_default (sd->file, DEFAULT_SOUND_DEVICE);
+  int fd = emacs_open (file, O_WRONLY, 0);
   if (fd >= 0)
     emacs_close (fd);
   else
@@ -883,7 +864,7 @@ vox_init (struct sound_device *sd)
 static void
 vox_write (struct sound_device *sd, const char *buffer, ptrdiff_t nbytes)
 {
-  if (emacs_write (sd->fd, buffer, nbytes) != nbytes)
+  if (emacs_write_sig (sd->fd, buffer, nbytes) != nbytes)
     sound_perror ("Error writing to sound device");
 }
 
@@ -912,23 +893,17 @@ struct alsa_params
   snd_pcm_uframes_t period_size;
 };
 
-/* Open device SD.  If SD->file is non-null, open that device,
+/* Open device SD.  If SD->file is a string, open that device,
    otherwise use a default device name.  */
 
 static void
 alsa_open (struct sound_device *sd)
 {
-  const char *file;
-  struct alsa_params *p;
-  int err;
-
   /* Open the sound device.  Default is "default".  */
-  if (sd->file)
-    file = sd->file;
-  else
-    file = DEFAULT_ALSA_SOUND_DEVICE;
+  struct alsa_params *p = xmalloc (sizeof *p);
+  char const *file = string_default (sd->file, DEFAULT_ALSA_SOUND_DEVICE);
+  int err;
 
-  p = xmalloc (sizeof *p);
   p->handle = NULL;
   p->hwparams = NULL;
   p->swparams = NULL;
@@ -1040,10 +1015,10 @@ alsa_configure (struct sound_device *sd)
       int chn;
       snd_mixer_t *handle;
       snd_mixer_elem_t *e;
-      const char *file = sd->file ? sd->file : DEFAULT_ALSA_SOUND_DEVICE;
-
       if (snd_mixer_open (&handle, 0) >= 0)
         {
+         char const *file = string_default (sd->file,
+                                            DEFAULT_ALSA_SOUND_DEVICE);
           if (snd_mixer_attach (handle, file) >= 0
               && snd_mixer_load (handle) >= 0
               && snd_mixer_selem_register (handle, NULL, NULL) >= 0)
@@ -1138,7 +1113,7 @@ alsa_choose_format (struct sound_device *sd, struct sound *s)
        }
     }
   else
-    abort ();
+    emacs_abort ();
 }
 
 
@@ -1200,19 +1175,14 @@ snd_error_quiet (const char *file, int line, const char *function, int err,
 /* Initialize device SD.  Set up the interface functions in the device
    structure.  */
 
-static int
+static bool
 alsa_init (struct sound_device *sd)
 {
-  const char *file;
+  /* Open the sound device.  Default is "default".  */
+  char const *file = string_default (sd->file, DEFAULT_ALSA_SOUND_DEVICE);
   snd_pcm_t *handle;
   int err;
 
-  /* Open the sound device.  Default is "default".  */
-  if (sd->file)
-    file = sd->file;
-  else
-    file = DEFAULT_ALSA_SOUND_DEVICE;
-
   snd_lib_error_set_handler ((snd_lib_error_handler_t) snd_error_quiet);
   err = snd_pcm_open (&handle, file, SND_PCM_STREAM_PLAYBACK, 0);
   snd_lib_error_set_handler (NULL);
@@ -1239,38 +1209,83 @@ alsa_init (struct sound_device *sd)
 
 /* BEGIN: Windows specific functions */
 
-#define SOUND_WARNING(fun, error, text)            \
-  {                                                \
-    char buf[1024];                                \
-    char err_string[MAXERRORLENGTH];               \
-    fun (error, err_string, sizeof (err_string));  \
-    _snprintf (buf, sizeof (buf), "%s\nError: %s", \
-              text, err_string);                  \
-    sound_warning (buf);                           \
-  }
+#define SOUND_WARNING(func, error, text)               \
+  do {                                                 \
+    char buf[1024];                                    \
+    char err_string[MAXERRORLENGTH];                   \
+    func (error, err_string, sizeof (err_string));     \
+    _snprintf (buf, sizeof (buf), "%s\nMCI Error: %s", \
+              text, err_string);                       \
+    message_with_string ("%s", build_string (buf), 1); \
+  } while (0)
 
 static int
 do_play_sound (const char *psz_file, unsigned long ui_volume)
 {
   int i_result = 0;
   MCIERROR mci_error = 0;
-  char sz_cmd_buf[520] = {0};
-  char sz_ret_buf[520] = {0};
+  char sz_cmd_buf_a[520];
+  char sz_ret_buf_a[520];
   MMRESULT mm_result = MMSYSERR_NOERROR;
   unsigned long ui_volume_org = 0;
   BOOL b_reset_volume = FALSE;
+  char warn_text[560];
+
+  /* Since UNICOWS.DLL includes only a stub for mciSendStringW, we
+     need to encode the file in the ANSI codepage on Windows 9X even
+     if w32_unicode_filenames is non-zero.  */
+  if (w32_major_version <= 4 || !w32_unicode_filenames)
+    {
+      char fname_a[MAX_PATH], shortname[MAX_PATH], *fname_to_use;
+
+      filename_to_ansi (psz_file, fname_a);
+      fname_to_use = fname_a;
+      /* If the file name is not encodable in ANSI, try its short 8+3
+        alias.  This will only work if w32_unicode_filenames is
+        non-zero.  */
+      if (_mbspbrk ((const unsigned char *)fname_a,
+                   (const unsigned char *)"?"))
+       {
+         if (w32_get_short_filename (psz_file, shortname, MAX_PATH))
+           fname_to_use = shortname;
+         else
+           mci_error = MCIERR_FILE_NOT_FOUND;
+       }
 
-  memset (sz_cmd_buf, 0, sizeof (sz_cmd_buf));
-  memset (sz_ret_buf, 0, sizeof (sz_ret_buf));
-  sprintf (sz_cmd_buf,
-           "open \"%s\" alias GNUEmacs_PlaySound_Device wait",
-           psz_file);
-  mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, sizeof (sz_ret_buf), NULL);
+      if (!mci_error)
+       {
+         memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a));
+         memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a));
+         sprintf (sz_cmd_buf_a,
+                  "open \"%s\" alias GNUEmacs_PlaySound_Device wait",
+                  fname_to_use);
+         mci_error = mciSendStringA (sz_cmd_buf_a,
+                                     sz_ret_buf_a, sizeof (sz_ret_buf_a), NULL);
+       }
+    }
+  else
+    {
+      wchar_t sz_cmd_buf_w[520];
+      wchar_t sz_ret_buf_w[520];
+      wchar_t fname_w[MAX_PATH];
+
+      filename_to_utf16 (psz_file, fname_w);
+      memset (sz_cmd_buf_w, 0, sizeof (sz_cmd_buf_w));
+      memset (sz_ret_buf_w, 0, sizeof (sz_ret_buf_w));
+      /* _swprintf is not available on Windows 9X, so we construct the
+        UTF-16 command string by hand.  */
+      wcscpy (sz_cmd_buf_w, L"open \"");
+      wcscat (sz_cmd_buf_w, fname_w);
+      wcscat (sz_cmd_buf_w, L"\" alias GNUEmacs_PlaySound_Device wait");
+      mci_error = mciSendStringW (sz_cmd_buf_w,
+                                 sz_ret_buf_w, ARRAYELTS (sz_ret_buf_w) , NULL);
+    }
   if (mci_error != 0)
     {
-      SOUND_WARNING (mciGetErrorString, mci_error,
-                    "The open mciSendString command failed to open "
-                    "the specified sound file.");
+      strcpy (warn_text,
+             "mciSendString: 'open' command failed to open sound file ");
+      strcat (warn_text, psz_file);
+      SOUND_WARNING (mciGetErrorString, mci_error, warn_text);
       i_result = (int) mci_error;
       return i_result;
     }
@@ -1284,44 +1299,47 @@ do_play_sound (const char *psz_file, unsigned long ui_volume)
           if (mm_result != MMSYSERR_NOERROR)
             {
              SOUND_WARNING (waveOutGetErrorText, mm_result,
-                            "waveOutSetVolume failed to set the volume level "
-                            "of the WAVE_MAPPER device.\n"
-                            "As a result, the user selected volume level will "
-                            "not be used.");
+                            "waveOutSetVolume: failed to set the volume level"
+                            " of the WAVE_MAPPER device.\n"
+                            "As a result, the user selected volume level will"
+                            " not be used.");
             }
         }
       else
         {
           SOUND_WARNING (waveOutGetErrorText, mm_result,
-                        "waveOutGetVolume failed to obtain the original "
-                         "volume level of the WAVE_MAPPER device.\n"
-                         "As a result, the user selected volume level will "
-                         "not be used.");
+                        "waveOutGetVolume: failed to obtain the original"
+                         " volume level of the WAVE_MAPPER device.\n"
+                         "As a result, the user selected volume level will"
+                         " not be used.");
         }
     }
-  memset (sz_cmd_buf, 0, sizeof (sz_cmd_buf));
-  memset (sz_ret_buf, 0, sizeof (sz_ret_buf));
-  strcpy (sz_cmd_buf, "play GNUEmacs_PlaySound_Device wait");
-  mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, sizeof (sz_ret_buf), NULL);
+  memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a));
+  memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a));
+  strcpy (sz_cmd_buf_a, "play GNUEmacs_PlaySound_Device wait");
+  mci_error = mciSendStringA (sz_cmd_buf_a, sz_ret_buf_a, sizeof (sz_ret_buf_a),
+                             NULL);
   if (mci_error != 0)
     {
-      SOUND_WARNING (mciGetErrorString, mci_error,
-                    "The play mciSendString command failed to play the "
-                    "opened sound file.");
+      strcpy (warn_text,
+             "mciSendString: 'play' command failed to play sound file ");
+      strcat (warn_text, psz_file);
+      SOUND_WARNING (mciGetErrorString, mci_error, warn_text);
       i_result = (int) mci_error;
     }
-  memset (sz_cmd_buf, 0, sizeof (sz_cmd_buf));
-  memset (sz_ret_buf, 0, sizeof (sz_ret_buf));
-  strcpy (sz_cmd_buf, "close GNUEmacs_PlaySound_Device wait");
-  mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, sizeof (sz_ret_buf), NULL);
+  memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a));
+  memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a));
+  strcpy (sz_cmd_buf_a, "close GNUEmacs_PlaySound_Device wait");
+  mci_error = mciSendStringA (sz_cmd_buf_a, sz_ret_buf_a, sizeof (sz_ret_buf_a),
+                             NULL);
   if (b_reset_volume == TRUE)
     {
       mm_result = waveOutSetVolume ((HWAVEOUT) WAVE_MAPPER, ui_volume_org);
       if (mm_result != MMSYSERR_NOERROR)
         {
           SOUND_WARNING (waveOutGetErrorText, mm_result,
-                        "waveOutSetVolume failed to reset the original volume "
-                         "level of the WAVE_MAPPER device.");
+                        "waveOutSetVolume: failed to reset the original"
+                         " volume level of the WAVE_MAPPER device.");
         }
     }
   return i_result;
@@ -1339,18 +1357,13 @@ Internal use only, use `play-sound' instead.  */)
 {
   Lisp_Object attrs[SOUND_ATTR_SENTINEL];
   ptrdiff_t count = SPECPDL_INDEX ();
-
-#ifndef WINDOWSNT
   Lisp_Object file;
-  struct gcpro gcpro1, gcpro2;
   Lisp_Object args[2];
-#else /* WINDOWSNT */
-  int len = 0;
-  Lisp_Object lo_file = {0};
-  char * psz_file = NULL;
+  struct gcpro gcpro1, gcpro2;
+
+#ifdef WINDOWSNT
   unsigned long ui_volume_tmp = UINT_MAX;
   unsigned long ui_volume = UINT_MAX;
-  int i_result = 0;
 #endif /* WINDOWSNT */
 
   /* Parse the sound specification.  Give up if it is invalid.  */
@@ -1362,14 +1375,15 @@ Internal use only, use `play-sound' instead.  */)
   GCPRO2 (sound, file);
   current_sound_device = xzalloc (sizeof *current_sound_device);
   current_sound = xzalloc (sizeof *current_sound);
-  record_unwind_protect (sound_cleanup, Qnil);
-  current_sound->header = alloca (MAX_SOUND_HEADER_BYTES);
+  record_unwind_protect_void (sound_cleanup);
+  char headerbuf[MAX_SOUND_HEADER_BYTES];
+  current_sound->header = headerbuf;
 
   if (STRINGP (attrs[SOUND_FILE]))
     {
       /* Open the sound file.  */
-      current_sound->fd = openp (Fcons (Vdata_directory, Qnil),
-                                attrs[SOUND_FILE], Qnil, &file, Qnil);
+      current_sound->fd = openp (list1 (Vdata_directory),
+                                attrs[SOUND_FILE], Qnil, &file, Qnil, false);
       if (current_sound->fd < 0)
        sound_perror ("Could not open sound file");
 
@@ -1392,12 +1406,7 @@ Internal use only, use `play-sound' instead.  */)
   find_sound_type (current_sound);
 
   /* Set up a device.  */
-  if (STRINGP (attrs[SOUND_DEVICE]))
-    {
-      int len = SCHARS (attrs[SOUND_DEVICE]);
-      current_sound_device->file = alloca (len + 1);
-      strcpy (current_sound_device->file, SSDATA (attrs[SOUND_DEVICE]));
-    }
+  current_sound_device->file = attrs[SOUND_DEVICE];
 
   if (INTEGERP (attrs[SOUND_VOLUME]))
     current_sound_device->volume = XFASTINT (attrs[SOUND_VOLUME]);
@@ -1425,10 +1434,8 @@ Internal use only, use `play-sound' instead.  */)
 
 #else /* WINDOWSNT */
 
-  lo_file = Fexpand_file_name (attrs[SOUND_FILE], Qnil);
-  len = XSTRING (lo_file)->size;
-  psz_file = alloca (len + 1);
-  strcpy (psz_file, XSTRING (lo_file)->data);
+  file = Fexpand_file_name (attrs[SOUND_FILE], Vdata_directory);
+  file = ENCODE_FILE (file);
   if (INTEGERP (attrs[SOUND_VOLUME]))
     {
       ui_volume_tmp = XFASTINT (attrs[SOUND_VOLUME]);
@@ -1437,6 +1444,13 @@ Internal use only, use `play-sound' instead.  */)
     {
       ui_volume_tmp = XFLOAT_DATA (attrs[SOUND_VOLUME]) * 100;
     }
+
+  GCPRO2 (sound, file);
+
+  args[0] = Qplay_sound_functions;
+  args[1] = sound;
+  Frun_hook_with_args (2, args);
+
   /*
     Based on some experiments I have conducted, a value of 100 or less
     for the sound volume is much too low.  You cannot even hear it.
@@ -1450,7 +1464,9 @@ Internal use only, use `play-sound' instead.  */)
     {
       ui_volume = ui_volume_tmp * (UINT_MAX / 100);
     }
-  i_result = do_play_sound (psz_file, ui_volume);
+  (void)do_play_sound (SSDATA (file), ui_volume);
+
+  UNGCPRO;
 
 #endif /* WINDOWSNT */