From 9ebd54b7fcb78e876837bc2415b66a89ec2dd9f3 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Thu, 26 Mar 2026 20:11:40 -0400 Subject: [PATCH] =?UTF-8?q?Add=20drum=20bus=20effects=20=E2=80=94=20same?= =?UTF-8?q?=20engine=20as=20parts,=20zero=20duplication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit score.set_drum_effects(reverb=0.2, reverb_type="plate", lowpass=8000) Uses _apply_effects_with_params on each stereo channel. Supports all effects: reverb, delay, lowpass, distortion, chorus. Co-Authored-By: Claude Opus 4.6 (1M context) --- pytheory/play.py | 7 +++++++ pytheory/rhythm.py | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/pytheory/play.py b/pytheory/play.py index e563bbf..5a78203 100644 --- a/pytheory/play.py +++ b/pytheory/play.py @@ -1989,6 +1989,13 @@ def render_score(score): panned = _pan_to_stereo(mono_hit, pan) drum_stereo[start:start + hit_len] += panned + # Apply drum bus effects (reverb, delay, etc.) to the stereo drum mix + if score.drum_effects: + fx = score.drum_effects + # Apply to each stereo channel using the same effects engine + for ch in range(2): + drum_stereo[:, ch] = _apply_effects_with_params(drum_stereo[:, ch], fx) + # Apply sidechain compression to parts that request it for part, part_buf in _pending_sidechain: part_buf = _apply_sidechain( diff --git a/pytheory/rhythm.py b/pytheory/rhythm.py index 8a4c467..fccf7ca 100644 --- a/pytheory/rhythm.py +++ b/pytheory/rhythm.py @@ -1784,6 +1784,7 @@ class Score: self.bpm = bpm self.swing = swing self._drum_humanize = drum_humanize + self.drum_effects: dict = {} self.notes: list[Note] = [] self.parts: dict[str, Part] = {} self._drum_hits: list[_Hit] = [] @@ -1904,6 +1905,29 @@ class Score: fill_pattern = Pattern.fill(name) return self.add_pattern(fill_pattern, repeats=1) + def set_drum_effects(self, **kwargs) -> "Score": + """Set effects on the drum bus. + + Uses the same parameters as Part effects — reverb, delay, + lowpass, distortion, chorus. Applied to the entire drum mix + before stereo panning. + + Example:: + + score.set_drum_effects(reverb=0.2, reverb_type="plate", + lowpass=8000, distortion=0.1) + + Returns: + Self for chaining. + """ + # Map shorthand names + param_map = {"reverb": "reverb_mix", "delay": "delay_mix", + "distortion": "distortion_mix", "chorus": "chorus_mix"} + for k, v in kwargs.items(): + key = param_map.get(k, k) + self.drum_effects[key] = v + return self + def drums(self, preset: str, repeats: int = 4, fill: str = None, fill_every: int = None) -> "Score": """Add a drum pattern by preset name, with optional auto-fills.