Skip to content

Commit

Permalink
Add support for volume management on Linux
Browse files Browse the repository at this point in the history
- Added support for volume increase, decrease, and set commands on Linux
- Updated volume.py to handle Linux-specific volume control using pulsectl
- Added pulsectl to requirements.txt
  • Loading branch information
Lenochxd committed Dec 16, 2024
1 parent 20acd87 commit b39333a
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 56 deletions.
140 changes: 85 additions & 55 deletions app/buttons/audio/volume.py
Original file line number Diff line number Diff line change
@@ -1,88 +1,118 @@
from app.utils.platform import is_win
from app.utils.platform import is_windows, is_linux

import time
import math
import ctypes
if is_win:
if is_windows:
import win32api
import win32con
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume, ISimpleAudioVolume
else:
from pulsectl import Pulse

from app.utils.logger import log



def get_current_volume():
if not is_win:
log.error("This command is only available on Windows")
raise RuntimeError("This command is only available on Windows")
if is_windows:
devices = AudioUtilities.GetSpeakers()
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
volume = ctypes.cast(interface, ctypes.POINTER(IAudioEndpointVolume))
return volume.GetMasterVolumeLevelScalar()

elif is_linux:
with Pulse('volume') as pulse:
sink = pulse.sink_list()[0]
return round(sink.volume.value_flat, 2)

devices = AudioUtilities.GetSpeakers()
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
volume = ctypes.cast(interface, ctypes.POINTER(IAudioEndpointVolume))
return volume.GetMasterVolumeLevelScalar()
else:
raise NotImplementedError("This command is not implemented for this platform")


def set_volume(target_volume):
if not is_win:
log.error("This command is only available on Windows")
raise RuntimeError("This command is only available on Windows")

current_volume = get_current_volume()
devices = AudioUtilities.GetSpeakers()
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
volume = ctypes.cast(interface, ctypes.POINTER(IAudioEndpointVolume))
while math.isclose(current_volume, target_volume, rel_tol=0.01) == False:
if current_volume > target_volume:
current_volume -= 0.01
else:
current_volume += 0.01
volume.SetMasterVolumeLevelScalar(current_volume, None)
time.sleep(0.01)
if is_windows:
current_volume = get_current_volume()
devices = AudioUtilities.GetSpeakers()
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
volume = ctypes.cast(interface, ctypes.POINTER(IAudioEndpointVolume))
while math.isclose(current_volume, target_volume, rel_tol=0.01) == False:
if current_volume > target_volume:
current_volume -= 0.01
else:
current_volume += 0.01
volume.SetMasterVolumeLevelScalar(current_volume, None)
time.sleep(0.01)

return current_volume
return current_volume

elif is_linux:
with Pulse('volume') as pulse:
sink = pulse.sink_list()[0]
volume = sink.volume
volume.value_flat = target_volume
pulse.volume_set(sink, volume)
return target_volume

else:
raise NotImplementedError("This command is not implemented for this platform.")


def increase_volume(delta):
if not is_win:
log.error("This command is only available on Windows")
raise RuntimeError("This command is only available on Windows")
def increase_volume(delta=1):
if is_windows:
win32api.keybd_event(win32con.VK_VOLUME_UP, 0)
win32api.keybd_event(win32con.VK_VOLUME_UP, 0, win32con.KEYEVENTF_KEYUP)
if delta == "":
return get_current_volume()
target_volume = get_current_volume() + (int(delta) / 100.0)
return set_volume(target_volume)

elif is_linux:
with Pulse('volume') as pulse:
sink = pulse.sink_list()[0]
target_volume = min(sink.volume.value_flat + (int(delta) / 100.0), 1.0)
volume = sink.volume
volume.value_flat = target_volume
pulse.volume_set(sink, volume)
return target_volume

win32api.keybd_event(win32con.VK_VOLUME_UP, 0)
win32api.keybd_event(win32con.VK_VOLUME_UP, 0, win32con.KEYEVENTF_KEYUP)
if delta == "":
return get_current_volume()
target_volume = get_current_volume() + (int(delta) / 100.0)
return set_volume(target_volume)
else:
raise NotImplementedError("This command is not implemented for this platform.")


def decrease_volume(delta):
if not is_win:
log.error("This command is only available on Windows")
raise RuntimeError("This command is only available on Windows")
def decrease_volume(delta=1):
if is_windows:
win32api.keybd_event(win32con.VK_VOLUME_DOWN, 0)
win32api.keybd_event(win32con.VK_VOLUME_DOWN, 0, win32con.KEYEVENTF_KEYUP)
if delta == "":
return get_current_volume()
target_volume = get_current_volume() - (int(delta) / 100.0)
return set_volume(target_volume)

elif is_linux:
with Pulse('volume') as pulse:
sink = pulse.sink_list()[0]
target_volume = max(sink.volume.value_flat - (int(delta) / 100.0), 0.0)
volume = sink.volume
volume.value_flat = target_volume
pulse.volume_set(sink, volume)
return target_volume

win32api.keybd_event(win32con.VK_VOLUME_DOWN, 0)
win32api.keybd_event(win32con.VK_VOLUME_DOWN, 0, win32con.KEYEVENTF_KEYUP)
if delta == "":
return get_current_volume()
target_volume = get_current_volume() - (int(delta) / 100.0)
return set_volume(target_volume)
else:
raise NotImplementedError("This command is not implemented for this platform.")


def handle_command(message):
if message.startswith("/volume +"):
delta = message.replace("/volume +", "")
if delta.replace(" ","") == "":
increase_volume("1")
else:
increase_volume(delta)
delta = message.replace("/volume +", "").strip()
delta = int(delta) if delta else 1
increase_volume(delta)

elif message.startswith("/volume -"):
delta = message.replace("/volume -", "")
if delta.replace(" ","") == "":
decrease_volume("1")
else:
decrease_volume(delta)
delta = message.replace("/volume -", "").strip()
delta = int(delta) if delta else 1
decrease_volume(delta)

elif message.startswith("/volume set"):
target_volume = int(message.replace("/volume set ", "")) / 100.0
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ notify_py==0.3.43
keyboard==0.13.5
PyAutoGUI==0.9.54
pywin32; sys_platform == 'win32'
pycaw==20240210
pycaw==20240210; sys_platform == 'win32'
pulsectl==24.11.0; sys_platform == 'linux'
pillow==11.0.0
PyAudio==0.2.14
webcolors==24.11.1
Expand Down

0 comments on commit b39333a

Please sign in to comment.