x Trigger Notation

In Roll You Own Syntax, I theorized how users could construct their own systems of notation using strings. I’ve constructed a working function, called trig(), to show how it’s done.

First, let’s see trig() in action. The following is complete conceptual prototype Slipmat program that generates a rock drum groove with 8th note hats:

#!/usr/bin/env slipmat

from JakeLib.Generators import trig
from EasyKit import hat, snare, kick
@trig('x.x. x.x. x.x. x.x.') hat()
@trig('.... x... .... x...') snare()
@trig('x... .... x... ....') kick()

This horizontal system for notating triggers, and others like it, can greatly improve the legibility of a piece, while catering to a composer’s preferred style of working. A composer quickly scans this and comprehends it without having to reconstruct it in their head from a list of individual events:

@0    hat()
@0    kick()
@0.5  hat()
@1    hat()
@1    snare()
@1.5  hat()
@2    hat()
@2    kick()
@2.5  hat()
@3    hat()
@3    snare()
@3.5  hat()

The form is lost in translation.

Building the function is pretty straight forward if you’re somewhat experienced with Python. I put together the following function definition, with docstrings, in roughly 15 minutes:

def trig(seq, res=0.25):
    Creates a numeric sequence from a string and returns a list.

    A string trigger sequencer, where an 'x' creates a trigger, and a
    '.' creates a rest. All other glyphs are ignored. The resolution
    of triggers and rests are determined by the argument res.

    seq -- A string containing a sequence of 'x' triggers and '.' rests
    res -- Resolution of note triggers and rests
    return -- A numeric list
    L = []  # The return list
    p = 0   # Position in sequence

    for c in seq:
        if c == 'x':
            L.append(p * res)
            p += 1            
        elif c == '.':
            p += 1

    return L

The trig() function accepts a string formatted in what I call ‘x trigger notation.’ The function parses the string and auto-generates a list of numbers representing trigger times. A trigger is denoted by an ‘x’, while a rest is a ‘.’. All other glyphs are ignored. I use a single space between beats for clarity. The default resolution is a 16th note, though trig() accepts an optional argument for changing the resolution, increasing its usefulness.

Import, Reuse, Remix

The best part about custom functions is that they can be reused multiple times in multiple programs by multiple people with the use of the import. No refactoring of code, no copy and paste, no reinventing of the wheel. Just import, reuse, remix.

Importing Modules and Reusing Code

I want to begin discussing the implications of yesterday’s Python-Csound mockup code (which I’ll refer to as slipmat for the time being), starting with with imports:

import Wavetable
from Gen import sine
from Pitch import cpspch

All of Csound’s 1400+ opcodes are available at all times. Great for convenience, perhaps not so great for organization. In contrast, the Python language starts out with only the basics, a clean slate. To extend functionality, users import modules. This is a cleaner approach than having it all hang out. There are some other advantages, too.

First, let’s look at a hypothetical import block. Let’s say you were to design a “computer network music” ensemble inspired by The Hub. Some communication modules you might include:

import Jack
import MIDI
import Network
import OSC

A computer network music ensemble sounds like it might be a complex piece of software. Complex enough where doing all your work in one file would be tedious. So you decide to start a new file, my_network.slip, where you store your own custom opcode/unit generator function definitions. In your main file, you write this to import:

import my_network

Not only can you use my_network for this project, but that code can be reused in any number of future projects. Code reusability is a beautiful thing. In fact, this would apply to any properly written slipmat document. For example, a composition would double as a library of synthesizers that you could plug into your own work:

import Trapped  # Trapped in Convert by Dr. Richard Boulanger
signal = Trapped.blue(22.13, 4, 0, 9.01, 600, 0.5, 20, 6, 0.66)

See trapped.csd.