mirror of
https://github.com/kennethreitz/interpretations.git
synced 2026-06-17 13:21:01 +00:00
00fc35de19
Every track: tuple-unpacked scale degrees, small local helpers (rest_bars, chord_bars, play_phrase), data-driven drum patterns and phrase tuples, sparse-event dicts, explicit velocity lists for fades, dead code removed. Net -1,415 lines across 25 files. Adds .fingerprint.py, a verification harness that hashes every audible parameter of a score (notes, voicings, velocities, bends, drum hits, LFO automation, part settings). All 25 tracks fingerprint identical to their pre-refactor baselines, stored in .fingerprints/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
456 lines
16 KiB
Python
456 lines
16 KiB
Python
"""
|
|
BEAST MODE — 135 BPM, no mercy.
|
|
Trap drums, 808 slides, distorted saw, sitar hook, mellotron drop,
|
|
timpani rolls. The hardest track on the album.
|
|
G minor, 135 BPM.
|
|
"""
|
|
|
|
from pytheory import Key, Duration, Score, play_score
|
|
from pytheory.rhythm import DrumSound
|
|
|
|
key = Key("G", "minor")
|
|
s = key.scale # G A Bb C D Eb F
|
|
|
|
G, A, Bb, C, D, Eb, F = s[:7]
|
|
|
|
score = Score("4/4", bpm=135)
|
|
|
|
K = DrumSound.KICK
|
|
S = DrumSound.SNARE
|
|
CH = DrumSound.CLOSED_HAT
|
|
OH = DrumSound.OPEN_HAT
|
|
|
|
prog = key.progression("i", "VII", "VI", "iv")
|
|
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
# STRUCTURE (80 bars, ~3:33):
|
|
# Bars 1-4: 808 alone — the warning
|
|
# Bars 5-8: Kick + hats drop — we're moving
|
|
# Bars 9-16: Sitar hook — the sample flip
|
|
# Bars 17-24: Full beat — saw bass, everything locked
|
|
# Bars 25-32: Mellotron choir drop — the heavens open
|
|
# Bars 33-40: Breakdown — just 808 + hats, tension
|
|
# Bars 41-48: Timpani build — the war drums
|
|
# Bars 49-56: BEAST MODE — everything, max aggression
|
|
# Bars 57-64: Sitar solo — shredding over the beat
|
|
# Bars 65-72: Last drop — mellotron + timpani + 808
|
|
# Bars 73-80: Outro — 808 slides into silence
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
|
|
|
|
def rest_bars(part, n):
|
|
for _ in range(n):
|
|
part.rest(Duration.WHOLE)
|
|
|
|
|
|
def chord_bars(part, chords, vel, repeats=1):
|
|
for _ in range(repeats):
|
|
for chord in chords:
|
|
part.add(chord, Duration.WHOLE, velocity=vel)
|
|
|
|
|
|
def play_phrase(part, phrase, repeats=1):
|
|
for _ in range(repeats):
|
|
for note, dur, vel in phrase:
|
|
if note is None:
|
|
part.rest(dur)
|
|
else:
|
|
part.add(note, dur, velocity=vel)
|
|
|
|
|
|
# ── 808 — the foundation, slides between roots ────────────────
|
|
sub = score.part("808", synth="sine", envelope="pad", volume=0.65,
|
|
lowpass=220, distortion=0.25, distortion_drive=3.5,
|
|
sub_osc=0.5, saturation=0.4, sidechain=0.5)
|
|
|
|
# Bars 1-4: alone — just the 808, announces itself
|
|
sub.add(G.add(-24), Duration.WHOLE, velocity=35)
|
|
sub.add(G.add(-24), Duration.WHOLE, velocity=38)
|
|
sub.add(G.add(-24), Duration.HALF, velocity=40)
|
|
sub.add(Bb.add(-24), Duration.HALF, velocity=38)
|
|
sub.add(G.add(-24), Duration.WHOLE, velocity=42)
|
|
|
|
# Bars 5-72: follows roots, slides between chords
|
|
roots_pattern = [
|
|
(G.add(-24), 40), (G.add(-24), 38),
|
|
(F.add(-24), 40), (G.add(-24), 42),
|
|
(Eb.add(-24), 38), (F.add(-24), 40),
|
|
(D.add(-24), 42), (G.add(-24), 40),
|
|
]
|
|
for _ in range(8):
|
|
for root, vel in roots_pattern:
|
|
sub.add(root, Duration.WHOLE, velocity=vel)
|
|
|
|
# Bars 73-80: slides down — dying
|
|
for root, vel in [(G.add(-24), 38), (F.add(-24), 35),
|
|
(Eb.add(-24), 32), (D.add(-24), 28),
|
|
(C.add(-24), 25), (Bb.add(-24), 22),
|
|
(A.add(-24), 18), (G.add(-36), 12)]:
|
|
sub.add(root, Duration.WHOLE, velocity=vel)
|
|
|
|
# ── KICK — hard, distorted ───────────────────────────────────
|
|
kick = score.part("kick", volume=0.85, humanize=0.02,
|
|
distortion=0.12, distortion_drive=2.0)
|
|
|
|
rest_bars(kick, 4)
|
|
|
|
# Bars 5-32: trap kick pattern — syncopated, heavy
|
|
for _ in range(28):
|
|
kick.hit(K, Duration.QUARTER, velocity=118)
|
|
kick.rest(Duration.EIGHTH)
|
|
kick.hit(K, Duration.EIGHTH, velocity=98)
|
|
kick.hit(K, Duration.QUARTER, velocity=112)
|
|
kick.rest(Duration.EIGHTH)
|
|
kick.hit(K, Duration.EIGHTH, velocity=95)
|
|
|
|
# Bars 33-40: breakdown — half time
|
|
for _ in range(8):
|
|
kick.hit(K, Duration.HALF, velocity=115)
|
|
kick.rest(Duration.HALF)
|
|
|
|
# Bars 41-48: build — kick gets busier
|
|
for _ in range(8):
|
|
kick.hit(K, Duration.QUARTER, velocity=118)
|
|
kick.hit(K, Duration.EIGHTH, velocity=100)
|
|
kick.hit(K, Duration.EIGHTH, velocity=95)
|
|
kick.hit(K, Duration.QUARTER, velocity=115)
|
|
kick.hit(K, Duration.EIGHTH, velocity=102)
|
|
kick.hit(K, Duration.EIGHTH, velocity=108)
|
|
|
|
# Bars 49-72: BEAST MODE + sitar solo + last drop
|
|
for _ in range(24):
|
|
kick.hit(K, Duration.QUARTER, velocity=122)
|
|
kick.rest(Duration.EIGHTH)
|
|
kick.hit(K, Duration.EIGHTH, velocity=105)
|
|
kick.hit(K, Duration.QUARTER, velocity=118)
|
|
kick.rest(Duration.EIGHTH)
|
|
kick.hit(K, Duration.EIGHTH, velocity=100)
|
|
|
|
# Bars 73-80: fading
|
|
for vel in [110, 95, 80, 65, 50, 35, 20, 0]:
|
|
if vel > 0:
|
|
kick.hit(K, Duration.QUARTER, velocity=vel)
|
|
kick.rest(Duration.DOTTED_HALF)
|
|
else:
|
|
kick.rest(Duration.WHOLE)
|
|
|
|
# ── SNARE — crack ────────────────────────────────────────────
|
|
snare = score.part("snare", volume=0.5, humanize=0.03,
|
|
reverb=0.12, distortion=0.08, distortion_drive=1.5,
|
|
delay=0.05, delay_time=0.222, delay_feedback=0.08,
|
|
pan=0.05)
|
|
|
|
rest_bars(snare, 4)
|
|
|
|
# Bars 5-32: 2 and 4
|
|
for _ in range(28):
|
|
snare.rest(Duration.QUARTER)
|
|
snare.hit(S, Duration.QUARTER, velocity=108)
|
|
snare.rest(Duration.QUARTER)
|
|
snare.hit(S, Duration.QUARTER, velocity=110)
|
|
|
|
# Bars 33-40: breakdown — half time snare
|
|
for _ in range(8):
|
|
snare.rest(Duration.HALF)
|
|
snare.hit(S, Duration.HALF, velocity=112)
|
|
|
|
# Bars 41-48: rolls building
|
|
for bar in range(8):
|
|
if bar >= 6:
|
|
# 16th note roll
|
|
for i in range(16):
|
|
snare.hit(S, Duration.SIXTEENTH, velocity=min(115, 70 + i * 3))
|
|
else:
|
|
snare.rest(Duration.QUARTER)
|
|
snare.hit(S, Duration.QUARTER, velocity=108)
|
|
snare.rest(Duration.QUARTER)
|
|
snare.hit(S, Duration.QUARTER, velocity=110)
|
|
|
|
# Bars 49-72: 2 and 4, harder
|
|
for _ in range(24):
|
|
snare.rest(Duration.QUARTER)
|
|
snare.hit(S, Duration.QUARTER, velocity=115)
|
|
snare.rest(Duration.QUARTER)
|
|
snare.hit(S, Duration.QUARTER, velocity=118)
|
|
|
|
# Bars 73-80: fading
|
|
for vel in [105, 90, 75, 58, 42, 25, 0, 0]:
|
|
if vel > 0:
|
|
snare.rest(Duration.QUARTER)
|
|
snare.hit(S, Duration.QUARTER, velocity=vel)
|
|
snare.rest(Duration.HALF)
|
|
else:
|
|
snare.rest(Duration.WHOLE)
|
|
|
|
# ── HATS — trap style, evolving ──────────────────────────────
|
|
hats = score.part("hats", volume=0.3, pan=0.3, humanize=0.04)
|
|
|
|
rest_bars(hats, 4)
|
|
|
|
# Bars 5-16: 16ths with ghost notes
|
|
for _ in range(12):
|
|
for beat in range(4):
|
|
for vel in (72, 38, 55, 35):
|
|
hats.hit(CH, Duration.SIXTEENTH, velocity=vel)
|
|
|
|
# Bars 17-32: more active — open hat accents
|
|
active_hats = [
|
|
(CH, 75), (CH, 40), (OH, 62), (CH, 35),
|
|
(CH, 72), (CH, 42), (CH, 58), (CH, 38),
|
|
(CH, 75), (CH, 40), (CH, 55), (CH, 35),
|
|
(OH, 60), (CH, 38), (CH, 52), (CH, 35),
|
|
]
|
|
for _ in range(16):
|
|
for sound, vel in active_hats:
|
|
hats.hit(sound, Duration.SIXTEENTH, velocity=vel)
|
|
|
|
# Bars 33-40: breakdown — sparse
|
|
for _ in range(8):
|
|
hats.hit(CH, Duration.QUARTER, velocity=55)
|
|
hats.rest(Duration.QUARTER)
|
|
hats.hit(CH, Duration.QUARTER, velocity=48)
|
|
hats.rest(Duration.QUARTER)
|
|
|
|
# Bars 41-48: build — 32nd note rolls
|
|
for bar in range(8):
|
|
if bar % 2 == 1:
|
|
for i in range(32):
|
|
hats.hit(CH, 0.125, velocity=min(82, 38 + i * 2))
|
|
else:
|
|
for beat in range(4):
|
|
for vel in (72, 40, 58, 35):
|
|
hats.hit(CH, Duration.SIXTEENTH, velocity=vel)
|
|
|
|
# Bars 49-72: BEAST MODE — full trap hats
|
|
beast_hats = [
|
|
(CH, 78), (CH, 42), (OH, 65), (CH, 38),
|
|
(CH, 75), (CH, 40), (CH, 58), (CH, 42),
|
|
(CH, 78), (CH, 40), (CH, 55), (CH, 35),
|
|
(OH, 62), (CH, 38), (CH, 55), (CH, 40),
|
|
]
|
|
for bar in range(24):
|
|
if bar % 4 == 3:
|
|
for i in range(32):
|
|
hats.hit(CH, 0.125, velocity=min(85, 40 + i * 2))
|
|
else:
|
|
for sound, vel in beast_hats:
|
|
hats.hit(sound, Duration.SIXTEENTH, velocity=vel)
|
|
|
|
# Bars 73-80: fading
|
|
for vel in [65, 55, 45, 35, 25, 15, 0, 0]:
|
|
if vel > 0:
|
|
for beat in range(4):
|
|
hats.hit(CH, Duration.EIGHTH, velocity=vel)
|
|
hats.hit(CH, Duration.EIGHTH, velocity=max(12, vel - 25))
|
|
else:
|
|
hats.rest(Duration.WHOLE)
|
|
|
|
# ── SITAR — the hook, enters bar 9 ───────────────────────────
|
|
sitar = score.part("sitar", instrument="sitar", volume=0.65,
|
|
reverb=0.2, reverb_type="taj_mahal",
|
|
delay=0.15, delay_time=0.222, delay_feedback=0.2,
|
|
pan=-0.3, humanize=0.08)
|
|
|
|
rest_bars(sitar, 8)
|
|
|
|
# Bars 9-16: THE HOOK — dark, catchy, repeating
|
|
hook = [
|
|
(G, Duration.EIGHTH, 82), (Bb, Duration.EIGHTH, 75),
|
|
(D, Duration.QUARTER, 85), (C, Duration.EIGHTH, 72),
|
|
(Bb, Duration.EIGHTH, 70), (A, Duration.QUARTER, 78),
|
|
(G, Duration.EIGHTH, 80), (None, Duration.EIGHTH, 0),
|
|
(F, Duration.EIGHTH, 72), (G, Duration.EIGHTH, 78),
|
|
(None, Duration.QUARTER, 0),
|
|
(Bb, Duration.EIGHTH, 75), (A, Duration.EIGHTH, 70),
|
|
(G, Duration.HALF, 80),
|
|
]
|
|
play_phrase(sitar, hook, repeats=4)
|
|
|
|
# Bars 17-24: hook continues under full beat
|
|
play_phrase(sitar, [(n, d, max(40, v - 5)) for n, d, v in hook], repeats=4)
|
|
|
|
# Bars 25-32: drops volume — mellotron takes focus
|
|
sitar.set(volume=0.3)
|
|
play_phrase(sitar, [(n, d, max(35, v - 15)) for n, d, v in hook], repeats=4)
|
|
|
|
# Bars 33-48: silent through breakdown and build
|
|
rest_bars(sitar, 16)
|
|
|
|
# Bars 49-56: hook returns HARD
|
|
sitar.set(volume=0.55)
|
|
play_phrase(sitar, [(n, d, min(100, v + 8)) for n, d, v in hook], repeats=4)
|
|
|
|
# Bars 57-64: SITAR SOLO — shredding over the beat
|
|
sitar.set(volume=0.65)
|
|
# 16th arps
|
|
arp_i = [G, Bb, D, Bb, G, D.add(-12), Bb.add(-12), D.add(-12)]
|
|
arp_vii = [F, A, C, A, F, C.add(-12), A.add(-12), C.add(-12)]
|
|
for _ in range(2):
|
|
for arp, vel in ((arp_i, 95), (arp_vii, 92)):
|
|
for note in arp:
|
|
sitar.add(note, Duration.SIXTEENTH, velocity=vel)
|
|
# 32nd shred
|
|
for note in [G, A, Bb, C, D, Eb, F, G.add(12),
|
|
F, Eb, D, C, Bb, A, G, A]:
|
|
sitar.add(note, 0.125, velocity=108)
|
|
for note in [G, D, G.add(12), D, Bb, G, D, Bb.add(-12),
|
|
G, C, Eb, G.add(12), Eb, C, G, Eb.add(-12)]:
|
|
sitar.add(note, 0.125, velocity=112)
|
|
# The bends — snap hard, big dynamics
|
|
sitar.add(G.add(12), Duration.SIXTEENTH, velocity=118, bend=-2.0)
|
|
sitar.add(D, Duration.SIXTEENTH, velocity=75, bend=1.5)
|
|
sitar.add(G.add(12), Duration.SIXTEENTH, velocity=120, bend=-1.5)
|
|
sitar.add(Bb, Duration.SIXTEENTH, velocity=70, bend=1.0)
|
|
sitar.add(D, Duration.SIXTEENTH, velocity=115, bend=-2.0)
|
|
sitar.add(G, Duration.SIXTEENTH, velocity=65)
|
|
sitar.add(G.add(12), Duration.EIGHTH, velocity=125, bend=-2.5)
|
|
sitar.rest(Duration.QUARTER)
|
|
sitar.rest(Duration.WHOLE)
|
|
|
|
# Bars 65-72: hook one more time
|
|
sitar.set(volume=0.5)
|
|
play_phrase(sitar, hook, repeats=4)
|
|
|
|
# Bars 73-80: fading
|
|
play_phrase(sitar, [(n, d, max(20, v - 25)) for n, d, v in hook], repeats=4)
|
|
rest_bars(sitar, 4)
|
|
|
|
# ── SAW BASS — distorted, enters bar 17 ──────────────────────
|
|
saw = score.part("saw_bass", synth="saw", volume=0.35,
|
|
lowpass=1200,
|
|
distortion=0.3, distortion_drive=4.0,
|
|
saturation=0.6, legato=True, glide=0.01,
|
|
reverb=0.1, reverb_type="spring",
|
|
delay=0.08, delay_time=0.222, delay_feedback=0.1,
|
|
sidechain=0.35, pan=0.3)
|
|
|
|
rest_bars(saw, 16)
|
|
|
|
# Bars 17-32: aggressive bass line — accents on 1, ghosts between
|
|
saw_line = [
|
|
(G.add(-12), Duration.SIXTEENTH, 100), (None, Duration.SIXTEENTH, 0),
|
|
(G.add(-12), Duration.SIXTEENTH, 65), (Bb.add(-12), Duration.SIXTEENTH, 60),
|
|
(G.add(-12), Duration.SIXTEENTH, 95), (None, Duration.SIXTEENTH, 0),
|
|
(F.add(-12), Duration.EIGHTH, 58),
|
|
(G.add(-12), Duration.SIXTEENTH, 98), (D.add(-12), Duration.SIXTEENTH, 62),
|
|
(G.add(-12), Duration.SIXTEENTH, 68), (None, Duration.SIXTEENTH, 0),
|
|
(Eb.add(-12), Duration.EIGHTH, 55),
|
|
(G.add(-12), Duration.EIGHTH, 92),
|
|
]
|
|
play_phrase(saw, saw_line, repeats=16)
|
|
|
|
# Bars 33-40: silent — breakdown
|
|
rest_bars(saw, 8)
|
|
|
|
# Bars 41-48: returns for build
|
|
play_phrase(saw, saw_line, repeats=8)
|
|
|
|
# Bars 49-72: full power
|
|
saw.set(volume=0.4)
|
|
play_phrase(saw, [(n, d, min(105, v + 5)) for n, d, v in saw_line], repeats=24)
|
|
|
|
# Bars 73-80: fading
|
|
saw.set(volume=0.2)
|
|
play_phrase(saw, [(n, d, max(25, v - 25)) for n, d, v in saw_line], repeats=4)
|
|
rest_bars(saw, 4)
|
|
|
|
# ── MELLOTRON CHOIR — the drop, enters bar 25 ────────────────
|
|
mello = score.part("mellotron", instrument="mellotron_flute", volume=0.35,
|
|
reverb=0.45, reverb_type="taj_mahal",
|
|
chorus=0.2, chorus_rate=0.06, chorus_depth=0.01,
|
|
pan=-0.15)
|
|
|
|
rest_bars(mello, 24)
|
|
|
|
# Bars 25-32: THE DROP — choir chords, heavens open
|
|
chord_bars(mello, prog, 55, repeats=2)
|
|
|
|
# Bars 33-48: silent
|
|
rest_bars(mello, 16)
|
|
|
|
# Bars 49-56: back for beast mode
|
|
chord_bars(mello, prog, 58, repeats=2)
|
|
|
|
# Bars 57-64: under sitar solo
|
|
chord_bars(mello, prog, 50, repeats=2)
|
|
|
|
# Bars 65-72: last drop — full
|
|
mello.set(volume=0.28)
|
|
chord_bars(mello, prog, 62, repeats=2)
|
|
|
|
# Bars 73-80: fading
|
|
for vel in [52, 42, 35, 28, 20, 12, 0, 0]:
|
|
if vel > 0:
|
|
mello.add(prog[0], Duration.WHOLE, velocity=vel)
|
|
else:
|
|
mello.rest(Duration.WHOLE)
|
|
|
|
# ── TIMPANI — war drums, bars 41-48 and 65-72 ────────────────
|
|
timp = score.part("timpani", instrument="timpani", volume=0.4,
|
|
reverb=0.25, reverb_type="cathedral",
|
|
delay=0.06, delay_time=0.222, delay_feedback=0.08,
|
|
pan=-0.05)
|
|
|
|
rest_bars(timp, 40)
|
|
|
|
# Bars 41-48: the build — war drums, 16th rolls crescendo
|
|
for bar in range(8):
|
|
if bar < 4:
|
|
timp.add(G.add(-12), Duration.QUARTER, velocity=min(85, 60 + bar * 8))
|
|
timp.rest(Duration.DOTTED_HALF)
|
|
else:
|
|
for i in range(16):
|
|
timp.add(G.add(-12), Duration.SIXTEENTH, velocity=min(98, 55 + i * 3))
|
|
|
|
# Bars 49-64: sparse hits with the beat
|
|
for _ in range(16):
|
|
timp.add(G.add(-12), Duration.QUARTER, velocity=72)
|
|
timp.rest(Duration.DOTTED_HALF)
|
|
|
|
# Bars 65-72: last drop — full rolls
|
|
for bar in range(8):
|
|
if bar % 2 == 1:
|
|
for i in range(16):
|
|
timp.add(G.add(-12), Duration.SIXTEENTH, velocity=min(95, 52 + i * 3))
|
|
else:
|
|
timp.add(G.add(-12), Duration.HALF, velocity=78)
|
|
timp.rest(Duration.HALF)
|
|
|
|
# Bars 73-80: fading
|
|
for vel in [65, 52, 40, 28, 0, 0, 0, 0]:
|
|
if vel > 0:
|
|
timp.add(G.add(-12), Duration.QUARTER, velocity=vel)
|
|
timp.rest(Duration.DOTTED_HALF)
|
|
else:
|
|
timp.rest(Duration.WHOLE)
|
|
|
|
# ── SINGING BOWL — section markers ───────────────────────────
|
|
bowl = score.part("bowl", instrument="singing_bowl", volume=0.25,
|
|
reverb=0.5, reverb_type="taj_mahal",
|
|
delay=0.12, delay_time=0.444, delay_feedback=0.15,
|
|
pan=0.2)
|
|
|
|
markers = {1: 60, 9: 55, 25: 62, 33: 50, 49: 65, 65: 60, 73: 48}
|
|
for bar in range(1, 81):
|
|
if bar in markers:
|
|
bowl.add(G.add(-24), Duration.WHOLE, velocity=markers[bar])
|
|
else:
|
|
bowl.rest(Duration.WHOLE)
|
|
|
|
# ═════════════════════════════════════════════════════════════════
|
|
import sys
|
|
|
|
print(f"Key: {key}")
|
|
print(f"BPM: 135")
|
|
print(f"Parts: {list(score.parts.keys())}")
|
|
print(f"Duration: {score.duration_ms / 1000:.1f}s | {score.measures} measures")
|
|
|
|
if "--live" in sys.argv:
|
|
print("Playing BEAST MODE (live engine)...")
|
|
from pytheory_live.live import LiveEngine
|
|
engine = LiveEngine(buffer_size=1024)
|
|
engine.play_score(score)
|
|
else:
|
|
print("Playing BEAST MODE...")
|
|
play_score(score)
|