From 95b7bd830c7d33a154f11cddbf228b3dd07f7732 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Mon, 30 Mar 2026 01:48:20 -0400 Subject: [PATCH] =?UTF-8?q?Fix=20tabla=20ge=5Fbend=20synth=20=E2=80=94=20w?= =?UTF-8?q?ider=20sweep,=20longer=20sustain,=20actually=20audible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sweep range: 50→450Hz (was 60→240Hz) - Slower sweep rate: exp(-1.5t) so ear can track it (was -4t) - Longer sustain: decay rate 2.5 (was 6) — bend lives long enough to hear - Removed static sub and metal that masked the sweep - Added 2nd harmonic for richness - Louder body (1.2 gain) Co-Authored-By: Claude Opus 4.6 (1M context) --- pytheory/play.py | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/pytheory/play.py b/pytheory/play.py index 15da968..79feebe 100644 --- a/pytheory/play.py +++ b/pytheory/play.py @@ -3335,36 +3335,35 @@ def _synth_tabla_ge_bend(n_samples): """Tabla Ge with upward pitch bend — palm pressing into bayan head. The player strikes the bayan and then presses their palm into the - head, raising the pitch dramatically. The signature bayan sound - in Bollywood and fusion music. + head, raising the pitch dramatically. The signature "woooo" bayan + sound in Bollywood and fusion music. Zakir Hussain's calling card. """ t = numpy.arange(n_samples, dtype=numpy.float32) / SAMPLE_RATE - # Membrane thud - thump_len = min(int(SAMPLE_RATE * 0.07), n_samples) + # Initial strike — membrane thud + thump_len = min(int(SAMPLE_RATE * 0.05), n_samples) thump_raw = _noise(thump_len) if thump_len > 20: - bl, al = scipy.signal.butter(2, [40, 250], btype='band', fs=SAMPLE_RATE) + bl, al = scipy.signal.butter(2, [30, 200], btype='band', fs=SAMPLE_RATE) thump = scipy.signal.lfilter(bl, al, numpy.pad(thump_raw, (0, max(0, n_samples - thump_len))))[:thump_len] else: thump = thump_raw - thump *= _exp_decay(thump_len, 20) * 0.8 - # Pitch sweep UP — 60 Hz rising to 200+ Hz as palm presses - # Gets quieter as pitch rises (palm mutes the head as it presses) - freq = 60 + 180 * (1 - numpy.exp(-4 * t)) + thump *= _exp_decay(thump_len, 25) * 0.7 + # THE SWEEP — this is the whole point. Palm presses into the head, + # pitch rises from deep bass to a singing tone. + # Slow sweep (-1.5*t) so the ear can track it, wide range (50→450Hz) + freq = 50 + 400 * (1 - numpy.exp(-1.5 * t)) phase = 2 * numpy.pi * numpy.cumsum(freq) / SAMPLE_RATE - body = numpy.sin(phase) * _exp_decay(n_samples, 6) * 0.9 - # Metal shell resonance - metal_len = min(int(SAMPLE_RATE * 0.1), n_samples) - metal = numpy.sin(2 * numpy.pi * 150 * t[:metal_len]) * _exp_decay(metal_len, 8) * 0.3 - # Sub - sub = _sine_f32(50, n_samples) * _exp_decay(n_samples, 5) * 0.4 - click_len = min(250, n_samples) - click = _noise(click_len) * _exp_decay(click_len, 35) * 0.3 - result = body + sub + # Longer sustain so the sweep is heard — decay rate 2.5 not 6 + body = numpy.sin(phase) * _exp_decay(n_samples, 2.5) * 1.2 + # Second harmonic for richness (follows the sweep) + body += numpy.sin(phase * 2) * _exp_decay(n_samples, 3.5) * 0.3 + # Click for attack definition + click_len = min(300, n_samples) + click = _noise(click_len) * _exp_decay(click_len, 40) * 0.25 + result = body result[:thump_len] += thump - result[:metal_len] += metal result[:click_len] += click - return numpy.tanh(result * 1.3).astype(numpy.float32) + return numpy.tanh(result * 1.2).astype(numpy.float32) def _synth_djembe_bass(n_samples):