Compare commits

..

2 Commits

Author SHA1 Message Date
kennethreitz e0a1ce9d18 Fix hold() inflating Part.total_beats and Score.duration_ms — v0.40.8
Note.beats now returns 0.0 for held notes (_hold=True), matching the
renderer which already skipped advancing the beat position. Previously
every hold() call added its full duration to the part's total, causing
duration reports to be 2-3x too long on tracks with drone notes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 13:27:08 -04:00
kennethreitz de7575fe0a Expose rhodes, wurlitzer, vibraphone, pipe organ, choir in Synth enum — v0.40.7
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 01:59:11 -04:00
7 changed files with 28 additions and 5 deletions
+16
View File
@@ -2,6 +2,22 @@
All notable changes to PyTheory are documented here.
## 0.40.8
- **Fix hold() inflating duration** — `Note.beats` was returning the full
duration for held notes (`_hold=True`), causing `Part.total_beats` and
`Score.duration_ms` to overcount. A part with `hold(Sa, WHOLE * 4)` followed
by `add(Pa, QUARTER)` would report 17 beats instead of 1. Now held notes
return 0 beats, matching the renderer which already skipped advancing the
timeline for held notes.
## 0.40.7
- **Expose missing Synth enum entries** — rhodes, wurlitzer, vibraphone,
pipe organ, and choir wave functions were already implemented but not
accessible via the Synth enum. Now available as `Synth.RHODES`,
`Synth.WURLITZER`, `Synth.VIBRAPHONE`, `Synth.PIPE_ORGAN`, `Synth.CHOIR`.
## 0.40.6
- **Saxophone presets cleaned up** — removed lowpass filters and vel_to_filter
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "pytheory"
version = "0.40.6"
version = "0.40.8"
description = "Music Theory for Humans"
readme = "README.md"
license = "MIT"
+1 -1
View File
@@ -1,6 +1,6 @@
"""PyTheory: Music Theory for Humans."""
__version__ = "0.40.6"
__version__ = "0.40.7"
from .tones import Tone, Interval
from .systems import System, SYSTEMS, TET
+5
View File
@@ -2453,6 +2453,11 @@ class Synth(Enum):
TINGSHA = "tingsha_synth"
SINGING_BOWL_STRIKE = "singing_bowl_strike_synth"
SINGING_BOWL_RING = "singing_bowl_ring_synth"
RHODES = "rhodes_synth"
WURLITZER = "wurlitzer_synth"
VIBRAPHONE = "vibraphone_synth"
PIPE_ORGAN = "pipe_organ_synth"
CHOIR = "choir_synth"
def __call__(self, hz, **kwargs):
"""Make Synth members callable — dispatches to the wave function."""
+2
View File
@@ -475,6 +475,8 @@ class Note:
@property
def beats(self) -> float:
if self._hold:
return 0.0
return self.duration.value
+2 -2
View File
@@ -5333,7 +5333,7 @@ def test_supersaw_wave():
@needs_portaudio
def test_all_synths_in_enum():
from pytheory.play import Synth
assert len(Synth) == 46
assert len(Synth) == 51
for s in Synth:
wave = s(440, n_samples=1000)
assert len(wave) == 1000
@@ -7155,7 +7155,7 @@ def test_score_system_propagates():
def test_synth_enum_count():
from pytheory.play import Synth
assert len(Synth) == 46
assert len(Synth) == 51
def test_all_synths_render_and_enum_match():
Generated
+1 -1
View File
@@ -690,7 +690,7 @@ wheels = [
[[package]]
name = "pytheory"
version = "0.40.6"
version = "0.40.7"
source = { editable = "." }
dependencies = [
{ name = "rich" },