The Easiest Way to Save and Share Code Snippets on the web

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