How can I change the master volume on my laptop using python? I know of one method, where I simulate volume-up/volume-down keypresses using ctypes, but without knowing the current volume I'd have to have my code execute 50 consecutive down-volume key-presses whenever I started the application to calibrate it to zero.
Is there a way for me to get the current system volume, or better, set the master volume to a specified value?
I am running a Windows 8 64 bit system with Python 3.4.
Tim Roberts posted a comtypes example in the python-win32 discussion list regarding how to Control Volume on Windows. In the example below, I've modified Tim's code and added class methods that return the default audio endpoint and its IAudioEndpointVolume
interface.
The dB volume range on the two systems that I tested (Windows 7 and Windows 10) normalized the maximum level to 0 dB, but the minimum level and volume increment differed between systems. I recommend using the scalar and integral step methods instead. The scalar range is 0.0 to 1.0.
import ctypes
import comtypes
from ctypes import wintypes
MMDeviceApiLib = comtypes.GUID(
'{2FDAAFA3-7523-4F66-9957-9D5E7FE698F6}')
IID_IMMDevice = comtypes.GUID(
'{D666063F-1587-4E43-81F1-B948E807363F}')
IID_IMMDeviceCollection = comtypes.GUID(
'{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}')
IID_IMMDeviceEnumerator = comtypes.GUID(
'{A95664D2-9614-4F35-A746-DE8DB63617E6}')
IID_IAudioEndpointVolume = comtypes.GUID(
'{5CDF2C82-841E-4546-9722-0CF74078229A}')
CLSID_MMDeviceEnumerator = comtypes.GUID(
'{BCDE0395-E52F-467C-8E3D-C4579291692E}')
# EDataFlow
eRender = 0 # audio rendering stream
eCapture = 1 # audio capture stream
eAll = 2 # audio rendering or capture stream
# ERole
eConsole = 0 # games, system sounds, and voice commands
eMultimedia = 1 # music, movies, narration
eCommunications = 2 # voice communications
LPCGUID = REFIID = ctypes.POINTER(comtypes.GUID)
LPFLOAT = ctypes.POINTER(ctypes.c_float)
LPDWORD = ctypes.POINTER(wintypes.DWORD)
LPUINT = ctypes.POINTER(wintypes.UINT)
LPBOOL = ctypes.POINTER(wintypes.BOOL)
PIUnknown = ctypes.POINTER(comtypes.IUnknown)
class IMMDevice(comtypes.IUnknown):
_iid_ = IID_IMMDevice
_methods_ = (
comtypes.COMMETHOD([], ctypes.HRESULT, 'Activate',
(['in'], REFIID, 'iid'),
(['in'], wintypes.DWORD, 'dwClsCtx'),
(['in'], LPDWORD, 'pActivationParams', None),
(['out','retval'], ctypes.POINTER(PIUnknown), 'ppInterface')),
comtypes.STDMETHOD(ctypes.HRESULT, 'OpenPropertyStore', []),
comtypes.STDMETHOD(ctypes.HRESULT, 'GetId', []),
comtypes.STDMETHOD(ctypes.HRESULT, 'GetState', []))
PIMMDevice = ctypes.POINTER(IMMDevice)
class IMMDeviceCollection(comtypes.IUnknown):
_iid_ = IID_IMMDeviceCollection
PIMMDeviceCollection = ctypes.POINTER(IMMDeviceCollection)
class IMMDeviceEnumerator(comtypes.IUnknown):
_iid_ = IID_IMMDeviceEnumerator
_methods_ = (
comtypes.COMMETHOD([], ctypes.HRESULT, 'EnumAudioEndpoints',
(['in'], wintypes.DWORD, 'dataFlow'),
(['in'], wintypes.DWORD, 'dwStateMask'),
(['out','retval'], ctypes.POINTER(PIMMDeviceCollection),
'ppDevices')),
comtypes.COMMETHOD([], ctypes.HRESULT, 'GetDefaultAudioEndpoint',
(['in'], wintypes.DWORD, 'dataFlow'),
(['in'], wintypes.DWORD, 'role'),
(['out','retval'], ctypes.POINTER(PIMMDevice), 'ppDevices')))
@classmethod
def get_default(cls, dataFlow, role):
enumerator = comtypes.CoCreateInstance(
CLSID_MMDeviceEnumerator, cls, comtypes.CLSCTX_INPROC_SERVER)
return enumerator.GetDefaultAudioEndpoint(dataFlow, role)
class IAudioEndpointVolume(comtypes.IUnknown):
_iid_ = IID_IAudioEndpointVolume
_methods_ = (
comtypes.STDMETHOD(ctypes.HRESULT, 'RegisterControlChangeNotify', []),
comtypes.STDMETHOD(ctypes.HRESULT, 'UnregisterControlChangeNotify', []),
comtypes.COMMETHOD([], ctypes.HRESULT, 'GetChannelCount',
(['out', 'retval'], LPUINT, 'pnChannelCount')),
comtypes.COMMETHOD([], ctypes.HRESULT, 'SetMasterVolumeLevel',
(['in'], ctypes.c_float, 'fLevelDB'),
(['in'], LPCGUID, 'pguidEventContext', None)),
comtypes.COMMETHOD([], ctypes.HRESULT, 'SetMasterVolumeLevelScalar',
(['in'], ctypes.c_float, 'fLevel'),
(['in'], LPCGUID, 'pguidEventContext', None)),
comtypes.COMMETHOD([], ctypes.HRESULT, 'GetMasterVolumeLevel',
(['out','retval'], LPFLOAT, 'pfLevelDB')),
comtypes.COMMETHOD([], ctypes.HRESULT, 'GetMasterVolumeLevelScalar',
(['out','retval'], LPFLOAT, 'pfLevel')),
comtypes.COMMETHOD([], ctypes.HRESULT, 'SetChannelVolumeLevel',
(['in'], wintypes.UINT, 'nChannel'),
(['in'], ctypes.c_float, 'fLevelDB'),
(['in'], LPCGUID, 'pguidEventContext', None)),
comtypes.COMMETHOD([], ctypes.HRESULT, 'SetChannelVolumeLevelScalar',
(['in'], wintypes.UINT, 'nChannel'),
(['in'], ctypes.c_float, 'fLevel'),
(['in'], LPCGUID, 'pguidEventContext', None)),
comtypes.COMMETHOD([], ctypes.HRESULT, 'GetChannelVolumeLevel',
(['in'], wintypes.UINT, 'nChannel'),
(['out','retval'], LPFLOAT, 'pfLevelDB')),
comtypes.COMMETHOD([], ctypes.HRESULT, 'GetChannelVolumeLevelScalar',
(['in'], wintypes.UINT, 'nChannel'),
(['out','retval'], LPFLOAT, 'pfLevel')),
comtypes.COMMETHOD([], ctypes.HRESULT, 'SetMute',
(['in'], wintypes.BOOL, 'bMute'),
(['in'], LPCGUID, 'pguidEventContext', None)),
comtypes.COMMETHOD([], ctypes.HRESULT, 'GetMute',
(['out','retval'], LPBOOL, 'pbMute')),
comtypes.COMMETHOD([], ctypes.HRESULT, 'GetVolumeStepInfo',
(['out','retval'], LPUINT, 'pnStep'),
(['out','retval'], LPUINT, 'pnStepCount')),
comtypes.COMMETHOD([], ctypes.HRESULT, 'VolumeStepUp',
(['in'], LPCGUID, 'pguidEventContext', None)),
comtypes.COMMETHOD([], ctypes.HRESULT, 'VolumeStepDown',
(['in'], LPCGUID, 'pguidEventContext', None)),
comtypes.COMMETHOD([], ctypes.HRESULT, 'QueryHardwareSupport',
(['out','retval'], LPDWORD, 'pdwHardwareSupportMask')),
comtypes.COMMETHOD([], ctypes.HRESULT, 'GetVolumeRange',
(['out','retval'], LPFLOAT, 'pfLevelMinDB'),
(['out','retval'], LPFLOAT, 'pfLevelMaxDB'),
(['out','retval'], LPFLOAT, 'pfVolumeIncrementDB')))
@classmethod
def get_default(cls):
endpoint = IMMDeviceEnumerator.get_default(eRender, eMultimedia)
interface = endpoint.Activate(cls._iid_, comtypes.CLSCTX_INPROC_SERVER)
return ctypes.cast(interface, ctypes.POINTER(cls))
For example:
if __name__ == '__main__':
def show_vol(ev):
voldb = ev.GetMasterVolumeLevel()
volsc = ev.GetMasterVolumeLevelScalar()
volst, nstep = ev.GetVolumeStepInfo()
print('Master Volume (dB): %0.4f' % voldb)
print('Master Volume (scalar): %0.4f' % volsc)
print('Master Volume (step): %d / %d' % (volst, nstep))
def test():
ev = IAudioEndpointVolume.get_default()
vol = ev.GetMasterVolumeLevelScalar()
vmin, vmax, vinc = ev.GetVolumeRange()
print('Volume Range (min, max, step) (dB): '
'%0.4f, %0.4f, %0.4f' % (vmin, vmax, vinc))
show_vol(ev)
try:
print('\nIncrement the master volume')
ev.VolumeStepUp()
show_vol(ev)
print('\nDecrement the master volume twice')
ev.VolumeStepDown()
ev.VolumeStepDown()
show_vol(ev)
print('\nSet the master volume to 0.75 scalar')
ev.SetMasterVolumeLevelScalar(0.75)
show_vol(ev)
print('\nSet the master volume to 0.25 scalar')
ev.SetMasterVolumeLevelScalar(0.25)
show_vol(ev)
finally:
ev.SetMasterVolumeLevelScalar(vol)
comtypes.CoInitialize()
try:
test()
finally:
comtypes.CoUninitialize()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With