emmental/emmental/audio/__init__.py

86 lines
3.0 KiB
Python

# Copyright 2022 (c) Anna Schumaker.
"""A custom GObject managing a GStreamer playbin."""
import pathlib
from gi.repository import GObject
from gi.repository import Gst
from . import replaygain
from .. import path
class Player(GObject.GObject):
"""Wraps a GStreamer Playbin with an interface for our application."""
volume = GObject.Property(type=float, default=1.0)
file = GObject.Property(type=GObject.TYPE_PYOBJECT)
have_track = GObject.Property(type=bool, default=False)
def __init__(self):
"""Initialize the audio Player."""
super().__init__()
self._replaygain = replaygain.Filter()
self._playbin = Gst.ElementFactory.make("playbin")
self._playbin.set_property("audio-filter", self._replaygain)
self._playbin.set_property("video-sink",
Gst.ElementFactory.make("fakesink"))
self._playbin.set_state(Gst.State.READY)
bus = self._playbin.get_bus()
bus.add_signal_watch()
bus.connect("message::eos", self.__msg_eos)
bus.connect("message::stream-start", self.__msg_stream_start)
self.bind_property("volume", self._playbin, "volume")
self.connect("notify::file", self.__notify_file)
def __msg_eos(self, bus: Gst.Bus, message: Gst.Message) -> None:
self.emit("eos")
def __msg_stream_start(self, bus: Gst.Bus, message: Gst.Message) -> None:
self.emit("file-loaded",
path.from_uri(self._playbin.get_property("current-uri")))
def __notify_file(self, player: GObject.GObject, param) -> None:
if self.file:
uri = self.file.as_uri()
print(f"audio: loading {uri}")
self._playbin.set_property("uri", uri)
def get_replaygain(self) -> tuple[bool, str | None]:
"""Get the current ReplayGain mode."""
mode = self._replaygain.mode
return (False, None) if mode == "disabled" else (True, mode)
def get_state(self) -> Gst.State:
"""Get the current state of the Player."""
return self._playbin.get_state(Gst.CLOCK_TIME_NONE).state
def set_replaygain(self, enabled: bool, mode: str) -> None:
"""Set the ReplayGain mode."""
self._replaygain.mode = mode if enabled else "disabled"
def set_state_sync(self, state: Gst.State) -> None:
"""Set the state of the playbin, and wait for it to change."""
if self._playbin.set_state(state) == Gst.StateChangeReturn.ASYNC:
self.get_state()
def shutdown(self) -> None:
"""Shut down the player."""
self._playbin.set_state(Gst.State.NULL)
@GObject.Signal(arg_types=(GObject.TYPE_PYOBJECT,))
def file_loaded(self, file: pathlib.Path) -> None:
"""Signal that a new URI has started."""
print("audio: file loaded")
self.have_track = True
@GObject.Signal
def eos(self) -> None:
"""Signal that the current track has ended."""
print("audio: end of stream")
self.set_state_sync(Gst.State.READY)
self.have_track = False
self.file = None