diff --git a/play.py b/play.py index ee5ef1c..d33cc06 100644 --- a/play.py +++ b/play.py @@ -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}") diff --git a/pyproject.toml b/pyproject.toml index 14c0d4f..d91c8b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ authors = [ ] requires-python = ">=3.10" dependencies = [ - "pytheory>=0.40.9", + "pytheory>=0.41.2", ] [tool.uv] diff --git a/uv.lock b/uv.lock index df8866c..fbdeece 100644 --- a/uv.lock +++ b/uv.lock @@ -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]]