Add a basic GUI

This commit is contained in:
Correl Roush 2020-09-02 09:15:27 -04:00
parent d8a175c358
commit 4a035ec5b6
6 changed files with 147 additions and 76 deletions

14
poetry.lock generated
View file

@ -289,6 +289,14 @@ optional = false
python-versions = "*" python-versions = "*"
version = "0.23.1" version = "0.23.1"
[[package]]
category = "main"
description = "Cross-platform windowing and multimedia library"
name = "pyglet"
optional = false
python-versions = "*"
version = "1.5.7"
[[package]] [[package]]
category = "main" category = "main"
description = "Python parsing module" description = "Python parsing module"
@ -414,7 +422,7 @@ secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0
socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"]
[metadata] [metadata]
content-hash = "7145f097b34f0bbe62825427b4ecc8c916fa48c8255a9b5da539edf6b990540f" content-hash = "3af542607f4ccaa3afe08d275174f664cdacedac31ce8278e059d4aca2b9edeb"
lock-version = "1.0" lock-version = "1.0"
python-versions = "^3.8" python-versions = "^3.8"
@ -626,6 +634,10 @@ pydub = [
{file = "pydub-0.23.1-py2.py3-none-any.whl", hash = "sha256:d29901a486fb421c5d7b0f3d5d3a60527179204d8ffb20e74e1ae81c17e81b46"}, {file = "pydub-0.23.1-py2.py3-none-any.whl", hash = "sha256:d29901a486fb421c5d7b0f3d5d3a60527179204d8ffb20e74e1ae81c17e81b46"},
{file = "pydub-0.23.1.tar.gz", hash = "sha256:c362fa02da1eebd1d08bd47aa9b0102582dff7ca2269dbe9e043d228a0c1ea93"}, {file = "pydub-0.23.1.tar.gz", hash = "sha256:c362fa02da1eebd1d08bd47aa9b0102582dff7ca2269dbe9e043d228a0c1ea93"},
] ]
pyglet = [
{file = "pyglet-1.5.7-py3-none-any.whl", hash = "sha256:9832442d59ee06acbeff12e128cf6d5aee271e94c09386040db8f0feae277013"},
{file = "pyglet-1.5.7.zip", hash = "sha256:3faac2dad34946aecbce79a8658f89155436fe5c07332229160c6eba302ff40d"},
]
pyparsing = [ pyparsing = [
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},

View file

@ -10,6 +10,7 @@ python = "^3.8"
pyalsaaudio = "^0.9.0" pyalsaaudio = "^0.9.0"
pydejavu = {git = "https://github.com/worldveil/dejavu.git"} pydejavu = {git = "https://github.com/worldveil/dejavu.git"}
requests = "^2.24.0" requests = "^2.24.0"
pyglet = "^1.5.7"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
black = "^20.8b1" black = "^20.8b1"
@ -17,7 +18,8 @@ mypy = "^0.782"
pytest = "^6.0.1" pytest = "^6.0.1"
[tool.poetry.scripts] [tool.poetry.scripts]
turntable = "turntable.cli:main" turntable = "turntable.gui:main"
turntable-cli = "turntable.cli:main"
[build-system] [build-system]
requires = ["poetry>=0.12"] requires = ["poetry>=0.12"]

74
turntable/application.py Normal file
View file

@ -0,0 +1,74 @@
import argparse
from contextlib import contextmanager
import importlib.metadata
import json
import logging
from multiprocessing import Queue
import os
from typing import Any, Dict, Iterator
from dejavu import Dejavu # type: ignore
from turntable.audio import Listener
from turntable.icecast import Icecast
from turntable.models import PCM
from turntable.turntable import (
Event,
NewMetadata,
StartedPlaying,
StoppedPlaying,
Turntable,
)
VERSION = importlib.metadata.version("turntable")
logger = logging.getLogger(__name__)
@contextmanager
def run() -> "Iterator[Queue[Event]]":
parser = argparse.ArgumentParser()
parser.add_argument(
"--config", default=os.path.expanduser("~/.config/turntable.json")
)
args = parser.parse_args()
with open(args.config, "r") as config_file:
config: Dict[str, Any] = json.load(config_file)
logging.basicConfig(level=logging.DEBUG if config.get("debug") else logging.INFO)
logger.info("Turntable version %s", VERSION)
pcm_in: "Queue[PCM]" = Queue()
events: "Queue[Event]" = Queue()
audio_config = config.get("audio", dict())
listener = Listener(
pcm_in,
audio_config.get("device", "default"),
framerate=audio_config.get("framerate", 44100),
channels=audio_config.get("channels", 2),
period_size=audio_config.get("period_size", 4096),
)
dejavu = Dejavu(config.get("dejavu", dict()))
player = Turntable(listener.framerate, listener.channels, dejavu, pcm_in, events)
icecast_config = config.get("icecast", dict())
icecast = Icecast(
host=icecast_config.get("host", "localhost"),
port=icecast_config.get("port", 8000),
mountpoint=icecast_config.get("mountpoint", "stream.mp3"),
user=icecast_config.get("admin_user", "admin"),
password=icecast_config.get("admin_password", "hackme"),
)
processes = [listener, player]
for process in processes:
process.daemon = True
process.start()
try:
yield events
except:
for process in processes:
if process.is_alive():
process.terminate()

View file

@ -1,80 +1,9 @@
import argparse
import importlib.metadata
import json
import logging import logging
from multiprocessing import Queue
import os
from typing import Any, Dict
from dejavu import Dejavu # type: ignore from turntable import application
from turntable.audio import Listener
from turntable.icecast import Icecast
from turntable.models import PCM
from turntable.turntable import (
Event,
NewMetadata,
StartedPlaying,
StoppedPlaying,
Turntable,
)
VERSION = importlib.metadata.version("turntable")
def main() -> None: def main() -> None:
print(f"Turntable {VERSION}") with application.run() as events:
parser = argparse.ArgumentParser()
parser.add_argument(
"--config", default=os.path.expanduser("~/.config/turntable.json")
)
args = parser.parse_args()
with open(args.config, "r") as config_file:
config: Dict[str, Any] = json.load(config_file)
logging.basicConfig(level=logging.DEBUG if config.get("debug") else logging.INFO)
pcm_in: "Queue[PCM]" = Queue()
events: "Queue[Event]" = Queue()
audio_config = config.get("audio", dict())
listener = Listener(
pcm_in,
audio_config.get("device", "default"),
framerate=audio_config.get("framerate", 44100),
channels=audio_config.get("channels", 2),
period_size=audio_config.get("period_size", 4096),
)
dejavu = Dejavu(config.get("dejavu", dict()))
player = Turntable(listener.framerate, listener.channels, dejavu, pcm_in, events)
icecast_config = config.get("icecast", dict())
icecast = Icecast(
host=icecast_config.get("host", "localhost"),
port=icecast_config.get("port", 8000),
mountpoint=icecast_config.get("mountpoint", "stream.mp3"),
user=icecast_config.get("admin_user", "admin"),
password=icecast_config.get("admin_password", "hackme"),
)
processes = [listener, player]
for process in processes:
process.daemon = True
process.start()
try:
icecast.set_title("<Idle>")
while event := events.get(): while event := events.get():
logging.info("Event: %s", event) logging.info("Event: %s", event)
if isinstance(event, StartedPlaying):
icecast.set_title("<Record starting...>")
elif isinstance(event, StoppedPlaying):
icecast.set_title("<Idle>")
elif isinstance(event, NewMetadata):
icecast.set_title(event.title)
except KeyboardInterrupt:
for process in processes:
if process.is_alive():
process.terminate()

52
turntable/gui.py Normal file
View file

@ -0,0 +1,52 @@
import logging
import queue
import pyglet # type: ignore
import pyglet.clock # type: ignore
from turntable import application, turntable
def main():
window = pyglet.window.Window(fullscreen=True)
with application.run() as events:
label = pyglet.text.Label(
"<Idle>",
font_name='Noto Sans',
font_size=36,
x = window.width // 2,
y = window.height // 2,
anchor_x = 'center',
anchor_y = 'center')
@window.event
def on_draw():
window.clear()
label.draw()
def check_events(dt):
try:
event = events.get(False)
logging.info("Event: %s", event)
logging.info("Label: %s", label)
if isinstance(event, turntable.StartedPlaying):
label.text = "<Record starting...>"
elif isinstance(event, turntable.StoppedPlaying):
label.text = "<Idle>"
elif isinstance(event, turntable.NewMetadata):
label.text = event.title
except queue.Empty:
...
except:
logging.exception("Oops")
pyglet.clock.schedule_interval_soft(check_events, 0.5)
pyglet.app.run()
# icecast.set_title("<Idle>")
# while event := events.get():
# logging.info("Event: %s", event)
# if isinstance(event, StartedPlaying):
# icecast.set_title("<Record starting...>")
# elif isinstance(event, StoppedPlaying):
# icecast.set_title("<Idle>")
# elif isinstance(event, NewMetadata):
# icecast.set_title(event.title)

View file

@ -122,7 +122,9 @@ class Turntable(Process):
>= FINGERPRINT_DELAY + FINGERPRINT_IDENTIFY_SECONDS >= FINGERPRINT_DELAY + FINGERPRINT_IDENTIFY_SECONDS
and self.identified == False and self.identified == False
): ):
startframe = self.buffer.framerate * FINGERPRINT_DELAY startframe = self.buffer.framerate * (
FINGERPRINT_DELAY + FINGERPRINT_IDENTIFY_DELAY
)
endframe = ( endframe = (
startframe + self.buffer.framerate * FINGERPRINT_IDENTIFY_SECONDS startframe + self.buffer.framerate * FINGERPRINT_IDENTIFY_SECONDS
) )