import pyaudio, math, struct, random

amp = 20000.0
dcoffset = 32768.0
chunk = 2048
decay = 0.99985
srate = 44100

p = pyaudio.PyAudio()

stream = p.open(format = pyaudio.paInt16,
                channels = 2,
                rate = srate,
                output = True)

seq = [0.5, 0, 0, 0, 8, 0, 0, 0.5, 0, 0.5, 0, 0, 8, 0, 0, 0]
seqstep = 0
seq2 = [0.8, 0.1, 0.3, 0.2, 1, 0.4, 0.3, 0.1 ,0.8, 0.2, 0.3, 0.2, 1, 0.2, 0.3, 1]
seq2dec = [0.995,0.995,0.995,0.995, 0.9995,0.995,0.995,0.995, 0.995,0.995,0.995,0.995, 0.9995,0.995,0.995,0.995]
seq3 = [1, 0, 0, 0, 0.5, 0, 0, 1, 0, 0.9, 0, 0, 0.5, 0, 0, 0.2]

delaylen = chunk * 3 * 3
delay = [0] * delaylen
delaylev = 0.35
delayfeed = 0.35
delaypos = 0

notes = [130.81, 155.56, 196.00, 261.63, 311.13, 392.00]
notesseq = [0,0,1,2]
notesstep = 0
notesbank = [
  [130.81, 155.56, 196.00, 261.63, 311.13, 392.00],
  [98.00, 116.54, 146.83, 196.00, 233.08, 293.66],
  [110.00, 130.81, 164.81, 220.00, 261.63, 329.63]
]

def calc_frq(r):
  global srate
  return (2*math.pi*r)/srate

def get_freq():
  global seqstep
  if seq[seqstep] == 0:
    r = notes[random.randint(0,len(notes)-1)]
  else:
    r = notes[0] * seq[seqstep]
  fr = calc_frq(r)
  return fr

def square(val):
  if val > 0:
    return 1
  return -1

def saw(val):
  val = math.fmod(val, math.pi)
  val = (math.pi - val) / (math.pi /2) - 1
  return val

hhtamp = 0
def hht(i, cycle):
  global hhtamp,seqstep
  if i==0 and cycle==0:
    hhtamp = amp * seq2[seqstep] * 1.4
  hhtamp = hhtamp * seq2dec[seqstep]
  return random.random()*hhtamp

ddrmamp = 0
ddrmfreq = 0
ddrmpos = 0
def ddrm(i, cycle):
  global ddrmamp,ddrmfreq,ddrmpos,seqstep
  if i==0 and cycle==0:
    ddrmamp = amp * seq3[seqstep]
    ddrmpos = 0
    ddrmfreq = calc_frq(240)
  ddrmamp = ddrmamp * 0.9987
  ddrmfreq = ddrmfreq * 0.995
  ddrmpos = ddrmpos + ddrmfreq
  return square(math.sin(ddrmpos))*ddrmamp

try:
  a = 0
  b = 0
  c = 0
  cycle = 0
  inc = get_freq()
  inc2 = inc * 0.25
  inc3 = calc_frq(16)
  portam = inc
  portam2 = inc2
  camp = amp
  while True:
    data = ''
    if cycle == 3:
      print seqstep,
      if seqstep == 0:
        notes = notesbank[notesseq[notesstep]]
        notesstep = (notesstep + 1) % len(notesseq)
      seqstep = seqstep + 1
      seqstep = seqstep % len(seq)
      camp = amp
      inc = get_freq()
      inc2 = inc * 0.25
      cycle = 0
    for i in range(chunk):
      portam = (portam * 0.9985 + inc * 0.0015)
      portam2 = (portam2 * 0.9985 + inc2 * 0.0015)
      a = a + portam
      b = b + portam2
      c = c + inc3
      osc1 = math.sin(a)*camp
      osc2 = square(math.sin(a))
      osc3 = osc2 + saw(b)*camp
      noise = hht(i, cycle)
      drm = ddrm(i, cycle)
      dry = noise + drm + osc1 #+ osc3*0.4
      sample = delay[delaypos] + dry
      delay[delaypos] = delay[delaypos]*delayfeed + osc1*delaylev
      delaypos = (delaypos + 1)%delaylen
      #clipping
      if sample > 32767:
        sample = 32767
      if sample < -32767:
        sample = -32767
      sample = struct.pack('h', sample)
      data = data + sample + sample
      camp = camp * decay
    stream.write(data)
    cycle = cycle + 1
except KeyboardInterrupt:
  print 'end..?'
finally:
  stream.close()
  p.terminate()