# 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