diff options
| author | Eli Zaretskii | 2014-09-15 18:51:57 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2014-09-15 18:51:57 +0300 |
| commit | a7fc3ab8f1e7061a468eaff0c0b47abd12c99003 (patch) | |
| tree | 5cccc74a0ca4ede4cbeaf82f0c876465a1c0ee69 /src | |
| parent | edb0288b83b45d295df52ce7644e897613358971 (diff) | |
| download | emacs-a7fc3ab8f1e7061a468eaff0c0b47abd12c99003.tar.gz emacs-a7fc3ab8f1e7061a468eaff0c0b47abd12c99003.zip | |
Support playing on MS-Windows non-ASCII sound files using Unicode APIs.
src/sound.c [WINDOWSNT]: Include w32common.h and mbstring.h.
(SOUND_WARNING) [WINDOWSNT]: Include in do..while and improve the
error message format. Use message_with_string to have non-ASCII
file names properly displayed.
(do_play_sound) [WINDOWSNT]: Use Unicode APIs to play sound files
when w32-unicode-filenames is non-nil, but not on Windows 9X,
where these APIs are not available even in UNICOWS.DLL. Improve
the format of error messages and include the file name in them
where appropriate.
(Fplay_sound_internal) [WINDOWSNT]: Make the MS-Windows branch
call play-sound-functions, per documentation.
src/w32.c (w32_get_long_filename, w32_get_short_filename): Constify
the input file name arguments.
src/w32.h (w32_get_long_filename, w32_get_short_filename): Update
prototypes.
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 20 | ||||
| -rw-r--r-- | src/sound.c | 158 | ||||
| -rw-r--r-- | src/w32.c | 4 | ||||
| -rw-r--r-- | src/w32.h | 4 |
4 files changed, 130 insertions, 56 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 185a6c4bd40..2258f58299e 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,23 @@ | |||
| 1 | 2014-09-15 Eli Zaretskii <eliz@gnu.org> | ||
| 2 | |||
| 3 | * sound.c [WINDOWSNT]: Include w32common.h and mbstring.h. | ||
| 4 | (SOUND_WARNING) [WINDOWSNT]: Include in do..while and improve the | ||
| 5 | error message format. Use message_with_string to have non-ASCII | ||
| 6 | file names properly displayed. | ||
| 7 | (do_play_sound) [WINDOWSNT]: Use Unicode APIs to play sound files | ||
| 8 | when w32-unicode-filenames is non-nil, but not on Windows 9X, | ||
| 9 | where these APIs are not available even in UNICOWS.DLL. Improve | ||
| 10 | the format of error messages and include the file name in them | ||
| 11 | where appropriate. | ||
| 12 | (Fplay_sound_internal) [WINDOWSNT]: Make the MS-Windows branch | ||
| 13 | call play-sound-functions, per documentation. | ||
| 14 | |||
| 15 | * w32.c (w32_get_long_filename, w32_get_short_filename): Constify | ||
| 16 | the input file name arguments. | ||
| 17 | |||
| 18 | * w32.h (w32_get_long_filename, w32_get_short_filename): Update | ||
| 19 | prototypes. | ||
| 20 | |||
| 1 | 2014-09-15 Dmitry Antipov <dmantipov@yandex.ru> | 21 | 2014-09-15 Dmitry Antipov <dmantipov@yandex.ru> |
| 2 | 22 | ||
| 3 | If USE_LOCAL_ALLOCATORS, allocate some Lisp objects on stack. | 23 | If USE_LOCAL_ALLOCATORS, allocate some Lisp objects on stack. |
diff --git a/src/sound.c b/src/sound.c index 7ba14b36f33..b49348f1256 100644 --- a/src/sound.c +++ b/src/sound.c | |||
| @@ -86,10 +86,12 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 86 | /* BEGIN: Windows Specific Includes */ | 86 | /* BEGIN: Windows Specific Includes */ |
| 87 | #include <stdio.h> | 87 | #include <stdio.h> |
| 88 | #include <limits.h> | 88 | #include <limits.h> |
| 89 | #include <mbstring.h> | ||
| 89 | #include <windows.h> | 90 | #include <windows.h> |
| 90 | #include <mmsystem.h> | 91 | #include <mmsystem.h> |
| 91 | 92 | ||
| 92 | #include "coding.h" | 93 | #include "coding.h" |
| 94 | #include "w32common.h" | ||
| 93 | #include "w32.h" | 95 | #include "w32.h" |
| 94 | /* END: Windows Specific Includes */ | 96 | /* END: Windows Specific Includes */ |
| 95 | 97 | ||
| @@ -1207,38 +1209,83 @@ alsa_init (struct sound_device *sd) | |||
| 1207 | 1209 | ||
| 1208 | /* BEGIN: Windows specific functions */ | 1210 | /* BEGIN: Windows specific functions */ |
| 1209 | 1211 | ||
| 1210 | #define SOUND_WARNING(fun, error, text) \ | 1212 | #define SOUND_WARNING(func, error, text) \ |
| 1211 | { \ | 1213 | do { \ |
| 1212 | char buf[1024]; \ | 1214 | char buf[1024]; \ |
| 1213 | char err_string[MAXERRORLENGTH]; \ | 1215 | char err_string[MAXERRORLENGTH]; \ |
| 1214 | fun (error, err_string, sizeof (err_string)); \ | 1216 | func (error, err_string, sizeof (err_string)); \ |
| 1215 | _snprintf (buf, sizeof (buf), "%s\nError: %s", \ | 1217 | _snprintf (buf, sizeof (buf), "%s\nMCI Error: %s", \ |
| 1216 | text, err_string); \ | 1218 | text, err_string); \ |
| 1217 | sound_warning (buf); \ | 1219 | message_with_string ("%s", build_string (buf), 1); \ |
| 1218 | } | 1220 | } while (0) |
| 1219 | 1221 | ||
| 1220 | static int | 1222 | static int |
| 1221 | do_play_sound (const char *psz_file, unsigned long ui_volume) | 1223 | do_play_sound (const char *psz_file, unsigned long ui_volume) |
| 1222 | { | 1224 | { |
| 1223 | int i_result = 0; | 1225 | int i_result = 0; |
| 1224 | MCIERROR mci_error = 0; | 1226 | MCIERROR mci_error = 0; |
| 1225 | char sz_cmd_buf[520] = {0}; | 1227 | char sz_cmd_buf_a[520]; |
| 1226 | char sz_ret_buf[520] = {0}; | 1228 | char sz_ret_buf_a[520]; |
| 1227 | MMRESULT mm_result = MMSYSERR_NOERROR; | 1229 | MMRESULT mm_result = MMSYSERR_NOERROR; |
| 1228 | unsigned long ui_volume_org = 0; | 1230 | unsigned long ui_volume_org = 0; |
| 1229 | BOOL b_reset_volume = FALSE; | 1231 | BOOL b_reset_volume = FALSE; |
| 1232 | char warn_text[560]; | ||
| 1230 | 1233 | ||
| 1231 | memset (sz_cmd_buf, 0, sizeof (sz_cmd_buf)); | 1234 | /* Since UNICOWS.DLL includes only a stub for mciSendStringW, we |
| 1232 | memset (sz_ret_buf, 0, sizeof (sz_ret_buf)); | 1235 | need to encode the file in the ANSI codepage on Windows 9X even |
| 1233 | sprintf (sz_cmd_buf, | 1236 | if w32_unicode_filenames is non-zero. */ |
| 1234 | "open \"%s\" alias GNUEmacs_PlaySound_Device wait", | 1237 | if (w32_major_version <= 4 || !w32_unicode_filenames) |
| 1235 | psz_file); | 1238 | { |
| 1236 | mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, sizeof (sz_ret_buf), NULL); | 1239 | char fname_a[MAX_PATH], shortname[MAX_PATH], *fname_to_use; |
| 1240 | |||
| 1241 | filename_to_ansi (psz_file, fname_a); | ||
| 1242 | fname_to_use = fname_a; | ||
| 1243 | /* If the file name is not encodable in ANSI, try its short 8+3 | ||
| 1244 | alias. This will only work if w32_unicode_filenames is | ||
| 1245 | non-zero. */ | ||
| 1246 | if (_mbspbrk ((const unsigned char *)fname_a, | ||
| 1247 | (const unsigned char *)"?")) | ||
| 1248 | { | ||
| 1249 | if (w32_get_short_filename (psz_file, shortname, MAX_PATH)) | ||
| 1250 | fname_to_use = shortname; | ||
| 1251 | else | ||
| 1252 | mci_error = MCIERR_FILE_NOT_FOUND; | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | if (!mci_error) | ||
| 1256 | { | ||
| 1257 | memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a)); | ||
| 1258 | memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a)); | ||
| 1259 | sprintf (sz_cmd_buf_a, | ||
| 1260 | "open \"%s\" alias GNUEmacs_PlaySound_Device wait", | ||
| 1261 | fname_to_use); | ||
| 1262 | mci_error = mciSendStringA (sz_cmd_buf_a, | ||
| 1263 | sz_ret_buf_a, sizeof (sz_ret_buf_a), NULL); | ||
| 1264 | } | ||
| 1265 | } | ||
| 1266 | else | ||
| 1267 | { | ||
| 1268 | wchar_t sz_cmd_buf_w[520]; | ||
| 1269 | wchar_t sz_ret_buf_w[520]; | ||
| 1270 | wchar_t fname_w[MAX_PATH]; | ||
| 1271 | |||
| 1272 | filename_to_utf16 (psz_file, fname_w); | ||
| 1273 | memset (sz_cmd_buf_w, 0, sizeof (sz_cmd_buf_w)); | ||
| 1274 | memset (sz_ret_buf_w, 0, sizeof (sz_ret_buf_w)); | ||
| 1275 | /* _swprintf is not available on Windows 9X, so we construct the | ||
| 1276 | UTF-16 command string by hand. */ | ||
| 1277 | wcscpy (sz_cmd_buf_w, L"open \""); | ||
| 1278 | wcscat (sz_cmd_buf_w, fname_w); | ||
| 1279 | wcscat (sz_cmd_buf_w, L"\" alias GNUEmacs_PlaySound_Device wait"); | ||
| 1280 | mci_error = mciSendStringW (sz_cmd_buf_w, | ||
| 1281 | sz_ret_buf_w, ARRAYELTS (sz_ret_buf_w) , NULL); | ||
| 1282 | } | ||
| 1237 | if (mci_error != 0) | 1283 | if (mci_error != 0) |
| 1238 | { | 1284 | { |
| 1239 | SOUND_WARNING (mciGetErrorString, mci_error, | 1285 | strcpy (warn_text, |
| 1240 | "The open mciSendString command failed to open " | 1286 | "mciSendString: 'open' command failed to open sound file "); |
| 1241 | "the specified sound file."); | 1287 | strcat (warn_text, psz_file); |
| 1288 | SOUND_WARNING (mciGetErrorString, mci_error, warn_text); | ||
| 1242 | i_result = (int) mci_error; | 1289 | i_result = (int) mci_error; |
| 1243 | return i_result; | 1290 | return i_result; |
| 1244 | } | 1291 | } |
| @@ -1252,44 +1299,47 @@ do_play_sound (const char *psz_file, unsigned long ui_volume) | |||
| 1252 | if (mm_result != MMSYSERR_NOERROR) | 1299 | if (mm_result != MMSYSERR_NOERROR) |
| 1253 | { | 1300 | { |
| 1254 | SOUND_WARNING (waveOutGetErrorText, mm_result, | 1301 | SOUND_WARNING (waveOutGetErrorText, mm_result, |
| 1255 | "waveOutSetVolume failed to set the volume level " | 1302 | "waveOutSetVolume: failed to set the volume level" |
| 1256 | "of the WAVE_MAPPER device.\n" | 1303 | " of the WAVE_MAPPER device.\n" |
| 1257 | "As a result, the user selected volume level will " | 1304 | "As a result, the user selected volume level will" |
| 1258 | "not be used."); | 1305 | " not be used."); |
| 1259 | } | 1306 | } |
| 1260 | } | 1307 | } |
| 1261 | else | 1308 | else |
| 1262 | { | 1309 | { |
| 1263 | SOUND_WARNING (waveOutGetErrorText, mm_result, | 1310 | SOUND_WARNING (waveOutGetErrorText, mm_result, |
| 1264 | "waveOutGetVolume failed to obtain the original " | 1311 | "waveOutGetVolume: failed to obtain the original" |
| 1265 | "volume level of the WAVE_MAPPER device.\n" | 1312 | " volume level of the WAVE_MAPPER device.\n" |
| 1266 | "As a result, the user selected volume level will " | 1313 | "As a result, the user selected volume level will" |
| 1267 | "not be used."); | 1314 | " not be used."); |
| 1268 | } | 1315 | } |
| 1269 | } | 1316 | } |
| 1270 | memset (sz_cmd_buf, 0, sizeof (sz_cmd_buf)); | 1317 | memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a)); |
| 1271 | memset (sz_ret_buf, 0, sizeof (sz_ret_buf)); | 1318 | memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a)); |
| 1272 | strcpy (sz_cmd_buf, "play GNUEmacs_PlaySound_Device wait"); | 1319 | strcpy (sz_cmd_buf_a, "play GNUEmacs_PlaySound_Device wait"); |
| 1273 | mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, sizeof (sz_ret_buf), NULL); | 1320 | mci_error = mciSendStringA (sz_cmd_buf_a, sz_ret_buf_a, sizeof (sz_ret_buf_a), |
| 1321 | NULL); | ||
| 1274 | if (mci_error != 0) | 1322 | if (mci_error != 0) |
| 1275 | { | 1323 | { |
| 1276 | SOUND_WARNING (mciGetErrorString, mci_error, | 1324 | strcpy (warn_text, |
| 1277 | "The play mciSendString command failed to play the " | 1325 | "mciSendString: 'play' command failed to play sound file "); |
| 1278 | "opened sound file."); | 1326 | strcat (warn_text, psz_file); |
| 1327 | SOUND_WARNING (mciGetErrorString, mci_error, warn_text); | ||
| 1279 | i_result = (int) mci_error; | 1328 | i_result = (int) mci_error; |
| 1280 | } | 1329 | } |
| 1281 | memset (sz_cmd_buf, 0, sizeof (sz_cmd_buf)); | 1330 | memset (sz_cmd_buf_a, 0, sizeof (sz_cmd_buf_a)); |
| 1282 | memset (sz_ret_buf, 0, sizeof (sz_ret_buf)); | 1331 | memset (sz_ret_buf_a, 0, sizeof (sz_ret_buf_a)); |
| 1283 | strcpy (sz_cmd_buf, "close GNUEmacs_PlaySound_Device wait"); | 1332 | strcpy (sz_cmd_buf_a, "close GNUEmacs_PlaySound_Device wait"); |
| 1284 | mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, sizeof (sz_ret_buf), NULL); | 1333 | mci_error = mciSendStringA (sz_cmd_buf_a, sz_ret_buf_a, sizeof (sz_ret_buf_a), |
| 1334 | NULL); | ||
| 1285 | if (b_reset_volume == TRUE) | 1335 | if (b_reset_volume == TRUE) |
| 1286 | { | 1336 | { |
| 1287 | mm_result = waveOutSetVolume ((HWAVEOUT) WAVE_MAPPER, ui_volume_org); | 1337 | mm_result = waveOutSetVolume ((HWAVEOUT) WAVE_MAPPER, ui_volume_org); |
| 1288 | if (mm_result != MMSYSERR_NOERROR) | 1338 | if (mm_result != MMSYSERR_NOERROR) |
| 1289 | { | 1339 | { |
| 1290 | SOUND_WARNING (waveOutGetErrorText, mm_result, | 1340 | SOUND_WARNING (waveOutGetErrorText, mm_result, |
| 1291 | "waveOutSetVolume failed to reset the original volume " | 1341 | "waveOutSetVolume: failed to reset the original" |
| 1292 | "level of the WAVE_MAPPER device."); | 1342 | " volume level of the WAVE_MAPPER device."); |
| 1293 | } | 1343 | } |
| 1294 | } | 1344 | } |
| 1295 | return i_result; | 1345 | return i_result; |
| @@ -1307,13 +1357,11 @@ Internal use only, use `play-sound' instead. */) | |||
| 1307 | { | 1357 | { |
| 1308 | Lisp_Object attrs[SOUND_ATTR_SENTINEL]; | 1358 | Lisp_Object attrs[SOUND_ATTR_SENTINEL]; |
| 1309 | ptrdiff_t count = SPECPDL_INDEX (); | 1359 | ptrdiff_t count = SPECPDL_INDEX (); |
| 1310 | |||
| 1311 | #ifndef WINDOWSNT | ||
| 1312 | Lisp_Object file; | 1360 | Lisp_Object file; |
| 1313 | struct gcpro gcpro1, gcpro2; | ||
| 1314 | Lisp_Object args[2]; | 1361 | Lisp_Object args[2]; |
| 1315 | #else /* WINDOWSNT */ | 1362 | struct gcpro gcpro1, gcpro2; |
| 1316 | Lisp_Object lo_file; | 1363 | |
| 1364 | #ifdef WINDOWSNT | ||
| 1317 | unsigned long ui_volume_tmp = UINT_MAX; | 1365 | unsigned long ui_volume_tmp = UINT_MAX; |
| 1318 | unsigned long ui_volume = UINT_MAX; | 1366 | unsigned long ui_volume = UINT_MAX; |
| 1319 | #endif /* WINDOWSNT */ | 1367 | #endif /* WINDOWSNT */ |
| @@ -1386,11 +1434,8 @@ Internal use only, use `play-sound' instead. */) | |||
| 1386 | 1434 | ||
| 1387 | #else /* WINDOWSNT */ | 1435 | #else /* WINDOWSNT */ |
| 1388 | 1436 | ||
| 1389 | lo_file = Fexpand_file_name (attrs[SOUND_FILE], Vdata_directory); | 1437 | file = Fexpand_file_name (attrs[SOUND_FILE], Vdata_directory); |
| 1390 | lo_file = ENCODE_FILE (lo_file); | 1438 | file = ENCODE_FILE (file); |
| 1391 | /* Since UNICOWS.DLL includes only a stub for mciSendStringW, we | ||
| 1392 | need to encode the file in the ANSI codepage. */ | ||
| 1393 | lo_file = ansi_encode_filename (lo_file); | ||
| 1394 | if (INTEGERP (attrs[SOUND_VOLUME])) | 1439 | if (INTEGERP (attrs[SOUND_VOLUME])) |
| 1395 | { | 1440 | { |
| 1396 | ui_volume_tmp = XFASTINT (attrs[SOUND_VOLUME]); | 1441 | ui_volume_tmp = XFASTINT (attrs[SOUND_VOLUME]); |
| @@ -1399,6 +1444,13 @@ Internal use only, use `play-sound' instead. */) | |||
| 1399 | { | 1444 | { |
| 1400 | ui_volume_tmp = XFLOAT_DATA (attrs[SOUND_VOLUME]) * 100; | 1445 | ui_volume_tmp = XFLOAT_DATA (attrs[SOUND_VOLUME]) * 100; |
| 1401 | } | 1446 | } |
| 1447 | |||
| 1448 | GCPRO2 (sound, file); | ||
| 1449 | |||
| 1450 | args[0] = Qplay_sound_functions; | ||
| 1451 | args[1] = sound; | ||
| 1452 | Frun_hook_with_args (2, args); | ||
| 1453 | |||
| 1402 | /* | 1454 | /* |
| 1403 | Based on some experiments I have conducted, a value of 100 or less | 1455 | Based on some experiments I have conducted, a value of 100 or less |
| 1404 | for the sound volume is much too low. You cannot even hear it. | 1456 | for the sound volume is much too low. You cannot even hear it. |
| @@ -1412,7 +1464,9 @@ Internal use only, use `play-sound' instead. */) | |||
| 1412 | { | 1464 | { |
| 1413 | ui_volume = ui_volume_tmp * (UINT_MAX / 100); | 1465 | ui_volume = ui_volume_tmp * (UINT_MAX / 100); |
| 1414 | } | 1466 | } |
| 1415 | do_play_sound (SDATA (lo_file), ui_volume); | 1467 | (void)do_play_sound (SSDATA (file), ui_volume); |
| 1468 | |||
| 1469 | UNGCPRO; | ||
| 1416 | 1470 | ||
| 1417 | #endif /* WINDOWSNT */ | 1471 | #endif /* WINDOWSNT */ |
| 1418 | 1472 | ||
| @@ -2294,7 +2294,7 @@ get_long_basename (char * name, char * buf, int size) | |||
| 2294 | 2294 | ||
| 2295 | /* Get long name for file, if possible (assumed to be absolute). */ | 2295 | /* Get long name for file, if possible (assumed to be absolute). */ |
| 2296 | BOOL | 2296 | BOOL |
| 2297 | w32_get_long_filename (char * name, char * buf, int size) | 2297 | w32_get_long_filename (const char * name, char * buf, int size) |
| 2298 | { | 2298 | { |
| 2299 | char * o = buf; | 2299 | char * o = buf; |
| 2300 | char * p; | 2300 | char * p; |
| @@ -2345,7 +2345,7 @@ w32_get_long_filename (char * name, char * buf, int size) | |||
| 2345 | } | 2345 | } |
| 2346 | 2346 | ||
| 2347 | unsigned int | 2347 | unsigned int |
| 2348 | w32_get_short_filename (char * name, char * buf, int size) | 2348 | w32_get_short_filename (const char * name, char * buf, int size) |
| 2349 | { | 2349 | { |
| 2350 | if (w32_unicode_filenames) | 2350 | if (w32_unicode_filenames) |
| 2351 | { | 2351 | { |
| @@ -144,10 +144,10 @@ extern char * w32_strerror (int error_no); | |||
| 144 | extern int w32_valid_pointer_p (void *, int); | 144 | extern int w32_valid_pointer_p (void *, int); |
| 145 | 145 | ||
| 146 | /* Get long (aka "true") form of file name, if it exists. */ | 146 | /* Get long (aka "true") form of file name, if it exists. */ |
| 147 | extern BOOL w32_get_long_filename (char * name, char * buf, int size); | 147 | extern BOOL w32_get_long_filename (const char * name, char * buf, int size); |
| 148 | 148 | ||
| 149 | /* Get the short (a.k.a. "8+3") form of a file name. */ | 149 | /* Get the short (a.k.a. "8+3") form of a file name. */ |
| 150 | extern unsigned int w32_get_short_filename (char *, char *, int); | 150 | extern unsigned int w32_get_short_filename (const char *, char *, int); |
| 151 | 151 | ||
| 152 | /* Prepare our standard handles for proper inheritance by child processes. */ | 152 | /* Prepare our standard handles for proper inheritance by child processes. */ |
| 153 | extern void prepare_standard_handles (int in, int out, | 153 | extern void prepare_standard_handles (int in, int out, |