From 300c6dc2f979ba358b44493cf871db2d49ae0a89 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sun, 7 Dec 2025 11:31:40 +0200 Subject: Allow volume control of system audio device on MS-Windows * src/sound.c (Fw32_sound_volume): New function. * etc/NEWS: Announce it. --- etc/NEWS | 5 +++++ src/sound.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/etc/NEWS b/etc/NEWS index 9f47cd57eac..455377f524c 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3719,6 +3719,11 @@ means of the GDI+ library. In addition to ':file FILE' for playing a sound from a file, ':data DATA' can now be used to play a sound from memory. +--- +** New primitive 'w32-sound-volume'. +This primitive allows to get and set the volume of the system's default +audio device (or the "optimal device", if there are several devices). + --- ** The MS-DOS port of Emacs now supports more recent releases of GCC and Binutils. Accordingly, we have revised our recommendations for a suitable DJGPP diff --git a/src/sound.c b/src/sound.c index 38f1b0c356c..6d833306d63 100644 --- a/src/sound.c +++ b/src/sound.c @@ -1357,6 +1357,70 @@ do_play_sound (const char *psz_file_or_data, unsigned long ui_volume, bool in_me return i_result; } +DEFUN ("w32-sound-volume", Fw32_sound_volume, Sw32_sound_volume, 0, 1, 0, + doc: /* Get or set the MS-Windows audio volume setting. +If VOLUME is specified, it should be the volume to set, either an integer +in the range [0, 100], or a float in the range [0, 1.0]. Value of zero +means mute the audio, value of 100 or 1.0 means maximum volume. +When called with the VOLUME argument nil or omitted, just return the +current volume setting. +The return value is the integer volume setting before the change, if any. */) + (Lisp_Object volume) +{ + DWORD ui_volume, ui_volume_orig; + MMRESULT mm_result = MMSYSERR_NOERROR; + + if (FIXNUMP (volume)) + ui_volume = XFIXNUM (volume); + else if (FLOATP (volume)) + ui_volume = XFLOAT_DATA (volume) * 100; + else if (BIGNUMP (volume)) + { + double dvolume = bignum_to_double (volume); + if (dvolume < 0) + ui_volume = 0; + else if (dvolume > 100.0) + ui_volume = 100; + else + ui_volume = dvolume + 0.5; + } + else if (!NILP (volume)) + wrong_type_argument (Qnumberp, volume); + + mm_result = waveOutGetVolume ((HWAVEOUT) WAVE_MAPPER, &ui_volume_orig); + if (mm_result == MMSYSERR_NOERROR) + { + /* Look only at the low 16 bits, assuming left and right channels + have identical volume settings. */ + ui_volume_orig &= 0xFFFF; + /* The value is between 0 and 0xFFFF, convert to our scale. */ + ui_volume_orig = ui_volume_orig * 100.0 / 65535.0 + 0.5; + } + else + { + SOUND_WARNING (waveOutGetErrorText, mm_result, + "waveOutGetVolume: failed to obtain the original" + " volume level of the WAVE_MAPPER device."); + ui_volume_orig = 100; + } + if (!NILP (volume)) + { + if (ui_volume > 100) + ui_volume = 100; + /* Set the same volume for left and right channels. */ + ui_volume = ui_volume * 0xFFFF / 100; + ui_volume = (ui_volume << 16) + ui_volume; + mm_result = waveOutSetVolume ((HWAVEOUT) WAVE_MAPPER, ui_volume); + if (mm_result != MMSYSERR_NOERROR) + { + SOUND_WARNING (waveOutGetErrorText, mm_result, + "waveOutSetVolume: failed to set the volume level" + " of the WAVE_MAPPER device."); + } + } + return make_fixnum (ui_volume_orig); +} + /* END: Windows specific functions */ #endif /* WINDOWSNT */ @@ -1482,6 +1546,9 @@ syms_of_sound (void) DEFSYM (Qplay_sound_functions, "play-sound-functions"); defsubr (&Splay_sound_internal); +#ifdef WINDOWSNT + defsubr (&Sw32_sound_volume); +#endif } #endif /* HAVE_SOUND */ -- cgit v1.2.1