19

How do I change it?

I'm looking for something like:

SetMasterVolume(0.5)
SetAppVolume('FooBar',0.5)

I tried using ctypes.windll.winmm, but I can't find much documentation on how to use it.

Thanks in advance.

f.rodrigues
  • 3,499
  • 6
  • 26
  • 62
  • 1
    Also sorry to one man this all, but changing the app volume depends on the library you are using, if you are using pygame it is `pygame.mixer.music.set_volume(value)` – KodyVanRy Dec 30 '13 at 01:36

11 Answers11

44

I'd hope after 5 years this is no longer a problem for you, but I've just had to do the same thing. It's possible using the PyCaw library.

Simple proof of concept, based on PyCaw's examples

from __future__ import print_function
from pycaw.pycaw import AudioUtilities, ISimpleAudioVolume


def main():
    sessions = AudioUtilities.GetAllSessions()
    for session in sessions:
        volume = session._ctl.QueryInterface(ISimpleAudioVolume)
        if session.Process and session.Process.name() == "vlc.exe":
            print("volume.GetMasterVolume(): %s" % volume.GetMasterVolume())
            volume.SetMasterVolume(0.6, None)


if __name__ == "__main__":
    main()
jblz
  • 1,001
  • 2
  • 13
  • 33
10

this is a very roundabout way to do it but it works.you can simulate the key presses for the computers master volume with pynput. it works but it is quite inacurate.

from pynput.keyboard import Key,Controller
keyboard = Controller()
import time
while True:
    for i in range(10):
        keyboard.press(Key.media_volume_up)
        keyboard.release(Key.media_volume_up)
        time.sleep(0.1)
    for i in range(10):
        keyboard.press(Key.media_volume_down)
        keyboard.release(Key.media_volume_down)
        time.sleep(0.1)
    time.sleep(2) 

this is an ok method of doing it. every keypress and release is equal to about 2 volume . hope this is somewhat helpful!

dragon445
  • 121
  • 1
  • 4
4

I ripped this from here and modified it to use functions only.

import time
import ctypes

# Import the SendInput object
SendInput = ctypes.windll.user32.SendInput

# C struct redefinitions
PUL = ctypes.POINTER(ctypes.c_ulong)

class KeyBoardInput(ctypes.Structure):
    _fields_ = [
        ("wVk", ctypes.c_ushort),
        ("wScan", ctypes.c_ushort),
        ("dwFlags", ctypes.c_ulong),
        ("time", ctypes.c_ulong),
        ("dwExtraInfo", PUL)
    ]

class HardwareInput(ctypes.Structure):
    _fields_ = [
        ("uMsg", ctypes.c_ulong),
        ("wParamL", ctypes.c_short),
        ("wParamH", ctypes.c_ushort)
    ]

class MouseInput(ctypes.Structure):
    _fields_ = [
        ("dx", ctypes.c_long),
        ("dy", ctypes.c_long),
        ("mouseData", ctypes.c_ulong),
        ("dwFlags", ctypes.c_ulong),
        ("time",ctypes.c_ulong),
        ("dwExtraInfo", PUL)
    ]

class Input_I(ctypes.Union):
    _fields_ = [
        ("ki", KeyBoardInput),
        ("mi", MouseInput),
        ("hi", HardwareInput)
    ]

class Input(ctypes.Structure):
    _fields_ = [
        ("type", ctypes.c_ulong),
        ("ii", Input_I)
    ]

VK_VOLUME_MUTE = 0xAD
VK_VOLUME_DOWN = 0xAE
VK_VOLUME_UP = 0xAF

def key_down(keyCode):
    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.ki = KeyBoardInput(keyCode, 0x48, 0, 0, ctypes.pointer(extra))
    x = Input( ctypes.c_ulong(1), ii_ )
    SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))


def key_up(keyCode):
    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.ki = KeyBoardInput(keyCode, 0x48, 0x0002, 0, ctypes.pointer(extra))
    x = Input(ctypes.c_ulong(1), ii_)
    SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))


def key(key_code, length = 0):    
    key_down(key_code)
    time.sleep(length)
    key_up(key_code)


def volume_up():
    key(VK_VOLUME_UP)


def volume_down():
    key(VK_VOLUME_DOWN)

def set_volume(int):
    for _ in range(0, 50):
        volume_down()
    for _ in range(int / 2):
        volume_up()
Elijah
  • 1,814
  • 21
  • 27
Matthew Moisen
  • 16,701
  • 27
  • 128
  • 231
  • This is a viable workaround for the EventGhost issue where the volume stops working in Win10 after installation of LAV filters. – alfadog67 Sep 10 '19 at 22:43
4

I know this is too late but if someone is still looking for a seamless solution

Download this exe application from here

and just call this command using python

./SoundVolumeView.exe /SetVolume "High Definition Audio Device\Device\Speakers" 50 
MedoAlmasry
  • 452
  • 5
  • 19
3
from ctypes import cast, POINTER
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume
 
devices = AudioUtilities.GetSpeakers()
interface = devices.Activate(
   IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
volume = cast(interface, POINTER(IAudioEndpointVolume))
 
# Control volume
#volume.SetMasterVolumeLevel(-0.0, None) #max
#volume.SetMasterVolumeLevel(-5.0, None) #72%
volume.SetMasterVolumeLevel(-10.0, None) #51%
1

So instead of editing my old answer I'm adding a new post to allow others who use self to use my old code and anyone who's not, to use my new code.

def get_master_volume():
    proc = subprocess.Popen('/usr/bin/amixer sget Master', shell=True, stdout=subprocess.PIPE)
    amixer_stdout = proc.communicate()[0].split('\n')[4]
    proc.wait()

    find_start = amixer_stdout.find('[') + 1
    find_end = amixer_stdout.find('%]', find_start)

    return float(amixer_stdout[find_start:find_end])

def set_master_volume(volume):
    val = float(int(volume))

    proc = subprocess.Popen('/usr/bin/amixer sset Master ' + str(val) + '%', shell=True, stdout=subprocess.PIPE)
    proc.wait()
KodyVanRy
  • 1,110
  • 1
  • 14
  • 25
  • Thanks again for the reply. I'm still getting 'The system cannot find the path specified.' – f.rodrigues Dec 30 '13 at 14:39
  • 1
    @f.rodrigues, this does not work on windows. the path `/usr` is only on unix-based systems – 56- Nov 17 '17 at 18:11
  • 2
    The question is about windows. Your answer is only for unix based systems, it does not answer the question. – Noam Jul 03 '20 at 13:14
1

You can use pyautogui:

import pyautogui
pyautogui.FAILSAFE = False
x = 1

while x < 10:
    pyautogui.press('volumedown')
    x+=1
FObersteiner
  • 22,500
  • 8
  • 42
  • 72
gabeyww
  • 31
  • 4
1
import pyautogui

x = 50
a = (x//2)
pyautogui.press('volumeup',a)

#this code will increase computer master volume by even numbers
Daiton
  • 11
  • 2
1

you can set the number of presses in pyautogui and create to an even output

e.g. 43 input = 42 output

imports used:

import pyautogui as p
import math, time
from tkinter import simpledialog

using tkinter askdialog to get an input

volume = simpledialog.askinteger("new volume", "what would you like the volume to be?")

and setting the volume

def set_vol(new_volume):
    p.press('volumedown', presses = 50) #sets volume to zero
    time.sleep(0.5) #using time.sleep to space the presses 
    x = math.floor(new_volume / 2) #setting the amount of presses required
    p.press('volumeup', presses = x) #setting volume

running the definition

set_vol(volume) 
Ethan
  • 11
  • 1
  • Welcome to Stack Overflow @Ethan. I am curious to why you would want to divide the new_volume with two, and why you need the sleep? – bohrax Nov 03 '22 at 18:28
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 03 '22 at 18:28
0

I figured out a way to set the master volume using numpy, while still using Pycaw.

from ctypes import cast, POINTER
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume
import numpy as np

Get the audio output device

devices = AudioUtilities.GetSpeakers()
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
volume = cast(interface, POINTER(IAudioEndpointVolume))

Set the desired volume in percentage (0 to 100)

desired_vol = 50

Get the volume range (min and max values in dB)

vol_range = volume.GetVolumeRange()
min_vol = vol_range[0]
max_vol = vol_range[1]

Convert the desired volume percentage to volume level in dB

desired_vol_db = np.interp(desired_vol, [0, 100], [min_vol, max_vol])
volume.SetMasterVolumeLevelScalar(desired_vol / 100, None)

Print the current volume percentage

curr_vol = int(volume.GetMasterVolumeLevelScalar() * 100)
print(f'Volume set to: {int(curr_vol)} %')
starball
  • 20,030
  • 7
  • 43
  • 238
Robert
  • 1
  • 1
-1

First import subprocess import subprocess

Then to get the master volume

def get_master_volume(self):
    proc = subprocess.Popen('/usr/bin/amixer sget Master', shell=True, stdout=subprocess.PIPE)
    amixer_stdout = proc.communicate()[0].split('\n')[4]
    proc.wait()

    find_start = amixer_stdout.find('[') + 1
    find_end = amixer_stdout.find('%]', find_start)

    return float(amixer_stdout[find_start:find_end])

And to set the Master volume

def set_master_volume(self, widget):
    val = self.volume
    val = float(int(val))
    proc = subprocess.Popen('/usr/bin/amixer sset Master ' + str(val) + '%', shell=True, stdout=subprocess.PIPE)
    proc.wait()
KodyVanRy
  • 1,110
  • 1
  • 14
  • 25
  • Thanks for the reply. If I try 'set_master_volume(0.5,'foobar2000')' I get: AttributeError: 'float' object has no attribute 'volume' If I chance val to 0.5 I get: 'The system cannot find the path specified.' I guess I need to get the application correct address, but I don't know how to do it. – f.rodrigues Dec 29 '13 at 19:29
  • I see no reason to set the volume to .5% as you might be able to see it will set the master volume to a percentage so say you want to change the volume to 50% `self.volume` needs to equal 50.0 so just by calling `val = float(int(val))` It will set 50 to an integer if its something like 50.1 and then changes it to a float of `50.0` – KodyVanRy Dec 30 '13 at 01:15
  • 18
    This is a Linux answer. How does it apply to MS-Windows? The question specifies Windows. – Brock Adams Aug 08 '14 at 23:08