Home About Performances Teaching Collaborations Tech Audiovisual Works 100 Sketches Calendar Blog

010 Moving Mountains

Published on: 2020-03-15

Description

Following the flow of kinesthetic experience, navigating the structures of the body and using the impact of the sound to inspire movement and imagery.

The sound is a combination of granulated cello and vocal recordings, as well as a heavily filtered kick drum sample.

Source Code

Requires KF Supercollider-quarks 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.

(
    // define global variables
    ~numSpeakers = 2;
    ~masterBus = 0;
    ~root = "/path/to/your/project/root";
    ~sketchdir = ~root+/+"sketches";
    ~mb = (9..16); // ids of minibees
    ~play = Dictionary.new();
    ~free = Dictionary.new();
    ~stop = Dictionary.new();

    // load in buffers, one dictionary entry per directory in the audio
    // directory. These can be found in the gitlab repo.
    ~buf = Dictionary.new;
    PathName((~root+/+"audio").standardizePath).folders.do({|folder| 
        s.makeBundle(nil, {
            ~buf.add(
                folder.folderName.asSymbol -> folder.entries.collect({|file|
                    Buffer.readChannel(s, file.fullPath, channels: 0);
                })
            );
        });
    });

    // set up MIDI, in this case a Launchpad controller
    MIDIClient.init;
    MIDIClient.sources.do{|device, idx|
        if(device.name.contains("Launchpad MIDI 1")){
            MIDIIn.connect(idx, device);
            "% connected\n".postf(device.name);
            ~launchPadOut = MIDIOut.newByName(
                "Launchpad", 
                "Launchpad MIDI 1"
            );
            ~hasLaunchPad = true;
            ~launchPadOut.control(176, 0, 127);
            ~launchPadOut.latency = 0;
        }
    };

    // some basic colors for the Launchpad
    ~launchPadColor = (
        \off: 12,
        \redLo: 13,
        \redHi: 15,
        \amberLo: 29,
        \amberHi: 63,
        \yellow: 62,
        \greenLo: 28,
        \greenHi: 60
    );

    // Evaluate the necessary SynthDefs:
    SynthDef(\route, {
        var sig, env;
        env = EnvGen.kr(
            Env.asr(
                \attack.kr(0.1), 1.0, 
                \release.kr(0.1)
            ), 
            gate: \gate.kr(1), 
            doneAction: \da.kr(2)
        );
        sig = In.ar(\in.kr(~numSpeakers), ~numSpeakers);
        sig = sig * env * \amp.kr(1, 0.1);
        Out.ar(\out.kr(0), sig);
    }).add;

    SynthDef(\pm, {
        var sig, env, freq, modenv, mod;
        freq = \freq.kr(440);
        modenv = EnvGen.kr(
            Env.asr(
                \modattack.kr(0.1), 1.0, 
                \modrelease.kr(0.1)
            ), 
            gate: \gate.kr(1)
        );
        mod = modenv * \pmindex.kr(0);
        env = EnvGen.kr(
            Env.asr(
                \attack.kr(0.1), 1.0, 
                \release.kr(0.1)
            ), 
            gate: \gate.kr(1), 
            doneAction: \da.kr(2)
        );
        sig = PMOsc.ar(freq, \modfreq.kr(440), mod);
        sig = sig * AmpCompA.kr(freq);
        sig = sig * env * \amp.kr(0.3, 0.1);
        sig = PanAz.ar(~numSpeakers, sig, \pan.kr(0), width: \width.kr(2));
        Out.ar(\out.kr(0), sig);
    }).add;

    SynthDef(\flanger, { 
        var in, sig, env, maxdelay, maxrate, dsig, mixed, mix, local; 
        env = EnvGen.kr(
            Env.asr(
                \attack.kr(0.01), 1,
                \release.kr(1)
            ), 
            gate: \gate.kr(1), 
            doneAction: \da.kr(2)
        );
        mix = \mix.kr(1);
        maxdelay = 0.013; 
        maxrate = 10.0; 
        in = In.ar(\in.kr((~numSpeakers + ~numSubs), ~numSpeakers)); 
        local = LocalIn.ar(~numSpeakers); 
        dsig = AllpassL.ar(
            in + (local * \feedback.kr(0.0)), 
            maxdelay * 2, 
            LFPar.kr( 
                \rate.kr(0.06) * maxrate, 
                0, 
                \depth.kr(0.08) * maxdelay, 
                \delay.kr(0.1) * maxdelay
            ), 
            \decay.kr(0.0)
        ); 
        mixed = in + dsig;
        LocalOut.ar(mixed); 
        sig = (1 - mix) * in + (mixed * mix);
        sig = sig * env * \amp.kr(1);
        Out.ar(\out.kr(0), sig); 
    }).add;

    SynthDef(\jpverb, {
        var source, sig, env, mix;
        mix = \mix.kr(1);
        env = EnvGen.kr(
            Env.asr(
                \attack.kr(0.01), 1,
                \release.kr(1)
            ), 
            gate: \gate.kr(1), 
            doneAction: \da.kr(2)
        );
        source = In.ar(\in.kr(~numSpeakers), ~numSpeakers);
        source = source.sum * env;
        sig = JPverb.ar(
            source, 
            \revtime.kr(1), 
            \damp.kr(0), 
            \size.kr(1), 
            \early.kr(0.707)
        );
        sig = (1 - mix) * source + (sig * mix);
        sig = SplayAz.ar(~numSpeakers, sig, spread: 0.5, width:~numSpeakers);
        Out.ar(\out.kr(0), sig);
    }).add;

    SynthDef(\eq, {
        arg in, attack=0.1, release=0.1, amp=1, out=0, gate=1, da=2,
        locut=20, loshelffreq=200, loshelfdb=0, 
        hishelffreq=1500, hishelfdb=0, hicut=20000, 
        peakfreq=600, peakrq=1, peakdb=0;
        var sig, env;
        env = EnvGen.kr(
            Env.asr(attack, 1, release), 
            gate: gate, 
            doneAction: da
        ); 
        sig = In.ar(in, ~numSpeakers);
        sig = BLowShelf.ar(sig, loshelffreq, db: loshelfdb);
        sig = BHiShelf.ar(sig, hishelffreq, db: hishelfdb);
        sig = BHiPass.ar(sig, locut);
        sig = BLowPass.ar(sig, hicut);
        sig = BPeakEQ.ar(sig, peakfreq, peakrq, peakdb);
        sig = sig * amp * env;
        Out.ar(out, sig);
    }).add;

    SynthDef(\grbufphasor, {
        var sig, env, pos, posDev, buf, duration, trig, rate, 
        rateDev, startPos, endPos, pan, panDev, playbackRate;
        buf = \buf.kr(0);
        playbackRate = \playbackRate.kr(1);
        startPos = \startPos.kr(0);
        endPos = \endPos.kr(1);
        duration = (endPos - startPos).abs * (
            BufFrames.kr(buf) / SampleRate.ir()
        );
        env = EnvGen.kr(
            Env.asr(
                \attack.kr(0.01), 1,
                \release.kr(1)
            ), 
            gate: \gate.kr(1), 
            doneAction: \da.kr(2)
        );
        trig = Impulse.kr(\grainfreq.kr(10));
        posDev = \posDev.kr(0);
        pos = Line.kr(
            startPos, 
            endPos, (duration * playbackRate.reciprocal)
        ); 
        pos = (pos + TRand.kr(0.0, posDev, trig)).mod(endPos);
        rate = \rate.kr(1);
        rateDev =  \rateDev.kr(0);
        rate = rate + TRand.kr(0.0,  rateDev, trig);
        pan = \pan.kr(0);
        panDev = \panDev.kr(0);
        pan = pan + TRand.kr(0.0, panDev, trig);
        pan = pan.mod(1.0);
        sig = GrainBuf.ar(
            ~numSpeakers, 
            trig, 
            \grainsize.kr(0.2), 
            buf, 
            rate, 
            pos, 
            pan: pan
        );
        sig = sig * env * \amp.kr(0.3, 0.1);
        Out.ar(\out.kr(0), sig);
    }).add;
)