From 6aad427fb82915d3aef5aee89806ce80fa387a7e Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sun, 22 Mar 2026 14:53:12 -0400 Subject: [PATCH] Fix 'pytheory play' chord name parsing for names containing digits Chord names like Cmaj7 and G7 were incorrectly treated as tone names because they contain digits. Now tries chord name lookup first. v0.5.1. Co-Authored-By: Claude Opus 4.6 (1M context) --- pyproject.toml | 2 +- pytheory/cli.py | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b10f5cd..a2bd6d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pytheory" -version = "0.5.0" +version = "0.5.1" description = "Music Theory for Humans" readme = "README.md" license = "MIT" diff --git a/pytheory/cli.py b/pytheory/cli.py index fdeadae..aae5708 100644 --- a/pytheory/cli.py +++ b/pytheory/cli.py @@ -100,14 +100,23 @@ def cmd_play(args): synth = synth_map[args.synth] duration = args.duration - # Parse notes — if single note, play as tone; otherwise as chord. - tones = [Tone.from_string(n if any(c.isdigit() for c in n) else f"{n}4", - system="western") for n in args.notes] - - if len(tones) == 1: - target = tones[0] - label = target.full_name + # Try chord name first (e.g. "Am", "Cmaj7"), then fall back to individual notes. + if len(args.notes) == 1: + note = args.notes[0] + # Try as chord name first (Am, G7, Cmaj7, etc.) + try: + target = Chord.from_name(note) + name = target.identify() or note + label = f"{name} ({' '.join(t.full_name for t in target.tones)})" + except (ValueError, KeyError): + # Fall back to single tone + target = Tone.from_string( + note if any(c.isdigit() for c in note) else f"{note}4", + system="western") + label = target.full_name else: + tones = [Tone.from_string(n if any(c.isdigit() for c in n) else f"{n}4", + system="western") for n in args.notes] target = Chord(tones=tones) name = target.identify() or "Custom" label = f"{name} ({' '.join(t.full_name for t in tones)})"