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




