From 5ebf0bdd97d099722276f34991e4c0717989a317 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Tue, 7 Apr 2026 07:47:44 -0400 Subject: [PATCH] =?UTF-8?q?Skip=20unpitched=20parts=20in=20to=5Fabc(),=20f?= =?UTF-8?q?ix=20'pitch=20is=20undefined'=20=E2=80=94=20v0.41.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parts with only drum tones or rests are excluded from ABC output. Chords correctly recognized as pitched content. Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- pytheory/__init__.py | 2 +- pytheory/rhythm.py | 16 +++++++++++++--- uv.lock | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7c3a88..63f5479 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to PyTheory are documented here. +## 0.41.3 + +- **Fix** — `to_abc()` now skips parts with only drum tones or rests (no pitched + notes), fixing "pitch is undefined" errors in abcjs. Chords are correctly + recognized as pitched content. + ## 0.41.2 - **Auto bass clef** — `to_abc()` detects low-register parts (808, bass, timpani) diff --git a/pyproject.toml b/pyproject.toml index a9308ba..a5e7daa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pytheory" -version = "0.41.2" +version = "0.41.3" description = "Music Theory for Humans" readme = "README.md" license = "MIT" diff --git a/pytheory/__init__.py b/pytheory/__init__.py index 43c4b2c..9a4ea48 100644 --- a/pytheory/__init__.py +++ b/pytheory/__init__.py @@ -1,6 +1,6 @@ """PyTheory: Music Theory for Humans.""" -__version__ = "0.41.2" +__version__ = "0.41.3" from .tones import Tone, Interval from .systems import System, SYSTEMS, TET diff --git a/pytheory/rhythm.py b/pytheory/rhythm.py index d2d9744..981fe21 100644 --- a/pytheory/rhythm.py +++ b/pytheory/rhythm.py @@ -4396,15 +4396,25 @@ class Score: f"L:1/{default_unit}", ] - # Collect voices: default notes first, then named parts (skip drums) + # Collect voices: default notes first, then named parts + # Skip drum parts and parts with no pitched notes voices: list[tuple[str, list]] = [] if self.notes: voices.append(("default", self.notes)) for name, part in self.parts.items(): if part.is_drums: continue - if part.notes: - voices.append((name, part.notes)) + if not part.notes: + continue + # Skip parts that have no pitched tones (only drum tones / rests) + has_pitched = any( + n.tone is not None + and (hasattr(n.tone, "name") or hasattr(n.tone, "tones")) + for n in part.notes + ) + if not has_pitched: + continue + voices.append((name, part.notes)) multi = len(voices) > 1 diff --git a/uv.lock b/uv.lock index 887f1d7..9f89de3 100644 --- a/uv.lock +++ b/uv.lock @@ -690,7 +690,7 @@ wheels = [ [[package]] name = "pytheory" -version = "0.41.2" +version = "0.41.3" source = { editable = "." } dependencies = [ { name = "rich" },