mirror of
https://github.com/kennethreitz/interpretations.git
synced 2026-06-05 06:46:15 +00:00
aa969fdaf9
12 parts panned across the field. Radio static far left, theremin signal far right — transmission crosses the stereo field. Clap gets delay ghost. Halo and choir spread wide. Organ slight left for chapel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
388 lines
12 KiB
Python
388 lines
12 KiB
Python
"""
|
|
THE OBSERVATORY — chapel harmonies broadcast through shortwave static.
|
|
An organ learns to levitate. By the time the kick arrives,
|
|
the room is already in orbit.
|
|
|
|
G minor, 112 BPM. Radio hiss, bell, organ, choir, arp,
|
|
sub bass, and a patient house pulse.
|
|
"""
|
|
|
|
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 = s[0]
|
|
A = s[1]
|
|
Bb = s[2]
|
|
C = s[3]
|
|
D = s[4]
|
|
Eb = s[5]
|
|
F = s[6]
|
|
|
|
score = Score("4/4", bpm=112)
|
|
|
|
K = DrumSound.KICK
|
|
CL = DrumSound.CLAP
|
|
CH = DrumSound.CLOSED_HAT
|
|
OH = DrumSound.OPEN_HAT
|
|
|
|
prog = key.progression("i", "VI", "III", "VII") # Gm - Eb - Bb - F
|
|
roots = [G.add(-24), Eb.add(-24), Bb.add(-24), F.add(-24)]
|
|
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
# STRUCTURE (96 bars, ~5.1 minutes at 112 BPM):
|
|
# Bars 1-8: Shortwave hiss + bell. Empty room, open sky.
|
|
# Bars 9-16: Organ enters — first human architecture.
|
|
# Bars 17-32: Arp appears, still no drums. The orbit begins.
|
|
# Bars 33-48: Kick/sub/clap enter — lift.
|
|
# Bars 49-64: Choir + signal melody — the windows open.
|
|
# Bars 65-80: Peak — full transmission, bright and weightless.
|
|
# Bars 81-88: Drums thin out. Organ and choir suspend.
|
|
# Bars 89-96: Static, bell, and one last heartbeat.
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
|
|
# ── RADIO HISS — the sky itself ───────────────────────────────────
|
|
radio = score.part("radio", synth="noise", envelope="pad", volume=0.04,
|
|
reverb=0.85, reverb_type="taj_mahal",
|
|
lowpass=900, pan=-0.35)
|
|
radio.lfo("lowpass", rate=0.012, min=300, max=1800, bars=96, shape="sine")
|
|
radio.lfo("volume", rate=0.02, min=0.02, max=0.06, bars=96, shape="triangle")
|
|
|
|
for _ in range(96):
|
|
radio.add(G, Duration.WHOLE, velocity=25)
|
|
|
|
# ── BOWL — section markers, like light reflecting off metal ──────
|
|
bowl = score.part("bowl", instrument="singing_bowl", volume=0.42,
|
|
reverb=1.0, reverb_type="taj_mahal",
|
|
delay=0.25, delay_time=0.875, delay_feedback=0.35,
|
|
pan=0.2)
|
|
|
|
marker_bars = {1: 78, 9: 72, 17: 68, 33: 75, 49: 72, 65: 76, 81: 60, 89: 54}
|
|
for bar in range(1, 97):
|
|
if bar in marker_bars:
|
|
note = G.add(-24) if bar < 65 else D.add(-12)
|
|
bowl.add(note, Duration.WHOLE, velocity=marker_bars[bar])
|
|
else:
|
|
bowl.rest(Duration.WHOLE)
|
|
|
|
# ── ORGAN — square-wave chapel, warm but slightly unreal ──────────
|
|
organ = score.part("organ", synth="square", envelope="organ", volume=0.14,
|
|
reverb=0.78, reverb_type="taj_mahal",
|
|
chorus=0.25, chorus_rate=0.18, chorus_depth=0.01,
|
|
lowpass=1800, pan=-0.15)
|
|
|
|
low_prog = [c.transpose(-12) for c in prog]
|
|
mid_prog = prog
|
|
high_prog = [c.transpose(12) for c in prog]
|
|
|
|
# Bars 1-8: silence
|
|
for _ in range(8):
|
|
organ.rest(Duration.WHOLE)
|
|
|
|
# Bars 9-16: first statement, low and slow
|
|
for _ in range(2):
|
|
for chord in low_prog:
|
|
organ.add(chord, Duration.WHOLE, velocity=52)
|
|
|
|
# Bars 17-32: stronger, still no pulse beneath it
|
|
for _ in range(4):
|
|
for chord in low_prog:
|
|
organ.add(chord, Duration.WHOLE, velocity=60)
|
|
|
|
# Bars 33-64: fully present
|
|
for _ in range(8):
|
|
for chord in mid_prog:
|
|
organ.add(chord, Duration.WHOLE, velocity=72)
|
|
|
|
# Bars 65-80: brighter voicings at the peak
|
|
for _ in range(4):
|
|
for chord in high_prog:
|
|
organ.add(chord, Duration.WHOLE, velocity=66)
|
|
|
|
# Bars 81-88: suspended, looking down at the earth
|
|
for _ in range(2):
|
|
for chord in [mid_prog[0], mid_prog[1], mid_prog[0], mid_prog[3]]:
|
|
organ.add(chord, Duration.WHOLE, velocity=54)
|
|
|
|
# Bars 89-96: fading architecture
|
|
for chord, vel in zip(low_prog * 2, [50, 44, 40, 36, 30, 24, 16, 8]):
|
|
organ.add(chord, Duration.WHOLE, velocity=vel)
|
|
|
|
# ── CHOIR — the air inside the room starts singing ────────────────
|
|
choir = score.part("choir", instrument="choir", volume=0.11,
|
|
reverb=0.95, reverb_type="taj_mahal",
|
|
chorus=0.35, chorus_rate=0.08, chorus_depth=0.012,
|
|
pan=0.25)
|
|
|
|
# Bars 1-32: silence
|
|
for _ in range(32):
|
|
choir.rest(Duration.WHOLE)
|
|
|
|
# Bars 33-48: arrives behind the drums, soft
|
|
for _ in range(4):
|
|
for chord in prog:
|
|
choir.add(chord, Duration.WHOLE, velocity=38)
|
|
|
|
# Bars 49-80: fully open
|
|
for _ in range(8):
|
|
for chord in prog:
|
|
choir.add(chord, Duration.WHOLE, velocity=48)
|
|
|
|
# Bars 81-88: held breath
|
|
for _ in range(2):
|
|
for chord in [prog[0], prog[1], prog[0], prog[3]]:
|
|
choir.add(chord, Duration.WHOLE, velocity=36)
|
|
|
|
# Bars 89-96: gone
|
|
for _ in range(8):
|
|
choir.rest(Duration.WHOLE)
|
|
|
|
# ── HALO PAD — the light around the chord, not the chord itself ──
|
|
halo = score.part("halo", synth="supersaw", envelope="pad", volume=0.08,
|
|
reverb=0.65, reverb_type="taj_mahal",
|
|
chorus=0.45, chorus_rate=0.2, chorus_depth=0.008,
|
|
lowpass=1200, pan=-0.25)
|
|
halo.lfo("lowpass", rate=0.01, min=500, max=2800, bars=96, shape="triangle")
|
|
|
|
# Bars 1-48: silence
|
|
for _ in range(48):
|
|
halo.rest(Duration.WHOLE)
|
|
|
|
# Bars 49-80: glow at the peak
|
|
for _ in range(8):
|
|
for chord in prog:
|
|
halo.add(chord, Duration.WHOLE, velocity=42)
|
|
|
|
# Bars 81-96: silence
|
|
for _ in range(16):
|
|
halo.rest(Duration.WHOLE)
|
|
|
|
# ── ARP — the signal lock, patient and inevitable ────────────────
|
|
arp = score.part("arp", synth="saw", envelope="pluck", volume=0.35,
|
|
reverb=0.3, delay=0.35, delay_time=0.234,
|
|
delay_feedback=0.42, lowpass=1300, detune=5,
|
|
pan=0.15, humanize=0.03)
|
|
arp.lfo("lowpass", rate=0.009, min=900, max=5200, bars=96, shape="saw")
|
|
|
|
P1 = [
|
|
G.add(12), None, D.add(12), None,
|
|
Bb, None, F.add(12), None,
|
|
G.add(12), None, D.add(12), None,
|
|
C.add(12), Bb, F, D,
|
|
]
|
|
|
|
P2 = [
|
|
G.add(12), D.add(12), F.add(12), D.add(12),
|
|
Eb.add(12), D.add(12), Bb, D.add(12),
|
|
G.add(12), D.add(12), F.add(12), D.add(12),
|
|
C.add(12), Bb, F, D,
|
|
]
|
|
|
|
|
|
def arp_bar(notes, vel):
|
|
for note in notes:
|
|
if note is None:
|
|
arp.rest(Duration.SIXTEENTH)
|
|
else:
|
|
arp.add(note, Duration.SIXTEENTH, velocity=vel)
|
|
|
|
|
|
# Bars 1-16: silence
|
|
for _ in range(16):
|
|
arp.rest(Duration.WHOLE)
|
|
|
|
# Bars 17-32: emerges quietly
|
|
for _ in range(8):
|
|
arp_bar(P1, 50)
|
|
arp_bar(P2, 54)
|
|
|
|
# Bars 33-48: steady and confident
|
|
for _ in range(8):
|
|
arp_bar(P1, 64)
|
|
arp_bar(P2, 68)
|
|
|
|
# Bars 49-64: brighter
|
|
for _ in range(8):
|
|
arp_bar(P1, 76)
|
|
arp_bar(P2, 80)
|
|
|
|
# Bars 65-80: peak — no more hesitation
|
|
for _ in range(8):
|
|
arp_bar(P2, 88)
|
|
arp_bar(P1, 92)
|
|
|
|
# Bars 81-88: thinning out
|
|
for _ in range(4):
|
|
arp_bar(P1, 62)
|
|
arp_bar(P2, 58)
|
|
|
|
# Bars 89-96: silence
|
|
for _ in range(8):
|
|
arp.rest(Duration.WHOLE)
|
|
|
|
# ── SUB — the floor finally arrives beneath the chapel ───────────
|
|
sub = score.part("sub", synth="sine", envelope="pad", volume=0.28,
|
|
lowpass=120, distortion=0.12, distortion_drive=2.5,
|
|
sub_osc=0.4, sidechain=0.25)
|
|
|
|
# Bars 1-32: silence
|
|
for _ in range(32):
|
|
sub.rest(Duration.WHOLE)
|
|
|
|
# Bars 33-80: root motion, simple and physical
|
|
for _ in range(12):
|
|
for root in roots:
|
|
sub.add(root, Duration.HALF, velocity=92)
|
|
sub.rest(Duration.HALF)
|
|
|
|
# Bars 81-88: just the home note, held
|
|
for _ in range(8):
|
|
sub.add(G.add(-24), Duration.HALF, velocity=72)
|
|
sub.rest(Duration.HALF)
|
|
|
|
# Bars 89-96: fading heartbeat
|
|
for vel in [66, 58, 50, 42, 34, 26, 16, 0]:
|
|
if vel > 0:
|
|
sub.add(G.add(-24), Duration.HALF, velocity=vel)
|
|
sub.rest(Duration.HALF)
|
|
else:
|
|
sub.rest(Duration.WHOLE)
|
|
|
|
# ── KICK — the patient release ────────────────────────────────────
|
|
kick = score.part("kick", volume=0.55, humanize=0.03)
|
|
|
|
# Bars 1-32: none
|
|
for _ in range(32):
|
|
kick.rest(Duration.WHOLE)
|
|
|
|
# Bars 33-80: four on the floor
|
|
for _ in range(48):
|
|
for _beat in range(4):
|
|
kick.hit(K, Duration.QUARTER, velocity=114)
|
|
|
|
# Bars 81-88: still there, but more human now
|
|
for _ in range(8):
|
|
kick.hit(K, Duration.QUARTER, velocity=95)
|
|
kick.rest(Duration.QUARTER)
|
|
kick.hit(K, Duration.QUARTER, velocity=88)
|
|
kick.rest(Duration.QUARTER)
|
|
|
|
# Bars 89-96: last pulses
|
|
for vel in [88, 82, 76, 68, 58, 46, 32, 18]:
|
|
kick.hit(K, Duration.QUARTER, velocity=vel)
|
|
kick.rest(Duration.DOTTED_HALF)
|
|
|
|
# ── CLAP — 2 and 4, but never aggressive ─────────────────────────
|
|
clap = score.part("clap", volume=0.28, reverb=0.18,
|
|
delay=0.1, delay_time=0.268, delay_feedback=0.15,
|
|
pan=-0.1, humanize=0.04)
|
|
|
|
# Bars 1-32: silence
|
|
for _ in range(32):
|
|
clap.rest(Duration.WHOLE)
|
|
|
|
# Bars 33-80: standard house backbeat
|
|
for _ in range(48):
|
|
clap.rest(Duration.QUARTER)
|
|
clap.hit(CL, Duration.QUARTER, velocity=96)
|
|
clap.rest(Duration.QUARTER)
|
|
clap.hit(CL, Duration.QUARTER, velocity=98)
|
|
|
|
# Bars 81-96: silence
|
|
for _ in range(16):
|
|
clap.rest(Duration.WHOLE)
|
|
|
|
# ── HATS — offbeat shimmer ────────────────────────────────────────
|
|
hats = score.part("hats", volume=0.22, pan=0.1, humanize=0.04)
|
|
|
|
# Bars 1-32: silence
|
|
for _ in range(32):
|
|
hats.rest(Duration.WHOLE)
|
|
|
|
# Bars 33-80: offbeat 8ths, occasional open hat on the turn
|
|
for bar in range(48):
|
|
for beat in range(4):
|
|
hats.rest(Duration.EIGHTH)
|
|
sound = OH if beat == 3 and bar % 4 == 3 else CH
|
|
vel = 74 if sound == CH else 68
|
|
hats.hit(sound, Duration.EIGHTH, velocity=vel)
|
|
|
|
# Bars 81-88: thinner
|
|
for _ in range(8):
|
|
for _beat in range(4):
|
|
hats.rest(Duration.EIGHTH)
|
|
hats.hit(CH, Duration.EIGHTH, velocity=52)
|
|
|
|
# Bars 89-96: silence
|
|
for _ in range(8):
|
|
hats.rest(Duration.WHOLE)
|
|
|
|
# ── SIGNAL — the melody finally resolves the transmission ────────
|
|
signal = score.part("signal", instrument="theremin", volume=0.3,
|
|
reverb=0.35, reverb_type="taj_mahal",
|
|
delay=0.2, delay_time=0.375, delay_feedback=0.25,
|
|
pan=0.35, humanize=0.05)
|
|
|
|
# Bars 1-48: silence
|
|
for _ in range(48):
|
|
signal.rest(Duration.WHOLE)
|
|
|
|
phrase_a = [
|
|
(D.add(12), Duration.HALF, 70, 0.2),
|
|
(F.add(12), Duration.QUARTER, 74, 0.0),
|
|
(G.add(12), Duration.QUARTER, 78, 0.35),
|
|
(Bb.add(12), Duration.HALF, 80, -0.2),
|
|
(A.add(12), Duration.QUARTER, 68, 0.0),
|
|
(G.add(12), Duration.QUARTER, 72, 0.0),
|
|
(F.add(12), Duration.HALF, 70, 0.15),
|
|
(Eb.add(12), Duration.QUARTER, 68, 0.0),
|
|
(D.add(12), Duration.QUARTER, 66, -0.1),
|
|
(G.add(12), Duration.WHOLE, 78, 0.0),
|
|
]
|
|
|
|
phrase_b = [
|
|
(G.add(12), Duration.QUARTER, 78, 0.0),
|
|
(Bb.add(12), Duration.QUARTER, 82, 0.15),
|
|
(D.add(24), Duration.HALF, 86, -0.2),
|
|
(F.add(24), Duration.HALF, 84, 0.0),
|
|
(Eb.add(24), Duration.QUARTER, 78, 0.0),
|
|
(D.add(24), Duration.QUARTER, 76, 0.0),
|
|
(Bb.add(12), Duration.HALF, 72, -0.15),
|
|
(A.add(12), Duration.QUARTER, 68, 0.0),
|
|
(G.add(12), Duration.QUARTER, 66, 0.0),
|
|
(F.add(12), Duration.WHOLE, 74, 0.0),
|
|
]
|
|
|
|
# Bars 49-64: first message
|
|
for _ in range(4):
|
|
for note, dur, vel, bend in phrase_a:
|
|
signal.add(note, dur, velocity=vel, bend=bend)
|
|
|
|
# Bars 65-80: stronger reply
|
|
for _ in range(4):
|
|
for note, dur, vel, bend in phrase_b:
|
|
signal.add(note, dur, velocity=vel, bend=bend)
|
|
|
|
# Bars 81-96: silence
|
|
for _ in range(16):
|
|
signal.rest(Duration.WHOLE)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════
|
|
import sys
|
|
|
|
print(f"Key: {key}")
|
|
print(f"BPM: 112")
|
|
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 THE OBSERVATORY (live engine)...")
|
|
from pytheory_live.live import LiveEngine
|
|
engine = LiveEngine(buffer_size=1024)
|
|
engine.play_score(score)
|
|
else:
|
|
print("Playing THE OBSERVATORY...")
|
|
play_score(score)
|