mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 14:50:18 +00:00
de112e0d9f
Harp: arpeggiated hold. Harpsichord: baroque runs. Guitars: strum chords. Piano: hold + melody. Cello: bowed ensemble=3. Sitar: drone under melody. Banjo/uke: strum. Mandolin: tremolo. Strings: ensemble=8. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
844 lines
32 KiB
Python
844 lines
32 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."""
|
|
# 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.2)
|
|
render("tabla", score)
|
|
|
|
|
|
# ── Marching snare ───────────────────────────────────────────────────────
|
|
|
|
def gen_metal_blast():
|
|
score = Score("4/4", bpm=200)
|
|
score.drums("metal blast", repeats=8, fill="metal cascade", fill_every=4)
|
|
render("metal_blast", score)
|
|
|
|
|
|
def gen_cajon():
|
|
score = Score("4/4", bpm=100)
|
|
score.drums("cajon", repeats=8, fill="cajon flam", fill_every=4)
|
|
render("cajon", score)
|
|
|
|
|
|
def gen_tabla_teental():
|
|
score = Score("4/4", bpm=160)
|
|
score.drums("teental", repeats=4)
|
|
score.set_drum_effects(reverb=0.2)
|
|
render("tabla_teental", score)
|
|
|
|
|
|
def gen_tabla_keherwa():
|
|
score = Score("4/4", bpm=180)
|
|
score.drums("keherwa", repeats=4, fill="chakkardar", fill_every=4)
|
|
score.set_drum_effects(reverb=0.2)
|
|
render("tabla_keherwa", score)
|
|
|
|
|
|
def gen_tabla_chakradar():
|
|
score = Score("4/4", bpm=200)
|
|
score.drums("teental", repeats=2)
|
|
score.drums("chakradar", repeats=1)
|
|
score.set_drum_effects(reverb=0.2)
|
|
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)
|
|
p = score.part("snare", synth="sine", volume=0.8, reverb=0.15)
|
|
S = DrumSound.MARCH_SNARE
|
|
R = DrumSound.MARCH_RIMSHOT
|
|
C = DrumSound.MARCH_CLICK
|
|
|
|
for _ in range(4):
|
|
p.hit(C, Duration.QUARTER, velocity=95)
|
|
|
|
for _ in range(4):
|
|
p.hit(R, Duration.SIXTEENTH, velocity=118)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
p.hit(R, Duration.SIXTEENTH, velocity=115)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
p.hit(R, Duration.SIXTEENTH, velocity=118)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=35)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
p.hit(R, Duration.SIXTEENTH, velocity=120)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=30)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=28)
|
|
p.hit(S, Duration.SIXTEENTH, velocity=32)
|
|
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="pluck", **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_fm():
|
|
_synth_demo("fm", "fm", envelope="piano")
|
|
|
|
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_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():
|
|
_synth_demo("timpani", "timpani_synth", envelope="none")
|
|
|
|
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=100)
|
|
p = score.part("demo", instrument="accordion", 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_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=100)
|
|
p = score.part("demo", instrument="bagpipe", volume=0.4, reverb=0.2)
|
|
for n in ["C4", "E4", "G4", "C5", "G4", "E4", "C4", "E4"]:
|
|
p.add(n, Duration.QUARTER, velocity=85)
|
|
render("synth_bagpipe", score)
|
|
|
|
def gen_synth_banjo():
|
|
from pytheory import Fretboard
|
|
score = Score("4/4", bpm=120)
|
|
p = score.part("demo", instrument="banjo", volume=0.5, reverb=0.15,
|
|
fretboard=Fretboard.guitar())
|
|
for ch in ["G", "C", "D", "G"]:
|
|
p.strum(ch, Duration.WHOLE, velocity=80)
|
|
render("synth_banjo", score)
|
|
|
|
def gen_synth_mandolin():
|
|
score = Score("4/4", bpm=120)
|
|
p = score.part("demo", instrument="mandolin", volume=0.5, reverb=0.2)
|
|
# Fast tremolo picking
|
|
for n in ["G4", "G4", "A4", "A4", "B4", "B4", "C5", "C5",
|
|
"D5", "D5", "C5", "C5", "B4", "B4", "A4", "G4"]:
|
|
p.add(n, Duration.EIGHTH, velocity=82)
|
|
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_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_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=600)
|
|
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_fm,
|
|
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_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_granular,
|
|
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}")
|