mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 14:50:18 +00:00
v0.36.0: Banjo, mandolin, ukulele, cajón, vocal synth, granular
34 synth waveforms, 26 songs, vocal/formant synthesis with choir preset, granular engine, banjo/mandolin/ukulele physical models, cajón drum with 3 patterns, strum sweep on fretboard instruments. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,25 @@
|
||||
|
||||
All notable changes to PyTheory are documented here.
|
||||
|
||||
## 0.36.0
|
||||
|
||||
- **Banjo synth** — steel strings on drum-head body, nasal twang,
|
||||
fast decay with membrane resonance
|
||||
- **Mandolin synth** — paired steel strings (natural chorus from
|
||||
doubled courses), bright body resonance
|
||||
- **Ukulele synth** — nylon strings, small mid-heavy body, shorter
|
||||
sustain than guitar
|
||||
- **Cajón drums** — bass (woody box thump), slap (snare wire buzz),
|
||||
tap (ghost note). 3 patterns: cajon, cajon rumba, cajon folk
|
||||
- **Vocal/formant synth** — LF glottal model, 5 Peterson & Barney
|
||||
formant peaks, jitter/shimmer, consonant onsets, per-note lyrics.
|
||||
Presets: vocal, choir
|
||||
- **Granular synthesis** — grain cloud engine with scatter, pitch
|
||||
variation, Hanning windows. Presets: granular_pad, granular_texture
|
||||
- **Strum sweep** — subtle grace notes before chord hit for natural
|
||||
strum feel on all fretboard instruments
|
||||
- Mandola preset, 34 synth waveforms, 26 songs
|
||||
|
||||
## 0.35.0
|
||||
|
||||
- **8.5x faster import** — dropped pytuning/sympy, lazy-load scipy.
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ What's Inside
|
||||
numbers), scale recommendation, modulation, voice leading
|
||||
- **Sequencing** — Score, Parts, arpeggiator, legato/glide, velocity,
|
||||
swing, humanize, tempo changes, song sections with repeat
|
||||
- **Synthesis** — 30 waveforms (including Karplus-Strong pluck, Hammond organ,
|
||||
- **Synthesis** — 34 waveforms (including Karplus-Strong pluck, Hammond organ,
|
||||
bowed string, and 14 dedicated instrument synths), 10 envelopes, 40+
|
||||
instrument presets, configurable FM, sub-oscillator, noise layer, filter
|
||||
envelope, velocity-to-brightness, analog oscillator drift, detune, stereo
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "pytheory"
|
||||
version = "0.35.1"
|
||||
version = "0.36.0"
|
||||
description = "Music Theory for Humans"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""PyTheory: Music Theory for Humans."""
|
||||
|
||||
__version__ = "0.35.1"
|
||||
__version__ = "0.36.0"
|
||||
|
||||
from .tones import Tone, Interval
|
||||
from .systems import System, SYSTEMS, TET
|
||||
|
||||
+45
-1
@@ -1130,6 +1130,48 @@ def granular_wave(hz, peak=SAMPLE_PEAK, n_samples=SAMPLE_RATE,
|
||||
return (peak * out).astype(numpy.int16)
|
||||
|
||||
|
||||
def banjo_wave(hz, peak=SAMPLE_PEAK, n_samples=SAMPLE_RATE):
|
||||
"""Banjo — steel strings on a drum-head body.
|
||||
|
||||
The banjo's distinctive twang comes from the membrane head
|
||||
(like a drum skin) instead of a wooden soundboard. This gives
|
||||
a sharp attack, bright tone, and fast decay with a nasal,
|
||||
metallic quality. The 5th string drone adds shimmer.
|
||||
"""
|
||||
period = int(SAMPLE_RATE / hz)
|
||||
if period < 2:
|
||||
period = 2
|
||||
rng = numpy.random.default_rng(int(hz * 100) % 2**31)
|
||||
|
||||
# Steel string — bright, sharp attack
|
||||
buf = rng.uniform(-0.9, 0.9, period).astype(numpy.float64)
|
||||
# Minimal filtering — banjo keeps the brightness
|
||||
for k in range(period - 1):
|
||||
buf[k] = 0.7 * buf[k] + 0.3 * buf[k + 1]
|
||||
|
||||
out = numpy.zeros(n_samples, dtype=numpy.float64)
|
||||
for i in range(n_samples):
|
||||
out[i] = buf[i % period]
|
||||
next_idx = (i + 1) % period
|
||||
# Moderate decay — drum head rings but shorter than guitar
|
||||
buf[i % period] = 0.5 * (buf[i % period] + buf[next_idx]) * 0.9988
|
||||
|
||||
# Drum-head resonance — nasal, ringy, mid-frequency peaks
|
||||
# The membrane head rings more than wood — that's the twang
|
||||
import scipy.signal as _sig
|
||||
for center, bw, gain in [(600, 200, 0.5), (1500, 300, 0.4), (3000, 500, 0.25)]:
|
||||
lo = max(20, center - bw)
|
||||
hi = min(SAMPLE_RATE // 2 - 1, center + bw)
|
||||
if lo < hi:
|
||||
bp, ap = _sig.butter(2, [lo, hi], btype='band', fs=SAMPLE_RATE)
|
||||
out += _sig.lfilter(bp, ap, out) * gain
|
||||
|
||||
mx = numpy.abs(out).max()
|
||||
if mx > 0:
|
||||
out /= mx
|
||||
return (peak * out).astype(numpy.int16)
|
||||
|
||||
|
||||
def mandolin_wave(hz, peak=SAMPLE_PEAK, n_samples=SAMPLE_RATE):
|
||||
"""Mandolin — paired steel strings, bright and ringing.
|
||||
|
||||
@@ -1523,6 +1565,7 @@ class Synth(Enum):
|
||||
SAXOPHONE = "saxophone_synth"
|
||||
GRANULAR = "granular_synth"
|
||||
VOCAL = "vocal_synth"
|
||||
BANJO = "banjo_synth"
|
||||
MANDOLIN = "mandolin_synth"
|
||||
UKULELE = "ukulele_synth"
|
||||
ACOUSTIC_GUITAR = "acoustic_guitar_synth"
|
||||
@@ -1548,7 +1591,8 @@ _SYNTH_FUNCTIONS = {
|
||||
"harp_synth": harp_wave, "upright_bass_synth": upright_bass_wave,
|
||||
"timpani_synth": timpani_wave, "saxophone_synth": saxophone_wave,
|
||||
"granular_synth": granular_wave, "vocal_synth": vocal_wave,
|
||||
"mandolin_synth": mandolin_wave, "ukulele_synth": ukulele_wave,
|
||||
"banjo_synth": banjo_wave, "mandolin_synth": mandolin_wave,
|
||||
"ukulele_synth": ukulele_wave,
|
||||
"acoustic_guitar_synth": acoustic_guitar_wave,
|
||||
"sitar_synth": sitar_wave, "electric_guitar_synth": electric_guitar_wave,
|
||||
}
|
||||
|
||||
@@ -195,6 +195,10 @@ INSTRUMENTS = {
|
||||
"detune": 12, "lowpass": 3000, "lowpass_q": 1.5,
|
||||
"humanize": 0.2,
|
||||
},
|
||||
"banjo": {
|
||||
"synth": "banjo_synth", "envelope": "none",
|
||||
"humanize": 0.2,
|
||||
},
|
||||
"mandolin": {
|
||||
"synth": "mandolin_synth", "envelope": "none",
|
||||
"humanize": 0.2,
|
||||
|
||||
Reference in New Issue
Block a user