diff options
Diffstat (limited to 'audio.py')
-rwxr-xr-x | audio.py | 154 |
1 files changed, 0 insertions, 154 deletions
diff --git a/audio.py b/audio.py deleted file mode 100755 index 37b0f7d..0000000 --- a/audio.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python3 - -"""Audio module for BLC. - -This module defines an AudioPlayer interface which allows for various audio backends to be used -interchangeably. It also defines a bare-bones better-than-nothing "FFPlayer" implementation and -a better "MPVPlayer" implementation. - -"DefaultAudioPlayer" should be used in general and will refer to MPVPlayer if available and -FFPlayer otherwise. -""" - -import atexit -import subprocess as subp -import time -import warnings - -from abc import ABC, abstractmethod, abstractproperty - -def ttoti(t): - """Convert seconds to milliseconds.""" - return int(1000*t + 0.5) - -def titot(ti): - """Convert milliseconds to seconds.""" - return ti/1000 - -class AudioPlayer(ABC): - """Class for playing audio. - - All time indices must be integers in milliseconds. - """ - @abstractmethod - def play(self, start=-1): - """Play the audio from the given time. - - If start is -1, play it from the current time index (e.g. if paused). If the player is - already playing, throw an error. - """ - return - - @abstractmethod - def seek(self, t): - """Seek to the given time index.""" - return - - @abstractmethod - def pause(self): - """Pause the player.""" - return - - @abstractmethod - def stop(self): - """Stop the player and reset to the first time index.""" - return - - @abstractproperty - def volume(self): - """Get or set the current volume.""" - return - - @abstractproperty - def position(self) -> int: - """The current position in milliseconds.""" - return - - @abstractproperty - def playing(self) -> bool: - """Return if the player is playing or not.""" - return - - def __init__(self, fname, args=()): - self.fname = fname - self.args = args - -class FFPlayer(AudioPlayer): - """Audio player using ffplay. - - Note that this is incredibly bad: the current position is guessed based on the start time of - the subprocess (meaning startup time of the ffplay process is counted in the current - position), no preloading of files is done, seeking is inaccurate and requires killing and - restarting the ffplay process, volume is ignored, and more. This is due to the fact that you - can't provide input to ffplay because it uses SDL exclusively for input (even though it can - be run without SDL?) so any change requires restarting the process. Use MPVPlayer if - possible. - """ - def play(self, start=-1): - if self.playing: - raise ValueError("Already playing") - - if start != -1: - self.start = titot(start) - self.player = subp.Popen(["ffplay", "-nodisp", "-autoexit", "-ss", str(self.start), *self.args, self.fname], - stdin=subp.DEVNULL, stdout=subp.DEVNULL, stderr=subp.DEVNULL) - atexit.register(self.stop) - self.start_time = time.monotonic() - - def stop(self): - if not self.playing: - return - self.player.terminate() - atexit.unregister(self.stop) - self.player = None - self.start = 0 - - def seek(self, t): - if self.playing: - self.stop() - self.start = titot(t) - self.play() - else: - self.start = titot(t) - - def pause(self): - if not self.playing: - return - self.stop() - self.start = self.start + time.monotonic() - - @property - def position(self): - if not self.playing: - return self.start - return ttoti(self.start + time.monotonic() - self.start_time) - - @property - def volume(self): - return 100 - - @volume.setter - def volume(self, vol): - return - - @property - def playing(self): - if self.player is not None: - if self.player.poll() is not None: - self.player = None - - return self.player is not None - - def __init__(self, fname, args=()): - super().__init__(fname, args=args) - - self.player = None - self.start = 0 - self.start_time = 0 - -try: - import mpv -except (OSError, ImportError): - warnings.warn("mpv backend unavailable, falling back to ffplay", RuntimeWarning) - - DefaultAudioPlayer = FFPlayer |