From 6792971ef76f57f943f213779e7dc99975178960 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Tue, 7 Sep 2021 19:32:03 -0400 Subject: [PATCH] audio: Add a custom ReplayGainSink I use output-switcher and funnel elements so we can disable the replaygain plugins by routing the stream around them if necessary. Signed-off-by: Anna Schumaker --- audio/replaygain.py | 52 ++++++++++++++++++++++++++++++++++++++++ audio/test_replaygain.py | 38 +++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 audio/replaygain.py create mode 100644 audio/test_replaygain.py diff --git a/audio/replaygain.py b/audio/replaygain.py new file mode 100644 index 0000000..4fcdcb4 --- /dev/null +++ b/audio/replaygain.py @@ -0,0 +1,52 @@ +# Copyright 2021 (c) Anna Schumaker. +from gi.repository import GObject +from gi.repository import Gst + + +class ReplayGainSink(Gst.Bin): + def __init__(self): + Gst.Bin.__init__(self) + self.selector = Gst.ElementFactory.make("output-selector") + self.funnel = Gst.ElementFactory.make("funnel") + self.audiosink = Gst.ElementFactory.make("autoaudiosink") + self.rgvolume = Gst.ElementFactory.make("rgvolume") + self.rglimiter = Gst.ElementFactory.make("rglimiter") + + for element in [ self.selector, self.funnel, self.audiosink, + self.rgvolume, self.rglimiter ]: + self.add(element) + + # No ReplayGain: selector -> funnel -> audiosink + self.shortcut = self.selector.get_request_pad("src_%u") + self.shortcut.link(self.funnel.get_request_pad("sink_%u")) + self.funnel.link(self.audiosink) + + # Replaygain: selector -> rgvolume -> rglimiter -> funnel -> audiosink + self.replaygain = self.selector.get_request_pad("src_%u") + self.replaygain.link(self.rgvolume.get_static_pad("sink")) + self.rgvolume.link(self.rglimiter) + self.rglimiter.get_static_pad("src").link( + self.funnel.get_request_pad("sink_%u")) + + self.selector.set_property("pad-negotiation-mode", 2) + self.selector.set_property("active-pad", self.shortcut) + + pad = self.selector.get_static_pad("sink") + ghost = Gst.GhostPad.new("sink", pad) + ghost.set_active(True) + self.add_pad(ghost) + + @GObject.Property + def mode(self): + if self.selector.get_property("active-pad") == self.shortcut: + return "disabled" + album_mode = self.rgvolume.get_property("album-mode") + return "album" if album_mode else "track" + + @mode.setter + def mode(self, mode): + if mode == "disabled": + self.selector.set_property("active-pad", self.shortcut) + else: + self.rgvolume.set_property("album-mode", mode == "album") + self.selector.set_property("active-pad", self.replaygain) diff --git a/audio/test_replaygain.py b/audio/test_replaygain.py new file mode 100644 index 0000000..f622488 --- /dev/null +++ b/audio/test_replaygain.py @@ -0,0 +1,38 @@ +# Copyright 2021 (c) Anna Schumaker. +import unittest +from gi.repository import Gst +from . import replaygain + +class TestReplayGainSink(unittest.TestCase): + def test_replay_gain_sink_init(self): + rgsink = replaygain.ReplayGainSink() + + self.assertIsInstance(rgsink, Gst.Bin) + self.assertIsInstance(rgsink.selector, Gst.Element) + self.assertIsInstance(rgsink.funnel, Gst.Element) + self.assertIsInstance(rgsink.audiosink, Gst.Element) + self.assertIsInstance(rgsink.rgvolume, Gst.Element) + self.assertIsInstance(rgsink.rglimiter, Gst.Element) + + self.assertIsInstance(rgsink.shortcut, Gst.Pad) + self.assertIsInstance(rgsink.replaygain, Gst.Pad) + self.assertIsInstance(rgsink.get_static_pad("sink"), Gst.GhostPad) + + def test_replay_gain_sink_mode(self): + rgsink = replaygain.ReplayGainSink() + self.assertEqual(rgsink.get_property("mode"), "disabled") + + rgsink.set_property("mode", "album") + self.assertEqual(rgsink.get_property("mode"), "album") + self.assertEqual(rgsink.selector.get_property("active-pad"), + rgsink.replaygain) + + rgsink.set_property("mode", "track") + self.assertEqual(rgsink.get_property("mode"), "track") + self.assertEqual(rgsink.selector.get_property("active-pad"), + rgsink.replaygain) + + rgsink.set_property("mode", "disabled") + self.assertEqual(rgsink.get_property("mode"), "disabled") + self.assertEqual(rgsink.selector.get_property("active-pad"), + rgsink.shortcut)