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

Score Events

python

posted: May, 3rd 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
 
def sec_to_frames(dur): return int(dur * sr / float(ksmps))
 
class UnitGenerator:
    '''All unit generator classes inherit from this.'''
 
    def __init__(self): pass
    def __iter__(self): pass
    def next(self): raise StopIteration
    def __add__(self, ug): return Add(self, ug)
    def __mul__(self, ug): return Mul(self, ug)
 
class Instr():
    '''Decorator for creating a UnitGenerator from a definition.'''
 
    def __init__(self, f): self.f = f
    def __call__(self, *args): return self.Iter(self.f, *args)
 
    class Iter(UnitGenerator):
        def __init__(self, f, *args): self.f = f(*args)
 
        def __iter__(self):
            self.index = 0
            self._iter = (i for i in self.f)
            return self
 
        def next(self):
            if self.index >= ksmps: raise StopIteration
            self.index += 1
            return self._iter.next()
 
class IterReduce(UnitGenerator):
    '''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 Mul(IterReduce): op = operator.mul
class Add(IterReduce): pass
 
class RiseFall(UnitGenerator):
    '''A rise-fall envelope generator.'''
 
    def __init__(self, dur, peak=0.5):
        self.frames = sec_to_frames(dur)
        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 Sine(UnitGenerator):
    '''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 UVal(UnitGenerator):
    '''Convert a numeric value to a unit generator object'''
 
    def __init__(self, v):
        self.v = v
 
    def __iter__(self):
        self.index = 0
        return self
 
    def next(self):
        if self.index >= ksmps: raise StopIteration
        self.index += 1
        return self.v
 
class ScoreEvents:
    '''Schedule events for unit generators.'''
 
    def __init__(self):
        self.event_list = {}
        self.ID = 0
        self.last_frame_v = 0
 
    def event(self, start, dur, ugen):
        ugen_start = sec_to_frames(start)
        ugen_end = ugen_start + sec_to_frames(dur)
        self.last_frame_v = max(ugen_start, ugen_end, self.last_frame_v)
 
        if ugen_start not in self.event_list.keys():
            self.event_list.update({ugen_start: [(self.ID, 'start', ugen)]})
        else:
            self.event_list[ugen_start].append((self.ID, 'start', ugen))
 
        if ugen_end not in self.event_list.keys():
            self.event_list.update({ugen_end: [(self.ID, 'stop', None)]})
        else:
            self.event_list[ugen_end].append((self.ID, 'stop', None))
 
        self.ID += 1
 
    def last_frame(self): return self.last_frame_v
 
class PrintSamples:
    def __init__(self, score):
        self.score = score
        self.event_list = self.score.event_list
        self.last_frame = self.score.last_frame()
        self.events = {}
 
    def __iter__(self):
        self.f = 0
        return self
 
    def next(self):
        if self.f >= self.last_frame: raise StopIteration
 
        if self.f in self.event_list:
            for L in self.event_list[self.f]:
                ID, command, function = L
 
                if command == 'start':
                    self.events.update({ID: function})
                elif command == 'stop':
                    del self.events[ID]
 
        self.iters = [(j for j in v) for v in self.events.itervalues()]
        print '%d:' % self.f
 
        for i in range(ksmps):
            if self.iters:
                v = reduce(operator.add, (i.next() for i in self.iters))
            else:
                v = 0
 
            print '\t%.6f' % v
 
        self.f += 1
        return True
 
if __name__ == "__main__":
    @Instr
    def RingTine(dur, amp, freq_1, freq_2):
        return Sine(amp, freq_1) * Sine(amp, freq_2) * RiseFall(dur, 0.05)
 
    s = ScoreEvents()
    s.event(0, 0.25, RingTine(0.25, 1, 440, 44))
    s.event(0.5, 0.125, RingTine(0.125, 0.333, 262, 44))
    s.event(0.75, 0.25, RingTine(0.25, 0.25, 262, 44))
    s.event(0.75, 0.25, RingTine(0.25, 0.5, 440, 44))
    for frame in PrintSamples(s): pass
283 views