mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 14:50:18 +00:00
9de113b6e7
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1230 lines
46 KiB
Python
1230 lines
46 KiB
Python
"""Generate audio samples for documentation.
|
|
|
|
Renders code examples from the docs as WAV files so they can be
|
|
embedded as <audio> players on the website.
|
|
|
|
Usage:
|
|
uv run python docs/generate_audio.py
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
|
|
from pytheory import Score, Duration, Key, Chord, Fretboard, DrumSound
|
|
from pytheory.play import render_score, SAMPLE_RATE
|
|
|
|
import numpy
|
|
import struct
|
|
|
|
AUDIO_DIR = os.path.join(os.path.dirname(__file__), "_static", "audio")
|
|
os.makedirs(AUDIO_DIR, exist_ok=True)
|
|
|
|
|
|
def save_wav(buf, path):
|
|
"""Save a float32 buffer as 16-bit stereo WAV, trimming trailing silence."""
|
|
# Trim trailing silence (below -60dB threshold)
|
|
threshold = 0.001
|
|
if buf.ndim == 2:
|
|
amplitude = numpy.abs(buf).max(axis=1)
|
|
else:
|
|
amplitude = numpy.abs(buf)
|
|
# Find last sample above threshold
|
|
above = numpy.where(amplitude > threshold)[0]
|
|
if len(above) > 0:
|
|
# Keep 0.2s of tail after last audible sample for natural decay
|
|
tail = min(int(SAMPLE_RATE * 0.2), len(buf) - above[-1])
|
|
buf = buf[:above[-1] + tail]
|
|
|
|
# Handle both mono (n,) and stereo (n, 2) buffers
|
|
if buf.ndim == 1:
|
|
channels = 1
|
|
n_frames = len(buf)
|
|
else:
|
|
channels = buf.shape[1]
|
|
n_frames = buf.shape[0]
|
|
peak = numpy.abs(buf).max()
|
|
if peak > 0:
|
|
buf = buf / peak * 0.9
|
|
samples = (buf * 32767).astype(numpy.int16)
|
|
byte_rate = SAMPLE_RATE * channels * 2
|
|
block_align = channels * 2
|
|
data_size = n_frames * channels * 2
|
|
with open(path, "wb") as f:
|
|
f.write(b"RIFF")
|
|
f.write(struct.pack("<I", 36 + data_size))
|
|
f.write(b"WAVE")
|
|
f.write(b"fmt ")
|
|
f.write(struct.pack("<IHHIIHH", 16, 1, channels, SAMPLE_RATE,
|
|
byte_rate, block_align, 16))
|
|
f.write(b"data")
|
|
f.write(struct.pack("<I", data_size))
|
|
f.write(samples.tobytes())
|
|
label = "stereo" if channels == 2 else "mono"
|
|
print(f" {os.path.basename(path)} ({n_frames/SAMPLE_RATE:.1f}s, {label})")
|
|
|
|
|
|
def render(name, score):
|
|
"""Render a score to WAV in the audio directory."""
|
|
buf = render_score(score)
|
|
save_wav(buf, os.path.join(AUDIO_DIR, f"{name}.wav"))
|
|
|
|
|
|
# ── Piano hold (polyphonic overlap) ──────────────────────────────────────
|
|
|
|
def gen_piano_hold():
|
|
score = Score("4/4", bpm=85)
|
|
piano = score.part("piano", instrument="piano", reverb=0.3)
|
|
piano.hold("C3", Duration.WHOLE * 2, velocity=60)
|
|
piano.hold("E3", Duration.WHOLE * 2, velocity=55)
|
|
piano.hold("G3", Duration.WHOLE * 2, velocity=55)
|
|
for n in ["E4", "G4", "C5", "G4", "E4", "D4", "C4", "E4"]:
|
|
piano.add(n, Duration.QUARTER, velocity=80)
|
|
render("piano_hold", score)
|
|
|
|
|
|
# ── Articulations ────────────────────────────────────────────────────────
|
|
|
|
def gen_articulations():
|
|
score = Score("4/4", bpm=90)
|
|
piano = score.part("piano", instrument="piano", reverb=0.25)
|
|
for n in ["C4", "E4", "G4", "C5"]:
|
|
piano.add(n, Duration.QUARTER, velocity=80)
|
|
for n in ["C4", "E4", "G4", "C5"]:
|
|
piano.add(n, Duration.QUARTER, velocity=80, articulation="staccato")
|
|
for n in ["C5", "G4", "E4", "C4"]:
|
|
piano.add(n, Duration.QUARTER, velocity=80, articulation="legato")
|
|
for n in ["C4", "E4", "G4", "C5"]:
|
|
piano.add(n, Duration.QUARTER, velocity=80, articulation="marcato")
|
|
render("articulations", score)
|
|
|
|
|
|
# ── Dynamic curves ───────────────────────────────────────────────────────
|
|
|
|
def gen_dynamics():
|
|
score = Score("4/4", bpm=90)
|
|
piano = score.part("piano", instrument="piano", reverb=0.3)
|
|
piano.crescendo(["C4", "D4", "E4", "F4", "G4", "A4", "B4", "C5"],
|
|
Duration.QUARTER, start_vel=30, end_vel=110)
|
|
piano.decrescendo(["C5", "B4", "A4", "G4", "F4", "E4", "D4", "C4"],
|
|
Duration.QUARTER, start_vel=110, end_vel=30)
|
|
render("dynamics", score)
|
|
|
|
|
|
# ── Filter ramp ──────────────────────────────────────────────────────────
|
|
|
|
def gen_filter_ramp():
|
|
score = Score("4/4", bpm=130)
|
|
score.drums("house", repeats=8, fill="house", fill_every=8)
|
|
score.set_drum_effects(volume=0.4)
|
|
acid = score.part("acid", synth="saw", volume=0.6,
|
|
lowpass=300, lowpass_q=8.0, distortion=0.3,
|
|
legato=True, glide=0.03)
|
|
acid.ramp(over=Duration.WHOLE * 4, curve="ease_in", lowpass=5000)
|
|
for _ in range(4):
|
|
for n in ["C2", "C3", "C2", "Eb2", "C2", "G2", "Bb2", "C2"]:
|
|
acid.add(n, Duration.EIGHTH, velocity=90)
|
|
acid.ramp(over=Duration.WHOLE * 4, curve="ease_out", lowpass=300)
|
|
for _ in range(4):
|
|
for n in ["C2", "C3", "C2", "Eb2", "C2", "G2", "Bb2", "C2"]:
|
|
acid.add(n, Duration.EIGHTH, velocity=88)
|
|
render("filter_ramp", score)
|
|
|
|
|
|
# ── Rock beat ────────────────────────────────────────────────────────────
|
|
|
|
def gen_rock_beat():
|
|
score = Score("4/4", bpm=120)
|
|
score.drums("rock", repeats=4, fill="rock", fill_every=4)
|
|
render("rock_beat", score)
|
|
|
|
|
|
def gen_bossa_nova_pattern():
|
|
score = Score("4/4", bpm=140)
|
|
score.drums("bossa nova", repeats=4)
|
|
render("bossa_nova_pattern", score)
|
|
|
|
|
|
def gen_salsa_pattern():
|
|
score = Score("4/4", bpm=180)
|
|
score.drums("salsa", repeats=4)
|
|
render("salsa_pattern", score)
|
|
|
|
|
|
def gen_afrobeat_pattern():
|
|
score = Score("4/4", bpm=110)
|
|
score.drums("afrobeat", repeats=8)
|
|
render("afrobeat_pattern", score)
|
|
|
|
|
|
# ── Bossa nova ───────────────────────────────────────────────────────────
|
|
|
|
def gen_bossa_nova():
|
|
score = Score("4/4", bpm=140)
|
|
score.drums("bossa nova", repeats=4)
|
|
rhodes = score.part("rhodes", synth="fm", envelope="piano", volume=0.3,
|
|
reverb=0.4, reverb_decay=1.8)
|
|
for sym in ["Am", "Am", "Dm", "Dm", "E7", "E7", "Am", "Am"]:
|
|
rhodes.add(Chord.from_symbol(sym), Duration.WHOLE)
|
|
render("bossa_nova", score)
|
|
|
|
|
|
# ── Djembe ───────────────────────────────────────────────────────────────
|
|
|
|
def gen_djembe():
|
|
score = Score("4/4", bpm=110)
|
|
score.drums("tiriba", repeats=4, fill="djembe call", fill_every=4)
|
|
score.set_drum_effects(reverb=0.2)
|
|
render("djembe", score)
|
|
|
|
|
|
# ── Tabla ────────────────────────────────────────────────────────────────
|
|
|
|
def gen_tabla():
|
|
score = Score("4/4", bpm=80)
|
|
score.drums("teental", repeats=2)
|
|
score.drums("chakradar", repeats=1)
|
|
score.set_drum_effects(reverb=0.3, reverb_type="hall")
|
|
render("tabla", score)
|
|
|
|
|
|
# ── Marching snare ───────────────────────────────────────────────────────
|
|
|
|
def gen_metal_blast():
|
|
score = Score("4/4", bpm=190)
|
|
# Showcase all metal patterns: groove → gallop → triplet fill → blast
|
|
score.drums("metal groove", repeats=2)
|
|
score.drums("metal gallop", repeats=4, fill="metal triplet", fill_every=4)
|
|
score.drums("metal blast", repeats=2, fill="metal cascade", fill_every=2)
|
|
score.drums("double kick", repeats=2, fill="metal blast", fill_every=2)
|
|
render("metal_blast", score)
|
|
|
|
|
|
def gen_cajon():
|
|
score = Score("4/4", bpm=100)
|
|
score.drums("cajon", repeats=8, fill="cajon flam", fill_every=4)
|
|
score.set_drum_effects(reverb=0.25, reverb_type="room")
|
|
render("cajon", score)
|
|
|
|
|
|
def gen_tabla_teental():
|
|
score = Score("4/4", bpm=160)
|
|
score.drums("teental", repeats=3)
|
|
score.drums("teental", repeats=1, fill="bayan", fill_every=1)
|
|
score.set_drum_effects(reverb=0.3, reverb_type="hall")
|
|
render("tabla_teental", score)
|
|
|
|
|
|
def gen_tabla_keherwa():
|
|
score = Score("4/4", bpm=180)
|
|
# Manual part so we can add ge_bend hits
|
|
tabla = score.part("tabla", synth="sine", volume=0.5, reverb=0.3, reverb_type="hall")
|
|
DHA = DrumSound.TABLA_DHA
|
|
NA = DrumSound.TABLA_NA
|
|
TIN = DrumSound.TABLA_TIN
|
|
TIT = DrumSound.TABLA_TIT
|
|
GE = DrumSound.TABLA_GE
|
|
GB = DrumSound.TABLA_GE_BEND
|
|
# Keherwa with ge_bend accents
|
|
for _ in range(3):
|
|
tabla.hit(DHA, Duration.EIGHTH, velocity=90, articulation="accent")
|
|
tabla.hit(GE, Duration.EIGHTH, velocity=65)
|
|
tabla.hit(NA, Duration.EIGHTH, velocity=72)
|
|
tabla.hit(TIT, Duration.EIGHTH, velocity=45)
|
|
tabla.hit(NA, Duration.EIGHTH, velocity=68)
|
|
tabla.hit(TIT, Duration.EIGHTH, velocity=42)
|
|
tabla.hit(DHA, Duration.EIGHTH, velocity=85, articulation="accent")
|
|
tabla.hit(NA, Duration.EIGHTH, velocity=70)
|
|
# Last bar with bayan bends
|
|
tabla.hit(DHA, Duration.EIGHTH, velocity=95, articulation="marcato")
|
|
tabla.hit(GB, Duration.EIGHTH, velocity=80)
|
|
tabla.hit(NA, Duration.EIGHTH, velocity=72)
|
|
tabla.hit(GB, Duration.EIGHTH, velocity=82)
|
|
tabla.hit(DHA, Duration.EIGHTH, velocity=100, articulation="accent")
|
|
tabla.hit(GB, Duration.EIGHTH, velocity=85)
|
|
tabla.hit(DHA, Duration.QUARTER, velocity=110, articulation="fermata")
|
|
render("tabla_keherwa", score)
|
|
|
|
|
|
def gen_tabla_chakradar():
|
|
score = Score("4/4", bpm=200)
|
|
score.drums("teental", repeats=1)
|
|
score.drums("teental", repeats=1, fill="bayan", fill_every=1)
|
|
score.drums("chakradar", repeats=1)
|
|
score.set_drum_effects(reverb=0.3, reverb_type="hall")
|
|
render("tabla_chakradar", score)
|
|
|
|
|
|
def gen_dhol():
|
|
score = Score("4/4", bpm=160)
|
|
score.drums("bhangra", repeats=4)
|
|
render("dhol", score)
|
|
|
|
|
|
def gen_dholak():
|
|
score = Score("4/4", bpm=120)
|
|
score.drums("qawwali", repeats=4)
|
|
render("dholak", score)
|
|
|
|
|
|
def gen_mridangam():
|
|
score = Score("4/4", bpm=90)
|
|
score.drums("adi talam", repeats=4)
|
|
render("mridangam", score)
|
|
|
|
|
|
def gen_march_snare():
|
|
score = Score("4/4", bpm=120)
|
|
|
|
S = DrumSound.MARCH_SNARE
|
|
R = DrumSound.MARCH_RIMSHOT
|
|
C = DrumSound.MARCH_CLICK
|
|
Q1 = DrumSound.QUAD_1
|
|
Q2 = DrumSound.QUAD_2
|
|
Q3 = DrumSound.QUAD_3
|
|
Q4 = DrumSound.QUAD_4
|
|
QS = DrumSound.QUAD_SPOCK
|
|
B1 = DrumSound.BASS_1
|
|
B2 = DrumSound.BASS_2
|
|
B3 = DrumSound.BASS_3
|
|
B4 = DrumSound.BASS_4
|
|
B5 = DrumSound.BASS_5
|
|
|
|
# Snare line — 8 players (high volume to compensate for ensemble division)
|
|
sn = score.part("snares", synth="sine", volume=1.5, reverb=0.2, ensemble=8)
|
|
# Quads — 4 players
|
|
q = score.part("quads", synth="sine", volume=0.5, reverb=0.2, ensemble=4)
|
|
# Basses — 5 players
|
|
b = score.part("basses", synth="sine", volume=0.55, reverb=0.2, ensemble=5)
|
|
|
|
# Click count-off
|
|
for _ in range(4):
|
|
sn.hit(C, Duration.QUARTER, velocity=95)
|
|
q.rest(Duration.QUARTER)
|
|
b.rest(Duration.QUARTER)
|
|
|
|
# Bar 1-2: snare groove, quads accent, bass on beats
|
|
for _ in range(2):
|
|
sn.hit(R, Duration.SIXTEENTH, velocity=118)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
sn.hit(R, Duration.SIXTEENTH, velocity=115)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
sn.hit(R, Duration.SIXTEENTH, velocity=118)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=35)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
sn.hit(R, Duration.SIXTEENTH, velocity=120)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
|
|
q.hit(Q1, Duration.SIXTEENTH, velocity=95)
|
|
q.hit(Q2, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(Q3, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(Q4, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(Q4, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(Q3, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(Q2, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(Q1, Duration.SIXTEENTH, velocity=95)
|
|
q.hit(QS, Duration.SIXTEENTH, velocity=100)
|
|
q.hit(Q1, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(Q3, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(Q1, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(QS, Duration.SIXTEENTH, velocity=100)
|
|
q.hit(Q4, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(Q2, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(Q1, Duration.SIXTEENTH, velocity=90)
|
|
|
|
b.hit(B3, Duration.QUARTER, velocity=100)
|
|
b.hit(B1, Duration.EIGHTH, velocity=90)
|
|
b.hit(B5, Duration.EIGHTH, velocity=95)
|
|
b.hit(B3, Duration.QUARTER, velocity=100)
|
|
b.hit(B5, Duration.EIGHTH, velocity=90)
|
|
b.hit(B1, Duration.EIGHTH, velocity=95)
|
|
|
|
# Bar 3: flams + diddles
|
|
sn.flam(S, Duration.QUARTER, velocity=120)
|
|
sn.diddle(S, Duration.EIGHTH, velocity=45)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
sn.flam(S, Duration.QUARTER, velocity=118)
|
|
sn.diddle(S, Duration.EIGHTH, velocity=42)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
sn.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
|
|
q.hit(Q1, Duration.QUARTER, velocity=95)
|
|
q.hit(Q3, Duration.EIGHTH, velocity=55)
|
|
q.hit(Q2, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(Q4, Duration.SIXTEENTH, velocity=55)
|
|
q.hit(QS, Duration.QUARTER, velocity=100)
|
|
q.hit(Q4, Duration.EIGHTH, velocity=55)
|
|
q.hit(Q1, Duration.EIGHTH, velocity=90)
|
|
|
|
b.hit(B5, Duration.QUARTER, velocity=100)
|
|
b.hit(B3, Duration.QUARTER, velocity=95)
|
|
b.hit(B1, Duration.QUARTER, velocity=100)
|
|
b.hit(B3, Duration.QUARTER, velocity=95)
|
|
|
|
# Bar 4: buzz roll finale into big hit
|
|
for i in range(28):
|
|
sn.hit(S, 0.0625, velocity=min(25 + i * 3, 100))
|
|
sn.hit(R, Duration.EIGHTH, velocity=125)
|
|
sn.hit(R, Duration.EIGHTH, velocity=127)
|
|
|
|
for i in range(8):
|
|
q.hit([Q1,Q2,Q3,Q4,Q4,Q3,Q2,Q1][i], Duration.SIXTEENTH, velocity=60+i*4)
|
|
q.hit(QS, Duration.HALF, velocity=110)
|
|
|
|
b.hit(B1, Duration.SIXTEENTH, velocity=90)
|
|
b.hit(B2, Duration.SIXTEENTH, velocity=90)
|
|
b.hit(B3, Duration.SIXTEENTH, velocity=90)
|
|
b.hit(B4, Duration.SIXTEENTH, velocity=90)
|
|
b.hit(B5, Duration.SIXTEENTH, velocity=95)
|
|
b.hit(B4, Duration.SIXTEENTH, velocity=90)
|
|
b.hit(B3, Duration.SIXTEENTH, velocity=90)
|
|
b.hit(B2, Duration.SIXTEENTH, velocity=90)
|
|
b.hit(B3, Duration.HALF, velocity=100)
|
|
|
|
render("march_snare", score)
|
|
|
|
|
|
# ── Ensemble comparison ──────────────────────────────────────────────────
|
|
|
|
def gen_ensemble():
|
|
score = Score("4/4", bpm=120)
|
|
S = DrumSound.MARCH_SNARE
|
|
R = DrumSound.MARCH_RIMSHOT
|
|
|
|
# Solo first
|
|
solo = score.part("solo", synth="sine", volume=0.7, reverb=0.15)
|
|
for _ in range(2):
|
|
solo.hit(R, Duration.SIXTEENTH, velocity=118)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
solo.hit(R, Duration.SIXTEENTH, velocity=115)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
solo.hit(R, Duration.SIXTEENTH, velocity=118)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=35)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
solo.hit(R, Duration.SIXTEENTH, velocity=120)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
solo.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
|
|
# Then ensemble
|
|
line = score.part("line", synth="sine", volume=0.7, reverb=0.15, ensemble=8)
|
|
for _ in range(8):
|
|
line.rest(Duration.QUARTER)
|
|
for _ in range(4):
|
|
line.hit(R, Duration.SIXTEENTH, velocity=118)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
line.hit(R, Duration.SIXTEENTH, velocity=115)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
line.hit(R, Duration.SIXTEENTH, velocity=118)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=35)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
line.hit(R, Duration.SIXTEENTH, velocity=120)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
line.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
render("ensemble", score)
|
|
|
|
|
|
# ── Guitar strum ─────────────────────────────────────────────────────────
|
|
|
|
def gen_strum():
|
|
score = Score("4/4", bpm=100)
|
|
guitar = score.part("guitar", instrument="acoustic_guitar",
|
|
fretboard=Fretboard.guitar())
|
|
for ch in ["G", "D", "Em", "C"] * 2:
|
|
guitar.strum(ch, duration=Duration.WHOLE, velocity=75)
|
|
render("strum", score)
|
|
|
|
|
|
# ── Swell ────────────────────────────────────────────────────────────────
|
|
|
|
def gen_swell():
|
|
score = Score("4/4", bpm=80)
|
|
strings = score.part("strings", instrument="string_ensemble",
|
|
reverb=0.4, ensemble=12)
|
|
strings.swell(["C4", "D4", "E4", "F4", "G4", "F4", "E4", "D4",
|
|
"C4", "D4", "E4", "F4", "G4", "A4", "G4", "E4"],
|
|
Duration.QUARTER, low_vel=30, peak_vel=100)
|
|
render("swell", score)
|
|
|
|
|
|
# ── Generate all ─────────────────────────────────────────────────────────
|
|
|
|
# ── Cookbook: Acid House ───────────────────────────────────────────────────
|
|
|
|
def gen_acid_house():
|
|
score = Score("4/4", bpm=132)
|
|
score.drums("house", repeats=8, fill="house", fill_every=8)
|
|
pad = score.part("pad", synth="supersaw", envelope="pad",
|
|
reverb=0.4, chorus=0.3, sidechain=0.85)
|
|
acid = score.part("acid", synth="saw", envelope="pad",
|
|
legato=True, glide=0.03, distortion=0.8,
|
|
distortion_drive=8.0, lowpass=1000, lowpass_q=5.0)
|
|
acid.lfo("lowpass", rate=0.5, min=600, max=2500, bars=8)
|
|
for sym in ["Cm", "Fm", "Abm", "Gm"]:
|
|
pad.add(Chord.from_symbol(sym), Duration.WHOLE)
|
|
pad.add(Chord.from_symbol(sym), Duration.WHOLE)
|
|
acid.arpeggio(sym, bars=2, pattern="up", octaves=2)
|
|
render("acid_house", score)
|
|
|
|
|
|
# ── Cookbook: Dub Reggae ──────────────────────────────────────────────────
|
|
|
|
def gen_dub_reggae():
|
|
score = Score("4/4", bpm=72)
|
|
score.drums("dub", repeats=8)
|
|
melodica = score.part("melodica", synth="triangle", envelope="pluck",
|
|
delay=0.5, delay_time=0.66, delay_feedback=0.55,
|
|
reverb=0.4, reverb_type="cathedral")
|
|
bass = score.part("bass", synth="sine", lowpass=400, lowpass_q=1.5)
|
|
melodica.add("A4", 2).rest(6)
|
|
melodica.add("E5", 1.5).rest(6.5)
|
|
melodica.add("D5", 1).add("C5", 1).add("A4", 2).rest(4)
|
|
for _ in range(16):
|
|
bass.add("A1", Duration.HALF)
|
|
render("dub_reggae", score)
|
|
|
|
|
|
# ── Cookbook: Jazz Ballad ─────────────────────────────────────────────────
|
|
|
|
def gen_jazz_ballad():
|
|
score = Score("4/4", bpm=72, swing=0.5)
|
|
score.drums("jazz", repeats=8)
|
|
rhodes = score.part("rhodes", synth="fm", envelope="piano",
|
|
reverb=0.4, reverb_type="plate", humanize=0.3)
|
|
lead = score.part("lead", synth="triangle", envelope="strings",
|
|
delay=0.25, reverb=0.3, humanize=0.35)
|
|
key = Key("Bb", "major")
|
|
for chord in key.progression("I", "vi", "ii", "V") * 2:
|
|
rhodes.add(chord, Duration.WHOLE)
|
|
for n, d in [("D5", 1.5), ("F5", 0.5), ("Bb5", 2), (None, 4),
|
|
("A5", 1), ("G5", 1), ("F5", 2), (None, 4)]:
|
|
lead.rest(d) if n is None else lead.add(n, d)
|
|
render("jazz_ballad", score)
|
|
|
|
|
|
# ── Quickstart example ───────────────────────────────────────────────────
|
|
|
|
# ── Synth waveform demos ──────────────────────────────────────────────────
|
|
|
|
def _synth_demo(name, synth, envelope="none", **kwargs):
|
|
"""Short C major melody on a given synth."""
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", synth=synth, envelope=envelope, volume=0.5,
|
|
reverb=0.2, **kwargs)
|
|
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
|
p.add(n, Duration.QUARTER, velocity=85)
|
|
render(f"synth_{name}", score)
|
|
|
|
|
|
def gen_synth_sine():
|
|
_synth_demo("sine", "sine")
|
|
|
|
def gen_synth_saw():
|
|
_synth_demo("saw", "saw")
|
|
|
|
def gen_synth_triangle():
|
|
_synth_demo("triangle", "triangle")
|
|
|
|
def gen_synth_square():
|
|
_synth_demo("square", "square")
|
|
|
|
def gen_synth_piano():
|
|
score = Score("4/4", bpm=85)
|
|
p = score.part("demo", instrument="piano", volume=0.5, reverb=0.3)
|
|
# Hold chords with melody on top
|
|
p.hold("C3", Duration.WHOLE * 2, velocity=60)
|
|
p.hold("E3", Duration.WHOLE * 2, velocity=55)
|
|
p.hold("G3", Duration.WHOLE * 2, velocity=55)
|
|
for n in ["E4", "G4", "C5", "G4", "E4", "D4", "C4", "E4"]:
|
|
p.add(n, Duration.QUARTER, velocity=80)
|
|
render("synth_piano", score)
|
|
|
|
def gen_synth_acoustic_guitar():
|
|
from pytheory import Fretboard
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", instrument="acoustic_guitar", volume=0.5,
|
|
reverb=0.25, fretboard=Fretboard.guitar())
|
|
for ch in ["G", "D", "Em", "C"]:
|
|
p.strum(ch, Duration.WHOLE, velocity=75)
|
|
render("synth_acoustic_guitar", score)
|
|
|
|
def gen_synth_pulse():
|
|
_synth_demo("pulse", "pulse")
|
|
|
|
def gen_synth_noise():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", synth="noise", envelope="pad", volume=0.3,
|
|
lowpass=2000, reverb=0.3)
|
|
p.add("C4", Duration.WHOLE * 2, velocity=80)
|
|
render("synth_noise", score)
|
|
|
|
def gen_synth_pwm_slow():
|
|
_synth_demo("pwm_slow", "pwm_slow", envelope="pad")
|
|
|
|
def gen_synth_pwm_fast():
|
|
_synth_demo("pwm_fast", "pwm_fast")
|
|
|
|
def gen_synth_fm():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", synth="fm", envelope="bell", volume=0.5,
|
|
fm_ratio=3.0, fm_index=5.0, reverb=0.3)
|
|
for n in ["C5", "E5", "G5", "C6", "G5", "E5", "C5", "E5"]:
|
|
p.add(n, Duration.QUARTER, velocity=80)
|
|
render("synth_fm", score)
|
|
|
|
def gen_synth_rhodes():
|
|
score = Score("4/4", bpm=80)
|
|
p = score.part("demo", instrument="electric_piano", volume=0.5, reverb=0.3)
|
|
# Jazz chords with hold
|
|
p.hold("C3", Duration.WHOLE * 2, velocity=60)
|
|
p.hold("E3", Duration.WHOLE * 2, velocity=55)
|
|
p.hold("Bb3", Duration.WHOLE * 2, velocity=55)
|
|
for n in ["G4", "Bb4", "C5", "Bb4", "G4", "F4", "E4", "G4"]:
|
|
p.add(n, Duration.QUARTER, velocity=75)
|
|
render("synth_rhodes", score)
|
|
|
|
def gen_synth_supersaw():
|
|
_synth_demo("supersaw", "supersaw", envelope="pad")
|
|
|
|
def gen_synth_bass_guitar():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", synth="bass_guitar_synth", envelope="none",
|
|
volume=0.5, reverb=0.2)
|
|
for n in ["C2", "E2", "G2", "C3", "G2", "E2", "C2", "E2"]:
|
|
p.add(n, Duration.QUARTER, velocity=85)
|
|
render("synth_bass_guitar", score)
|
|
|
|
def gen_synth_flute():
|
|
_synth_demo("flute", "flute_synth", envelope="none")
|
|
|
|
def gen_synth_trumpet():
|
|
_synth_demo("trumpet", "trumpet_synth", envelope="none")
|
|
|
|
def gen_synth_clarinet():
|
|
_synth_demo("clarinet", "clarinet_synth", envelope="none")
|
|
|
|
def gen_synth_oboe():
|
|
_synth_demo("oboe", "oboe_synth", envelope="none")
|
|
|
|
def gen_synth_cello():
|
|
score = Score("4/4", bpm=70)
|
|
p = score.part("demo", instrument="cello", volume=0.5, reverb=0.3, ensemble=3)
|
|
for n in ["C3", "E3", "G3", "C4"]:
|
|
p.add(n, Duration.WHOLE, velocity=80)
|
|
render("synth_cello", score)
|
|
|
|
def gen_synth_harpsichord():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", synth="harpsichord_synth", envelope="none",
|
|
volume=0.5, reverb=0.25)
|
|
# Baroque ornamental runs
|
|
p.hold("C3", Duration.WHOLE, velocity=70)
|
|
for n in ["C4", "D4", "E4", "F4", "G4", "A4", "B4", "C5"]:
|
|
p.add(n, Duration.EIGHTH, velocity=80)
|
|
p.hold("G3", Duration.WHOLE, velocity=70)
|
|
for n in ["C5", "B4", "A4", "G4", "F4", "E4", "D4", "C4"]:
|
|
p.add(n, Duration.EIGHTH, velocity=78)
|
|
render("synth_harpsichord", score)
|
|
|
|
def gen_synth_electric_guitar():
|
|
from pytheory import Fretboard
|
|
score = Score("4/4", bpm=110)
|
|
p = score.part("demo", instrument="electric_guitar", volume=0.5,
|
|
reverb=0.15, fretboard=Fretboard.guitar())
|
|
for ch in ["Am", "F", "C", "G"]:
|
|
p.strum(ch, Duration.WHOLE, velocity=80)
|
|
render("synth_electric_guitar", score)
|
|
|
|
def gen_synth_kalimba():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", instrument="kalimba", volume=0.5, reverb=0.3)
|
|
for n in ["C5", "E5", "G5", "C6", "G5", "E5", "C5", "E5"]:
|
|
p.add(n, Duration.QUARTER, velocity=85)
|
|
render("synth_kalimba", score)
|
|
|
|
def gen_synth_wurlitzer():
|
|
score = Score("4/4", bpm=90)
|
|
p = score.part("demo", instrument="wurlitzer", volume=0.5, reverb=0.25)
|
|
p.hold("C3", Duration.WHOLE * 2, velocity=60)
|
|
p.hold("Eb3", Duration.WHOLE * 2, velocity=55)
|
|
p.hold("G3", Duration.WHOLE * 2, velocity=55)
|
|
for n in ["G4", "Bb4", "C5", "Bb4", "G4", "F4", "Eb4", "G4"]:
|
|
p.add(n, Duration.QUARTER, velocity=78)
|
|
render("synth_wurlitzer", score)
|
|
|
|
def gen_synth_vibraphone():
|
|
score = Score("4/4", bpm=90)
|
|
p = score.part("demo", instrument="vibraphone", volume=0.5)
|
|
for n in ["C5", "E5", "G5", "C6", "G5", "E5", "C5", "E5"]:
|
|
p.add(n, Duration.QUARTER, velocity=75)
|
|
render("synth_vibraphone", score)
|
|
|
|
def gen_synth_pipe_organ():
|
|
score = Score("4/4", bpm=70)
|
|
p = score.part("demo", instrument="pipe_organ", volume=0.5)
|
|
p.hold("C3", Duration.WHOLE * 4, velocity=70)
|
|
p.hold("G3", Duration.WHOLE * 4, velocity=65)
|
|
for n in ["C4", "D4", "E4", "F4", "G4", "F4", "E4", "D4",
|
|
"C4", "E4", "G4", "C5", "G4", "E4", "C4", "C4"]:
|
|
p.add(n, Duration.QUARTER, velocity=75)
|
|
render("synth_pipe_organ", score)
|
|
|
|
def gen_synth_choir():
|
|
score = Score("4/4", bpm=70)
|
|
p = score.part("demo", instrument="choir", volume=0.5)
|
|
for n, v in [("C4", "ah"), ("E4", "oh"), ("G4", "ah"), ("C5", "ee"),
|
|
("G4", "oh"), ("E4", "ah"), ("C4", "oo"), ("C4", "ah")]:
|
|
p.add(n, Duration.HALF, velocity=70, lyric=v)
|
|
render("synth_choir", score)
|
|
|
|
def gen_synth_organ():
|
|
_synth_demo("organ", "organ_synth", envelope="organ")
|
|
|
|
def gen_synth_marimba():
|
|
_synth_demo("marimba", "marimba_synth", envelope="mallet")
|
|
|
|
def gen_synth_sitar():
|
|
score = Score("4/4", bpm=80)
|
|
p = score.part("demo", instrument="sitar", volume=0.4, reverb=0.35)
|
|
# Drone under melody
|
|
p.hold("C3", Duration.WHOLE * 4, velocity=55)
|
|
for n, d in [("C4", 1.0), ("D4", 0.5), ("E4", 0.5), ("G4", 1.0),
|
|
("A4", 0.5), ("G4", 0.5), ("E4", 1.0), ("D4", 0.5),
|
|
("C4", 0.5), ("D4", 1.0), ("C4", 2.0)]:
|
|
p.add(n, d, velocity=75)
|
|
render("synth_sitar", score)
|
|
|
|
|
|
def gen_synth_harp():
|
|
score = Score("4/4", bpm=80)
|
|
p = score.part("demo", synth="harp_synth", envelope="none",
|
|
volume=0.5, reverb=0.3)
|
|
# Arpeggiated chords with hold — harp style
|
|
p.hold("C3", Duration.WHOLE * 2, velocity=70)
|
|
for n in ["E4", "G4", "C5", "E5", "G5", "C5", "G4", "E4"]:
|
|
p.add(n, Duration.QUARTER, velocity=75)
|
|
render("synth_harp", score)
|
|
|
|
def gen_synth_upright_bass():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", synth="upright_bass_synth", envelope="none",
|
|
volume=0.5, reverb=0.2)
|
|
for n in ["C2", "E2", "G2", "C3", "G2", "E2", "C2", "E2"]:
|
|
p.add(n, Duration.QUARTER, velocity=85)
|
|
render("synth_upright_bass", score)
|
|
|
|
def gen_synth_timpani():
|
|
score = Score("4/4", bpm=140)
|
|
timp = score.part("timp", synth="timpani_synth", volume=0.5, reverb=0.25)
|
|
timp.roll("C3", Duration.WHOLE, velocity_start=20, velocity_end=110, speed=0.125)
|
|
timp.add("C3", Duration.HALF, velocity=127)
|
|
timp.rest(Duration.HALF)
|
|
timp.roll("G2", Duration.WHOLE, velocity_start=20, velocity_end=110, speed=0.125)
|
|
timp.add("G2", Duration.HALF, velocity=127)
|
|
render("synth_timpani", score)
|
|
|
|
def gen_synth_strings():
|
|
score = Score("4/4", bpm=70)
|
|
p = score.part("demo", synth="strings_synth", envelope="bowed",
|
|
volume=0.5, reverb=0.35, ensemble=8)
|
|
for n in ["C4", "E4", "G4", "C5"]:
|
|
p.add(n, Duration.WHOLE, velocity=75)
|
|
render("synth_strings", score)
|
|
|
|
def gen_synth_saxophone():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", instrument="tenor_sax", volume=0.5, reverb=0.2)
|
|
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
|
p.add(n, Duration.QUARTER, velocity=85)
|
|
render("synth_saxophone", score)
|
|
|
|
def gen_synth_pedal_steel():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", instrument="pedal_steel", volume=0.5, reverb=0.3)
|
|
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
|
p.add(n, Duration.QUARTER, velocity=85)
|
|
render("synth_pedal_steel", score)
|
|
|
|
def gen_synth_theremin():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", instrument="theremin", volume=0.5, reverb=0.3)
|
|
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
|
p.add(n, Duration.QUARTER, velocity=85)
|
|
render("synth_theremin", score)
|
|
|
|
def gen_synth_steel_drum():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", instrument="steel_drum", volume=0.5, reverb=0.2)
|
|
for n in ["C5", "E5", "G5", "C6", "G5", "E5", "C5", "E5"]:
|
|
p.add(n, Duration.QUARTER, velocity=85)
|
|
render("synth_steel_drum", score)
|
|
|
|
def gen_synth_accordion():
|
|
score = Score("4/4", bpm=110)
|
|
p = score.part("demo", instrument="accordion", volume=0.5, reverb=0.2)
|
|
# Waltz feel with held chords
|
|
p.hold("C3", Duration.WHOLE, velocity=65)
|
|
p.hold("E3", Duration.WHOLE, velocity=60)
|
|
p.hold("G3", Duration.WHOLE, velocity=60)
|
|
for n in ["E4", "G4", "C5", "G4"]:
|
|
p.add(n, Duration.QUARTER, velocity=78)
|
|
p.hold("F3", Duration.WHOLE, velocity=65)
|
|
p.hold("A3", Duration.WHOLE, velocity=60)
|
|
p.hold("C4", Duration.WHOLE, velocity=60)
|
|
for n in ["A4", "C5", "F5", "C5"]:
|
|
p.add(n, Duration.QUARTER, velocity=78)
|
|
render("synth_accordion", score)
|
|
|
|
def gen_synth_didgeridoo():
|
|
score = Score("4/4", bpm=80)
|
|
p = score.part("demo", instrument="didgeridoo", volume=0.5, reverb=0.3)
|
|
p.add("C2", Duration.WHOLE * 2, velocity=85)
|
|
render("synth_didgeridoo", score)
|
|
|
|
def gen_synth_bagpipe():
|
|
score = Score("4/4", bpm=90)
|
|
p = score.part("demo", instrument="bagpipe", volume=0.4, reverb=0.2)
|
|
# Drone on low A + E (like real Highland pipes)
|
|
p.hold("A3", Duration.WHOLE * 4, velocity=70)
|
|
p.hold("E3", Duration.WHOLE * 4, velocity=65)
|
|
# Chanter melody on top
|
|
for n, d in [("A4", 1.0), ("B4", 0.5), ("C5", 0.5), ("D5", 1.0),
|
|
("E5", 1.0), ("D5", 0.5), ("C5", 0.5), ("B4", 1.0),
|
|
("A4", 1.0), ("G4", 1.0), ("A4", 2.0)]:
|
|
p.add(n, d, velocity=80)
|
|
render("synth_bagpipe", score)
|
|
|
|
def gen_synth_banjo():
|
|
from pytheory import Fretboard
|
|
score = Score("4/4", bpm=130)
|
|
p = score.part("demo", instrument="banjo", volume=0.5, reverb=0.15,
|
|
fretboard=Fretboard.guitar())
|
|
# Strum into a picking lick
|
|
p.strum("G", Duration.WHOLE, velocity=80)
|
|
p.strum("C", Duration.WHOLE, velocity=78)
|
|
# Bluegrass lick — 16th note picking
|
|
for n in ["G4", "B4", "D5", "G5", "D5", "B4", "A4", "G4",
|
|
"D4", "G4", "B4", "D5", "B4", "G4", "D4", "G4"]:
|
|
p.add(n, Duration.SIXTEENTH, velocity=82)
|
|
render("synth_banjo", score)
|
|
|
|
def gen_synth_mandolin():
|
|
score = Score("4/4", bpm=110)
|
|
p = score.part("demo", instrument="mandolin", volume=0.5, reverb=0.2)
|
|
# Tremolo rolls on held notes — the mandolin signature
|
|
p.roll("G4", Duration.WHOLE, velocity_start=65, velocity_end=85, speed=Duration.SIXTEENTH)
|
|
p.roll("A4", Duration.WHOLE, velocity_start=65, velocity_end=85, speed=Duration.SIXTEENTH)
|
|
# Quick melody
|
|
for n in ["B4", "C5", "D5", "C5", "B4", "A4", "G4", "A4"]:
|
|
p.add(n, Duration.EIGHTH, velocity=80)
|
|
# End on a roll
|
|
p.roll("G4", Duration.WHOLE, velocity_start=70, velocity_end=90, speed=Duration.SIXTEENTH)
|
|
render("synth_mandolin", score)
|
|
|
|
def gen_synth_ukulele():
|
|
from pytheory import Fretboard
|
|
score = Score("4/4", bpm=110)
|
|
p = score.part("demo", instrument="ukulele", volume=0.5, reverb=0.25,
|
|
fretboard=Fretboard.ukulele())
|
|
for ch in ["C", "Am", "F", "G"]:
|
|
p.strum(ch, Duration.WHOLE, velocity=72)
|
|
render("synth_ukulele", score)
|
|
|
|
def gen_synth_hard_sync():
|
|
score = Score("4/4", bpm=120)
|
|
p = score.part("demo", instrument="sync_lead_bright", volume=0.5)
|
|
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
|
p.add(n, Duration.QUARTER, velocity=90)
|
|
render("synth_hard_sync", score)
|
|
|
|
|
|
def gen_synth_ring_mod():
|
|
score = Score("4/4", bpm=90)
|
|
p = score.part("demo", instrument="ring_mod_bell", volume=0.5)
|
|
for n in ["C5", "E5", "G5", "C6", "G5", "E5", "C5", "E5"]:
|
|
p.add(n, Duration.QUARTER, velocity=80)
|
|
render("synth_ring_mod", score)
|
|
|
|
|
|
def gen_synth_wavefold():
|
|
score = Score("4/4", bpm=110)
|
|
p = score.part("demo", instrument="wavefold_warm", volume=0.5)
|
|
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
|
p.add(n, Duration.QUARTER, velocity=85)
|
|
render("synth_wavefold", score)
|
|
|
|
|
|
def gen_synth_drift():
|
|
score = Score("4/4", bpm=90)
|
|
p = score.part("demo", instrument="drift_saw", volume=0.5, reverb=0.35,
|
|
reverb_type="taj_mahal")
|
|
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
|
p.add(n, Duration.HALF, velocity=75)
|
|
render("synth_drift", score)
|
|
|
|
|
|
def gen_synth_karplus():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", synth="pluck_synth", envelope="none",
|
|
volume=0.5, reverb=0.2)
|
|
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
|
p.add(n, Duration.QUARTER, velocity=85)
|
|
render("synth_karplus", score)
|
|
|
|
|
|
def gen_synth_mellotron():
|
|
score = Score("4/4", bpm=80)
|
|
p = score.part("demo", instrument="mellotron_flute", volume=0.5)
|
|
for n in ["C4", "E4", "G4", "C5"]:
|
|
p.add(n, Duration.WHOLE, velocity=75)
|
|
render("synth_mellotron", score)
|
|
|
|
|
|
def gen_synth_granular():
|
|
score = Score("4/4", bpm=80)
|
|
p = score.part("demo", instrument="granular_pad", volume=0.5, reverb=0.4)
|
|
for n in ["C4", "E4", "G4", "C5"]:
|
|
p.add(n, Duration.WHOLE, velocity=75)
|
|
render("synth_granular", score)
|
|
|
|
|
|
def gen_synth_crotales():
|
|
score = Score("4/4", bpm=60)
|
|
p = score.part("demo", synth="crotales_synth", envelope="none",
|
|
volume=0.5, reverb=0.3)
|
|
for n in ["C6", "E6", "G6", "C7", "G6", "E6", "C6"]:
|
|
p.add(n, Duration.HALF, velocity=80)
|
|
render("synth_crotales", score)
|
|
|
|
|
|
def gen_synth_tingsha():
|
|
score = Score("4/4", bpm=40)
|
|
p = score.part("demo", synth="tingsha_synth", envelope="none",
|
|
volume=0.5, reverb=0.4)
|
|
for n in ["E5", "A5", "E6", "A5"]:
|
|
p.add(n, Duration.WHOLE, velocity=75)
|
|
render("synth_tingsha", score)
|
|
|
|
|
|
def gen_rainstick():
|
|
score = Score("4/4", bpm=60)
|
|
p = score.part("demo", synth="sine", volume=1.0)
|
|
p.hit(DrumSound.RAINSTICK, Duration.WHOLE * 3, velocity=90)
|
|
render("rainstick", score)
|
|
|
|
|
|
def gen_rainstick_slow():
|
|
score = Score("4/4", bpm=60)
|
|
p = score.part("demo", synth="sine", volume=1.0)
|
|
p.hit(DrumSound.RAINSTICK_SLOW, Duration.WHOLE * 4, velocity=85)
|
|
render("rainstick_slow", score)
|
|
|
|
|
|
def gen_ocean_drum():
|
|
score = Score("4/4", bpm=60)
|
|
p = score.part("demo", synth="sine", volume=1.0)
|
|
p.hit(DrumSound.OCEAN_DRUM, Duration.WHOLE * 3, velocity=85)
|
|
render("ocean_drum", score)
|
|
|
|
|
|
def gen_cabasa():
|
|
score = Score("4/4", bpm=100)
|
|
p = score.part("demo", synth="sine", volume=1.0)
|
|
for _ in range(16):
|
|
p.hit(DrumSound.CABASA, Duration.EIGHTH, velocity=100)
|
|
render("cabasa", score)
|
|
|
|
|
|
def gen_wind_chimes():
|
|
score = Score("4/4", bpm=60)
|
|
p = score.part("demo", synth="sine", volume=1.0)
|
|
p.hit(DrumSound.WIND_CHIMES, Duration.WHOLE * 3, velocity=85)
|
|
render("wind_chimes", score)
|
|
|
|
|
|
def gen_finger_cymbal():
|
|
score = Score("4/4", bpm=80)
|
|
p = score.part("demo", synth="sine", volume=1.0)
|
|
for _ in range(8):
|
|
p.hit(DrumSound.FINGER_CYMBAL, Duration.QUARTER, velocity=85)
|
|
render("finger_cymbal", score)
|
|
|
|
|
|
def gen_synth_singing_bowl_strike():
|
|
score = Score("4/4", bpm=40)
|
|
p = score.part("demo", synth="singing_bowl_strike_synth", envelope="none",
|
|
volume=0.5, reverb=0.4)
|
|
for n in ["A3", "D4", "F4", "A4"]:
|
|
p.add(n, Duration.WHOLE, velocity=80)
|
|
render("synth_singing_bowl_strike", score)
|
|
|
|
|
|
def gen_synth_singing_bowl_ring():
|
|
score = Score("4/4", bpm=30)
|
|
p = score.part("demo", synth="singing_bowl_ring_synth", envelope="none",
|
|
volume=0.5, reverb=0.4)
|
|
for n in ["A3", "D4", "A4"]:
|
|
p.add(n, Duration.WHOLE * 2, velocity=75)
|
|
render("synth_singing_bowl_ring", score)
|
|
|
|
|
|
def gen_arpeggio():
|
|
score = Score("4/4", bpm=132)
|
|
score.drums("house", repeats=8)
|
|
score.set_drum_effects(volume=0.3)
|
|
lead = score.part("lead", synth="saw", envelope="pluck", volume=0.5,
|
|
lowpass=5000, detune=8, chorus=0.15, reverb=0.25,
|
|
delay=0.2, delay_time=0.33, delay_feedback=0.3)
|
|
for sym in ["Cm", "Fm", "Abm", "Gm"]:
|
|
lead.arpeggio(sym, bars=2, pattern="updown", octaves=2)
|
|
render("arpeggio", score)
|
|
|
|
|
|
def gen_legato_glide():
|
|
score = Score("4/4", bpm=132)
|
|
score.drums("house", repeats=4)
|
|
score.set_drum_effects(volume=0.35)
|
|
acid = score.part("acid", synth="saw", volume=0.6,
|
|
legato=True, glide=0.04,
|
|
lowpass=3000, lowpass_q=6.0,
|
|
distortion=0.3, distortion_drive=3.0)
|
|
acid.lfo("lowpass", rate=0.5, min=800, max=4000, bars=4)
|
|
for _ in range(4):
|
|
acid.add("C2", 0.25).add("C3", 0.25).add("G2", 0.25).add("C2", 0.25)
|
|
acid.add("C2", 0.25).add("Eb2", 0.25).add("G2", 0.25).add("Bb2", 0.25)
|
|
render("legato_glide", score)
|
|
|
|
|
|
def gen_quickstart():
|
|
score = Score("4/4", bpm=120)
|
|
score.drums("rock", repeats=8, fill="rock", fill_every=4)
|
|
piano = score.part("piano", instrument="piano", reverb=0.3)
|
|
lead = score.part("lead", synth="saw", envelope="pluck", volume=0.4,
|
|
delay=0.2, reverb=0.2, lowpass=4000)
|
|
bass = score.part("bass", synth="triangle", lowpass=900)
|
|
for chord in Key("G", "major").progression("I", "V", "vi", "IV") * 2:
|
|
piano.add(chord, Duration.WHOLE)
|
|
lead.add("D5", 1).add("B4", 0.5).add("D5", 0.5)
|
|
lead.add("G5", 1).add("E5", 1)
|
|
lead.add("D5", 0.5).add("B4", 0.5).add("A4", 1)
|
|
lead.add("G4", 2).rest(2)
|
|
lead.add("D5", 1).add("B4", 0.5).add("D5", 0.5)
|
|
lead.add("G5", 1).add("A5", 1)
|
|
lead.add("G5", 0.5).add("E5", 0.5).add("D5", 1)
|
|
lead.add("B4", 2).rest(2)
|
|
for n in ["G2", "G2", "D2", "D2", "E2", "E2", "C2", "C2"] * 2:
|
|
bass.add(n, Duration.HALF)
|
|
render("quickstart", score)
|
|
|
|
|
|
# ── Sequencing complete example (bossa nova) ─────────────────────────────
|
|
|
|
def gen_chords_basic():
|
|
score = Score("4/4", bpm=120)
|
|
key = Key("C", "major")
|
|
chords = key.progression("I", "V", "vi", "IV")
|
|
for chord in chords:
|
|
score.add(chord, Duration.WHOLE)
|
|
render("chords_basic", score)
|
|
|
|
|
|
def gen_complete_rock():
|
|
score = Score("4/4", bpm=120)
|
|
score.drums("rock", repeats=8, fill="rock", fill_every=4)
|
|
piano = score.part("piano", instrument="piano", volume=0.4, reverb=0.3)
|
|
lead = score.part("lead", synth="saw", envelope="pluck", volume=0.4,
|
|
delay=0.2, delay_time=0.33, reverb=0.2, lowpass=3000)
|
|
bass = score.part("bass", synth="triangle", envelope="pluck", volume=0.45,
|
|
lowpass=1200)
|
|
for chord in Key("G", "major").progression("I", "V", "vi", "IV") * 2:
|
|
piano.add(chord, Duration.WHOLE)
|
|
lead.add("D5", 1).add("B4", 0.5).add("D5", 0.5)
|
|
lead.add("G5", 1).add("E5", 1)
|
|
lead.add("D5", 0.5).add("B4", 0.5).add("A4", 1)
|
|
lead.add("G4", 2).rest(2)
|
|
lead.add("D5", 1).add("B4", 0.5).add("D5", 0.5)
|
|
lead.add("G5", 1).add("A5", 1)
|
|
lead.add("G5", 0.5).add("E5", 0.5).add("D5", 1)
|
|
lead.add("B4", 2).rest(2)
|
|
for n in ["G2", "G2", "D2", "D2", "E2", "E2", "C2", "C2"] * 2:
|
|
bass.add(n, Duration.HALF)
|
|
render("complete_rock", score)
|
|
|
|
|
|
# ── Drums layering (salsa) ───────────────────────────────────────────────
|
|
|
|
def gen_salsa_layered():
|
|
score = Score("4/4", bpm=180)
|
|
score.drums("salsa", repeats=4, fill="salsa", fill_every=4)
|
|
pads = score.part("pads", synth="sine", envelope="pad", volume=0.3)
|
|
lead = score.part("lead", synth="saw", envelope="pluck", volume=0.4)
|
|
bass = score.part("bass", synth="sine", envelope="pluck", volume=0.45)
|
|
for chord in Key("D", "minor").progression("ii", "V", "i", "i") * 2:
|
|
pads.add(chord, Duration.WHOLE)
|
|
lead.add("A5", 0.67).add("G5", 0.33).add("F5", 0.67).add("E5", 0.33)
|
|
for n in ["D2", "A2", "D2", "F2"] * 2:
|
|
bass.add(n, Duration.QUARTER)
|
|
render("salsa_layered", score)
|
|
|
|
|
|
# ── Playback basic ───────────────────────────────────────────────────────
|
|
|
|
def gen_playback_basic():
|
|
score = Score("4/4", bpm=140)
|
|
score.drums("bossa nova", repeats=4)
|
|
chords = score.part("chords", synth="sine", envelope="pad")
|
|
for sym in ["Am", "Dm", "E7", "Am"]:
|
|
chords.add(Chord.from_symbol(sym), Duration.WHOLE)
|
|
render("playback_basic", score)
|
|
|
|
|
|
# ── Cookbook: song with sections ──────────────────────────────────────────
|
|
|
|
def gen_song_sections():
|
|
score = Score("4/4", bpm=120)
|
|
score.drums("rock", repeats=16, fill="rock", fill_every=4)
|
|
chords = score.part("chords", synth="saw", envelope="pad")
|
|
lead = score.part("lead", synth="triangle", envelope="pluck")
|
|
score.section("verse")
|
|
for sym in ["Am", "F", "C", "G"]:
|
|
chords.add(Chord.from_symbol(sym), Duration.WHOLE)
|
|
lead.add("A4", 1).add("C5", 1).add("E5", 1).rest(1)
|
|
lead.add("F5", 1).add("E5", 1).add("C5", 2)
|
|
score.section("chorus")
|
|
lead.set(reverb=0.4, lowpass=5000)
|
|
for sym in ["F", "G", "Am", "C"]:
|
|
chords.add(Chord.from_symbol(sym), Duration.WHOLE)
|
|
lead.add("C6", 2).add("A5", 1).add("G5", 1)
|
|
lead.add("F5", 2).add("E5", 2)
|
|
score.end_section()
|
|
score.repeat("verse")
|
|
score.repeat("chorus", times=2)
|
|
render("song_sections", score)
|
|
|
|
|
|
GENERATORS = [
|
|
gen_piano_hold,
|
|
gen_articulations,
|
|
gen_dynamics,
|
|
gen_filter_ramp,
|
|
gen_rock_beat,
|
|
gen_bossa_nova_pattern,
|
|
gen_salsa_pattern,
|
|
gen_afrobeat_pattern,
|
|
gen_bossa_nova,
|
|
gen_djembe,
|
|
gen_tabla,
|
|
gen_metal_blast,
|
|
gen_cajon,
|
|
gen_tabla_teental,
|
|
gen_tabla_keherwa,
|
|
gen_tabla_chakradar,
|
|
gen_dhol,
|
|
gen_dholak,
|
|
gen_mridangam,
|
|
gen_march_snare,
|
|
gen_ensemble,
|
|
gen_strum,
|
|
gen_swell,
|
|
gen_synth_sine,
|
|
gen_synth_saw,
|
|
gen_synth_triangle,
|
|
gen_synth_square,
|
|
gen_synth_pulse,
|
|
gen_synth_noise,
|
|
gen_synth_pwm_slow,
|
|
gen_synth_pwm_fast,
|
|
gen_synth_fm,
|
|
gen_synth_rhodes,
|
|
gen_synth_supersaw,
|
|
gen_synth_piano,
|
|
gen_synth_bass_guitar,
|
|
gen_synth_flute,
|
|
gen_synth_trumpet,
|
|
gen_synth_clarinet,
|
|
gen_synth_oboe,
|
|
gen_synth_cello,
|
|
gen_synth_harpsichord,
|
|
gen_synth_acoustic_guitar,
|
|
gen_synth_electric_guitar,
|
|
gen_synth_sitar,
|
|
gen_synth_kalimba,
|
|
gen_synth_wurlitzer,
|
|
gen_synth_vibraphone,
|
|
gen_synth_pipe_organ,
|
|
gen_synth_choir,
|
|
gen_synth_organ,
|
|
gen_synth_marimba,
|
|
gen_synth_harp,
|
|
gen_synth_upright_bass,
|
|
gen_synth_timpani,
|
|
gen_synth_strings,
|
|
gen_synth_saxophone,
|
|
gen_synth_pedal_steel,
|
|
gen_synth_theremin,
|
|
gen_synth_steel_drum,
|
|
gen_synth_accordion,
|
|
gen_synth_didgeridoo,
|
|
gen_synth_bagpipe,
|
|
gen_synth_banjo,
|
|
gen_synth_mandolin,
|
|
gen_synth_ukulele,
|
|
gen_synth_hard_sync,
|
|
gen_synth_ring_mod,
|
|
gen_synth_wavefold,
|
|
gen_synth_drift,
|
|
gen_synth_karplus,
|
|
gen_synth_mellotron,
|
|
gen_synth_granular,
|
|
gen_synth_crotales,
|
|
gen_synth_tingsha,
|
|
gen_synth_singing_bowl_strike,
|
|
gen_synth_singing_bowl_ring,
|
|
gen_rainstick,
|
|
gen_rainstick_slow,
|
|
gen_ocean_drum,
|
|
gen_cabasa,
|
|
gen_wind_chimes,
|
|
gen_finger_cymbal,
|
|
gen_arpeggio,
|
|
gen_legato_glide,
|
|
gen_acid_house,
|
|
gen_dub_reggae,
|
|
gen_jazz_ballad,
|
|
gen_quickstart,
|
|
gen_chords_basic,
|
|
gen_complete_rock,
|
|
gen_salsa_layered,
|
|
gen_playback_basic,
|
|
gen_song_sections,
|
|
]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("Generating audio samples for docs...")
|
|
print()
|
|
for gen in GENERATORS:
|
|
gen()
|
|
print()
|
|
print(f"Done. {len(GENERATORS)} files in {AUDIO_DIR}")
|