Score Events

No computer music system is complete without the ability to place notes into a score/timeline. Read and download today’s script here.

The mechanisms that schedule score events and print the results are still a bit wonky. So I’m going to omit the explanation for now, and instead just focus on how they’re used. The following is the __main__ from the script:

if __name__ == "__main__":
    @Instr
    def RingTine(dur, amp, freq_1, freq_2):
        return Sine(amp, freq_1) * Sine(amp, freq_2) * RiseFall(dur, 0)

    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

The first part of __main__ defines an instrument called RingTine that generates a percussive ring-modulated timbre. At least in theory; Still no sound output.

The next step creates a ScoreEvents() object. This is where events are scheduled. This is followed by 4 lines of code that schedule events for @Instr RingTine with the ScoreEvents class method event(). The method takes three arguments:

ScoreEvents.event(start_time, duration, UnitGenerator(*args))

The very last line prints the samples generates by the score with the PrintSamples() iterator. The implementation is seriously lame at the moment, but is perfectly acceptable for a crude prototype.

Instruments Simplified with Python Decorators

Python can be molded to look like an audio synthesis language with the help of decorators. In my last post, I showed an instrument graph built within a Python iterator class. However, Iterator classes are too clunky looking, and require more work than should be necessary for creating a simple graph. Read and download this blog’s example script here.

So how does it look in comparison?

@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))

The @Instr decorator signals to both the human and the computer that the proceeding definition is an instrument class. Much easier to deal with than having to fiddle around with class methods __init__(), __iter__() and next(). There’s less code to write, and far less chance to break the internals of an iterator class. And it more closely resembles an audio synthesis language. Here’s a near equivalent version written in Csound:

instr MyInstr
    idur = p3
    iamp = p4
    ifreq = p5
    itune = p6
    
    a1 oscils iamp * 0.5, ifreq, 0
    a2 oscils iamp * 0.5, ifreq * 2 ^ (itune / 12), 0
    kenv linseg 0, idur * 0.5, 1, idur * 0.5, 0
    out kenv * (a1 + a2)
endin

What’s going on exactly? Well, to be honest, decorators have been eluding me for the past two weeks. It wasn’t until last night as I was halfway through watching Eddie Izzard’s Dress to Kill that it suddenly clicked. So any explanation I give will be insufficient. I’ll just say that @Instr refers to a generic graph iterator class designed by me that creates a new iterator class that assimilates def MyInstr. If you want to know more, you can read Bruce Eckel’s Introduction to Python Decorators.

Question: Cake or death?

Python Iterator Class as Instrument Graph

I built an iterator class from the graph in yesterday’s example. Read and download the full script here. Let’s take a look at the class, then discuss the implications:

class MyInstr:
    '''My Instrument'''

    def __init__(self, dur, amp=1.0, freq=440, tune=12):
        a1 = Sine(amp * 0.5, freq)
        a2 = Sine(amp * 0.5, freq * 2 ** (tune / 12.0))
        self.out = Multiply(Sum(a1, a2), RiseFall(dur, 0.5))

    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()

What good is a graph defined as an iterator class? The class essentially works like a Csound instrument. Multiple instances with varied input can be created, like Csound score i-events. It is also, more or less, a new unit generator that can be patched into a new graph. Most importantly, they can be easily shared with others in the Slipmat community. Import. Reuse. Remix.

There is a downside. If you’re not familiar with Python code or Python iterator classes, then you probably can’t make heads or tails out of this. One of the fundamental principles of the Slipmat philosophy is that the code needs to be user-friendly. Iterator classes as graphs, well, that’s a big technical hurdle, especially for n00bs. I don’t like hurdles; they restrict the creative flow, and can be a show stopper.

Fortunately, there is a solution, which I’ll post later today. If you happen to follow my twitter feed, then you probably have figured out where I’m going.

Here’s the the new __main__:

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

This produces identical results to yesterday’s script. The only difference is that the code has been refactored to increase modularity and possibly clarity.

Thing-a-day Day 1: Single-Grain Synth

So I’ve decided to participate in Thing-a-day 2009. Which means that starting yesterday, I will create ten things over ten days, and share it with the world. Here’s a repost from my entry from yesterday:

Single Grain Presentation

Single-Grain is a simple granular synthesizer/audio processor built with Max 5.

You can download the original Max 5 Single-Grain patches here. If you don’t own Max 5, but own a Mac, you can download the stand-alone application here. Mileage may vary.

Single Grain Final Patch