mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 06:46:14 +00:00
Remove live extras from pytheory — split to pytheory-live repo
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,9 +26,6 @@ dependencies = [
|
||||
"rich>=14.3.3",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
live = ["python-rtmidi>=1.5.8"]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/kennethreitz/pytheory"
|
||||
Documentation = "https://pytheory.kennethreitz.org"
|
||||
@@ -37,7 +34,6 @@ Issues = "https://github.com/kennethreitz/pytheory/issues"
|
||||
|
||||
[project.scripts]
|
||||
pytheory = "pytheory.cli:main"
|
||||
pytheory-live = "pytheory.live_tui:main"
|
||||
|
||||
[dependency-groups]
|
||||
dev = ["pytest"]
|
||||
|
||||
+90
-1
@@ -163,6 +163,7 @@ class LiveTUI:
|
||||
tab_prefix = ""
|
||||
self.kbd_active = False
|
||||
self._kbd_held = {} # key → last_press_time
|
||||
self._picker = None # {"channel": int, "index": int, "scroll": int, "filter": str}
|
||||
|
||||
while self.running:
|
||||
try:
|
||||
@@ -348,6 +349,50 @@ class LiveTUI:
|
||||
except curses.error:
|
||||
pass
|
||||
|
||||
# ═══ PICKER OVERLAY ═══
|
||||
if self._picker is not None:
|
||||
filt = self._picker["filter"]
|
||||
items = [i for i in self.instruments if filt in i] if filt else self.instruments
|
||||
idx = self._picker["index"]
|
||||
pw = min(32, w - 4)
|
||||
ph = min(len(items) + 2, h - 4)
|
||||
px = (w - pw) // 2
|
||||
py = (h - ph) // 2
|
||||
# Border
|
||||
title = f" Ch {self._picker['channel']} "
|
||||
try:
|
||||
stdscr.addstr(py, px, "┌" + title + "─" * (pw - 2 - len(title)) + "┐", curses.color_pair(2) | curses.A_BOLD)
|
||||
for ri in range(1, ph - 1):
|
||||
stdscr.addstr(py + ri, px, "│" + " " * (pw - 2) + "│", curses.color_pair(2))
|
||||
stdscr.addstr(py + ph - 1, px, "└" + "─" * (pw - 2) + "┘", curses.color_pair(2))
|
||||
except curses.error:
|
||||
pass
|
||||
# Items
|
||||
vis_h = ph - 2
|
||||
scroll = self._picker["scroll"]
|
||||
if idx < scroll:
|
||||
scroll = idx
|
||||
elif idx >= scroll + vis_h:
|
||||
scroll = idx - vis_h + 1
|
||||
self._picker["scroll"] = scroll
|
||||
for ri in range(vis_h):
|
||||
li = scroll + ri
|
||||
if li >= len(items):
|
||||
break
|
||||
name = items[li][:pw - 4]
|
||||
attr = curses.A_REVERSE | curses.color_pair(1) if li == idx else curses.color_pair(0)
|
||||
try:
|
||||
padded = f" {name}" + " " * (pw - 3 - len(name))
|
||||
stdscr.addstr(py + 1 + ri, px + 1, padded, attr)
|
||||
except curses.error:
|
||||
pass
|
||||
# Filter hint
|
||||
hint = f"/{filt}" if filt else "type to filter"
|
||||
try:
|
||||
stdscr.addstr(py + ph - 1, px + 2, hint[:pw - 4], curses.color_pair(3) | curses.A_DIM)
|
||||
except curses.error:
|
||||
pass
|
||||
|
||||
stdscr.refresh()
|
||||
|
||||
# ═══ INPUT ═══
|
||||
@@ -393,6 +438,35 @@ class LiveTUI:
|
||||
|
||||
continue
|
||||
|
||||
# PICKER MODE
|
||||
if self._picker is not None:
|
||||
filt = self._picker["filter"]
|
||||
items = [i for i in self.instruments if filt in i] if filt else self.instruments
|
||||
if ch == 27: # Escape cancels
|
||||
self._picker = None
|
||||
elif ch == curses.KEY_UP:
|
||||
self._picker["index"] = max(0, self._picker["index"] - 1)
|
||||
elif ch == curses.KEY_DOWN:
|
||||
self._picker["index"] = min(len(items) - 1, self._picker["index"] + 1)
|
||||
elif ch == 10 or ch == 13: # Enter selects
|
||||
if items:
|
||||
inst = items[self._picker["index"]]
|
||||
n = self._picker["channel"]
|
||||
self.picks[n - 1] = inst
|
||||
self.engine.channel(n, instrument=inst, reverb=0.3)
|
||||
self.log(f"Ch {n} → {inst}", 1)
|
||||
self._picker = None
|
||||
elif ch == curses.KEY_BACKSPACE or ch == 127:
|
||||
if filt:
|
||||
self._picker["filter"] = filt[:-1]
|
||||
self._picker["index"] = 0
|
||||
self._picker["scroll"] = 0
|
||||
elif 32 <= ch < 127:
|
||||
self._picker["filter"] = filt + chr(ch)
|
||||
self._picker["index"] = 0
|
||||
self._picker["scroll"] = 0
|
||||
continue
|
||||
|
||||
if ch == 10 or ch == 13:
|
||||
if cmd_buf.strip():
|
||||
cmd_history.append(cmd_buf)
|
||||
@@ -521,7 +595,10 @@ class LiveTUI:
|
||||
try:
|
||||
n = int(parts[1])
|
||||
if 1 <= n <= len(self.picks):
|
||||
self.log(f"Ch {n}: {self.picks[n-1]}", 2)
|
||||
# Open instrument picker with current instrument pre-selected
|
||||
current = self.picks[n - 1]
|
||||
idx = self.instruments.index(current) if current in self.instruments else 0
|
||||
self._picker = {"channel": n, "index": idx, "scroll": max(0, idx - 5), "filter": ""}
|
||||
else:
|
||||
self.log(f"Channel 1-{len(self.picks)}", 4)
|
||||
except ValueError:
|
||||
@@ -725,6 +802,18 @@ def main():
|
||||
drum_pattern=args.drums, buffer_size=args.buffer)
|
||||
curses.wrapper(tui.run)
|
||||
|
||||
# Print resume command on exit
|
||||
cmd_parts = ["pytheory-live", str(tui.seed)]
|
||||
if tui.port != "OP-XY":
|
||||
cmd_parts += ["--port", tui.port]
|
||||
if tui.n_channels != 8:
|
||||
cmd_parts += ["--channels", str(tui.n_channels)]
|
||||
if tui.current_drum != "rock":
|
||||
cmd_parts += ["--drums", tui.current_drum]
|
||||
if tui.buffer_size != 128:
|
||||
cmd_parts += ["--buffer", str(tui.buffer_size)]
|
||||
print(f"\nResume this session with:\n {' '.join(cmd_parts)}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
+1
-1
@@ -5421,7 +5421,7 @@ def render_score(score):
|
||||
buzz *= _exp_decay(buzz_len, 25)
|
||||
wave[:buzz_len] = wave[:buzz_len] + buzz.astype(numpy.float32)
|
||||
|
||||
mono_hit = wave * vel_scale * 0.7
|
||||
mono_hit = wave * vel_scale * 0.7 * drum_part.volume
|
||||
# Sidechain trigger — kick only
|
||||
if hit.sound.value == DrumSound.KICK.value:
|
||||
drum_buf[start:start + hit_len] += mono_hit
|
||||
|
||||
@@ -699,11 +699,6 @@ dependencies = [
|
||||
{ name = "sounddevice" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
live = [
|
||||
{ name = "python-rtmidi" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "pytest" },
|
||||
@@ -718,12 +713,10 @@ docs = [
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "python-rtmidi", marker = "extra == 'live'", specifier = ">=1.5.8" },
|
||||
{ name = "rich", specifier = ">=14.3.3" },
|
||||
{ name = "scipy" },
|
||||
{ name = "sounddevice" },
|
||||
]
|
||||
provides-extras = ["live"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [{ name = "pytest" }]
|
||||
@@ -732,29 +725,6 @@ docs = [
|
||||
{ name = "sphinx" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-rtmidi"
|
||||
version = "1.5.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/dd/ee/0f91965dcc471714c69df21e5ca3d94dc81411b7dee2d31ff1184bea07c9/python_rtmidi-1.5.8.tar.gz", hash = "sha256:7f9ade68b068ae09000ecb562ae9521da3a234361ad5449e83fc734544d004fa", size = 368130, upload-time = "2023-11-20T21:55:02.192Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/46/9c/95c0a6a43bd24a17568e1e31008b1fab7e9a2e54c0ed7301e8d5cc9fa109/python_rtmidi-1.5.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:efc07413b30b0039c0d35abe25a81d740c7405124eb58eed141a8f24388e6fe0", size = 148826, upload-time = "2023-11-20T21:54:20.658Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/9b/8e452d6edc2c04e3407f542d3185c66ffc2d39c8811cf2b117653a0a4d63/python_rtmidi-1.5.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:844bd12840c9d4e03dfc89b2cd57c55dcbf5ed7246504d69c6c661732249b19c", size = 145363, upload-time = "2023-11-20T21:54:23.023Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/48/aa1d4924f7aa238a192d69aa565b315af0037f684c9475e8b860c679a655/python_rtmidi-1.5.8-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:8bbaf7c7164471712a93ac60c8f9ed146b336a294a5103223bbaf8f10709a0bf", size = 293253, upload-time = "2023-11-20T21:54:24.899Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/24/32dc239047a56f44d8d8090d55010f85a38ed959ffe517c2e87a2aa34190/python_rtmidi-1.5.8-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:878ce085dfb65c0974810a7e919f73708cbb4c0430c7924b78f25aea1dd4ebee", size = 304051, upload-time = "2023-11-20T21:54:26.82Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/0c/cf771eca1b64610e627ca1e67be8390ecdf5e0e1914efbdd9d50ac4c5986/python_rtmidi-1.5.8-cp310-cp310-win_amd64.whl", hash = "sha256:f2138005c6bd3d8b9af05df383679f6d0827d16056e68a941110732310dcb7dd", size = 132157, upload-time = "2023-11-20T21:54:30.059Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/0c/23be16b75c90946784b8d233e61db14cf0482def5396821a1ae0bdcd2739/python_rtmidi-1.5.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:30d117193dcad8af67c600c405f53eb096e4ff84849760be14c97270af334922", size = 150205, upload-time = "2023-11-20T21:54:31.849Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/12/37d41151b08a292719f05dbeae15475537f8aa291cda34c6634b35916dff/python_rtmidi-1.5.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4e234dca7f9d783dd3f1e9c9c5c2f295f02b7af3085301d6eed3b428cf49d327", size = 146737, upload-time = "2023-11-20T21:54:33.299Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/5e/b866491545135c699bfbed62f54b93c4d6587afc2bba6e2cbbe898570c32/python_rtmidi-1.5.8-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:271d625c489fffb39b3edc5aba67f7c8e29a04a0a0f056ce19e5a888a08b4c59", size = 294847, upload-time = "2023-11-20T21:54:35.017Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/79/1ddb4fb1bdb1a8b8bd62007ca4980344a53b7f29633a7bca1088eed964ce/python_rtmidi-1.5.8-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:46bbf32c8a4bf6c8f0df1c02a68689d0757f13cb7a69f27ccbbed3d7b2365918", size = 305433, upload-time = "2023-11-20T21:54:36.322Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/ff/2c55797dbf020d462132d1bc5b34d596b400fa197e2a259b8dd2ea2e5954/python_rtmidi-1.5.8-cp311-cp311-win_amd64.whl", hash = "sha256:cfea32c91752fa7aecfe3d6827535c190ba0e646a9accd6604f4fc70cf4b780f", size = 132937, upload-time = "2023-11-20T21:54:38.3Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/27/887b0378e0a907489a07bdeb808fa5ed349675245c6ee14d9f6d00304f96/python_rtmidi-1.5.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5443634597eb340cdec0734f76267a827c2d366f00a6f9195141c78828016ac2", size = 158861, upload-time = "2023-11-20T21:54:39.549Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/ec/57cecde253daab896ce53778520cd41eb062641862ebdb0ee6f97511b1d9/python_rtmidi-1.5.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:29d9c9d9f82ce679fecad7bb4cb79f3a24574ea84600e377194b4cc1baacec0e", size = 153416, upload-time = "2023-11-20T21:54:40.835Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/5b/dc19c53d9d512b74dc2cca3725591cc612b9465645695a0696352a8c8b54/python_rtmidi-1.5.8-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:25f5a5db7be98911c41ca5bebb262fcf9a7c89600b88fd3c207ceafd3101e721", size = 305696, upload-time = "2023-11-20T21:54:42.037Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/92/5a60f56dfb2740e644e932233928947423cd2101895319b331f84527eb31/python_rtmidi-1.5.8-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:cec30924e305f55284594ccf35a71dee7216fd308dfa2dec1b3ed03e6f243803", size = 315579, upload-time = "2023-11-20T21:54:43.339Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/46/6af077d262f521ea2bf1ab60b8aad72f34fe6dd55af739176605369d449c/python_rtmidi-1.5.8-cp312-cp312-win_amd64.whl", hash = "sha256:052c89933cae4fca354012d8ca7248f4f9e1e3f062471409d48415a7f7d7e59e", size = 129755, upload-time = "2023-11-20T21:54:44.935Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.3"
|
||||
|
||||
Reference in New Issue
Block a user