From f9c81fe05f400bb71a6d0232eb6a39457a98aeb2 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Thu, 26 Mar 2026 21:18:27 -0400 Subject: [PATCH] v0.31.0: 3 new synths, 38 instrument presets - Karplus-Strong pluck (physical modeling for guitar/harp/koto) - Hammond organ (additive drawbar synthesis) - String ensemble (filtered saw with body resonance formants) - 38 instrument presets: score.part("lead", instrument="violin") - Demo updated with pluck_synth, organ_synth, strings_synth Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 7 ++++ docs/guide/synths.rst | 83 +++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- pytheory/__init__.py | 2 +- pytheory/cli.py | 10 +++--- uv.lock | 2 +- 6 files changed, 98 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27abae5..aa64fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to PyTheory are documented here. +## 0.31.0 + +- 3 new synth engines: Karplus-Strong pluck, Hammond organ, string ensemble with body formants +- 38 instrument presets: `score.part("lead", instrument="violin")` +- Keys, strings, woodwinds, brass, plucked, synth, and mallet categories +- 13 total synth waveforms + ## 0.30.0 - Drums are a real Part — same effects pipeline as any voice diff --git a/docs/guide/synths.rst b/docs/guide/synths.rst index 622e7d5..1627df8 100644 --- a/docs/guide/synths.rst +++ b/docs/guide/synths.rst @@ -341,6 +341,89 @@ Reverb is also stereo — the left and right channels get different early reflection patterns, so the reverb tail occupies real space in the stereo field rather than sitting dead center. +Physical Modeling +----------------- + +Three synths go beyond traditional waveform synthesis into physical +modeling territory — they simulate how real instruments produce sound. + +Karplus-Strong Pluck +~~~~~~~~~~~~~~~~~~~~ + +A burst of noise fed into a short delay line. The delay length sets +the pitch, the feedback filter models the string decaying. This is +how every physical modeling synth since 1983 does plucked strings. +It sounds genuinely like a real guitar, harp, or koto. + +.. code-block:: python + + guitar = score.part("guitar", synth="pluck_synth") + harp = score.part("harp", instrument="harp") # uses pluck_synth + +Hammond Organ +~~~~~~~~~~~~~ + +Additive synthesis with drawbar harmonics — sine waves at the +fundamental plus 2nd, 3rd, 4th, 5th, 6th, and 8th harmonics mixed +at musical levels. Warm, round, unmistakably organ. + +.. code-block:: python + + organ = score.part("organ", synth="organ_synth") + +String Ensemble +~~~~~~~~~~~~~~~ + +Filtered sawtooth with body resonance formants at ~500 Hz and ~1500 Hz, +modeling the way a violin or cello body shapes the sound. Warmer and +more "wooden" than a raw saw wave. + +.. code-block:: python + + violin = score.part("violin", synth="strings_synth") + +Instrument Presets +------------------ + +Instead of choosing synth + envelope + effects manually, use an +instrument preset — 38 predefined combinations that approximate real +instruments: + +.. code-block:: python + + piano = score.part("piano", instrument="piano") + violin = score.part("violin", instrument="violin") + guitar = score.part("guitar", instrument="acoustic_guitar") + organ = score.part("organ", instrument="organ") + bass = score.part("bass", instrument="upright_bass") + +Available instruments: + +**Keys**: piano, electric_piano, organ, harpsichord, celesta, music_box + +**Strings**: violin, viola, cello, contrabass, string_ensemble + +**Woodwinds**: flute, clarinet, oboe, bassoon + +**Brass**: trumpet, trombone, french_horn, tuba, brass_ensemble + +**Plucked**: acoustic_guitar, electric_guitar, distorted_guitar, +bass_guitar, upright_bass, harp, sitar, koto + +**Synth**: synth_lead, synth_pad, synth_bass, acid_bass, 808_bass + +**Percussion**: vibraphone, marimba, xylophone, glockenspiel, tubular_bells + +Explicit kwargs override preset defaults: + +.. code-block:: python + + # Piano with extra reverb + piano = score.part("piano", instrument="piano", reverb=0.5) + + # Violin panned left + violin = score.part("v", instrument="violin", pan=-0.4) + Choosing Synth and Envelope Combos ---------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 9144c7e..f7991cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pytheory" -version = "0.30.0" +version = "0.31.0" description = "Music Theory for Humans" readme = "README.md" license = "MIT" diff --git a/pytheory/__init__.py b/pytheory/__init__.py index 0b4e519..b116ca8 100644 --- a/pytheory/__init__.py +++ b/pytheory/__init__.py @@ -1,6 +1,6 @@ """PyTheory: Music Theory for Humans.""" -__version__ = "0.30.0" +__version__ = "0.31.0" from .tones import Tone, Interval from .systems import System, SYSTEMS diff --git a/pytheory/cli.py b/pytheory/cli.py index 31756a5..40354f2 100644 --- a/pytheory/cli.py +++ b/pytheory/cli.py @@ -230,7 +230,7 @@ def cmd_demo(args): {"name": "Bossa Nova", "key": ("A", "minor"), "drums": "bossa nova", "fill": "bossa nova", "bpm": 140, "prog": ("i", "iv", "V", "i"), - "lead": ("triangle", "strings", 0.2, -0.1), + "lead": ("pluck_synth", "none", 0.2, -0.1), "pad": ("fm", "pad", -0.2), "bass_lp": 600, "reverb_type": "plate"}, {"name": "Jazz Club", "key": ("Bb", "major"), "drums": "jazz", @@ -254,8 +254,8 @@ def cmd_demo(args): {"name": "Reggae", "key": ("G", "major"), "drums": "reggae", "fill": "reggae", "bpm": 80, "prog": ("I", "IV", "V", "IV"), - "lead": ("triangle", "strings", 0.25, 0.15), - "pad": ("pwm_slow", "pad", -0.3), + "lead": ("pluck_synth", "none", 0.25, 0.15), + "pad": ("organ_synth", "organ", -0.3), "bass_lp": 400, "reverb_type": "cathedral"}, {"name": "Funk", "key": ("E", "minor"), "drums": "funk", "fill": "funk", "bpm": 100, @@ -272,8 +272,8 @@ def cmd_demo(args): {"name": "Temple", "key": ("E", "minor"), "drums": "bolero", "fill": "bossa nova", "bpm": 65, "prog": ("i", "iv", "V", "i"), - "lead": ("triangle", "pluck", 0.3, 0.2), - "pad": ("sine", "pad", 0.0), + "lead": ("pluck_synth", "none", 0.3, 0.2), + "pad": ("strings_synth", "pad", 0.0), "bass_lp": 200, "reverb_type": "taj_mahal"}, ] diff --git a/uv.lock b/uv.lock index 683db35..71c44cb 100644 --- a/uv.lock +++ b/uv.lock @@ -707,7 +707,7 @@ wheels = [ [[package]] name = "pytheory" -version = "0.30.0" +version = "0.31.0" source = { editable = "." } dependencies = [ { name = "numeral" },