From 7d678e364e0bff05150c1315f26fcbee624f6c99 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sat, 28 Mar 2026 23:01:18 -0400 Subject: [PATCH] v0.39.2: Marching drumline, ensemble rendering, rudiments Full marching percussion: snare, quads, pitched bass drums. Part.flam(), Part.diddle(), Part.cheese() rudiment methods. Part ensemble= for multi-player rendering with timing tendencies. Sympathetic snare resonance. Updated docs. Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/guide/drums.rst | 60 +++++++++++++++++++++++++++++++++++++-- docs/guide/sequencing.rst | 41 ++++++++++++++++++++++++++ pyproject.toml | 2 +- pytheory/__init__.py | 2 +- uv.lock | 2 +- 5 files changed, 102 insertions(+), 5 deletions(-) diff --git a/docs/guide/drums.rst b/docs/guide/drums.rst index e3e1ecb..2455bdc 100644 --- a/docs/guide/drums.rst +++ b/docs/guide/drums.rst @@ -10,7 +10,7 @@ the genre -- they tell the listener's body how to move before a single melodic note is played. PyTheory includes a complete drum system -- 51 synthesized percussion -sounds, 85+ pattern presets across dozens of genres, and 30 fill presets. +sounds, 95+ pattern presets across dozens of genres, and 30 fill presets. Every sound is generated from waveforms; no samples needed. Drum Sounds @@ -121,10 +121,18 @@ MRIDANGAM_THA (101) **Djembe:** DJEMBE_BASS (102), DJEMBE_TONE (103), DJEMBE_SLAP (104) -**Cajón:** CAJON_SLAP (109), CAJON_TAP (110) +**Cajón:** CAJON_BASS (108), CAJON_SLAP (109), CAJON_TAP (110) **Metal Kit:** METAL_KICK (105), METAL_SNARE (106), METAL_HAT (107) +**Marching Snare:** MARCH_SNARE (115), MARCH_RIMSHOT (116), MARCH_CLICK (118) + +**Quads (Tenors):** QUAD_1 (119), QUAD_2 (120), QUAD_3 (121), QUAD_4 (122), +QUAD_SPOCK (123) + +**Marching Bass:** BASS_1 (124), BASS_2 (125), BASS_3 (126), BASS_4 (127), +BASS_5 (80) + Drum Synthesis -------------- @@ -496,6 +504,54 @@ bass-slap groove). score = Score("4/4", bpm=100) score.drums("cajon", repeats=8, fill="cajon flam", fill_every=4) +Marching Percussion +~~~~~~~~~~~~~~~~~~~ + +A full drumline — snare, quads (tenors), and pitched bass drums. +Every sound is synthesized: kevlar snare heads, aluminum shell ting +on the quads, felt-beater thwack on the basses. + +**Snare** -- 3 sounds: MARCH_SNARE (tight kevlar tap), MARCH_RIMSHOT +(woody-metallic crack), MARCH_CLICK (stick click for count-offs). + +**Quads** -- 5 sounds: QUAD_1 through QUAD_4 (high to low pitched +tenors) plus QUAD_SPOCK (rim click on the shell). + +**Bass drums** -- 5 pitched drums: BASS_1 (highest/smallest) through +BASS_5 (lowest/biggest), each with a prominent felt-beater thwack. + +**6 patterns:** march (basic 4/4), cadence (8-beat street beat), +march paradiddle, march roll (buzz crescendo), quad sweep (run across +all 4 drums), quad groove, bass split (cascading across the line), +bass unison (all 5 hit together), drumline (snare + quads + bass). + +**Rudiment methods:** ``Part.flam()``, ``Part.diddle()``, and +``Part.cheese()`` for marching rudiments on any drum sound. + +**Ensemble rendering:** ``ensemble=N`` on any Part duplicates the +voice with per-player timing tendencies and micro pitch drift. +``ensemble=8`` for a snare line, ``ensemble=20`` for a massive section. + +.. code-block:: python + + # Full drumline with ensemble + snares = score.part("snares", synth="sine", volume=0.9, + reverb=0.2, ensemble=8) + quads = score.part("quads", synth="sine", volume=0.5, + reverb=0.2, ensemble=4) + basses = score.part("basses", synth="sine", volume=0.55, + reverb=0.2, ensemble=5) + + snares.flam(DrumSound.MARCH_SNARE, Duration.QUARTER, velocity=120) + snares.diddle(DrumSound.MARCH_SNARE, Duration.EIGHTH, velocity=60) + + # Or use patterns + score.drums("drumline", repeats=4) + +**Sympathetic resonance:** The marching snare builds up snare wire +buzz as hits accumulate, and the buzz decays during rests — just like +a real drum. + MIDI Export ----------- diff --git a/docs/guide/sequencing.rst b/docs/guide/sequencing.rst index 66b0cbe..3b178be 100644 --- a/docs/guide/sequencing.rst +++ b/docs/guide/sequencing.rst @@ -507,6 +507,47 @@ effects, and articulations for free. Use this for custom beats that don't fit a preset pattern, or for one-shot accent hits layered on top of a pattern. +Rudiments — Flam, Diddle, Cheese +--------------------------------- + +Marching percussion rudiments as methods on any Part: + +.. code-block:: python + + from pytheory import DrumSound + + p = score.part("snares", synth="sine", volume=0.9) + + # Flam: grace note + main hit (gap controls tightness) + p.flam(DrumSound.MARCH_SNARE, Duration.QUARTER, velocity=120) + + # Diddle: two equal strokes in one note duration + p.diddle(DrumSound.MARCH_SNARE, Duration.EIGHTH, velocity=60) + + # Cheese: flam + diddle combined + p.cheese(DrumSound.MARCH_SNARE, Duration.QUARTER, velocity=120) + +Ensemble +-------- + +Any Part can be rendered as an ensemble — multiple players with +per-player timing tendencies and micro pitch drift: + +.. code-block:: python + + # 8-player snare line + snares = score.part("snares", synth="sine", volume=0.9, ensemble=8) + + # 20-player string section + strings = score.part("strings", instrument="string_ensemble", ensemble=20) + + # Single player (default) + solo = score.part("solo", instrument="violin") + +Each ensemble voice gets a consistent timing personality (some rush, +some drag) plus small per-note wobble, and slightly different tuning. +The result sounds like a real section — together but alive. + Swing and Groove ---------------- diff --git a/pyproject.toml b/pyproject.toml index 4c0c4aa..636e249 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pytheory" -version = "0.39.0" +version = "0.39.2" description = "Music Theory for Humans" readme = "README.md" license = "MIT" diff --git a/pytheory/__init__.py b/pytheory/__init__.py index 62a605c..5c3557e 100644 --- a/pytheory/__init__.py +++ b/pytheory/__init__.py @@ -1,6 +1,6 @@ """PyTheory: Music Theory for Humans.""" -__version__ = "0.39.0" +__version__ = "0.39.2" from .tones import Tone, Interval from .systems import System, SYSTEMS, TET diff --git a/uv.lock b/uv.lock index 7569646..4a7b896 100644 --- a/uv.lock +++ b/uv.lock @@ -690,7 +690,7 @@ wheels = [ [[package]] name = "pytheory" -version = "0.39.0" +version = "0.39.2" source = { editable = "." } dependencies = [ { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },