Instruments Simplified with Python Decorators
python
posted: Apr, 30th 2010 | jump to bottom
#!/usr/bin/env python # Copyright (c) 2010 Jacob Joaquin, jacobjoaquin@gmail.com # Visit Slipmat -- http://slipmat.noisepages.com/ import math import operator sr = 44100 ksmps = 10 class IterReduce: '''Reduces a list of iterators with the op function.''' op = operator.add def __init__(self, *ins): self.ins = ins def __iter__(self): self.index = 0 self.iters = [(j for j in i) for i in self.ins] return self def next(self): if self.index >= ksmps: raise StopIteration self.index += 1 return reduce(self.op, (i.next() for i in self.iters)) class Multiply(IterReduce): op = operator.mul class Sum(IterReduce): pass class RiseFall: '''A rise-fall envelope generator.''' def __init__(self, dur, peak=0.5): self.frames = int(dur * sr / float(ksmps)) self.rise = int(peak * self.frames) self.fall = int(self.frames - self.rise) self.inc = 0 self.v = 0 def __iter__(self): self.index = 0 if self.inc <= self.rise and self.rise != 0: self.v = self.inc / float(self.rise) else: self.v = (self.fall - (self.inc - self.rise)) / float(self.fall) self.inc += 1 return self def next(self): if self.index >= ksmps: raise StopIteration self.index += 1 return self.v class Run: '''Render frames over time.''' def __init__(self, dur=1.0): self.frames = int(dur * sr / float(ksmps)) def __iter__(self): self.index = 0 return self def next(self): if self.index >= self.frames: raise StopIteration self.index += 1 return self.index class Sine: '''A sine wave oscillator.''' def __init__(self, amp=1.0, freq=440, phase=0.0): self.amp = amp self.freq = float(freq) self.phase = phase def __iter__(self): self.index = 0 return self def next(self): if self.index >= ksmps: raise StopIteration self.index += 1 v = math.sin(self.phase * 2 * math.pi) self.phase += self.freq / sr return v * self.amp class Instr: def __init__(self, f): self.f = f def __call__(self, *args): self.out = self.f(*args) return self def __iter__(self): self.index = 0 self._iter = (i for i in self.out) return self def next(self): if self.index >= ksmps: raise StopIteration self.index += 1 return self._iter.next() @Instr def MyInstr(dur=1.0, amp=1.0, freq=440, tune=12): a1 = Sine(amp * 0.5, freq) a2 = Sine(amp * 0.5, freq * 2 ** (tune / 12.0)) return Multiply(Sum(a1, a2), RiseFall(dur, 0.5)) if __name__ == "__main__": t = 0.002 my_instr = MyInstr(t, 1, 440, 7) for frame in Run(t): print '%d:' % frame for i in my_instr: print '\t%.8f' % i
104 views




