aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCecilio Pardo2024-12-15 01:13:16 +0100
committerEli Zaretskii2024-12-21 12:35:09 +0200
commitfd529bbd076d14087d70c50d94bc9ef231cf1997 (patch)
tree446f92ac0062c3ba335d6286703cda9904722bcd
parentb6852b67b0d462c7ba0012e5ac3b79a245f4b7e5 (diff)
downloademacs-fd529bbd076d14087d70c50d94bc9ef231cf1997.tar.gz
emacs-fd529bbd076d14087d70c50d94bc9ef231cf1997.zip
Add support for the ':data' keyword for play-sound in MS-Windows.
* 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.
-rw-r--r--etc/NEWS5
-rw-r--r--etc/PROBLEMS5
-rw-r--r--src/sound.c242
3 files changed, 127 insertions, 125 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 5b0c89b6b20..12a318f5ed7 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1111,6 +1111,11 @@ current buffer, if the major mode supports it. (Support for
1111Transformed images are smoothed using the bilinear interpolation by 1111Transformed images are smoothed using the bilinear interpolation by
1112means of the GDI+ library. 1112means of the GDI+ library.
1113 1113
1114---
1115** Emacs on MS-Windows now supports the ':data' keyword for 'play-sound'.
1116In addition to ':file FILE' for playing a sound from a file, ':data
1117DATA' can now be used to play a sound from memory.
1118
1114 1119
1115---------------------------------------------------------------------- 1120----------------------------------------------------------------------
1116This file is part of GNU Emacs. 1121This file is part of GNU Emacs.
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index 8de12a78613..c1745e8d18f 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -3278,11 +3278,6 @@ Files larger than 4GB cause overflow in the size (represented as a
3278well, since the Windows port uses a Lisp emulation of 'ls', which relies 3278well, since the Windows port uses a Lisp emulation of 'ls', which relies
3279on 'file-attributes'. 3279on 'file-attributes'.
3280 3280
3281** Playing sound doesn't support the :data method
3282
3283Sound playing is not supported with the ':data DATA' key-value pair.
3284You _must_ use the ':file FILE' method.
3285
3286** Typing Alt-Shift has strange effects on MS-Windows. 3281** Typing Alt-Shift has strange effects on MS-Windows.
3287 3282
3288This combination of keys is a command to change keyboard layout. If 3283This combination of keys is a command to change keyboard layout. If
diff --git a/src/sound.c b/src/sound.c
index 004015fc936..843d05d2349 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -26,14 +26,12 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
26 implementation of the play-sound specification for Windows. 26 implementation of the play-sound specification for Windows.
27 27
28 Notes: 28 Notes:
29 In the Windows implementation of play-sound-internal only the 29 In the Windows implementation of play-sound-internal the :device
30 :file and :volume keywords are supported. The :device keyword, 30 keyword, if present, is ignored.
31 if present, is ignored. The :data keyword, if present, will
32 cause an error to be generated.
33 31
34 The Windows implementation of play-sound is implemented via the 32 The Windows implementation of play-sound is implemented via the
35 Windows API functions mciSendString, waveOutGetVolume, and 33 Windows API functions mciSendString, waveOutGetVolume,
36 waveOutSetVolume which are exported by Winmm.dll. 34 waveOutSetVolume and PlaySound which are exported by Winmm.dll.
37*/ 35*/
38 36
39#include <config.h> 37#include <config.h>
@@ -91,6 +89,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
91#include "w32.h" 89#include "w32.h"
92/* END: Windows Specific Includes */ 90/* END: Windows Specific Includes */
93 91
92/* Missing in mingw32. */
93#ifndef SND_SENTRY
94#define SND_SENTRY 0x00080000
95#endif
96
94#endif /* WINDOWSNT */ 97#endif /* WINDOWSNT */
95 98
96/* BEGIN: Common Definitions */ 99/* BEGIN: Common Definitions */
@@ -278,7 +281,7 @@ static void au_play (struct sound *, struct sound_device *);
278#else /* WINDOWSNT */ 281#else /* WINDOWSNT */
279 282
280/* BEGIN: Windows Specific Definitions */ 283/* BEGIN: Windows Specific Definitions */
281static int do_play_sound (const char *, unsigned long); 284static int do_play_sound (const char *, unsigned long, bool);
282/* 285/*
283 END: Windows Specific Definitions */ 286 END: Windows Specific Definitions */
284#endif /* WINDOWSNT */ 287#endif /* WINDOWSNT */
@@ -366,21 +369,10 @@ parse_sound (Lisp_Object sound, Lisp_Object *attrs)
366 attrs[SOUND_DEVICE] = plist_get (sound, QCdevice); 369 attrs[SOUND_DEVICE] = plist_get (sound, QCdevice);
367 attrs[SOUND_VOLUME] = plist_get (sound, QCvolume); 370 attrs[SOUND_VOLUME] = plist_get (sound, QCvolume);
368 371
369#ifndef WINDOWSNT
370 /* File name or data must be specified. */ 372 /* File name or data must be specified. */
371 if (!STRINGP (attrs[SOUND_FILE]) 373 if (!STRINGP (attrs[SOUND_FILE])
372 && !STRINGP (attrs[SOUND_DATA])) 374 && !STRINGP (attrs[SOUND_DATA]))
373 return 0; 375 return 0;
374#else /* WINDOWSNT */
375 /*
376 Data is not supported in Windows. Therefore a
377 File name MUST be supplied.
378 */
379 if (!STRINGP (attrs[SOUND_FILE]))
380 {
381 return 0;
382 }
383#endif /* WINDOWSNT */
384 376
385 /* Volume must be in the range 0..100 or unspecified. */ 377 /* Volume must be in the range 0..100 or unspecified. */
386 if (!NILP (attrs[SOUND_VOLUME])) 378 if (!NILP (attrs[SOUND_VOLUME]))
@@ -1225,7 +1217,7 @@ alsa_init (struct sound_device *sd)
1225 } while (0) 1217 } while (0)
1226 1218
1227static int 1219static int
1228do_play_sound (const char *psz_file, unsigned long ui_volume) 1220do_play_sound (const char *psz_file_or_data, unsigned long ui_volume, bool in_memory)
1229{ 1221{
1230 int i_result = 0; 1222 int i_result = 0;
1231 MCIERROR mci_error = 0; 1223 MCIERROR mci_error = 0;
@@ -1236,65 +1228,7 @@ do_play_sound (const char *psz_file, unsigned long ui_volume)
1236 BOOL b_reset_volume = FALSE; 1228 BOOL b_reset_volume = FALSE;
1237 char warn_text[560]; 1229 char warn_text[560];
1238 1230
1239 /* Since UNICOWS.DLL includes only a stub for mciSendStringW, we 1231 if (ui_volume > 0)
1240 need to encode the file in the ANSI codepage on Windows 9X even
1241 if w32_unicode_filenames is non-zero. */
1242 if (w32_major_version <= 4 || !w32_unicode_filenames)
1243 {
1244 char fname_a[MAX_PATH], shortname[MAX_PATH], *fname_to_use;
1245
1246 filename_to_ansi (psz_file, fname_a);
1247 fname_to_use = fname_a;
1248 /* If the file name is not encodable in ANSI, try its short 8+3
1249 alias. This will only work if w32_unicode_filenames is
1250 non-zero. */
1251 if (_mbspbrk ((const unsigned char *)fname_a,
1252 (const unsigned char *)"?"))
1253 {
1254 if (w32_get_short_filename (psz_file, shortname, MAX_PATH))
1255 fname_to_use = shortname;
1256 else
1257 mci_error = MCIERR_FILE_NOT_FOUND;
1258 }
1259
1260 if (!mci_error)
1261 {
1262 memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a));
1263 memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a));
1264 sprintf (sz_cmd_buf_a,
1265 "open \"%s\" alias GNUEmacs_PlaySound_Device wait",
1266 fname_to_use);
1267 mci_error = mciSendStringA (sz_cmd_buf_a,
1268 sz_ret_buf_a, sizeof (sz_ret_buf_a), NULL);
1269 }
1270 }
1271 else
1272 {
1273 wchar_t sz_cmd_buf_w[520];
1274 wchar_t sz_ret_buf_w[520];
1275 wchar_t fname_w[MAX_PATH];
1276
1277 filename_to_utf16 (psz_file, fname_w);
1278 memset (sz_cmd_buf_w, 0, sizeof (sz_cmd_buf_w));
1279 memset (sz_ret_buf_w, 0, sizeof (sz_ret_buf_w));
1280 /* _swprintf is not available on Windows 9X, so we construct the
1281 UTF-16 command string by hand. */
1282 wcscpy (sz_cmd_buf_w, L"open \"");
1283 wcscat (sz_cmd_buf_w, fname_w);
1284 wcscat (sz_cmd_buf_w, L"\" alias GNUEmacs_PlaySound_Device wait");
1285 mci_error = mciSendStringW (sz_cmd_buf_w,
1286 sz_ret_buf_w, ARRAYELTS (sz_ret_buf_w) , NULL);
1287 }
1288 if (mci_error != 0)
1289 {
1290 strcpy (warn_text,
1291 "mciSendString: 'open' command failed to open sound file ");
1292 strcat (warn_text, psz_file);
1293 SOUND_WARNING (mciGetErrorString, mci_error, warn_text);
1294 i_result = (int) mci_error;
1295 return i_result;
1296 }
1297 if ((ui_volume > 0) && (ui_volume != UINT_MAX))
1298 { 1232 {
1299 mm_result = waveOutGetVolume ((HWAVEOUT) WAVE_MAPPER, &ui_volume_org); 1233 mm_result = waveOutGetVolume ((HWAVEOUT) WAVE_MAPPER, &ui_volume_org);
1300 if (mm_result == MMSYSERR_NOERROR) 1234 if (mm_result == MMSYSERR_NOERROR)
@@ -1319,34 +1253,105 @@ do_play_sound (const char *psz_file, unsigned long ui_volume)
1319 " not be used."); 1253 " not be used.");
1320 } 1254 }
1321 } 1255 }
1322 memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a)); 1256
1323 memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a)); 1257 if (in_memory)
1324 strcpy (sz_cmd_buf_a, "play GNUEmacs_PlaySound_Device wait");
1325 mci_error = mciSendStringA (sz_cmd_buf_a, sz_ret_buf_a, sizeof (sz_ret_buf_a),
1326 NULL);
1327 if (mci_error != 0)
1328 { 1258 {
1329 strcpy (warn_text, 1259 int flags = SND_MEMORY;
1330 "mciSendString: 'play' command failed to play sound file "); 1260 if (w32_major_version >= 6) /* Vista and later */
1331 strcat (warn_text, psz_file); 1261 flags |= SND_SENTRY;
1332 SOUND_WARNING (mciGetErrorString, mci_error, warn_text); 1262 i_result = !PlaySound (psz_file_or_data, NULL, flags);
1333 i_result = (int) mci_error;
1334 } 1263 }
1335 memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a)); 1264 else
1336 memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a)); 1265 {
1337 strcpy (sz_cmd_buf_a, "close GNUEmacs_PlaySound_Device wait"); 1266 /* Since UNICOWS.DLL includes only a stub for mciSendStringW, we
1338 mci_error = mciSendStringA (sz_cmd_buf_a, sz_ret_buf_a, sizeof (sz_ret_buf_a), 1267 need to encode the file in the ANSI codepage on Windows 9X even
1339 NULL); 1268 if w32_unicode_filenames is non-zero. */
1269 if (w32_major_version <= 4 || !w32_unicode_filenames)
1270 {
1271 char fname_a[MAX_PATH], shortname[MAX_PATH], *fname_to_use;
1272
1273 filename_to_ansi (psz_file_or_data, fname_a);
1274 fname_to_use = fname_a;
1275 /* If the file name is not encodable in ANSI, try its short 8+3
1276 alias. This will only work if w32_unicode_filenames is
1277 non-zero. */
1278 if (_mbspbrk ((const unsigned char *)fname_a,
1279 (const unsigned char *)"?"))
1280 {
1281 if (w32_get_short_filename (psz_file_or_data, shortname, MAX_PATH))
1282 fname_to_use = shortname;
1283 else
1284 mci_error = MCIERR_FILE_NOT_FOUND;
1285 }
1286
1287 if (!mci_error)
1288 {
1289 memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a));
1290 memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a));
1291 sprintf (sz_cmd_buf_a,
1292 "open \"%s\" alias GNUEmacs_PlaySound_Device wait",
1293 fname_to_use);
1294 mci_error = mciSendStringA (sz_cmd_buf_a,
1295 sz_ret_buf_a, sizeof (sz_ret_buf_a), NULL);
1296 }
1297 }
1298 else
1299 {
1300 wchar_t sz_cmd_buf_w[520];
1301 wchar_t sz_ret_buf_w[520];
1302 wchar_t fname_w[MAX_PATH];
1303
1304 filename_to_utf16 (psz_file_or_data, fname_w);
1305 memset (sz_cmd_buf_w, 0, sizeof (sz_cmd_buf_w));
1306 memset (sz_ret_buf_w, 0, sizeof (sz_ret_buf_w));
1307 /* _swprintf is not available on Windows 9X, so we construct the
1308 UTF-16 command string by hand. */
1309 wcscpy (sz_cmd_buf_w, L"open \"");
1310 wcscat (sz_cmd_buf_w, fname_w);
1311 wcscat (sz_cmd_buf_w, L"\" alias GNUEmacs_PlaySound_Device wait");
1312 mci_error = mciSendStringW (sz_cmd_buf_w,
1313 sz_ret_buf_w, ARRAYELTS (sz_ret_buf_w) , NULL);
1314 }
1315 if (mci_error != 0)
1316 {
1317 strcpy (warn_text,
1318 "mciSendString: 'open' command failed to open sound file ");
1319 strcat (warn_text, psz_file_or_data);
1320 SOUND_WARNING (mciGetErrorString, mci_error, warn_text);
1321 i_result = (int) mci_error;
1322 return i_result;
1323 }
1324 memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a));
1325 memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a));
1326 strcpy (sz_cmd_buf_a, "play GNUEmacs_PlaySound_Device wait");
1327 mci_error = mciSendStringA (sz_cmd_buf_a, sz_ret_buf_a, sizeof (sz_ret_buf_a),
1328 NULL);
1329 if (mci_error != 0)
1330 {
1331 strcpy (warn_text,
1332 "mciSendString: 'play' command failed to play sound file ");
1333 strcat (warn_text, psz_file_or_data);
1334 SOUND_WARNING (mciGetErrorString, mci_error, warn_text);
1335 i_result = (int) mci_error;
1336 }
1337 memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a));
1338 memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a));
1339 strcpy (sz_cmd_buf_a, "close GNUEmacs_PlaySound_Device wait");
1340 mci_error = mciSendStringA (sz_cmd_buf_a, sz_ret_buf_a, sizeof (sz_ret_buf_a),
1341 NULL);
1342 }
1343
1340 if (b_reset_volume == TRUE) 1344 if (b_reset_volume == TRUE)
1341 { 1345 {
1342 mm_result = waveOutSetVolume ((HWAVEOUT) WAVE_MAPPER, ui_volume_org); 1346 mm_result = waveOutSetVolume ((HWAVEOUT) WAVE_MAPPER, ui_volume_org);
1343 if (mm_result != MMSYSERR_NOERROR) 1347 if (mm_result != MMSYSERR_NOERROR)
1344 { 1348 {
1345 SOUND_WARNING (waveOutGetErrorText, mm_result, 1349 SOUND_WARNING (waveOutGetErrorText, mm_result,
1346 "waveOutSetVolume: failed to reset the original" 1350 "waveOutSetVolume: failed to reset the original"
1347 " volume level of the WAVE_MAPPER device."); 1351 " volume level of the WAVE_MAPPER device.");
1348 } 1352 }
1349 } 1353 }
1354
1350 return i_result; 1355 return i_result;
1351} 1356}
1352 1357
@@ -1364,8 +1369,7 @@ Internal use only, use `play-sound' instead. */)
1364 specpdl_ref count = SPECPDL_INDEX (); 1369 specpdl_ref count = SPECPDL_INDEX ();
1365 1370
1366#ifdef WINDOWSNT 1371#ifdef WINDOWSNT
1367 unsigned long ui_volume_tmp = UINT_MAX; 1372 unsigned long ui_volume = 0;
1368 unsigned long ui_volume = UINT_MAX;
1369#endif /* WINDOWSNT */ 1373#endif /* WINDOWSNT */
1370 1374
1371 /* Parse the sound specification. Give up if it is invalid. */ 1375 /* Parse the sound specification. Give up if it is invalid. */
@@ -1432,33 +1436,31 @@ Internal use only, use `play-sound' instead. */)
1432 1436
1433#else /* WINDOWSNT */ 1437#else /* WINDOWSNT */
1434 1438
1435 file = Fexpand_file_name (attrs[SOUND_FILE], Vdata_directory); 1439
1436 file = ENCODE_FILE (file);
1437 if (FIXNUMP (attrs[SOUND_VOLUME])) 1440 if (FIXNUMP (attrs[SOUND_VOLUME]))
1438 { 1441 ui_volume = XFIXNAT (attrs[SOUND_VOLUME]);
1439 ui_volume_tmp = XFIXNAT (attrs[SOUND_VOLUME]);
1440 }
1441 else if (FLOATP (attrs[SOUND_VOLUME])) 1442 else if (FLOATP (attrs[SOUND_VOLUME]))
1442 { 1443 ui_volume = XFLOAT_DATA (attrs[SOUND_VOLUME]) * 100;
1443 ui_volume_tmp = XFLOAT_DATA (attrs[SOUND_VOLUME]) * 100; 1444
1444 } 1445 if (ui_volume > 100)
1446 ui_volume = 100;
1447
1448 /* For volume (32 bits), low order 16 bits are the value for left
1449 channel, and high order 16 bits for the right channel. We use the
1450 specified volume on both channels. */
1451 ui_volume = ui_volume * 0xFFFF / 100;
1452 ui_volume = (ui_volume << 16) + ui_volume;
1445 1453
1446 CALLN (Frun_hook_with_args, Qplay_sound_functions, sound); 1454 CALLN (Frun_hook_with_args, Qplay_sound_functions, sound);
1447 1455
1448 /* 1456 if (STRINGP (attrs[SOUND_FILE]))
1449 Based on some experiments I have conducted, a value of 100 or less
1450 for the sound volume is much too low. You cannot even hear it.
1451 A value of UINT_MAX indicates that you wish for the sound to played
1452 at the maximum possible volume. A value of UINT_MAX/2 plays the
1453 sound at 50% maximum volume. Therefore the value passed to do_play_sound
1454 (and thus to waveOutSetVolume) must be some fraction of UINT_MAX.
1455 The following code adjusts the user specified volume level appropriately.
1456 */
1457 if ((ui_volume_tmp > 0) && (ui_volume_tmp <= 100))
1458 { 1457 {
1459 ui_volume = ui_volume_tmp * (UINT_MAX / 100); 1458 file = Fexpand_file_name (attrs[SOUND_FILE], Vdata_directory);
1459 file = ENCODE_FILE (file);
1460 do_play_sound (SSDATA (file), ui_volume, false);
1460 } 1461 }
1461 (void)do_play_sound (SSDATA (file), ui_volume); 1462 else
1463 do_play_sound (SDATA (attrs[SOUND_DATA]), ui_volume, true);
1462 1464
1463#endif /* WINDOWSNT */ 1465#endif /* WINDOWSNT */
1464 1466