♻️ refactor(osd): improve audio and microphone volume/mute handling with 'changed' signal

This commit is contained in:
sankalp committed 2025-08-04 12:30:48 +05:45
1 parent 93a002b475
commit 50d5ba42f5
2 files changed
+169 -164

No files matched your search

+166 -161
View File
@@ -129,16 +129,40 @@ class AudioOSDContainer(Box):
self.scale.connect("value-changed", self.on_volume_changed)
self.audio.connect("notify::speaker", self.on_audio_speaker_changed)
# Connect to speaker-changed signal directly
self.audio.connect("speaker-changed", self.on_speaker_changed)
# Connect to current speaker if available
self._connect_speaker_signals()
def _connect_speaker_signals(self):
"""Connect to speaker's changed signal which should fire on both volume and mute changes"""
if self.audio.speaker:
self.audio.speaker.connect(
"notify::volume", self.on_volume_property_changed
# Connect to the main 'changed' signal from AudioStream
self.audio.speaker.connect("changed", self.on_speaker_stream_changed)
def on_speaker_stream_changed(self, *_):
"""This should be called whenever the speaker stream changes (volume OR mute)"""
if self.audio.speaker:
current_volume = (
round(self.audio.speaker.volume)
if hasattr(self.audio.speaker, "volume")
else 0
)
self.audio.speaker.connect("notify::muted", self.on_mute_changed)
current_muted = (
self.audio.speaker.muted
if hasattr(self.audio.speaker, "muted")
else False
)
# Check if either volume or mute state changed
if (
self.previous_volume != current_volume
or self.previous_muted != current_muted
):
self.previous_volume = current_volume
self.previous_muted = current_muted
self.update_volume()
self.emit("volume-changed")
def get_svg(self, value):
audio_level = 0 if value == 0 else min(int(math.ceil(value / 33)), 3)
@@ -146,92 +170,75 @@ class AudioOSDContainer(Box):
def sync_with_audio(self):
if self.audio.speaker:
volume = round(self.audio.speaker.volume)
volume = (
round(self.audio.speaker.volume)
if hasattr(self.audio.speaker, "volume")
else 0
)
self.scale.set_value(volume)
# self.update_icon(volume)
self.previous_volume = volume
self.previous_muted = self.audio.speaker.muted
self.previous_muted = (
self.audio.speaker.muted
if hasattr(self.audio.speaker, "muted")
else False
)
def on_volume_changed(self, *_):
if self.audio.speaker:
volume = self.scale.value
if 0 <= volume <= 100:
self.audio.speaker.set_volume(volume)
if volume == 0 or (self.audio.speaker and self.audio.speaker.muted):
self.scale.add_style_class("muted")
volume = 0
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/volume/audio-volume-{
self.get_svg(volume)
}.svg"
)
)
else:
self.on_volume_property_changed()
self.scale.remove_style_class("muted")
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/volume/audio-volume-{
self.get_svg(volume)
}.svg"
)
)
# self.icon.remove_style_class("muted")
self.update_volume_display(volume)
self.emit("volume-changed")
def update_volume_display(self, volume=None):
"""Update the visual display based on current volume/mute state"""
if not self.audio.speaker:
return
if volume is None:
volume = (
round(self.audio.speaker.volume)
if hasattr(self.audio.speaker, "volume")
else 0
)
is_muted = (
self.audio.speaker.muted if hasattr(self.audio.speaker, "muted") else False
)
if volume == 0 or is_muted:
self.scale.add_style_class("muted")
display_volume = 0
else:
self.scale.remove_style_class("muted")
display_volume = volume
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/volume/audio-volume-{
self.get_svg(display_volume)
}.svg"
)
)
def on_audio_speaker_changed(self, *_):
if self.audio.speaker:
self.audio.speaker.connect(
"notify::volume", self.on_volume_property_changed
)
self.audio.speaker.connect("notify::muted", self.on_mute_changed)
self.update_volume()
def on_speaker_changed(self, *_):
self._connect_speaker_signals()
self.update_volume()
def on_volume_property_changed(self, *_):
if self.audio.speaker:
current_volume = round(self.audio.speaker.volume)
if self.previous_volume is None or current_volume != self.previous_volume:
self.previous_volume = current_volume
self.update_volume()
self.emit("volume-changed")
def on_speaker_changed(self, *_):
self._connect_speaker_signals()
self.update_volume()
def update_volume(self, *_):
if self.audio.speaker and not self.is_hovered():
volume = round(self.audio.speaker.volume)
volume = (
round(self.audio.speaker.volume)
if hasattr(self.audio.speaker, "volume")
else 0
)
self.scale.set_value(volume)
if volume == 0 or (self.audio.speaker and self.audio.speaker.muted):
self.scale.add_style_class("muted")
volume = 0
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/volume/audio-volume-{
self.get_svg(volume)
}.svg"
)
)
else:
self.scale.remove_style_class("muted")
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/volume/audio-volume-{
self.get_svg(volume)
}.svg"
)
)
def on_mute_changed(self, *_):
if self.audio.speaker:
current_muted = self.audio.speaker.muted
if self.previous_muted is None or current_muted != self.previous_muted:
self.previous_muted = current_muted
self.emit("volume-changed")
self.update_volume_display(volume)
class MicrophoneOSDContainer(Box):
@@ -269,15 +276,40 @@ class MicrophoneOSDContainer(Box):
self.scale.connect("value-changed", self.on_volume_changed)
self.audio.connect("notify::microphone", self.on_audio_microphone_changed)
# Connect to microphone-changed signal directly
self.audio.connect("microphone-changed", self.on_microphone_changed)
# Connect to current microphone if available
self._connect_microphone_signals()
def _connect_microphone_signals(self):
"""Connect to microphone's changed signal which should fire on both volume and mute changes"""
if self.audio.microphone:
self.audio.microphone.connect(
"notify::volume", self.on_volume_property_changed
# Connect to the main 'changed' signal from AudioStream
self.audio.microphone.connect("changed", self.on_microphone_stream_changed)
def on_microphone_stream_changed(self, *_):
"""This should be called whenever the microphone stream changes (volume OR mute)"""
if self.audio.microphone:
current_volume = (
round(self.audio.microphone.volume)
if hasattr(self.audio.microphone, "volume")
else 0
)
self.audio.microphone.connect("notify::muted", self.on_mute_changed)
current_muted = (
self.audio.microphone.muted
if hasattr(self.audio.microphone, "muted")
else False
)
# Check if either volume or mute state changed
if (
self.previous_volume != current_volume
or self.previous_muted != current_muted
):
self.previous_volume = current_volume
self.previous_muted = current_muted
self.update_volume()
self.emit("mic-changed")
def get_svg(self, value):
audio_level = 0 if value == 0 else min(int(math.ceil(value / 33)), 3)
@@ -285,105 +317,77 @@ class MicrophoneOSDContainer(Box):
def sync_with_audio(self):
if self.audio.microphone:
volume = round(self.audio.microphone.volume)
volume = (
round(self.audio.microphone.volume)
if hasattr(self.audio.microphone, "volume")
else 0
)
self.scale.set_value(volume)
self.previous_volume = volume
self.previous_muted = self.audio.microphone.muted
self.previous_muted = (
self.audio.microphone.muted
if hasattr(self.audio.microphone, "muted")
else False
)
def on_volume_changed(self, *_):
if self.audio.microphone:
volume = self.scale.value
if 0 <= volume <= 100:
self.audio.microphone.set_volume(volume)
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/mic/microphone-{
self.get_svg(volume)
}.svg"
)
)
if volume == 0 or (
self.audio.microphone and self.audio.microphone.muted
):
volume = 0
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/mic/microphone-{
self.get_svg(volume)
}.svg"
)
)
else:
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/mic/microphone-{
self.get_svg(volume)
}.svg"
)
)
self.scale.remove_style_class("muted")
self.update_volume_display(volume)
self.emit("mic-changed")
def update_volume_display(self, volume=None):
"""Update the visual display based on current volume/mute state"""
if not self.audio.microphone:
return
if volume is None:
volume = (
round(self.audio.microphone.volume)
if hasattr(self.audio.microphone, "volume")
else 0
)
is_muted = (
self.audio.microphone.muted
if hasattr(self.audio.microphone, "muted")
else False
)
if volume == 0 or is_muted:
self.scale.add_style_class("muted")
display_volume = 0
else:
self.scale.remove_style_class("muted")
display_volume = volume
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/mic/microphone-{
self.get_svg(display_volume)
}.svg"
)
)
def on_audio_microphone_changed(self, *_):
if self.audio.microphone:
self.audio.microphone.connect(
"notify::volume", self.on_volume_property_changed
)
self.audio.microphone.connect("notify::muted", self.on_mute_changed)
self.update_volume()
def on_microphone_changed(self, *_):
self._connect_microphone_signals()
self.update_volume()
def on_volume_property_changed(self, *_):
if self.audio.microphone:
current_volume = round(self.audio.microphone.volume)
if self.previous_volume is None or current_volume != self.previous_volume:
self.previous_volume = current_volume
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/mic/microphone-{
self.get_svg(current_volume)
}.svg"
)
)
self.update_volume()
self.emit("mic-changed")
def on_microphone_changed(self, *_):
self._connect_microphone_signals()
self.update_volume()
def update_volume(self, *_):
if self.audio.microphone and not self.is_hovered():
volume = round(self.audio.microphone.volume)
volume = (
round(self.audio.microphone.volume)
if hasattr(self.audio.microphone, "volume")
else 0
)
self.scale.set_value(volume)
if volume == 0 or (self.audio.microphone and self.audio.microphone.muted):
self.scale.add_style_class("muted")
volume = 0
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/mic/microphone-{
self.get_svg(volume)
}.svg"
)
)
else:
self.osd_window_image.set_from_file(
get_relative_path(
f"../config/assets/icons/mic/microphone-{
self.get_svg(volume)
}.svg"
)
)
self.scale.remove_style_class("muted")
def on_mute_changed(self, *_):
if self.audio.microphone:
current_muted = self.audio.microphone.muted
if self.previous_muted is None or current_muted != self.previous_muted:
self.previous_muted = current_muted
GLib.idle_add(lambda: self.emit("mic-changed"))
self.update_volume_display(volume)
class OSD(Window):
@@ -419,6 +423,7 @@ class OSD(Window):
self.last_activity_time = time.time()
# Connect to the containers' signals
self.audio_container.connect("volume-changed", self.show_audio)
self.brightness_container.brightness_service.connect(
"screen", self.show_brightness
+3 -3
View File
@@ -1,8 +1,8 @@
#osd {
background-color: alpha(#fff, 0.09);
background-color: alpha(#fff, 0.09);
padding: 12px 20px;
margin: 70px;
min-height: 200px;
min-height: 200px;
border-radius: 16px;
}
@@ -19,7 +19,7 @@
}
#osd trough highlight {
border-radius: 100px 0px 0px 100px;
border-radius: 100px;
background: var(--primary);
transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}