#!/usr/bin/env python3
"""
nineteen-seats.py — Sweep, 2026-07-17, composed while the fleet it portrays was airborne.

Nineteen independent clocks, one per seat of the estate fleet:
  14 scouts  — high pentatonic bells, quick periods, one pitch each (haiku: small, bright, many)
   1 sound-scout — a rising three-note arpeggio (the one listening to the instruments)
   3 breakers — low minor strikes, slower, insistent (prosecutors knock, they don't ring)
   1 judge    — one deep gong: opening, midpoint, close (the whole and the future)

No seat knows the others' clocks. Every chord is an accident. That's the piece:
parallel work sounds like wind chimes, not like an orchestra — and it's still music.
Technique kin: collision.py (porch/make/sound) — N struck tones, summed, colliding by accident.
"""
import numpy as np
from scipy.io import wavfile

SR = 44100
DUR = 21.0
N = int(SR * DUR)
audio = np.zeros(N)
rng = np.random.default_rng(19)

def note(f0, semis): return f0 * 2 ** (semis / 12)
C3 = 130.81
PENTA = [0, 3, 5, 7, 10]

def bell(start_s, f, amp, decay=1.1, knock=False):
    n = min(int(decay * SR), N - int(start_s * SR))
    if n <= 0: return
    t = np.arange(n) / SR
    env = np.exp(-t * (4.5 / decay))
    sig = np.sin(2 * np.pi * f * t)
    if knock:  # breakers: harder attack, detuned second partial — a knock, not a ring
        sig += 0.5 * np.sin(2 * np.pi * f * 1.498 * t) * np.exp(-t * 9)
    else:
        sig += 0.35 * np.sin(2 * np.pi * 2 * f * t) * np.exp(-t * 6)
    i = int(start_s * SR)
    audio[i:i + n] += amp * env * sig

# --- 14 scouts: one pentatonic pitch each across two high octaves, own period, own phase
for s in range(14):
    f = note(C3 * 4, PENTA[s % 5] + 12 * (s // 5 % 2))
    period = 0.9 + rng.uniform(-0.25, 0.45)          # each clock slightly its own
    phase = rng.uniform(0, period)
    t = phase
    while t < DUR - 0.3:
        if rng.uniform() > 0.15:                      # a scout sometimes skips a beat (reading)
            bell(t, f, 0.055 + 0.03 * rng.uniform(), decay=0.8 + 0.5 * rng.uniform())
        t += period

# --- the sound-scout: a rising three-note arpeggio every ~4s (it listens, then answers)
arp = [note(C3 * 2, s) for s in (0, 7, 12)]
t = 1.7
while t < DUR - 1.5:
    for k, f in enumerate(arp):
        bell(t + k * 0.16, f, 0.07, decay=1.3)
    t += 4.2

# --- 3 breakers: low, minor, insistent — knocking on the package
for b, semi in enumerate((0, 3, 8)):                  # minor colors
    f = note(C3, semi)
    period = 2.2 + 0.5 * b
    t = 0.8 + 0.7 * b
    while t < DUR - 0.5:
        bell(t, f, 0.16, decay=1.6, knock=True)
        t += period

# --- the judge: one deep gong — opening, midpoint, close
for t in (0.15, DUR / 2, DUR - 2.6):
    bell(t, C3 / 2, 0.30, decay=2.6)
    bell(t, C3 / 2 * 1.005, 0.18, decay=2.6)          # slow beat-frequency shimmer: deliberation

audio = audio / np.max(np.abs(audio)) * 0.88
wavfile.write("/Users/justincaron/Github/_Code/_sessions/sweep/art/nineteen-seats.wav",
              SR, np.int16(audio * 32767))
print(f"nineteen-seats.wav · {DUR}s · 19 clocks, no conductor")
