001 Granular Piano

Description

Short samples from Arvo Pärt's Für Alina are granulated and triggered with movement sensors attached to the wrists and ankles of the performer. The movements are based on a feedback loop between full-body listening to and creation of the sounds.

Performed by Külli Roosna, created on 13 December 2019 at Dansearena nord in Hammerfest, Norway.

Source Code

Requires MiniBeeUtils and movement sensors. We use MiniBees, but with a bit of rewriting the MiniBeeUtils should work with any sensor data. See the full gitlab repo for more details on implementation.

To use: run the setup code first, then the sketch. If you run several sketches in a row you should only run setup once.

// 001-granular-piano.scd

(
    // create a globally accessible function that can be 
    // triggered when required

    ~arvopbind = {|fadeInTime=10, level=0.2, 
        fadeOutTime=10, mb, threshold=0.03|
        var bufs, mbTrig;
        var piano;
        var eq, eqBus;
        var rev, revBus, revTime=3;
        var out, outBus;

        outBus = Bus.audio(s, ~numSpeakers);
        out = Synth(\route, [
            \in, outBus,
            \out, ~masterBus,
            \attack, fadeInTime,
            \release, fadeOutTime,
            \amp, level,
        ]);

        eqBus = Bus.audio(s, ~numSpeakers);
        eq = Synth(\eq, [
            \in, eqBus,
            \out, outBus,
            \locut, 120,
            \hishelffreq, 800,
            \hishelfdb, -6
        ]);

        revBus = Bus.audio(s, ~numSpeakers);
        rev = Synth(\jpverb, [
            \in, revBus,
            \out, eqBus,
            \revtime, revTime,
            \mix, 0.2,
        ]);

        mbTrig = mb.collect{|id, idx|
            MBDeltaTrig.new(
                speedlim: 0.5, 
                threshold: threshold, 
                minibeeID: id,
                minAmp: -20,
                maxAmp: -6,
                function: {|dt, minAmp, maxAmp|
                    // the minibee triggers a pattern. Make sure that 
                    // this pattern terminates by including at least
                    // one key that stops by itself!
                    Pbind(
                        \instrument, \grbufphasor,
                        \buf, Prand(~buf[\arvo]),
                        \dur, dt.linlin(0.0, 1.0, 0.5, 2),
                        \attack, Pkey(\dur) * 0.2,
                        \release, Pkey(\dur) * Pwhite(1.0, 2.0),  
                        \rate, Pwrand(
                            [-1, -0.5, 0.25, 0.25, 1], 
                            [0.75, 0.5, 0.25, 0.1, 0.1].normalizeSum, 
                            inf
                        ),
                        \rateDev, Pwhite(0, 0.001),
                        \posDev, Pwhite(0, 0.01),
                        \playbackRate, Pwrand(
                            [-1, -0.5, 0.25, 0.25, 1], 
                            [0.5, 0.5, 0.25, 0.3, 0.3].normalizeSum, 
                            inf
                        ),
                        \grainsize, Pwhite(0.01, 0.8),
                        \grainfreq, Pwhite(4, 20),
                        \db, dt.linlin(0.0, 1.0, minAmp, maxAmp),
                        \pan, Pwhite(-1.0, 1.0),
                        \panDev, Pwhite(0.0, 1.0),
                        \out, revBus,
                    ).play;
                };
            ).play;
        }; 

        ~play[\arvopbind] = {|i|
            if(i.isInteger){
                mbTrig[~mb.indexOf(i)].play;
            };
            if(i.isCollection){
                i.do{|item|
                    mbTrig[~mb.indexOf(item)].play;
                }
            };
        };

        ~stop[\arvopbind] = {|i|
            if(i.isInteger){
                mbTrig[~mb.indexOf(i)].stop;
            };
            if(i.isCollection){
                i.do{|item|
                    mbTrig[~mb.indexOf(item)].stop;
                }
            };
        };

        ~free[\arvopbind] = {
            out.set(\gate, 0);
            fadeOutTime.wait;
            mbTrig.do(_.free);
            [revBus, eqBus].do(_.free);
        };
    }
)