Controlling the Master Volume

by Richard Russell, October 2016

The main Help documentation describes how to control the sound volume but in Windows Vista and later each running application has its own volume control and the code shown there affects only the sound output from your own program. If instead you want to control the master volume you can use the code listed here.

The code contains two functions: FNgetVolume() which returns the current setting and PROCsetVolume() which sets the volume to a specified value. In each case you can specify whether you want to work with deciBels (in which case the 'scalar%' parameter should be set to zero) or with a 'linear' volume range from 0.0 to 1.0 (in which case the 'scalar%' parameter should be set non-zero).

The code is adapted from the C++ code listed here.

        DEF PROCsetVolume(volume#, scalar%)
        LOCAL ole32%, oleaut32%, deviceEnumerator%, endpointDevice%, endpointVolume%
        LOCAL Release, Activate, GetDefaultAudioEndpoint, volume%
        LOCAL SetMasterVolumeLevel, SetMasterVolumeLevelScalar
 
        SYS "LoadLibrary", "ole32.dll" TO ole32%
        SYS "LoadLibrary", "oleaut32.dll" TO oleaut32%
        SYS "GetProcAddress", ole32%, "CoInitialize" TO `CoInitialize`
        SYS "GetProcAddress", ole32%, "CoUninitialize" TO `CoUninitialize`
        SYS "GetProcAddress", ole32%, "CoCreateInstance" TO `CoCreateInstance`
        SYS "GetProcAddress", ole32%, "CLSIDFromString" TO `CLSIDFromString`
        SYS "GetProcAddress", oleaut32%, "VarR4FromR8" TO `VarR4FromR8`
 
        SYS `CoInitialize`, 0
 
        CLSCTX_INPROC_SERVER = 1
        IID_MMDeviceEnumerator = FNguid("{A95664D2-9614-4F35-A746-DE8DB63617E6}")
        CLSID_MMDeviceEnumerator = FNguid("{BCDE0395-E52F-467C-8E3D-C4579291692E}")
        IID_IAudioEndpointVolume = FNguid("{5CDF2C82-841E-4546-9722-0CF74078229A}")
 
        Release = 2*4
        Activate = 3*4
        GetDefaultAudioEndpoint = 4*4
        SetMasterVolumeLevel = 6*4
        SetMasterVolumeLevelScalar = 7*4
 
        SYS `CoCreateInstance`, CLSID_MMDeviceEnumerator, 0, CLSCTX_INPROC_SERVER, \
        \  IID_MMDeviceEnumerator, ^deviceEnumerator%
 
        SYS !(!deviceEnumerator% + GetDefaultAudioEndpoint), deviceEnumerator%, \
        \   0, 0, ^endpointDevice%
 
        SYS !(!endpointDevice% + Activate), endpointDevice%, IID_IAudioEndpointVolume, \
        \   CLSCTX_INPROC_SERVER, 0, ^endpointVolume%
 
        SYS !(!endpointDevice% + Release), endpointDevice%
        endpointDevice% = 0
 
        SYS `VarR4FromR8`, !^volume#, !(^volume# + 4), ^volume%
        IF scalar% THEN
          SYS !(!endpointVolume% + SetMasterVolumeLevelScalar), endpointVolume%, volume%, 0
        ELSE
          SYS !(!endpointVolume% + SetMasterVolumeLevel), endpointVolume%, volume%, 0
        ENDIF
 
        SYS !(!endpointVolume% + Release), endpointVolume%
        endpointVolume% = 0
 
        SYS `CoUninitialize`
        SYS "FreeLibrary", oleaut32%
        SYS "FreeLibrary", ole32%
        ENDPROC
 
        DEF FNgetVolume(scalar%)
        LOCAL ole32%, oleaut32%, deviceEnumerator%, endpointDevice%, endpointVolume%
        LOCAL Release, Activate, GetDefaultAudioEndpoint, volume#, volume%
        LOCAL GetMasterVolumeLevel, GetMasterVolumeLevelScalar
 
        SYS "LoadLibrary", "ole32.dll" TO ole32%
        SYS "LoadLibrary", "oleaut32.dll" TO oleaut32%
        SYS "GetProcAddress", ole32%, "CoInitialize" TO `CoInitialize`
        SYS "GetProcAddress", ole32%, "CoUninitialize" TO `CoUninitialize`
        SYS "GetProcAddress", ole32%, "CoCreateInstance" TO `CoCreateInstance`
        SYS "GetProcAddress", ole32%, "CLSIDFromString" TO `CLSIDFromString`
        SYS "GetProcAddress", oleaut32%, "VarR8FromR4" TO `VarR8FromR4`
 
        SYS `CoInitialize`, 0
 
        CLSCTX_INPROC_SERVER = 1
        IID_MMDeviceEnumerator = FNguid("{A95664D2-9614-4F35-A746-DE8DB63617E6}")
        CLSID_MMDeviceEnumerator = FNguid("{BCDE0395-E52F-467C-8E3D-C4579291692E}")
        IID_IAudioEndpointVolume = FNguid("{5CDF2C82-841E-4546-9722-0CF74078229A}")
 
        Release = 2*4
        Activate = 3*4
        GetDefaultAudioEndpoint = 4*4
        GetMasterVolumeLevel = 8*4
        GetMasterVolumeLevelScalar = 9*4
 
        SYS `CoCreateInstance`, CLSID_MMDeviceEnumerator, 0, CLSCTX_INPROC_SERVER, \
        \  IID_MMDeviceEnumerator, ^deviceEnumerator%
 
        SYS !(!deviceEnumerator% + GetDefaultAudioEndpoint), deviceEnumerator%, \
        \   0, 0, ^endpointDevice%
 
        SYS !(!endpointDevice% + Activate), endpointDevice%, IID_IAudioEndpointVolume, \
        \   CLSCTX_INPROC_SERVER, 0, ^endpointVolume%
 
        SYS !(!endpointDevice% + Release), endpointDevice%
        endpointDevice% = 0
 
        IF scalar% THEN
          SYS !(!endpointVolume% + GetMasterVolumeLevelScalar), endpointVolume%, ^volume%
        ELSE
          SYS !(!endpointVolume% + GetMasterVolumeLevel), endpointVolume%, ^volume%
        ENDIF
        SYS `VarR8FromR4`, volume%, ^volume#
 
        SYS !(!endpointVolume% + Release), endpointVolume%
        endpointVolume% = 0
 
        SYS `CoUninitialize`
        SYS "FreeLibrary", oleaut32%
        SYS "FreeLibrary", ole32%
        = volume#
 
        DEF FNguid(A$)
        LOCAL C%, M%
        DIM C% 15, M% LOCAL 2*LENA$+1
        SYS "MultiByteToWideChar", 0, 0, A$, -1, M%, LENA$+1
        SYS `CLSIDFromString`, M%, C%
        = C%