There was something that seriously annoyed me about yesterday’s instrument graph. The use of the Multiply() and the Sum() is bothersome; I’m used to expressing this functionality in a more concise manner using the * and + operators. Download today’s code here.
This doesn’t work for me:
return Multiply(Sum(a1, a2), RiseFall(dur, 0.5))
Thankfully, Python allows us to overload the operators, so we can express the same line as this:
return (a1 + a2) * RiseFall(dur, 0.5)
Less typing, easier to read. Let’s see it in the context of @Instr MyInstr:
@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 (a1 + a2) * RiseFall(dur, 0.5)
Here’s how I implemented it. I started by creating a generic iterator class called UnitGenerator:
class UnitGenerator: def __init__(self): pass def __iter__(self): pass def next(self): raise StopIteration def __add__(self, i): return Add(self, i) def __mul__(self, i): return Mul(self, i)
The last two lines of the class redefine __add__() and __mul__(), which control the behaviors of + and *. These functions use the custom classes Add() and Mul(). These were originally called Sum() and Multiply(), though I renamed them to follow Python naming conventions. The last thing I had to do was alter some of the existing classes to derive from class UnitGenerator, so they automatically incorporate the overloaded operators.
class Instr(UnitGenerator): ... class IterReduce(UnitGenerator): ... class Mul(IterReduce): ... class Add(IterReduce): ... class RiseFall(UnitGenerator): ... class Sine(UnitGenerator): ...
Classes Mul and Add are also of type UnitGenerator. They inherit for class IterReduce which inherents from UnitGenerator.