audio: Replace the playbin audio-filter with the new filter

And wire up the bg-enabled and bg-volume properties from the header to
the playbin properties with the same name.

Implements: #50 ("Background Music Mode")
Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
Anna Schumaker 2023-05-28 16:51:30 -04:00
parent 84fbd94aa1
commit 8afd1a6240
4 changed files with 50 additions and 14 deletions

View File

@ -127,7 +127,8 @@ class Application(Adw.Application):
def build_header(self) -> header.Header: def build_header(self) -> header.Header:
"""Build a new header instance.""" """Build a new header instance."""
hdr = header.Header(sql=self.db, title=VERSION_STRING) hdr = header.Header(sql=self.db, title=VERSION_STRING)
hdr.bind_property("volume", self.player, "volume") for prop in ["bg-enabled", "bg-volume", "volume"]:
hdr.bind_property(prop, self.player, prop)
for (setting, property) in [("audio.volume", "volume"), for (setting, property) in [("audio.volume", "volume"),
("audio.background.enabled", "bg-enabled"), ("audio.background.enabled", "bg-enabled"),
("audio.background.volume", "bg-volume"), ("audio.background.volume", "bg-volume"),

View File

@ -4,7 +4,7 @@ import pathlib
from gi.repository import GObject from gi.repository import GObject
from gi.repository import GLib from gi.repository import GLib
from gi.repository import Gst from gi.repository import Gst
from . import replaygain from . import filter
from .. import path from .. import path
from .. import tmpdir from .. import tmpdir
@ -35,16 +35,18 @@ class Player(GObject.GObject):
playtime = GObject.Property(type=float) playtime = GObject.Property(type=float)
savedtime = GObject.Property(type=float) savedtime = GObject.Property(type=float)
bg_enabled = GObject.Property(type=bool, default=False)
bg_volume = GObject.Property(type=float, default=0.5)
pause_on_load = GObject.Property(type=bool, default=False) pause_on_load = GObject.Property(type=bool, default=False)
def __init__(self): def __init__(self):
"""Initialize the audio Player.""" """Initialize the audio Player."""
super().__init__() super().__init__()
self._replaygain = replaygain.Filter() self._filter = filter.Filter()
self._timeout = None self._timeout = None
self._playbin = Gst.ElementFactory.make("playbin") self._playbin = Gst.ElementFactory.make("playbin")
self._playbin.set_property("audio-filter", self._replaygain) self._playbin.set_property("audio-filter", self._filter)
self._playbin.set_property("video-sink", self._playbin.set_property("video-sink",
Gst.ElementFactory.make("fakesink")) Gst.ElementFactory.make("fakesink"))
self._playbin.set_state(Gst.State.READY) self._playbin.set_state(Gst.State.READY)
@ -58,6 +60,8 @@ class Player(GObject.GObject):
bus.connect("message::tag", self.__msg_tags) bus.connect("message::tag", self.__msg_tags)
self.bind_property("volume", self._playbin, "volume") self.bind_property("volume", self._playbin, "volume")
self.bind_property("bg-enabled", self._filter, "bg-enabled")
self.bind_property("bg-volume", self._filter, "bg-volume")
self.connect("notify::file", self.__notify_file) self.connect("notify::file", self.__notify_file)
@ -163,7 +167,7 @@ class Player(GObject.GObject):
def get_replaygain(self) -> tuple[bool, str | None]: def get_replaygain(self) -> tuple[bool, str | None]:
"""Get the current ReplayGain mode.""" """Get the current ReplayGain mode."""
mode = self._replaygain.mode mode = self._filter.rg_mode
return (False, None) if mode == "disabled" else (True, mode) return (False, None) if mode == "disabled" else (True, mode)
def get_state(self) -> Gst.State: def get_state(self) -> Gst.State:
@ -191,7 +195,7 @@ class Player(GObject.GObject):
def set_replaygain(self, enabled: bool, mode: str) -> None: def set_replaygain(self, enabled: bool, mode: str) -> None:
"""Set the ReplayGain mode.""" """Set the ReplayGain mode."""
self._replaygain.mode = mode if enabled else "disabled" self._filter.rg_mode = mode if enabled else "disabled"
def set_state_sync(self, state: Gst.State) -> None: def set_state_sync(self, state: Gst.State) -> None:
"""Set the state of the playbin, and wait for it to change.""" """Set the state of the playbin, and wait for it to change."""

View File

@ -53,6 +53,27 @@ class TestAudio(unittest.TestCase):
self.assertRegex(self.player._playbin.get_property("video-sink").name, self.assertRegex(self.player._playbin.get_property("video-sink").name,
r"fakesink\d+") r"fakesink\d+")
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
def test_filter(self, mock_stdout: io.StringIO):
"""Test the filter element added to the playbin."""
self.assertIsInstance(self.player._filter,
emmental.audio.filter.Filter)
self.assertEqual(self.player._playbin.get_property("audio-filter"),
self.player._filter)
self.assertFalse(self.player.bg_enabled)
self.assertEqual(self.player.bg_volume, 0.5)
self.player.bg_enabled = True
self.player.bg_volume = 0.75
self.assertTrue(self.player._filter.bg_enabled)
self.assertEqual(self.player._filter.bg_volume, 0.75)
self.player.bg_enabled = False
self.player.bg_volume = 0.5
self.assertFalse(self.player._filter.bg_enabled)
self.assertEqual(self.player.bg_volume, 0.5)
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO) @unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
def test_eos(self, mock_stdout: io.StringIO): def test_eos(self, mock_stdout: io.StringIO):
"""Test handling an EOS message.""" """Test handling an EOS message."""
@ -297,22 +318,17 @@ class TestAudio(unittest.TestCase):
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO) @unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
def test_replaygain(self, mock_stdout: io.StringIO): def test_replaygain(self, mock_stdout: io.StringIO):
"""Test that ReplayGain functions work as expected.""" """Test that ReplayGain functions work as expected."""
self.assertIsInstance(self.player._replaygain, self.assertEqual(self.player._filter.rg_mode, "disabled")
emmental.audio.replaygain.Filter)
self.assertEqual(self.player._playbin.get_property("audio-filter"),
self.player._replaygain)
self.assertEqual(self.player._replaygain.mode, "disabled")
self.assertEqual(self.player.get_replaygain(), (False, None)) self.assertEqual(self.player.get_replaygain(), (False, None))
self.player.set_replaygain(True, "album") self.player.set_replaygain(True, "album")
self.assertEqual(self.player._replaygain.mode, "album") self.assertEqual(self.player._filter.rg_mode, "album")
self.assertEqual(self.player.get_replaygain(), (True, "album")) self.assertEqual(self.player.get_replaygain(), (True, "album"))
self.assertRegex(mock_stdout.getvalue(), self.assertRegex(mock_stdout.getvalue(),
r"audio: setting ReplayGain mode to 'album'") r"audio: setting ReplayGain mode to 'album'")
self.player.set_replaygain(False, "track") self.player.set_replaygain(False, "track")
self.assertEqual(self.player._replaygain.mode, "disabled") self.assertEqual(self.player._filter.rg_mode, "disabled")
self.assertEqual(self.player.get_replaygain(), (False, None)) self.assertEqual(self.player.get_replaygain(), (False, None))
self.assertRegex(mock_stdout.getvalue(), self.assertRegex(mock_stdout.getvalue(),
r"audio: setting ReplayGain mode to 'disabled'") r"audio: setting ReplayGain mode to 'disabled'")

View File

@ -220,3 +220,18 @@ class TestEmmental(unittest.TestCase):
self.assertEqual(player.get_replaygain(), (True, "track")) self.assertEqual(player.get_replaygain(), (True, "track"))
win.header.rg_mode = "album" win.header.rg_mode = "album"
self.assertEqual(player.get_replaygain(), (True, "album")) self.assertEqual(player.get_replaygain(), (True, "album"))
@unittest.mock.patch("sys.stdout", new_callable=io.StringIO)
def test_background_mode(self, mock_stdout: io.StringIO):
"""Test setting background mode."""
self.application.db = emmental.db.Connection()
self.application.factory = emmental.playlist.Factory(
self.application.db)
self.application.player = emmental.audio.Player()
win = self.application.build_window()
player = self.application.player
win.header.bg_enabled = True
win.header.bg_volume = 0.5
self.assertTrue(player.bg_enabled)
self.assertEqual(player.bg_volume, 0.5)