audio: Create a new audio filter for ReplayGain and Background Mode
This links together our ReplayGain filter with a volume element that is set to the user configured background volume when background listening mode is enabled, and 100% when background listening mode is disabled. Implements: #50 ("Background Music Mode") Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
7155fa9db5
commit
84fbd94aa1
|
@ -0,0 +1,45 @@
|
||||||
|
# Copyright 2023 (c) Anna Schumaker.
|
||||||
|
"""A custom Gst.Bin with our audio filter effects."""
|
||||||
|
from gi.repository import GObject
|
||||||
|
from gi.repository import Gst
|
||||||
|
from . import replaygain
|
||||||
|
|
||||||
|
|
||||||
|
class Filter(Gst.Bin):
|
||||||
|
"""The audio filter element."""
|
||||||
|
|
||||||
|
bg_enabled = GObject.Property(type=bool, default=False)
|
||||||
|
bg_volume = GObject.Property(type=float, default=0.5)
|
||||||
|
rg_mode = GObject.Property(type=str, default="disabled")
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize the audio filter."""
|
||||||
|
super().__init__()
|
||||||
|
self._replaygain = replaygain.Filter()
|
||||||
|
self._volume = Gst.ElementFactory.make("volume")
|
||||||
|
|
||||||
|
self.add(self._replaygain)
|
||||||
|
self.add(self._volume)
|
||||||
|
|
||||||
|
rg_pad = self._replaygain.get_static_pad("src")
|
||||||
|
rg_pad.link(self._volume.get_static_pad("sink"))
|
||||||
|
|
||||||
|
self.__add_ghost_pad("sink", self._replaygain)
|
||||||
|
self.__add_ghost_pad("src", self._volume)
|
||||||
|
|
||||||
|
self.connect("notify", self.__notify)
|
||||||
|
|
||||||
|
def __add_ghost_pad(self, pad: str, elm: Gst.Element) -> None:
|
||||||
|
self.add_pad(Gst.GhostPad.new(pad, elm.get_static_pad(pad)))
|
||||||
|
|
||||||
|
def __notify(self, filter: Gst.Bin, param: GObject.ParamSpec) -> None:
|
||||||
|
match param.name:
|
||||||
|
case "bg-enabled" | "bg-volume":
|
||||||
|
vol = self.bg_volume if self.bg_enabled else 1.0
|
||||||
|
if vol != self._volume.get_property("volume"):
|
||||||
|
vs = f"{round(vol * 100)}%" if self.bg_enabled else "off"
|
||||||
|
print(f"audio: setting background listening to {vs}")
|
||||||
|
self._volume.set_property("volume", vol)
|
||||||
|
case "rg-mode":
|
||||||
|
if self.rg_mode != self._replaygain.mode:
|
||||||
|
self._replaygain.mode = self.rg_mode
|
|
@ -0,0 +1,72 @@
|
||||||
|
# Copyright 2023 (c) Anna Schumaker.
|
||||||
|
"""Tests our combined Filter element."""
|
||||||
|
import io
|
||||||
|
import unittest
|
||||||
|
import emmental.audio.filter
|
||||||
|
from gi.repository import Gst
|
||||||
|
|
||||||
|
|
||||||
|
class TestFilter(unittest.TestCase):
|
||||||
|
"""Tests our custom Filter element."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Set up common variables."""
|
||||||
|
self.filter = emmental.audio.filter.Filter()
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
"""Test that the filter is set up correctly."""
|
||||||
|
self.assertIsInstance(self.filter, Gst.Bin)
|
||||||
|
self.assertIsInstance(self.filter.get_static_pad("src"), Gst.GhostPad)
|
||||||
|
self.assertIsInstance(self.filter.get_static_pad("sink"), Gst.GhostPad)
|
||||||
|
|
||||||
|
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||||
|
def test_replaygain(self, mock_stdout: io.StringIO):
|
||||||
|
"""Test the ReplayGain element in the filter."""
|
||||||
|
self.assertIsInstance(self.filter._replaygain,
|
||||||
|
emmental.audio.replaygain.Filter)
|
||||||
|
|
||||||
|
self.assertEqual(self.filter.rg_mode, "disabled")
|
||||||
|
self.filter.rg_mode = "track"
|
||||||
|
self.assertEqual(self.filter._replaygain.mode, "track")
|
||||||
|
self.filter.rg_mode = "track"
|
||||||
|
self.assertEqual(mock_stdout.getvalue(),
|
||||||
|
"audio: setting ReplayGain mode to 'track'\n")
|
||||||
|
|
||||||
|
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
|
||||||
|
def test_volume(self, mock_stdout: io.StringIO):
|
||||||
|
"""Test the Volume element in the filter."""
|
||||||
|
self.assertIsInstance(self.filter._volume, Gst.Element)
|
||||||
|
self.assertRegex(self.filter._volume.name, r"volume\d+")
|
||||||
|
self.assertEqual(self.filter._volume.get_property("volume"), 1.0)
|
||||||
|
|
||||||
|
self.assertFalse(self.filter.bg_enabled)
|
||||||
|
self.assertEqual(self.filter.bg_volume, 0.5)
|
||||||
|
|
||||||
|
self.filter.bg_enabled = True
|
||||||
|
self.assertEqual(self.filter._volume.get_property("volume"), 0.5)
|
||||||
|
self.assertEqual(mock_stdout.getvalue(),
|
||||||
|
"audio: setting background listening to 50%\n")
|
||||||
|
|
||||||
|
self.filter.bg_volume = 0.50
|
||||||
|
self.assertEqual(mock_stdout.getvalue(),
|
||||||
|
"audio: setting background listening to 50%\n")
|
||||||
|
|
||||||
|
self.filter.bg_volume = 0.75
|
||||||
|
self.assertEqual(self.filter._volume.get_property("volume"), 0.75)
|
||||||
|
self.assertEqual(mock_stdout.getvalue(),
|
||||||
|
"audio: setting background listening to 50%\n"
|
||||||
|
"audio: setting background listening to 75%\n")
|
||||||
|
|
||||||
|
self.filter.bg_enabled = False
|
||||||
|
self.assertEqual(self.filter._volume.get_property("volume"), 1.0)
|
||||||
|
self.assertEqual(mock_stdout.getvalue(),
|
||||||
|
"audio: setting background listening to 50%\n"
|
||||||
|
"audio: setting background listening to 75%\n"
|
||||||
|
"audio: setting background listening to off\n")
|
||||||
|
|
||||||
|
self.filter.bg_volume = 0.5
|
||||||
|
self.assertEqual(self.filter._volume.get_property("volume"), 1.0)
|
||||||
|
self.assertEqual(mock_stdout.getvalue(),
|
||||||
|
"audio: setting background listening to 50%\n"
|
||||||
|
"audio: setting background listening to 75%\n"
|
||||||
|
"audio: setting background listening to off\n")
|
Loading…
Reference in New Issue