iii residency – steno


19 June 2017

From 10.6.2017 – 24.6.2017 I had the pleasure to be a resident at the instrument inventors initiative (iii) in the Hague. Other posts on the iii residency.


I decided to spend the first week at iii concentrating on software setups. This included looking into recent developments in SuperCollider-based live-coding systems where I was pleasantly surprised to find Steno by Julian Rohrhuber,

a little concatenative livecoding language embedded in SuperCollider, [which] makes code for combining synth graphs very short, so that writing endless chains is like writing a single long word. A word is a program. A letter is a synth.

Without going into further details, Steno allows to write concatenative synthesis trees of (mostly simplistic) filters and sources (“quellen”) in a short yet reasonably writable string.

I pasted an already quite complex environment using my recent implementation of the Lotka-Volterra chaotic oscillator below.

One of the niceties of this system is that it makes Julian’s most recent work on “Generic additive synthesis” very easy to implement and manipulate. A paper he wrote together with Juan Sebastián Lach Lau will soon appear in the Proceedings of the International Congress on Music and Mathematics (2015). I am sure that it will appear as a link on Julian’s publication list. Curious people may contact him for a preprint.

t = Steno.push; s.boot;

// the concatenated synthesis string, i.e. "main program"
-- {(uxxxp(([xupq])xqqqqqq))u+}q

// change synthesis parameters in the fly
g = #[2, 5, 2, 9, 8].scramble * 0.005;

t.setGlobal(\h, {|args| g.wrapAt(args[\index]) * 0.125});
t.setGlobal(\h, {|args| g.wrapAt(args[\index])});
t.setGlobal(\h, {|args| g.wrapAt(args[\index]) * 16});

t.set(\x, \a, {rrand(-1, 1.0)}, \b, {rrand(-1, 1.0)}, \c, {rrand(-1, 1.0)}, \d, {rrand(-1, 1.0)} );
t.set(\x, \a,  0.0);
t.set(\x, \b,  0.0);
t.set(\x, \c,  0.0);
t.set(\x, \d,  0.0);

( // synthesis definitions
t.fadeTime = 5;

t.operator('+', { |x, y, ctl|
    x * y
}, 2);

t.filter(\x, { |in, ctl|
    var influence = RMS.ar(in, 10).sum;
    var a = (1.5 + (\a.kr(0, 5) * influence)).clip(1.4, 2);
    var b = (1.5 + (\b.kr(0, 5) * influence)).clip(1.4, 2);
    var c = (0.5 + (\c.kr(0, 5) * influence)).clip(0.5, 0.7);
    var d = (1.5 + (\d.kr(0, 5) * influence)).clip(1.4, 2);
    
    var h = \h.kr(0.01, 0.1);
    
    (
        in + (LotkaVolterra.ar(
            SampleRate.ir*0.5,
            a, b, c, d,
            h,
            xi: 0.1,
            mul: Line.kr(0, 1, 10)
        )) * 0.2
    ) * ctl[\env]
});

t.filter(\p, { |in, ctl|
    (
        LeakDC.ar(in)
        * ({
            LFNoise1.kr(LFNoise1.kr(0.5).range(1, 4)).exprange(1, 14.2).lag(0, 1)
        }!2)
    ).tanh * ctl[\env]
});

t.filter(\u, { |in, ctl|
    in + Splay.ar(
        SinOsc.ar(750 * [0.5, 1, 0.125, 0.75, 0.25] * 0.99)
    ) * ctl[\env]
});

t.filter(\q, { |in, ctl|
    in * Splay.ar(
        LFPulse.ar([5, 10, 5.03], (ctl[\depth]+1).reciprocal, 0.2).lag(0.1)
    ) * ctl[\env]
});
t.set(\q, \mix, 0.96)
)

Steno is available from within SuperCollider through the Quarks extension system.