]> git.eshelyaron.com Git - emacs.git/commitdiff
Add support for the ':data' keyword for play-sound in MS-Windows.
authorCecilio Pardo <cpardo@imayhem.com>
Sun, 15 Dec 2024 00:13:16 +0000 (01:13 +0100)
committerEshel Yaron <me@eshelyaron.com>
Mon, 23 Dec 2024 15:19:44 +0000 (16:19 +0100)
* src/sound.c (parse_sound) [WINDOWSNT]: Check that either :file
or :data is present.
(do_play_sound): Added parameter to select file or data, and code to
play from data.
(Fplay_sound_internal): Fixed volume format, and send file or data
to 'do_play_sound'.  (Bug#74863)
* etc/NEWS: Add entry for this change.
* etc/PROBLEMS: Remove entry about missing support for :data.

(cherry picked from commit fd529bbd076d14087d70c50d94bc9ef231cf1997)

etc/NEWS
etc/PROBLEMS
src/sound.c

index ecf035764e57e4125d86c3680abc20cbbfc30cdc..661c4dbc6b499012faf6351b6504059a108fc03d 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -951,6 +951,11 @@ current buffer, if the major mode supports it.  (Support for
 Transformed images are smoothed using the bilinear interpolation by
 means of the GDI+ library.
 
+---
+** Emacs on MS-Windows now supports the ':data' keyword for 'play-sound'.
+In addition to ':file FILE' for playing a sound from a file, ':data
+DATA' can now be used to play a sound from memory.
+
 \f
 ----------------------------------------------------------------------
 This file is part of GNU Emacs.
index 8de12a78613521ab11c553d353de4e3ccb80c60d..c1745e8d18fd2b350d71e2d9b018d10cbd452347 100644 (file)
@@ -3278,11 +3278,6 @@ Files larger than 4GB cause overflow in the size (represented as a
 well, since the Windows port uses a Lisp emulation of 'ls', which relies
 on 'file-attributes'.
 
-** Playing sound doesn't support the :data method
-
-Sound playing is not supported with the ':data DATA' key-value pair.
-You _must_ use the ':file FILE' method.
-
 ** Typing Alt-Shift has strange effects on MS-Windows.
 
 This combination of keys is a command to change keyboard layout.  If
index 004015fc936e8f95c86598772997ba2178f00004..843d05d2349b9a02ac5c451445bc6825f19b639c 100644 (file)
@@ -26,14 +26,12 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
   implementation of the play-sound specification for Windows.
 
   Notes:
-  In the Windows implementation of play-sound-internal only the
-  :file and :volume keywords are supported.  The :device keyword,
-  if present, is ignored.  The :data keyword, if present, will
-  cause an error to be generated.
+  In the Windows implementation of play-sound-internal the :device
+  keyword, if present, is ignored.
 
   The Windows implementation of play-sound is implemented via the
-  Windows API functions mciSendString, waveOutGetVolume, and
-  waveOutSetVolume which are exported by Winmm.dll.
+  Windows API functions mciSendString, waveOutGetVolume,
+  waveOutSetVolume and PlaySound which are exported by Winmm.dll.
 */
 
 #include <config.h>
@@ -91,6 +89,11 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #include "w32.h"
 /* END: Windows Specific Includes */
 
+/* Missing in mingw32.  */
+#ifndef SND_SENTRY
+#define SND_SENTRY 0x00080000
+#endif
+
 #endif /* WINDOWSNT */
 
 /* BEGIN: Common Definitions */
@@ -278,7 +281,7 @@ static void au_play (struct sound *, struct sound_device *);
 #else /* WINDOWSNT */
 
 /* BEGIN: Windows Specific Definitions */
-static int do_play_sound (const char *, unsigned long);
+static int do_play_sound (const char *, unsigned long, bool);
 /*
   END: Windows Specific Definitions */
 #endif /* WINDOWSNT */
@@ -366,21 +369,10 @@ parse_sound (Lisp_Object sound, Lisp_Object *attrs)
   attrs[SOUND_DEVICE] = plist_get (sound, QCdevice);
   attrs[SOUND_VOLUME] = plist_get (sound, QCvolume);
 
-#ifndef WINDOWSNT
   /* File name or data must be specified.  */
   if (!STRINGP (attrs[SOUND_FILE])
       && !STRINGP (attrs[SOUND_DATA]))
     return 0;
-#else /* WINDOWSNT */
-  /*
-    Data is not supported in Windows.  Therefore a
-    File name MUST be supplied.
-  */
-  if (!STRINGP (attrs[SOUND_FILE]))
-    {
-      return 0;
-    }
-#endif /* WINDOWSNT */
 
   /* Volume must be in the range 0..100 or unspecified.  */
   if (!NILP (attrs[SOUND_VOLUME]))
@@ -1225,7 +1217,7 @@ alsa_init (struct sound_device *sd)
   } while (0)
 
 static int
-do_play_sound (const char *psz_file, unsigned long ui_volume)
+do_play_sound (const char *psz_file_or_data, unsigned long ui_volume, bool in_memory)
 {
   int i_result = 0;
   MCIERROR mci_error = 0;
@@ -1236,65 +1228,7 @@ do_play_sound (const char *psz_file, unsigned long ui_volume)
   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;
-       }
-
-      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)
-    {
-      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;
-    }
-  if ((ui_volume > 0) && (ui_volume != UINT_MAX))
+  if (ui_volume > 0)
     {
       mm_result = waveOutGetVolume ((HWAVEOUT) WAVE_MAPPER, &ui_volume_org);
       if (mm_result == MMSYSERR_NOERROR)
@@ -1319,34 +1253,105 @@ do_play_sound (const char *psz_file, unsigned long ui_volume)
                          " not be used.");
         }
     }
-  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)
+
+  if (in_memory)
     {
-      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;
+      int flags = SND_MEMORY;
+      if (w32_major_version >= 6) /* Vista and later */
+       flags |= SND_SENTRY;
+      i_result = !PlaySound (psz_file_or_data, NULL, flags);
     }
-  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);
+  else
+    {
+      /* 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_or_data, 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_or_data, shortname, MAX_PATH))
+               fname_to_use = shortname;
+             else
+               mci_error = MCIERR_FILE_NOT_FOUND;
+           }
+
+         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_or_data, 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)
+       {
+         strcpy (warn_text,
+                 "mciSendString: 'open' command failed to open sound file ");
+         strcat (warn_text, psz_file_or_data);
+         SOUND_WARNING (mciGetErrorString, mci_error, warn_text);
+         i_result = (int) mci_error;
+         return i_result;
+       }
+      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)
+       {
+         strcpy (warn_text,
+                 "mciSendString: 'play' command failed to play sound file ");
+         strcat (warn_text, psz_file_or_data);
+         SOUND_WARNING (mciGetErrorString, mci_error, warn_text);
+         i_result = (int) mci_error;
+       }
+      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,
+       {
+         SOUND_WARNING (waveOutGetErrorText, mm_result,
                         "waveOutSetVolume: failed to reset the original"
-                         " volume level of the WAVE_MAPPER device.");
-        }
+                        " volume level of the WAVE_MAPPER device.");
+       }
     }
+
   return i_result;
 }
 
@@ -1364,8 +1369,7 @@ Internal use only, use `play-sound' instead.  */)
   specpdl_ref count = SPECPDL_INDEX ();
 
 #ifdef WINDOWSNT
-  unsigned long ui_volume_tmp = UINT_MAX;
-  unsigned long ui_volume = UINT_MAX;
+  unsigned long ui_volume = 0;
 #endif /* WINDOWSNT */
 
   /* Parse the sound specification.  Give up if it is invalid.  */
@@ -1432,33 +1436,31 @@ Internal use only, use `play-sound' instead.  */)
 
 #else /* WINDOWSNT */
 
-  file = Fexpand_file_name (attrs[SOUND_FILE], Vdata_directory);
-  file = ENCODE_FILE (file);
+
   if (FIXNUMP (attrs[SOUND_VOLUME]))
-    {
-      ui_volume_tmp = XFIXNAT (attrs[SOUND_VOLUME]);
-    }
+    ui_volume = XFIXNAT (attrs[SOUND_VOLUME]);
   else if (FLOATP (attrs[SOUND_VOLUME]))
-    {
-      ui_volume_tmp = XFLOAT_DATA (attrs[SOUND_VOLUME]) * 100;
-    }
+    ui_volume = XFLOAT_DATA (attrs[SOUND_VOLUME]) * 100;
+
+  if (ui_volume > 100)
+    ui_volume = 100;
+
+  /* For volume (32 bits), low order 16 bits are the value for left
+     channel, and high order 16 bits for the right channel.  We use the
+     specified volume on both channels.  */
+  ui_volume = ui_volume * 0xFFFF / 100;
+  ui_volume = (ui_volume << 16) + ui_volume;
 
   CALLN (Frun_hook_with_args, Qplay_sound_functions, sound);
 
-  /*
-    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.
-    A value of UINT_MAX indicates that you wish for the sound to played
-    at the maximum possible volume.  A value of UINT_MAX/2 plays the
-    sound at 50% maximum volume.  Therefore the value passed to do_play_sound
-    (and thus to waveOutSetVolume) must be some fraction of UINT_MAX.
-    The following code adjusts the user specified volume level appropriately.
-  */
-  if ((ui_volume_tmp > 0) && (ui_volume_tmp <= 100))
+  if (STRINGP (attrs[SOUND_FILE]))
     {
-      ui_volume = ui_volume_tmp * (UINT_MAX / 100);
+      file = Fexpand_file_name (attrs[SOUND_FILE], Vdata_directory);
+      file = ENCODE_FILE (file);
+      do_play_sound (SSDATA (file), ui_volume, false);
     }
-  (void)do_play_sound (SSDATA (file), ui_volume);
+  else
+    do_play_sound (SDATA (attrs[SOUND_DATA]), ui_volume, true);
 
 #endif /* WINDOWSNT */