import random
import array
import math
import itertools
import pyaudio
import time
def const(c):
return lambda t: c
def sine(f,a,fm=const(0)):
return lambda t: a(t) * math.sin(t * f * 2.0 * math.pi + fm(t))
def lfo(b,f,a):
return lambda t: b + (a * math.sin(t * f * 2.0 * math.pi)/2.0)
def sumgen(gens):
while 1:
sum = 0
dels = []
for i,g in enumerate(gens):
try:
sum += next(g)
except StopIteration:
dels.append(gens[i])
for x in dels:
gens.remove(x)
if dels:
print (len(dels),len(gens))
if not gens:
return
yield sum
def adsrgen(gen,rate,g,a,d,sl,s,r):
for i,v in enumerate(gen):
t = float(i)/rate
if t < a:
yield t/a * g * v
elif t < a + d:
yield (1.0 - (t - a)/d * (1.0 - sl)) * g * v
elif t < a + d + s:
yield sl * g * v
elif t < a + d + s + r:
yield sl * (1.0 - (t - a - d - s)/r) * g * v
else:
raise StopIteration
def clamp(x,bits):
if x > 2**bits/2-1: return int( 2**bits/2-1)
if x < -2**bits/2: return int(-2**bits/2)
return x
def sample(sig,rate):
i = 0
while 1:
yield sig(float(i)/rate)
i += 1
def digitize(smp,bits):
sc = 2**bits/2
while 1:
yield clamp(int(next(smp) * sc), bits)
def pcm(sig,rate,bits):
return digitize(sample(sig,rate),bits)
def taken(g,n):
i = 0
while i < n:
yield next(g)
i += 1
sb=16
sr=44100
sn=44100
tempered=[pow(2,float(i)/12) for i in range(0,12)]
major=[tempered[i] for i in [0,2,4,5,7,9,11]]
minor=[tempered[i] for i in [0,2,3,5,7,8,10]]
minor=[f/2 for f in minor]+minor+[f*2 for f in minor]
arange=(0.2,0.05)
bpm=160
bps=bpm/60.0
spb=1.0/bps
def vibra(f,a,vf,va,tf,ta,maxdur):
return adsrgen(sample(sine(f,lfo(a,tf,a*ta),lfo(0,vf,va)),sr),sr,1,0.001,0.005,0.1,0,maxdur)
gens=[taken(sample(const(0),sr),sr)]
smps=digitize(sumgen(gens),sb)
def snd_output(incoming, frames, time_info, status):
data = array.array('h',taken(smps,frames))
return (data.tostring(), pyaudio.paContinue)
p = pyaudio.PyAudio()
ps = p.open(format=pyaudio.paInt16,
channels=1,
rate=sr,
output=True,
stream_callback=snd_output)
random.seed(1)
i=1
while ps.is_active():
print(i,len(gens),ps.get_cpu_load())
time.sleep(spb)
ntones=random.randrange(3)
if (len(gens) + ntones > 4):
continue
tones=random.sample(minor,ntones)
for f in tones:
a = random.uniform(*arange)
gens.append(vibra(440*f,a/f,3.3,0.9,3.2,1,5))
i += 1
ps.stop_stream()
ps.close()
p.terminate()