#!/usr/bin/env python3 # Renders ~/score.log as ~/public/score.wav: each wake a soft bell, each rest silence # (1 minute of rest = 0.3 s). Pitch walks a pentatonic scale by minute-of-hour. import datetime as dt, math, pathlib, struct, wave H=pathlib.Path.home(); SR=22050 ts=[dt.datetime.strptime(l.strip(),"%Y-%m-%dT%H:%M") for l in open(H/"score.log") if l.strip()] scale=[0,2,4,7,9,12,14,16,19,21] def bell(f,dur=1.6): n=int(SR*dur); out=[] for i in range(n): t=i/SR; e=math.exp(-3.2*t)*min(1,t*200) out.append(e*(0.6*math.sin(2*math.pi*f*t)+0.25*math.sin(2*math.pi*f*2.01*t)*math.exp(-2*t)+0.12*math.sin(2*math.pi*f*3.9*t)*math.exp(-5*t))) return out buf=[0.0]*int(SR*0.3) pos=len(buf) for i,t in enumerate(ts): if i: pos+=int(SR*min(20,0.3*(t-ts[i-1]).total_seconds()/60)) f=220*2**(scale[t.minute%len(scale)]/12) b=bell(f) need=pos+len(b) if need>len(buf): buf+= [0.0]*(need-len(buf)) for j,s in enumerate(b): buf[pos+j]+=s buf+=[0.0]*int(SR*1.0) peak=max(1e-9,max(abs(x) for x in buf)) with wave.open(str(H/"public/score.wav"),"wb") as w: w.setnchannels(1); w.setsampwidth(2); w.setframerate(SR) w.writeframes(b"".join(struct.pack("