mirror of
https://github.com/kennethreitz/interpretations.git
synced 2026-06-05 06:46:15 +00:00
04d13255e1
Ghost Protocol: arp → drift (analog wobble) Deep Time: shimmer → drift, added granular_pad grain layer Voltage: added hard_sync part at bar 49 The Interruption: reese bass → drift (analog menace) An Exception Occurred: added ring_mod during psychosis Intrusive: thought → wavefold with organ envelope (uglier, harder) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
333 lines
13 KiB
Python
333 lines
13 KiB
Python
"""
|
|
INTRUSIVE — the thought you can't stop thinking.
|
|
One phrase. Over and over. You try to play something else.
|
|
It comes back. It always comes back.
|
|
Until you learn to let it pass.
|
|
Bb minor, 92 BPM.
|
|
"""
|
|
|
|
from pytheory import Key, Duration, Score, Tone, play_score
|
|
from pytheory.rhythm import DrumSound
|
|
|
|
key = Key("Bb", "minor")
|
|
s = key.scale # Bb C Db Eb F Gb Ab
|
|
|
|
Bb = s[0]; C = s[1]; Db = s[2]; Eb = s[3]
|
|
F = s[4]; Gb = s[5]; Ab = s[6]
|
|
|
|
score = Score("4/4", bpm=92)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
# STRUCTURE (56 bars, ~2:26):
|
|
# Bars 1-8: THE THOUGHT — one piano phrase, repeating
|
|
# Bars 9-16: TRYING — Rhodes plays something different. The thought returns.
|
|
# Bars 17-24: FIGHTING — drums try to drown it. The thought is louder.
|
|
# Bars 25-32: SPIRALING — the thought fragments, multiplies, distorts
|
|
# Bars 33-40: ACCEPTING — stop fighting. Let it play. Something changes.
|
|
# Bars 41-48: RELEASING — the thought slows down. Spaces between.
|
|
# Bars 49-56: PASSING — it plays once more. Quiet. And doesn't come back.
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
|
|
# The intrusive thought — this exact phrase, burned into your brain
|
|
THOUGHT = [
|
|
(Bb, Duration.EIGHTH, 72), (Db, Duration.EIGHTH, 68),
|
|
(F, Duration.QUARTER, 75), (Eb, Duration.EIGHTH, 65),
|
|
(Db, Duration.EIGHTH, 62), (Bb, Duration.QUARTER, 70),
|
|
]
|
|
|
|
def play_thought(part, vel_offset=0, rest_after=Duration.WHOLE):
|
|
"""The thought. Always the same. Always unwanted."""
|
|
for note, dur, vel in THOUGHT:
|
|
part.add(note, dur, velocity=min(127, max(15, vel + vel_offset)))
|
|
part.rest(rest_after)
|
|
|
|
|
|
# ── THE THOUGHT — piano, the thing you can't unthink ──────────
|
|
thought = score.part("thought", synth="wavefold", envelope="organ", volume=0.4,
|
|
lowpass=3000, distortion=0.1, distortion_drive=1.5,
|
|
saturation=0.4, legato=True, glide=0.03,
|
|
reverb=0.25, reverb_type="spring",
|
|
delay=0.12, delay_time=0.326, delay_feedback=0.15,
|
|
pan=-0.1, humanize=0.06)
|
|
|
|
# Bars 1-8: repeating. and repeating. and repeating.
|
|
for _ in range(4):
|
|
play_thought(thought)
|
|
|
|
# Bars 9-12: it keeps going while Rhodes tries
|
|
for _ in range(2):
|
|
play_thought(thought)
|
|
|
|
# Bars 13-16: louder — you noticed it and now it's worse
|
|
for _ in range(2):
|
|
play_thought(thought, vel_offset=8)
|
|
|
|
# Bars 17-20: drums enter, thought persists
|
|
for _ in range(2):
|
|
play_thought(thought, vel_offset=5)
|
|
|
|
# Bars 21-24: even louder — fighting it makes it stronger
|
|
thought.set(volume=0.6)
|
|
for _ in range(2):
|
|
play_thought(thought, vel_offset=12)
|
|
|
|
# Bars 25-28: SPIRALING — the thought starts fragmenting, distorting
|
|
thought.set(volume=0.55, reverb=0.5)
|
|
# Fragment 1: just the first three notes, repeated
|
|
for _ in range(3):
|
|
thought.add(Bb, Duration.EIGHTH, velocity=78)
|
|
thought.add(Db, Duration.EIGHTH, velocity=72)
|
|
thought.add(F, Duration.QUARTER, velocity=80)
|
|
thought.rest(Duration.QUARTER)
|
|
# Fragment 2: the ending, wrong rhythm
|
|
thought.add(Eb, Duration.SIXTEENTH, velocity=70)
|
|
thought.add(Db, Duration.SIXTEENTH, velocity=65)
|
|
thought.add(Bb, Duration.EIGHTH, velocity=72)
|
|
thought.rest(Duration.HALF)
|
|
thought.add(Eb, Duration.SIXTEENTH, velocity=72)
|
|
thought.add(Db, Duration.SIXTEENTH, velocity=68)
|
|
thought.add(Bb, Duration.EIGHTH, velocity=75)
|
|
thought.rest(Duration.QUARTER)
|
|
thought.add(F, Duration.QUARTER, velocity=78)
|
|
|
|
# Bars 29-32: the thought in wrong octaves, displaced
|
|
thought.add(Bb.add(12), Duration.EIGHTH, velocity=75)
|
|
thought.add(Db.add(12), Duration.EIGHTH, velocity=70)
|
|
thought.add(F.add(12), Duration.QUARTER, velocity=78)
|
|
thought.add(Eb, Duration.EIGHTH, velocity=62)
|
|
thought.add(Db, Duration.EIGHTH, velocity=58)
|
|
thought.add(Bb.add(-12), Duration.QUARTER, velocity=68)
|
|
thought.rest(Duration.HALF)
|
|
# Overlapping — like it's echoing inside your skull
|
|
thought.add(Bb, Duration.EIGHTH, velocity=70)
|
|
thought.add(Db, Duration.EIGHTH, velocity=65)
|
|
thought.add(F, Duration.QUARTER, velocity=72)
|
|
thought.add(Bb.add(12), Duration.EIGHTH, velocity=68)
|
|
thought.add(Db.add(12), Duration.EIGHTH, velocity=62)
|
|
thought.add(F, Duration.QUARTER, velocity=70)
|
|
thought.rest(Duration.WHOLE)
|
|
|
|
# Bars 33-40: ACCEPTING — stop fighting. The thought plays but softer.
|
|
thought.set(volume=0.4, reverb=0.3)
|
|
for _ in range(2):
|
|
play_thought(thought, vel_offset=-5)
|
|
# Slower now — gaps between repetitions
|
|
play_thought(thought, vel_offset=-10, rest_after=Duration.WHOLE)
|
|
thought.rest(Duration.WHOLE)
|
|
play_thought(thought, vel_offset=-15, rest_after=Duration.WHOLE)
|
|
thought.rest(Duration.WHOLE)
|
|
|
|
# Bars 41-48: RELEASING — more space, softer each time
|
|
thought.set(volume=0.3)
|
|
play_thought(thought, vel_offset=-20, rest_after=Duration.WHOLE)
|
|
thought.rest(Duration.WHOLE)
|
|
thought.rest(Duration.WHOLE)
|
|
play_thought(thought, vel_offset=-28, rest_after=Duration.WHOLE)
|
|
thought.rest(Duration.WHOLE)
|
|
thought.rest(Duration.WHOLE)
|
|
thought.rest(Duration.WHOLE)
|
|
|
|
# Bars 49-52: PASSING — one last time. Barely there.
|
|
thought.set(volume=0.2)
|
|
play_thought(thought, vel_offset=-40, rest_after=Duration.WHOLE)
|
|
|
|
# Bars 53-56: silence. it passed.
|
|
for _ in range(4):
|
|
thought.rest(Duration.WHOLE)
|
|
|
|
# ── RHODES — what you're trying to think instead ──────────────
|
|
rhodes = score.part("rhodes", instrument="electric_piano", volume=0.35,
|
|
reverb=0.45, reverb_type="taj_mahal",
|
|
delay=0.12, delay_time=0.326, delay_feedback=0.15,
|
|
tremolo_depth=0.1, tremolo_rate=2.5,
|
|
pan=0.2, humanize=0.1)
|
|
|
|
for _ in range(8):
|
|
rhodes.rest(Duration.WHOLE)
|
|
|
|
# Bars 9-12: tries to play its own melody — interrupted
|
|
rhodes.add(Db, Duration.QUARTER, velocity=60)
|
|
rhodes.add(Eb, Duration.QUARTER, velocity=55)
|
|
rhodes.add(F, Duration.HALF, velocity=62)
|
|
# interrupted — rest while thought plays
|
|
rhodes.rest(Duration.WHOLE)
|
|
rhodes.add(Ab, Duration.QUARTER, velocity=58)
|
|
rhodes.add(Gb, Duration.QUARTER, velocity=55)
|
|
rhodes.add(F, Duration.HALF, velocity=60)
|
|
rhodes.rest(Duration.WHOLE)
|
|
|
|
# Bars 13-16: tries again — longer phrases
|
|
rhodes.add(Db, Duration.QUARTER, velocity=62)
|
|
rhodes.add(Eb, Duration.QUARTER, velocity=58)
|
|
rhodes.add(F, Duration.QUARTER, velocity=65)
|
|
rhodes.add(Ab, Duration.QUARTER, velocity=60)
|
|
rhodes.add(Gb, Duration.HALF, velocity=62)
|
|
rhodes.add(F, Duration.HALF, velocity=58)
|
|
rhodes.rest(Duration.WHOLE)
|
|
rhodes.rest(Duration.WHOLE)
|
|
|
|
# Bars 17-24: keeps trying through the drums
|
|
for _ in range(2):
|
|
rhodes.add(Db, Duration.QUARTER, velocity=58)
|
|
rhodes.add(Eb, Duration.QUARTER, velocity=55)
|
|
rhodes.add(F, Duration.HALF, velocity=60)
|
|
rhodes.add(Ab, Duration.QUARTER, velocity=55)
|
|
rhodes.add(Gb, Duration.EIGHTH, velocity=50)
|
|
rhodes.add(F, Duration.EIGHTH, velocity=48)
|
|
rhodes.add(Eb, Duration.HALF, velocity=55)
|
|
rhodes.rest(Duration.WHOLE)
|
|
rhodes.rest(Duration.WHOLE)
|
|
|
|
# Bars 25-32: gives up — sparse, defeated
|
|
for _ in range(4):
|
|
rhodes.add(Db, Duration.QUARTER, velocity=45)
|
|
rhodes.rest(Duration.DOTTED_HALF)
|
|
rhodes.rest(Duration.WHOLE)
|
|
|
|
# Bars 33-40: acceptance — Rhodes and thought coexist
|
|
rhodes.set(volume=0.4)
|
|
prog_chords = key.progression("i", "VI", "iv", "v")
|
|
for _ in range(2):
|
|
for chord in prog_chords:
|
|
rhodes.add(chord, Duration.WHOLE, velocity=52)
|
|
|
|
# Bars 41-48: Rhodes grows — it's winning, gently
|
|
rhodes.set(volume=0.5)
|
|
rhodes.add(Db, Duration.QUARTER, velocity=65)
|
|
rhodes.add(Eb, Duration.QUARTER, velocity=60)
|
|
rhodes.add(F, Duration.HALF, velocity=68)
|
|
rhodes.add(Ab, Duration.QUARTER, velocity=62)
|
|
rhodes.add(Gb, Duration.EIGHTH, velocity=58)
|
|
rhodes.add(F, Duration.EIGHTH, velocity=55)
|
|
rhodes.add(Eb, Duration.HALF, velocity=60)
|
|
rhodes.rest(Duration.QUARTER)
|
|
rhodes.add(Bb.add(-12), Duration.QUARTER, velocity=55)
|
|
rhodes.add(Db, Duration.QUARTER, velocity=60)
|
|
rhodes.add(F, Duration.QUARTER, velocity=58)
|
|
rhodes.add(Ab, Duration.HALF, velocity=65)
|
|
rhodes.add(Gb, Duration.HALF, velocity=60)
|
|
rhodes.add(F, Duration.WHOLE, velocity=62)
|
|
rhodes.rest(Duration.WHOLE)
|
|
|
|
# Bars 49-56: Rhodes owns the space now — peaceful
|
|
rhodes.add(Db, Duration.QUARTER, velocity=62)
|
|
rhodes.add(Eb, Duration.QUARTER, velocity=58)
|
|
rhodes.add(F, Duration.HALF, velocity=65)
|
|
rhodes.add(Eb, Duration.QUARTER, velocity=58)
|
|
rhodes.add(Db, Duration.QUARTER, velocity=55)
|
|
rhodes.add(Bb.add(-12), Duration.HALF, velocity=60)
|
|
rhodes.add(Bb.add(-12), Duration.WHOLE, velocity=55)
|
|
for _ in range(5):
|
|
rhodes.rest(Duration.WHOLE)
|
|
|
|
# ── DRUMS — trying to drown the thought, bars 17-32 ──────────
|
|
K = DrumSound.KICK
|
|
S = DrumSound.SNARE
|
|
CH = DrumSound.CLOSED_HAT
|
|
|
|
drums = score.part("drums", volume=0.35, humanize=0.06,
|
|
reverb=0.2, reverb_decay=0.8,
|
|
delay=0.08, delay_time=0.326, delay_feedback=0.12,
|
|
pan=-0.05)
|
|
|
|
for _ in range(16):
|
|
drums.rest(Duration.WHOLE)
|
|
|
|
# Bars 17-24: trying to overpower it
|
|
for _ in range(8):
|
|
drums.hit(K, Duration.QUARTER, velocity=90)
|
|
drums.hit(CH, Duration.EIGHTH, velocity=55)
|
|
drums.hit(CH, Duration.EIGHTH, velocity=42)
|
|
drums.hit(S, Duration.QUARTER, velocity=88)
|
|
drums.hit(CH, Duration.EIGHTH, velocity=50)
|
|
drums.hit(K, Duration.EIGHTH, velocity=78)
|
|
|
|
# Bars 25-32: drums get frantic — can't overpower it
|
|
for bar in range(8):
|
|
if bar % 4 == 3:
|
|
# Frustrated fill
|
|
for i in range(8):
|
|
drums.hit(S, Duration.SIXTEENTH, velocity=min(100, 60 + i * 5))
|
|
drums.hit(K, Duration.HALF, velocity=95)
|
|
else:
|
|
drums.hit(K, Duration.QUARTER, velocity=95)
|
|
drums.hit(CH, Duration.SIXTEENTH, velocity=58)
|
|
drums.hit(CH, Duration.SIXTEENTH, velocity=42)
|
|
drums.hit(CH, Duration.EIGHTH, velocity=50)
|
|
drums.hit(S, Duration.QUARTER, velocity=92)
|
|
drums.hit(CH, Duration.EIGHTH, velocity=48)
|
|
drums.hit(K, Duration.EIGHTH, velocity=82)
|
|
|
|
# Bars 33-40: drums soften — stop fighting
|
|
for vel in [75, 68, 60, 52, 45, 38, 30, 22]:
|
|
drums.hit(K, Duration.QUARTER, velocity=vel)
|
|
drums.rest(Duration.QUARTER)
|
|
drums.hit(S, Duration.QUARTER, velocity=max(15, vel - 8))
|
|
drums.rest(Duration.QUARTER)
|
|
|
|
# Bars 41-56: gone — no need to fight anymore
|
|
for _ in range(16):
|
|
drums.rest(Duration.WHOLE)
|
|
|
|
# ── CELLO — enters at acceptance, warmth ───────────────────────
|
|
cello = score.part("cello", instrument="cello", volume=0.15,
|
|
reverb=0.4, reverb_type="cathedral",
|
|
delay=0.08, delay_time=0.326, delay_feedback=0.1,
|
|
pan=0.25, humanize=0.08)
|
|
|
|
for _ in range(32):
|
|
cello.rest(Duration.WHOLE)
|
|
|
|
# Bars 33-48: long tones — acceptance has weight
|
|
for note, vel in [(Bb.add(-12), 38), (Bb.add(-12), 42),
|
|
(Db, 40), (Eb, 42),
|
|
(F, 45), (Eb, 42),
|
|
(Db, 40), (Bb.add(-12), 45),
|
|
(Bb.add(-12), 48), (Db, 45),
|
|
(F, 48), (Eb, 45),
|
|
(Db, 42), (Bb.add(-12), 48),
|
|
(Bb.add(-12), 42), (Bb.add(-12), 35)]:
|
|
cello.add(note, Duration.WHOLE, velocity=vel)
|
|
|
|
# Bars 49-56: fading
|
|
for vel in [32, 25, 18, 12, 8, 0, 0, 0]:
|
|
if vel > 0:
|
|
cello.add(Bb.add(-12), Duration.WHOLE, velocity=vel)
|
|
else:
|
|
cello.rest(Duration.WHOLE)
|
|
|
|
# ── SUB — the weight of acceptance, bars 33 onward ────────────
|
|
sub = score.part("sub", synth="sine", envelope="pad", volume=0.6,
|
|
lowpass=180, distortion=0.15, distortion_drive=2.5,
|
|
sub_osc=0.5, sidechain=0.3)
|
|
|
|
for _ in range(32):
|
|
sub.rest(Duration.WHOLE)
|
|
|
|
# Bars 33-48: enters with acceptance — the ground beneath you
|
|
roots = [Bb.add(-24), Gb.add(-24), Eb.add(-24), F.add(-24)]
|
|
for _ in range(4):
|
|
for root in roots:
|
|
sub.add(root, Duration.WHOLE, velocity=35)
|
|
|
|
# Bars 49-56: one long Bb — settling
|
|
for vel in [35, 32, 28, 25, 20, 15, 10, 5]:
|
|
sub.add(Bb.add(-24), Duration.WHOLE, velocity=vel)
|
|
|
|
# ═════════════════════════════════════════════════════════════════
|
|
import sys
|
|
|
|
print(f"Key: {key}")
|
|
print(f"BPM: 92")
|
|
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 INTRUSIVE (live engine)...")
|
|
from pytheory_live.live import LiveEngine
|
|
engine = LiveEngine(buffer_size=1024)
|
|
engine.play_score(score)
|
|
else:
|
|
print("Playing INTRUSIVE...")
|
|
play_score(score)
|