21 |
/* Written by Gerd Moellmann <gerd@gnu.org>. Tested with Luigi's |
/* Written by Gerd Moellmann <gerd@gnu.org>. Tested with Luigi's |
22 |
driver on FreeBSD 2.2.7 with a SoundBlaster 16. */ |
driver on FreeBSD 2.2.7 with a SoundBlaster 16. */ |
23 |
|
|
24 |
|
/* |
25 |
|
Modified by Ben Key <Bkey1@tampabay.rr.com> to add a partial |
26 |
|
implementation of the play-sound specification for Windows. |
27 |
|
|
28 |
|
Notes: |
29 |
|
In the Windows implementation of play-sound-internal only the |
30 |
|
:file and :volume keywords are supported. The :device keyword, |
31 |
|
if present, is ignored. The :data keyword, if present, will |
32 |
|
cause an error to be generated. |
33 |
|
|
34 |
|
The Windows implementation of play-sound is implemented via the |
35 |
|
Win32 API functions mciSendString, waveOutGetVolume, and |
36 |
|
waveOutSetVolume which are exported by Winmm.dll. |
37 |
|
*/ |
38 |
|
|
39 |
#include <config.h> |
#include <config.h> |
40 |
|
|
41 |
#if defined HAVE_SOUND |
#if defined HAVE_SOUND |
42 |
|
|
43 |
|
/* BEGIN: Common Includes */ |
44 |
#include <fcntl.h> |
#include <fcntl.h> |
45 |
#include <unistd.h> |
#include <unistd.h> |
46 |
#include <sys/types.h> |
#include <sys/types.h> |
50 |
#include "atimer.h" |
#include "atimer.h" |
51 |
#include <signal.h> |
#include <signal.h> |
52 |
#include "syssignal.h" |
#include "syssignal.h" |
53 |
|
/* END: Common Includes */ |
54 |
|
|
55 |
|
|
56 |
|
/* BEGIN: Non Windows Includes */ |
57 |
|
#ifndef WINDOWSNT |
58 |
|
|
59 |
#ifndef MSDOS |
#ifndef MSDOS |
60 |
#include <sys/ioctl.h> |
#include <sys/ioctl.h> |
72 |
#ifdef HAVE_SOUNDCARD_H |
#ifdef HAVE_SOUNDCARD_H |
73 |
#include <soundcard.h> |
#include <soundcard.h> |
74 |
#endif |
#endif |
75 |
|
/* END: Non Windows Includes */ |
76 |
|
|
77 |
|
#else /* WINDOWSNT */ |
78 |
|
|
79 |
|
/* BEGIN: Windows Specific Includes */ |
80 |
|
#include <stdio.h> |
81 |
|
#include <stdlib.h> |
82 |
|
#include <string.h> |
83 |
|
#include <limits.h> |
84 |
|
#include <windows.h> |
85 |
|
#include <mmsystem.h> |
86 |
|
/* END: Windows Specific Includes */ |
87 |
|
|
88 |
|
#endif /* WINDOWSNT */ |
89 |
|
|
90 |
|
/* BEGIN: Common Definitions */ |
91 |
|
#define abs(X) ((X) < 0 ? -(X) : (X)) |
92 |
|
|
93 |
|
/* Symbols. */ |
94 |
|
|
95 |
|
extern Lisp_Object QCfile, QCdata; |
96 |
|
Lisp_Object QCvolume, QCdevice; |
97 |
|
Lisp_Object Qsound; |
98 |
|
Lisp_Object Qplay_sound_functions; |
99 |
|
|
100 |
|
/* Indices of attributes in a sound attributes vector. */ |
101 |
|
|
102 |
|
enum sound_attr |
103 |
|
{ |
104 |
|
SOUND_FILE, |
105 |
|
SOUND_DATA, |
106 |
|
SOUND_DEVICE, |
107 |
|
SOUND_VOLUME, |
108 |
|
SOUND_ATTR_SENTINEL |
109 |
|
}; |
110 |
|
|
111 |
|
static void sound_perror P_ ((char *)); |
112 |
|
static void sound_warning P_ ((char *)); |
113 |
|
static int parse_sound P_ ((Lisp_Object, Lisp_Object *)); |
114 |
|
|
115 |
|
/* END: Common Definitions */ |
116 |
|
|
117 |
|
/* BEGIN: Non Windows Definitions */ |
118 |
|
#ifndef WINDOWSNT |
119 |
|
|
120 |
#ifndef DEFAULT_SOUND_DEVICE |
#ifndef DEFAULT_SOUND_DEVICE |
121 |
#define DEFAULT_SOUND_DEVICE "/dev/dsp" |
#define DEFAULT_SOUND_DEVICE "/dev/dsp" |
122 |
#endif |
#endif |
123 |
|
|
|
#define abs(X) ((X) < 0 ? -(X) : (X)) |
|
124 |
|
|
125 |
/* Structure forward declarations. */ |
/* Structure forward declarations. */ |
126 |
|
|
227 |
struct sound *s)); |
struct sound *s)); |
228 |
|
|
229 |
/* Write NYBTES bytes from BUFFER to device SD. */ |
/* Write NYBTES bytes from BUFFER to device SD. */ |
230 |
void (* write) P_ ((struct sound_device *sd, char *buffer, int nbytes)); |
void (* write) P_ ((struct sound_device *sd, const char *buffer, |
231 |
|
int nbytes)); |
232 |
|
|
233 |
/* A place for devices to store additional data. */ |
/* A place for devices to store additional data. */ |
234 |
void *data; |
void *data; |
267 |
void (* play) P_ ((struct sound *s, struct sound_device *sd)); |
void (* play) P_ ((struct sound *s, struct sound_device *sd)); |
268 |
}; |
}; |
269 |
|
|
|
/* Indices of attributes in a sound attributes vector. */ |
|
|
|
|
|
enum sound_attr |
|
|
{ |
|
|
SOUND_FILE, |
|
|
SOUND_DATA, |
|
|
SOUND_DEVICE, |
|
|
SOUND_VOLUME, |
|
|
SOUND_ATTR_SENTINEL |
|
|
}; |
|
|
|
|
|
/* Symbols. */ |
|
|
|
|
|
extern Lisp_Object QCfile, QCdata; |
|
|
Lisp_Object QCvolume, QCdevice; |
|
|
Lisp_Object Qsound; |
|
|
Lisp_Object Qplay_sound_functions; |
|
|
|
|
270 |
/* These are set during `play-sound-internal' so that sound_cleanup has |
/* These are set during `play-sound-internal' so that sound_cleanup has |
271 |
access to them. */ |
access to them. */ |
272 |
|
|
280 |
static void vox_close P_ ((struct sound_device *sd)); |
static void vox_close P_ ((struct sound_device *sd)); |
281 |
static void vox_choose_format P_ ((struct sound_device *, struct sound *)); |
static void vox_choose_format P_ ((struct sound_device *, struct sound *)); |
282 |
static void vox_init P_ ((struct sound_device *)); |
static void vox_init P_ ((struct sound_device *)); |
283 |
static void vox_write P_ ((struct sound_device *, char *, int)); |
static void vox_write P_ ((struct sound_device *, const char *, int)); |
|
static void sound_perror P_ ((char *)); |
|
|
static void sound_warning P_ ((char *)); |
|
|
static int parse_sound P_ ((Lisp_Object, Lisp_Object *)); |
|
284 |
static void find_sound_type P_ ((struct sound *)); |
static void find_sound_type P_ ((struct sound *)); |
285 |
static u_int32_t le2hl P_ ((u_int32_t)); |
static u_int32_t le2hl P_ ((u_int32_t)); |
286 |
static u_int16_t le2hs P_ ((u_int16_t)); |
static u_int16_t le2hs P_ ((u_int16_t)); |
294 |
static u_int16_t be2hs P_ ((u_int16_t)); |
static u_int16_t be2hs P_ ((u_int16_t)); |
295 |
#endif |
#endif |
296 |
|
|
297 |
|
/* END: Non Windows Definitions */ |
298 |
|
#else /* WINDOWSNT */ |
299 |
|
|
300 |
|
/* BEGIN: Windows Specific Definitions */ |
301 |
|
static int do_play_sound P_ ((const char *, unsigned long)); |
302 |
|
/* |
303 |
|
END: Windows Specific Definitions */ |
304 |
|
#endif /* WINDOWSNT */ |
305 |
|
|
306 |
|
|
307 |
/*********************************************************************** |
/*********************************************************************** |
308 |
General |
General |
309 |
***********************************************************************/ |
***********************************************************************/ |
310 |
|
|
311 |
|
/* BEGIN: Common functions */ |
312 |
|
|
313 |
/* Like perror, but signals an error. */ |
/* Like perror, but signals an error. */ |
314 |
|
|
315 |
static void |
static void |
380 |
attrs[SOUND_DEVICE] = Fplist_get (sound, QCdevice); |
attrs[SOUND_DEVICE] = Fplist_get (sound, QCdevice); |
381 |
attrs[SOUND_VOLUME] = Fplist_get (sound, QCvolume); |
attrs[SOUND_VOLUME] = Fplist_get (sound, QCvolume); |
382 |
|
|
383 |
|
#ifndef WINDOWSNT |
384 |
/* File name or data must be specified. */ |
/* File name or data must be specified. */ |
385 |
if (!STRINGP (attrs[SOUND_FILE]) |
if (!STRINGP (attrs[SOUND_FILE]) |
386 |
&& !STRINGP (attrs[SOUND_DATA])) |
&& !STRINGP (attrs[SOUND_DATA])) |
387 |
return 0; |
return 0; |
388 |
|
#else /* WINDOWSNT */ |
389 |
|
/* |
390 |
|
Data is not supported in Windows. Therefore a |
391 |
|
File name MUST be supplied. |
392 |
|
*/ |
393 |
|
if (!STRINGP (attrs[SOUND_FILE])) |
394 |
|
{ |
395 |
|
return 0; |
396 |
|
} |
397 |
|
#endif /* WINDOWSNT */ |
398 |
|
|
399 |
/* Volume must be in the range 0..100 or unspecified. */ |
/* Volume must be in the range 0..100 or unspecified. */ |
400 |
if (!NILP (attrs[SOUND_VOLUME])) |
if (!NILP (attrs[SOUND_VOLUME])) |
415 |
return 0; |
return 0; |
416 |
} |
} |
417 |
|
|
418 |
|
#ifndef WINDOWSNT |
419 |
/* Device must be a string or unspecified. */ |
/* Device must be a string or unspecified. */ |
420 |
if (!NILP (attrs[SOUND_DEVICE]) |
if (!NILP (attrs[SOUND_DEVICE]) |
421 |
&& !STRINGP (attrs[SOUND_DEVICE])) |
&& !STRINGP (attrs[SOUND_DEVICE])) |
422 |
return 0; |
return 0; |
423 |
|
#endif /* WINDOWSNT */ |
424 |
|
/* |
425 |
|
Since device is ignored in Windows, it does not matter |
426 |
|
what it is. |
427 |
|
*/ |
428 |
return 1; |
return 1; |
429 |
} |
} |
430 |
|
|
431 |
|
/* END: Common functions */ |
432 |
|
|
433 |
|
/* BEGIN: Non Windows functions */ |
434 |
|
#ifndef WINDOWSNT |
435 |
|
|
436 |
/* Find out the type of the sound file whose file descriptor is FD. |
/* Find out the type of the sound file whose file descriptor is FD. |
437 |
S is the sound file structure to fill in. */ |
S is the sound file structure to fill in. */ |
462 |
return Qnil; |
return Qnil; |
463 |
} |
} |
464 |
|
|
|
|
|
|
DEFUN ("play-sound-internal", Fplay_sound_internal, Splay_sound_internal, 1, 1, 0, |
|
|
doc: /* Play sound SOUND. |
|
|
|
|
|
Internal use only, use `play-sound' instead. */) |
|
|
(sound) |
|
|
Lisp_Object sound; |
|
|
{ |
|
|
Lisp_Object attrs[SOUND_ATTR_SENTINEL]; |
|
|
Lisp_Object file; |
|
|
struct gcpro gcpro1, gcpro2; |
|
|
struct sound_device sd; |
|
|
struct sound s; |
|
|
Lisp_Object args[2]; |
|
|
int count = specpdl_ptr - specpdl; |
|
|
|
|
|
file = Qnil; |
|
|
GCPRO2 (sound, file); |
|
|
bzero (&sd, sizeof sd); |
|
|
bzero (&s, sizeof s); |
|
|
current_sound_device = &sd; |
|
|
current_sound = &s; |
|
|
record_unwind_protect (sound_cleanup, Qnil); |
|
|
s.header = (char *) alloca (MAX_SOUND_HEADER_BYTES); |
|
|
|
|
|
/* Parse the sound specification. Give up if it is invalid. */ |
|
|
if (!parse_sound (sound, attrs)) |
|
|
error ("Invalid sound specification"); |
|
|
|
|
|
if (STRINGP (attrs[SOUND_FILE])) |
|
|
{ |
|
|
/* Open the sound file. */ |
|
|
s.fd = openp (Fcons (Vdata_directory, Qnil), |
|
|
attrs[SOUND_FILE], Qnil, &file, Qnil); |
|
|
if (s.fd < 0) |
|
|
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 ("Invalid sound file header"); |
|
|
} |
|
|
else |
|
|
{ |
|
|
s.data = attrs[SOUND_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. */ |
|
|
find_sound_type (&s); |
|
|
|
|
|
/* Set up a device. */ |
|
|
if (STRINGP (attrs[SOUND_DEVICE])) |
|
|
{ |
|
|
int len = XSTRING (attrs[SOUND_DEVICE])->size; |
|
|
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])) |
|
|
sd.volume = XFLOAT_DATA (attrs[SOUND_VOLUME]) * 100; |
|
|
|
|
|
args[0] = Qplay_sound_functions; |
|
|
args[1] = sound; |
|
|
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 |
|
|
device. */ |
|
|
vox_init (&sd); |
|
|
|
|
|
/* Open the device. */ |
|
|
sd.open (&sd); |
|
|
|
|
|
/* Play the sound. */ |
|
|
s.play (&s, &sd); |
|
|
|
|
|
/* Close the input file, if any. */ |
|
|
if (!STRINGP (s.data)) |
|
|
{ |
|
|
emacs_close (s.fd); |
|
|
s.fd = -1; |
|
|
} |
|
|
|
|
|
/* Close the device. */ |
|
|
sd.close (&sd); |
|
|
|
|
|
/* Clean up. */ |
|
|
current_sound_device = NULL; |
|
|
current_sound = NULL; |
|
|
UNGCPRO; |
|
|
unbind_to (count, Qnil); |
|
|
return Qnil; |
|
|
} |
|
|
|
|
|
|
|
465 |
/*********************************************************************** |
/*********************************************************************** |
466 |
Byte-order Conversion |
Byte-order Conversion |
467 |
***********************************************************************/ |
***********************************************************************/ |
529 |
|
|
530 |
#endif /* 0 */ |
#endif /* 0 */ |
531 |
|
|
|
|
|
532 |
/*********************************************************************** |
/*********************************************************************** |
533 |
RIFF-WAVE (*.wav) |
RIFF-WAVE (*.wav) |
534 |
***********************************************************************/ |
***********************************************************************/ |
598 |
files I found so far. If someone feels inclined to implement the |
files I found so far. If someone feels inclined to implement the |
599 |
whole RIFF-WAVE spec, please do. */ |
whole RIFF-WAVE spec, please do. */ |
600 |
if (STRINGP (s->data)) |
if (STRINGP (s->data)) |
601 |
sd->write (sd, XSTRING (s->data)->data + sizeof *header, |
sd->write (sd, SDATA (s->data) + sizeof *header, |
602 |
STRING_BYTES (XSTRING (s->data)) - sizeof *header); |
SBYTES (s->data) - sizeof *header); |
603 |
else |
else |
604 |
{ |
{ |
605 |
char *buffer; |
char *buffer; |
618 |
} |
} |
619 |
|
|
620 |
|
|
|
|
|
621 |
/*********************************************************************** |
/*********************************************************************** |
622 |
Sun Audio (*.au) |
Sun Audio (*.au) |
623 |
***********************************************************************/ |
***********************************************************************/ |
685 |
sd->configure (sd); |
sd->configure (sd); |
686 |
|
|
687 |
if (STRINGP (s->data)) |
if (STRINGP (s->data)) |
688 |
sd->write (sd, XSTRING (s->data)->data + header->data_offset, |
sd->write (sd, SDATA (s->data) + header->data_offset, |
689 |
STRING_BYTES (XSTRING (s->data)) - header->data_offset); |
SBYTES (s->data) - header->data_offset); |
690 |
else |
else |
691 |
{ |
{ |
692 |
int blksize = 2048; |
int blksize = 2048; |
707 |
} |
} |
708 |
|
|
709 |
|
|
|
|
|
710 |
/*********************************************************************** |
/*********************************************************************** |
711 |
Voxware Driver Interface |
Voxware Driver Interface |
712 |
***********************************************************************/ |
***********************************************************************/ |
880 |
sd->write = vox_write; |
sd->write = vox_write; |
881 |
} |
} |
882 |
|
|
|
|
|
883 |
/* Write NBYTES bytes from BUFFER to device SD. */ |
/* Write NBYTES bytes from BUFFER to device SD. */ |
884 |
|
|
885 |
static void |
static void |
886 |
vox_write (sd, buffer, nbytes) |
vox_write (sd, buffer, nbytes) |
887 |
struct sound_device *sd; |
struct sound_device *sd; |
888 |
char *buffer; |
const char *buffer; |
889 |
int nbytes; |
int nbytes; |
890 |
{ |
{ |
891 |
int nwritten = emacs_write (sd->fd, buffer, nbytes); |
int nwritten = emacs_write (sd->fd, buffer, nbytes); |
893 |
sound_perror ("Error writing to sound device"); |
sound_perror ("Error writing to sound device"); |
894 |
} |
} |
895 |
|
|
896 |
|
/* END: Non Windows functions */ |
897 |
|
#else /* WINDOWSNT */ |
898 |
|
|
899 |
|
/* BEGIN: Windows specific functions */ |
900 |
|
|
901 |
|
static int |
902 |
|
do_play_sound (psz_file, ui_volume) |
903 |
|
const char *psz_file; |
904 |
|
unsigned long ui_volume; |
905 |
|
{ |
906 |
|
int i_result = 0; |
907 |
|
MCIERROR mci_error = 0; |
908 |
|
char sz_cmd_buf[520] = {0}; |
909 |
|
char sz_ret_buf[520] = {0}; |
910 |
|
MMRESULT mm_result = MMSYSERR_NOERROR; |
911 |
|
unsigned long ui_volume_org = 0; |
912 |
|
BOOL b_reset_volume = FALSE; |
913 |
|
|
914 |
|
memset (sz_cmd_buf, 0, sizeof(sz_cmd_buf)); |
915 |
|
memset (sz_ret_buf, 0, sizeof(sz_ret_buf)); |
916 |
|
sprintf (sz_cmd_buf, |
917 |
|
"open \"%s\" alias GNUEmacs_PlaySound_Device wait", |
918 |
|
psz_file); |
919 |
|
mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, 520, NULL); |
920 |
|
if (mci_error != 0) |
921 |
|
{ |
922 |
|
sound_warning ("The open mciSendString command failed to open\n" |
923 |
|
"the specified sound file"); |
924 |
|
i_result = (int) mci_error; |
925 |
|
return i_result; |
926 |
|
} |
927 |
|
if ((ui_volume > 0) && (ui_volume != UINT_MAX)) |
928 |
|
{ |
929 |
|
mm_result = waveOutGetVolume ((HWAVEOUT) WAVE_MAPPER, &ui_volume_org); |
930 |
|
if (mm_result == MMSYSERR_NOERROR) |
931 |
|
{ |
932 |
|
b_reset_volume = TRUE; |
933 |
|
mm_result = waveOutSetVolume ((HWAVEOUT) WAVE_MAPPER, ui_volume); |
934 |
|
if ( mm_result != MMSYSERR_NOERROR) |
935 |
|
{ |
936 |
|
sound_warning ("waveOutSetVolume failed to set the volume level\n" |
937 |
|
"of the WAVE_MAPPER device.\n" |
938 |
|
"As a result, the user selected volume level will\n" |
939 |
|
"not be used."); |
940 |
|
} |
941 |
|
} |
942 |
|
else |
943 |
|
{ |
944 |
|
sound_warning ("waveOutGetVolume failed to obtain the original\n" |
945 |
|
"volume level of the WAVE_MAPPER device.\n" |
946 |
|
"As a result, the user selected volume level will\n" |
947 |
|
"not be used."); |
948 |
|
} |
949 |
|
} |
950 |
|
memset (sz_cmd_buf, 0, sizeof(sz_cmd_buf)); |
951 |
|
memset (sz_ret_buf, 0, sizeof(sz_ret_buf)); |
952 |
|
strcpy (sz_cmd_buf, "play GNUEmacs_PlaySound_Device wait"); |
953 |
|
mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, 520, NULL); |
954 |
|
if (mci_error != 0) |
955 |
|
{ |
956 |
|
sound_warning ("The play mciSendString command failed to play the\n" |
957 |
|
"opened sound file."); |
958 |
|
i_result = (int) mci_error; |
959 |
|
} |
960 |
|
memset (sz_cmd_buf, 0, sizeof(sz_cmd_buf)); |
961 |
|
memset (sz_ret_buf, 0, sizeof(sz_ret_buf)); |
962 |
|
strcpy (sz_cmd_buf, "close GNUEmacs_PlaySound_Device wait"); |
963 |
|
mci_error = mciSendString (sz_cmd_buf, sz_ret_buf, 520, NULL); |
964 |
|
if (b_reset_volume == TRUE) |
965 |
|
{ |
966 |
|
mm_result=waveOutSetVolume ((HWAVEOUT) WAVE_MAPPER, ui_volume_org); |
967 |
|
if (mm_result != MMSYSERR_NOERROR) |
968 |
|
{ |
969 |
|
sound_warning ("waveOutSetVolume failed to reset the original volume\n" |
970 |
|
"level of the WAVE_MAPPER device."); |
971 |
|
} |
972 |
|
} |
973 |
|
return i_result; |
974 |
|
} |
975 |
|
|
976 |
|
/* END: Windows specific functions */ |
977 |
|
|
978 |
|
#endif /* WINDOWSNT */ |
979 |
|
|
980 |
|
DEFUN ("play-sound-internal", Fplay_sound_internal, Splay_sound_internal, 1, 1, 0, |
981 |
|
doc: /* Play sound SOUND. |
982 |
|
|
983 |
|
Internal use only, use `play-sound' instead.\n */) |
984 |
|
(sound) |
985 |
|
Lisp_Object sound; |
986 |
|
{ |
987 |
|
Lisp_Object attrs[SOUND_ATTR_SENTINEL]; |
988 |
|
int count = SPECPDL_INDEX (); |
989 |
|
|
990 |
|
#ifndef WINDOWSNT |
991 |
|
Lisp_Object file; |
992 |
|
struct gcpro gcpro1, gcpro2; |
993 |
|
struct sound_device sd; |
994 |
|
struct sound s; |
995 |
|
Lisp_Object args[2]; |
996 |
|
#else /* WINDOWSNT */ |
997 |
|
int len = 0; |
998 |
|
Lisp_Object lo_file = {0}; |
999 |
|
char * psz_file = NULL; |
1000 |
|
unsigned long ui_volume_tmp = UINT_MAX; |
1001 |
|
unsigned long ui_volume = UINT_MAX; |
1002 |
|
int i_result = 0; |
1003 |
|
#endif /* WINDOWSNT */ |
1004 |
|
|
1005 |
|
/* Parse the sound specification. Give up if it is invalid. */ |
1006 |
|
if (!parse_sound (sound, attrs)) |
1007 |
|
error ("Invalid sound specification"); |
1008 |
|
|
1009 |
|
#ifndef WINDOWSNT |
1010 |
|
file = Qnil; |
1011 |
|
GCPRO2 (sound, file); |
1012 |
|
bzero (&sd, sizeof sd); |
1013 |
|
bzero (&s, sizeof s); |
1014 |
|
current_sound_device = &sd; |
1015 |
|
current_sound = &s; |
1016 |
|
record_unwind_protect (sound_cleanup, Qnil); |
1017 |
|
s.header = (char *) alloca (MAX_SOUND_HEADER_BYTES); |
1018 |
|
|
1019 |
|
if (STRINGP (attrs[SOUND_FILE])) |
1020 |
|
{ |
1021 |
|
/* Open the sound file. */ |
1022 |
|
s.fd = openp (Fcons (Vdata_directory, Qnil), |
1023 |
|
attrs[SOUND_FILE], Qnil, &file, Qnil); |
1024 |
|
if (s.fd < 0) |
1025 |
|
sound_perror ("Could not open sound file"); |
1026 |
|
|
1027 |
|
/* Read the first bytes from the file. */ |
1028 |
|
s.header_size = emacs_read (s.fd, s.header, MAX_SOUND_HEADER_BYTES); |
1029 |
|
if (s.header_size < 0) |
1030 |
|
sound_perror ("Invalid sound file header"); |
1031 |
|
} |
1032 |
|
else |
1033 |
|
{ |
1034 |
|
s.data = attrs[SOUND_DATA]; |
1035 |
|
s.header_size = min (MAX_SOUND_HEADER_BYTES, SBYTES (s.data)); |
1036 |
|
bcopy (SDATA (s.data), s.header, s.header_size); |
1037 |
|
} |
1038 |
|
|
1039 |
|
/* Find out the type of sound. Give up if we can't tell. */ |
1040 |
|
find_sound_type (&s); |
1041 |
|
|
1042 |
|
/* Set up a device. */ |
1043 |
|
if (STRINGP (attrs[SOUND_DEVICE])) |
1044 |
|
{ |
1045 |
|
int len = SCHARS (attrs[SOUND_DEVICE]); |
1046 |
|
sd.file = (char *) alloca (len + 1); |
1047 |
|
strcpy (sd.file, SDATA (attrs[SOUND_DEVICE])); |
1048 |
|
} |
1049 |
|
|
1050 |
|
if (INTEGERP (attrs[SOUND_VOLUME])) |
1051 |
|
sd.volume = XFASTINT (attrs[SOUND_VOLUME]); |
1052 |
|
else if (FLOATP (attrs[SOUND_VOLUME])) |
1053 |
|
sd.volume = XFLOAT_DATA (attrs[SOUND_VOLUME]) * 100; |
1054 |
|
|
1055 |
|
args[0] = Qplay_sound_functions; |
1056 |
|
args[1] = sound; |
1057 |
|
Frun_hook_with_args (2, args); |
1058 |
|
|
1059 |
|
/* There is only one type of device we currently support, the VOX |
1060 |
|
sound driver. Set up the device interface functions for that |
1061 |
|
device. */ |
1062 |
|
vox_init (&sd); |
1063 |
|
|
1064 |
|
/* Open the device. */ |
1065 |
|
sd.open (&sd); |
1066 |
|
|
1067 |
|
/* Play the sound. */ |
1068 |
|
s.play (&s, &sd); |
1069 |
|
|
1070 |
|
/* Close the input file, if any. */ |
1071 |
|
if (!STRINGP (s.data)) |
1072 |
|
{ |
1073 |
|
emacs_close (s.fd); |
1074 |
|
s.fd = -1; |
1075 |
|
} |
1076 |
|
|
1077 |
|
/* Close the device. */ |
1078 |
|
sd.close (&sd); |
1079 |
|
|
1080 |
|
/* Clean up. */ |
1081 |
|
current_sound_device = NULL; |
1082 |
|
current_sound = NULL; |
1083 |
|
UNGCPRO; |
1084 |
|
|
1085 |
|
#else /* WINDOWSNT */ |
1086 |
|
|
1087 |
|
lo_file = Fexpand_file_name (attrs[SOUND_FILE], Qnil); |
1088 |
|
len = XSTRING (lo_file)->size; |
1089 |
|
psz_file = (char *) alloca (len + 1); |
1090 |
|
strcpy (psz_file, XSTRING (lo_file)->data); |
1091 |
|
if (INTEGERP (attrs[SOUND_VOLUME])) |
1092 |
|
{ |
1093 |
|
ui_volume_tmp = XFASTINT (attrs[SOUND_VOLUME]); |
1094 |
|
} |
1095 |
|
else if (FLOATP (attrs[SOUND_VOLUME])) |
1096 |
|
{ |
1097 |
|
ui_volume_tmp = (unsigned long) XFLOAT_DATA (attrs[SOUND_VOLUME]) * 100; |
1098 |
|
} |
1099 |
|
/* |
1100 |
|
Based on some experiments I have conducted, a value of 100 or less |
1101 |
|
for the sound volume is much too low. You cannot even hear it. |
1102 |
|
A value of UINT_MAX indicates that you wish for the sound to played |
1103 |
|
at the maximum possible volume. A value of UINT_MAX/2 plays the |
1104 |
|
sound at 50% maximum volume. Therefore the value passed to do_play_sound |
1105 |
|
(and thus to waveOutSetVolume) must be some fraction of UINT_MAX. |
1106 |
|
The following code adjusts the user specified volume level appropriately. |
1107 |
|
*/ |
1108 |
|
if ((ui_volume_tmp > 0) && (ui_volume_tmp <= 100)) |
1109 |
|
{ |
1110 |
|
ui_volume = ui_volume_tmp * (UINT_MAX / 100); |
1111 |
|
} |
1112 |
|
i_result = do_play_sound (psz_file, ui_volume); |
1113 |
|
|
1114 |
|
#endif /* WINDOWSNT */ |
1115 |
|
|
1116 |
|
unbind_to (count, Qnil); |
1117 |
|
return Qnil; |
1118 |
|
} |
1119 |
|
|
1120 |
/*********************************************************************** |
/*********************************************************************** |
1121 |
Initialization |
Initialization |