Frogger

This is what happens when frogger doesn’t look both ways before crossing the street / pond:

gi_square ftgen 4, 0, 256, -7, 1, 128, 1, 0, -1, 128, -1

instr 1
    idur = p3
    iamp = p4
    
	kenv line 0.00230, idur, 0.01978
	a1 oscil iamp, 1 / kenv, gi_square
	out a1
endin
...
i 1 0 0.96493 -0.707

Download: dead_frogger.csd

Lists as Micro-Sequencers

On Friday, I listed nine ways in which python methodologies could be used with the @ scheduler. How would they work in a real-world musical context? Today, I’m showcasing the List as a super convenient micro-sequencer.

When the @ scheduler is given a list of numbers, every value in the list is used to schedule an event; This saves keystrokes and increases legibility. Let’s see this applied to a simple four-beat rock groove with 8th note hats:

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

That plays hat() eight times, and snare() and kick() twice each. This beats having to type out 12 events.

Alternatively, an identifier can point to a predefined list, thus, a sequence can be reused multiple times. The following stores a complex hi-hat pattern in identifier p, and then plays it four times:

p = [0, 0.5, 1, 1.5, 2, 2.25, 2.5, 2.75, 3, 3.75]

@0  @p hat()
@4  @p hat()
@8  @p hat()
@12 @p hat()

Here’s the shorthand equivalent:

p = [0, 0.5, 1, 1.5, 2, 2.25, 2.5, 2.75, 3, 3.75]

@[0, 4, 8, 12] @p hat()

That’s 40 events in two lines of code, with improved legibility. If this was presented as 40 individual events, it would not be obvious that the same hat pattern is repeated four times.

Banks of Patterns

A list can be utilized as a bank of patterns, a list of lists. In the following example, an empty bank is created, filled with three patterns, and then used in a four measure sequence.

b = []
b.append([0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5])               # x.x. x.x. x.x. x.x.
b.append([0, 0.5, 1, 1.5, 2, 2.25, 2.5, 2.75, 3, 3.75])  # x.x. x.x. xxxx x..x
b.append([0, 0.5, 1, 1.5, 2, 2.5, 2.75, 3, 3.5, 3.75])   # x.x. x.x. x.xx x.xx

@0  @b[0] hat()
@4  @b[1] hat()
@8  @b[0] hat()
@12 @b[2] hat()

Bonus Round — Eight Ways to Notate 8th Note Hats

Some good, some bad, some ugly. All produce the same result.

1. @map(lambda x: x / 2.0, range(0, 8)) hat()
2. @[0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5] hat()
3. @[i / 2.0 for i in range(0, 8)] hat()
4. @[i / 2.0 for i in range(8)] hat()
5. @[0, 2] @[0, 1] @[0, 0.5] hat()
6. @[0, 1, 2, 3] @[0, 0.5] hat()
7. @range(0, 4) @[0, 0.5] hat()
8. @range(4) @[0, 0.5] hat()

Examples 2, 6 and 8 are my personal favorites.

Nine Rules for Scheduling Events

How would Slipmat’s @ scheduler work when applying various Python methodologies?

To celebrate the release of my friend Sarah MacLean’s new book, Nine Rules to Break When Romancing a Rake, here are 9 rules for scheduling events. Each of the following examples produce the exact same result:

1. Individual Events

@0 foo()
@1 foo()
@2 foo()
@3 foo()

2. List

@[0, 1, 2, 3] foo()

3. Identifier

t = [0, 1, 2, 3]
@t foo()

4. List Comprehension

@[i for i in range(0, 4)] foo()

5. Loop

for i in range(0, 4):
    @i foo()    

6. Function

def bar():
    @0 foo()
    @1 foo()
    @2 foo()
    @3 foo()
    
bar()

7. Nested

foo()
1:
    foo()
    1:
        foo()
        1:
            foo()

8. Item Iteration

items = (0, 1, 2, 3)
@[i for i in items] foo()

9. Map

def bar(x): return x
@(map(bar, range(0, 4)) foo()

And a special message for Sarah, “Ninjas.”

Slipmat Prototype & Processing

I experimenting combining the first Slipmat prototype (built with the Csound API) with Processing. Getting the two systems working together was fairly straight forward. Here’s the code that generated the video.

The following method from class Disc does two things when a disc collides with a border: change direction of the disc and trigger a slipmat event:

public void moveDisc() {
    x += xVelocity * SCALE_VELOCITY;
    y += yVelocity * SCALE_VELOCITY;

    if (x <= DISC_RADIUS || x >= width - 1 - DISC_RADIUS) {
        xVelocity *= -1;
        playNote();
    }

    if (y <= DISC_RADIUS || y >= height - 1 - DISC_RADIUS) {
        yVelocity *= -1;
        playNote();
    }
}

The method playNote() also belongs to class Disc, and is where the actual triggering of the Slipmat event occurs. The instances sp1 and sp2 are part of the Slipmat system. The “sp” is short for “sine percussion.”

public void playNote() {
    sp1.playNote(0.0, 0.35 * (1 - x / width), frequency);
    sp2.playNote(0.0, 0.35 * x / width, frequency);
}

Live Coding and Capturing a Perfomance

It’s the latest fad that’s sweeping computer music. And I would love for Slipmat to have this ability in its arsenal of tools. Without having to sacrifice non-realtime rendering for computationally expensive processes, of course.

The following conceptual live coding prototype shows what a simple session would look like if it was modeled on the Python interpreter:

$ slipmat --capture_performance my_session.txt
>>> from LiveCodeSeq import seq
>>> from MyBassLibrary import rad_rezzy
>>> from random import random
>>> p[0] = [int(random() * 12) for i in range(0, 16)]
>>> p[1] = [int(random() * 12) for i in range(0, 16)]
>>> p[0]
[5, 9, 11, 8, 7, 8, 5, 1, 10, 7, 4, 4, 6, 4, 4, 2]
>>> p[1]
[6, 6, 5, 3, 5, 7, 8, 4, 0, 0, 8, 7, 9, 7, 2, 4]
>>> r = rad_rezzy()
>>> s = seq(instr=r, pattern=p[0], base_pch=6.00, resolution=1/16, tempo=133)
>>> s.start()
>>> s.change_pattern(pattern=p[1], on_beat=0)
>>> @60 s.stop(onbeat=0)

I have a gut feeling that there are some changes that should be made. Though as a starting point, this isn’t a terrible one.

Being able to capture a live coding performance would be fantastic. Not sure how workable it would be, but perhaps such a feature would produce a file that could be played back later:

$ cat my_session.txt
@0             global.seed(7319991298)
@4.04977535403 from LiveCodeSeq import seq
@8.43528123231 from MyBassLibrary import rad_rezzy
@10.9562488312 from random import random
@15.6027957075 p[0] = [int(random() * 12) for i in range(0, 16)]
@20.7757632586 p[1] = [int(random() * 12) for i in range(0, 16)]
@26.2462371683 p[0]
@29.3961696828 p[1]
@34.0424988199 r = rad_rezzy()
@40.3211374075 s = seq(instr=r, pattern=p[0], base_pch=6.00, resolution=1/16, 
                   tempo=133)
@45.5491938514 s.start()
@47.8991166715 s.change_pattern(pattern=p[1], onbeat=0)
@52.6267958091 @60 s.stop(onbeat=0)

The @ schedules are the times in which return was originally pressed for each event. Looks like I’ll be spending some time with ChucK soon.

Early Java Slipmat Prototype

Slipmat has been a pet project of mine for a few years now. The first working prototype was realized in Java. The code for this is up at sourceforge.

Originally, the idea was for slipmat to be a java interface layer that sits on top of Csound. Hence the slipmat metaphor. At one point, it became much too unwieldily and I had abandoned it. Until later when I picked up Python, and decided to give it another go. Python made things much easier all the way around, but I eventually abandoned that too, for reasons I’ll go into later.

Here is an excerpt from one of the original Java prototype examples:

public ControllerExample() {
    /* Create empty SynthRack */
    SynthRack synthRack = new SynthRack(false);
    
    /* Create modules */
    LinearSlide linearSlider = new LinearSlide();
    SynthBass bass = new SynthBass();
    Output output = new Output();

    /* Add modules to the SynthRack */
    synthRack.addModule(linearSlider);
    synthRack.addModule(bass);
    synthRack.addModule(output);

    /* Patch the SynthBass to the Output */
    output.setInput(bass.getOutput());

    /* Create a sequence */
    linearSlider.slide(1.0, 0.05, 2.0, bass.getOscillator1Detune());
    linearSlider.slide(1.125, 0.05, 1.5, bass.getOscillator1Detune());
    bass.playNote(0.0, 8.0, 0.5, 100);
    bass.playNote(2.5, 6.0, 0.25, 200);

    /* Set duration and press play */
    synthRack.setDuration((double) SCORE_DURATION);
    synthRack.startCsound();
}

The full code can be seen here. Once I get things sorted out at sourceforge, I’ll post the Python package there as well.

Music and Visuals

If slipmat at its core is a general purpose programming language augmented with advanced timing and scheduling capabilities, then slipmat would be well suited for both music and visuals.

This isn’t really too much of a stretch. Audio unit generators won’t be built into the core language, but instead will be stored as separate modules and imported as needed; The same would be true for visual modules. Musical GUI elements such as sliders are already visual in nature. There are existing audio/visual systems out in the wild, such as Max/MSP/Jitter and Impromptu. Various functions and objects designed for music could also be applied to visuals; The same envelope that controls frequency can be used to control the position of a circle:

t = 10                          # time = 10 beats
my_circle = Circle(0, 200, 15)  # x position, y position, radius
env = line(0, t, 1)             # start value, duration, end value
sine(1.0, 440 * env)            # amp, frequency
my_circle.xpos(env * 400)       # x position over time

The example is a bit crude and omits many practical things, but the idea of syncing visuals with audio is there.

Beyond music and visuals, anything that is time-based would theoretically work in slipmat, providing a module is written.

Roll Your Own Score Syntax

A music language that comes with a fully-loaded set of string capabilities, including regular expressions, would allow users to develop their own methods for notating music. For example, here is a horizontal representation for rhythm guitar.

@0  strum('C',  '/... /... /./. /...')
@4  strum('Dm', '/... /... //// /./.')
@8  strum('G7', '/... /... ///. ../.')
@12 strum('C',  '/... /... /... ///.')

The slash triggers an event and advances time by a 16th note. The dot just advances time by a 16th note. A space does nothing.

These 4 lines of code represent 24 events. Not only does this shorten the score and save keystrokes, but is far more readable than if each individual event was explicitly written. In fact, I bet there are many guitar players out there that could play this as it’s written, providing he or she was given a brief explanation about the notation.

What about a system for triggering drums? Here’s the all-to-familiar 4-beat rock pattern with 8th note hats.

trigger(hat(),   'x.x. x.x. x.x. x.x.')
trigger(snare(), '.... x... .... x...')
trigger(kick(),  'x... .... x... ....')

Once again, the music is notated horizontally. Instead of 12 lines of code, there are only 3. Let’s take a look at the vertical equivalent:

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

In theory, people could write slipmat modules that mimic other text-based notation systems, such as the Csound score language.

import CsoundScore as CS
from MyLibrary import bassFM
from MyLibrary import pad

# For mapping slipmat instruments to a Csound-styled score
instrs = {"i1": bassFM(), "i2": pad()}

# A Csound-styled score
score = '''
i 1 0 2 0.5 7.00
i 1 + . .   6.05

i 2 0 8 0.333 8.09 0.77 1000
'''

CS.playScore(instrs, score)  # Play Csound-styled score

That would translate to:

@0 bassFM(2, 0.5, 7.00)
@2 bassFM(2, 0.5, 6.05)
@0 pad(8, 0.333, 8.09, 0.77, 1000)

Much of this can be done with some of the existing music languages out in the wild. The guitar strum and drum trigger examples can be accomplished in pure Csound code (see dseq — A Drum Machine Micro-Language). Even more so, Csound combined with the Python API can do some truly amazing things along these lines. Though it probably wouldn’t be nearly as fluid as a language that had this in mind when being designed from the ground up.

If you haven’t done so yet, take a moment to ponder the possibilities. And be sure to think about the things others will dream up that can be simply imported into your own compositions/synthesizers/sequencers/etc…