"""Classroom Spark: original composition and recording, CC0 1.0.
No samples, external services or third-party melodies. Deterministic local synthesis.
Run: python3 scripts/compose-classroom-music.py
"""
from pathlib import Path
import math, random, struct, wave
SR=44100; BPM=108; BEAT=60/BPM; SECONDS=20
samples=[0.0]*int(SR*SECONDS)
rng=random.Random(108)
def note(midi, beat, duration, gain, kind="keys"):
 start=int(beat*BEAT*SR); n=int(duration*BEAT*SR); hz=440*2**((midi-69)/12)
 for j in range(min(n,len(samples)-start)):
  t=j/SR; env=min(1,t/0.009)*math.exp(-t/(0.32 if kind=="keys" else 0.16))
  value=sum(math.sin(2*math.pi*hz*h*t)*a for h,a in [(1,1),(2,.25),(3,.08)])
  samples[start+j]+=value*gain*env*min(1,(n-j)/(SR*.05))
chords=[[60,64,67,71],[57,60,64,67],[53,57,60,64],[55,59,62,69]]
for bar in range(9):
 c=chords[bar%4]; start=bar*4
 for k in range(8):note(c[[0,2,1,3,2,1,3,2][k]]+12,start+k*.5,.8,.10)
 for m in c:note(m,start,3.8,.035)
 for beat in [0,2]:note(c[0]-12,start+beat,1.3,.12,"bass")
 for beat in range(4):
  p=int((start+beat)*BEAT*SR)
  for j in range(min(int(.13*SR),len(samples)-p)):
   t=j/SR; samples[p+j]+=.11*math.sin(2*math.pi*(58*t+24*(1-math.exp(-25*t))/25))*math.exp(-33*t)
 for beat in [1,3]:
  p=int((start+beat)*BEAT*SR)
  for j in range(min(int(.13*SR),len(samples)-p)):
   samples[p+j]+=(rng.random()*2-1)*.037*math.exp(-j/(SR*.035))
 for k in range(8):
  p=int((start+k*.5)*BEAT*SR)
  for j in range(min(int(.04*SR),len(samples)-p)):
   samples[p+j]+=(rng.random()*2-1)*.014*math.exp(-j/(SR*.009))
peak=max(abs(v) for v in samples); path=Path(__file__).resolve().parent.parent/"public/music/classroom-spark.wav"
with wave.open(str(path),"wb") as w:
 w.setnchannels(1);w.setsampwidth(2);w.setframerate(SR)
 w.writeframes(b"".join(struct.pack("<h",int(v/peak*.65*min(1,i/(SR*.1),(len(samples)-i)/(SR*.7))*32767)) for i,v in enumerate(samples)))
print("Wrote original CC0 instrumental:",path.name)
