mirror of
https://github.com/kennethreitz/interpretations.git
synced 2026-06-05 23:00:19 +00:00
Add sheet music command — [s] in picker or --sheet flag
Opens ABC notation rendered as sheet music in the browser via abcjs. Auto-detects key from track's Key object, bass clef for low parts. Bumps pytheory to >=0.41.2 for to_abc() support. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -613,7 +613,7 @@ def pick_track():
|
||||
pass
|
||||
stdscr.addstr(3, max(0, (w - 15) // 2), "─" * 15,
|
||||
curses.color_pair(2))
|
||||
stdscr.addstr(4, max(0, (w - 56) // 2), "↑/↓ navigate ↵ play r render a play all R render all q quit",
|
||||
stdscr.addstr(4, max(0, (w - 68) // 2), "↑/↓ navigate ↵ play s sheet r render a play all R render all q quit",
|
||||
curses.A_DIM)
|
||||
|
||||
# Track list
|
||||
@@ -711,6 +711,10 @@ def pick_track():
|
||||
result[0] = entries[selected[0]][0]
|
||||
action[0] = "play"
|
||||
return
|
||||
elif key == ord("s"):
|
||||
result[0] = entries[selected[0]][0]
|
||||
action[0] = "sheet"
|
||||
return
|
||||
elif key == ord("r"):
|
||||
result[0] = entries[selected[0]][0]
|
||||
action[0] = "render"
|
||||
@@ -765,6 +769,8 @@ examples:
|
||||
help="Show score metadata and stats")
|
||||
insp.add_argument("--parts", action="store_true",
|
||||
help="List all parts with details")
|
||||
insp.add_argument("--sheet", action="store_true",
|
||||
help="Open sheet music (ABC notation) in browser")
|
||||
|
||||
play = p.add_argument_group("playback")
|
||||
play.add_argument("--from", dest="from_measure", type=int, metavar="N",
|
||||
@@ -857,6 +863,32 @@ def _render_and_cache(path, args):
|
||||
return buf, sr, offset_sec, score, mod
|
||||
|
||||
|
||||
def _open_sheet(path):
|
||||
"""Render a track's score as ABC notation and open sheet music in the browser."""
|
||||
import tempfile
|
||||
import webbrowser
|
||||
|
||||
score, mod = load_score(path)
|
||||
title = get_title(mod, Path(path))
|
||||
|
||||
# Derive ABC key from the module's Key object (e.g. Key("Eb", "minor"))
|
||||
abc_key = "C"
|
||||
if hasattr(mod, "key"):
|
||||
k = mod.key
|
||||
root = getattr(k, "tonic_name", "C")
|
||||
mode = getattr(k, "mode", "major")
|
||||
abc_key = str(root)
|
||||
if "minor" in mode.lower():
|
||||
abc_key += "m"
|
||||
|
||||
html = score.to_abc(title=title, key=abc_key, html=True)
|
||||
|
||||
out = Path(tempfile.gettempdir()) / f"{Path(path).stem}_sheet.html"
|
||||
out.write_text(html)
|
||||
webbrowser.open(f"file://{out}")
|
||||
print(f" Sheet music -> {out}")
|
||||
|
||||
|
||||
def _play_track(path, args, force_render=False, render_only=False):
|
||||
"""Load, render, and play a single track. Uses cached WAV if available.
|
||||
Returns 'next', 'prev', or None."""
|
||||
@@ -1046,6 +1078,8 @@ def main():
|
||||
print(f" ✗ {f.name}: {e}")
|
||||
|
||||
print(f"\n Done! {done[0]} tracks cached.\n")
|
||||
elif act == "sheet":
|
||||
_open_sheet(path)
|
||||
elif act == "render":
|
||||
_play_track(path, args, force_render=True)
|
||||
else:
|
||||
@@ -1070,8 +1104,8 @@ def main():
|
||||
print(f"File not found: {path}")
|
||||
sys.exit(1)
|
||||
|
||||
# ── Info / parts (no playback) ────────────────────────────────
|
||||
if args.info or args.parts or args.midi:
|
||||
# ── Info / parts / sheet (no playback) ──────────────────────────
|
||||
if args.info or args.parts or args.midi or args.sheet:
|
||||
score, mod = load_score(path)
|
||||
if args.bpm:
|
||||
score.bpm = args.bpm
|
||||
@@ -1081,6 +1115,8 @@ def main():
|
||||
show_info(score, mod, path)
|
||||
elif args.parts:
|
||||
show_parts(score)
|
||||
elif args.sheet:
|
||||
_open_sheet(path)
|
||||
elif args.midi:
|
||||
score.save_midi(args.midi)
|
||||
print(f"Exported MIDI -> {args.midi}")
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ authors = [
|
||||
]
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"pytheory>=0.40.9",
|
||||
"pytheory>=0.41.2",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
|
||||
@@ -97,7 +97,7 @@ dependencies = [
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "pytheory", specifier = ">=0.40.9" }]
|
||||
requires-dist = [{ name = "pytheory", specifier = ">=0.41.2" }]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
@@ -287,7 +287,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pytheory"
|
||||
version = "0.40.9"
|
||||
version = "0.41.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "rich" },
|
||||
@@ -295,9 +295,9 @@ dependencies = [
|
||||
{ name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
|
||||
{ name = "sounddevice" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7e/cf/0366e974c27261de5219a032637512b3b7594118dfb5a219700a96e49445/pytheory-0.40.9.tar.gz", hash = "sha256:5a15b9c15c94d81f794edec5dbe03fc0c58a0bd08233777a62209df197755b2b", size = 187008, upload-time = "2026-04-03T03:15:15.939Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d4/55/625d43b77c96bcd7eb0782c34c55dd9c0fba831e47e74af1f14f8d9088ff/pytheory-0.41.2.tar.gz", hash = "sha256:86eb9484921e2be2451215fba293e6daa55a1339593acb96dc061024097577e0", size = 188842, upload-time = "2026-04-07T11:44:13.51Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/db/87/3d0fb3cb84c8d199e2cf4807259524119912335211c2ee2531a9dc5c3527/pytheory-0.40.9-py3-none-any.whl", hash = "sha256:d9bad930c6e38d684fb75519ea989dc93331cb71628fbd47cf3449b9316fb730", size = 191155, upload-time = "2026-04-03T03:15:17.308Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/69/4fc3bd3580ad45f05f2f3e6a77ac637b0b12f663c5999685b712d521e502/pytheory-0.41.2-py3-none-any.whl", hash = "sha256:90298eb5df74ad187e512faac0856458b4b906905e4d1eeb24c02a972599fc5f", size = 193031, upload-time = "2026-04-07T11:44:14.705Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user