1. Video documentation of me using a brain-computer-interface (in this case an Emotiv headset reading EEG) to turn a motor controlling a pinch-valve using Open Sound Control.  This is a proof-of-concept project for a number of upcoming performance and interactive pieces.

  2. First experiment with Firefly, a plugin that allows communication in and our of Grasshopper, itself a parametric modeling plugin for Rhino, using an Arduino.  Here I am using two IR sensors as a sort of rudimentary 3D scan to explore the architectural properties of the Fremont Lab and using that data to control the loft of a surface in Rhino.

    First experiment with Firefly, a plugin that allows communication in and our of Grasshopper, itself a parametric modeling plugin for Rhino, using an Arduino.  Here I am using two IR sensors as a sort of rudimentary 3D scan to explore the architectural properties of the Fremont Lab and using that data to control the loft of a surface in Rhino.

  3. Karst final SuperCollider code

    //Karst
    /*
    a site-specific performance work for amplified gesture and prosthetic voice featuring Sonnets to Orpheus (Part 2, Number 11) by Rainer Maria Rilke.
    */

    //audio sample available here: http://soundcloud.com/meghatron

    //SERVER
    (
    Server.default = s = Server.internal.boot;
    s.waitForBoot({
    });
    //took out paren to save a step since I have to reboot entirely after every kill

    //ARDUINO
    //ibid
    SerialPort.listDevices;
    d = SerialPort.devices[2];
    f = SCPyduino.new(d);  //declares f an SCPyduino object
    )

    //NON SEQUENTIAL CLOSE  (rather than scrolling to the end, kill SCPyduion here when code block stopped before reboot)
    //close the serial port
    (
    f.close;
    )

    //KARST MAIN CODE BLOCK
    (
    //VARIABLES
    //synths
    var synth, scrape;

    //functions
    //ACTION - remane these functions and create a 4th, one for each sample
    var func, pfunc, ffunc;

    //sample variables
    var transform, death, machine, noisefloor;

    //more joshparametrical magic right here
    var flex1, flex2, controlflex1, controlflex2;

    //CONTROL
    controlflex1 = CtkControl.play;
    controlflex2 = CtkControl.play;

    //SAMPLES
    //Source = Rilke: Orpheus Sonnet pt2 10 “The machine threatens all we have gained, so long as it dare become the tyrant of spirit, rather than its servant.”
    machine = CtkBuffer.playbuf(“sounds/Karst_Machine.aif”).load;

    //Source = Rilke: Orpheus Sonnet pt2 11 “Many quietly acceptable methods of death have come to pass since onward conquering man first laid claim to the hunter’s arts;….”
    death = CtkBuffer.playbuf(“sounds/Karst_Death.aif”).load;

    //Source = Rilke: Orpheus Sonnet pt2 12 “Aspire to transform, O enraptured be by the fire wherein something elusive flames with brazen tidings of change;….”
    transform = CtkBuffer.playbuf(“sounds/Karst_Transform.aif”).load;

    //Source = Site Noisefloor, May 29, 2010
    noisefloor = CtkBuffer.playbuf(“sounds/Karst_Noisefloor.aif”).load;

    //KILL SWITCH
    //actions on CmdPeriod which is triggered by a sensor on pin 5
    CmdPeriod.doOnce({
        machine.free;
        death.free;
        transform.free;
        noisefloor.free;
        a.stop;
        b.stop;
        c.stop;
        g.stop;
        h.stop;
        k.stop;
        Server.killAll;
        ”I’m freeing the buffers!”.postln;
        });
        
    //SYNTHDEFS
    // trigger synth
    synth = CtkSynthDef(\synth, {arg imp, begin, out = 0, bufnum = 0, pan = 0, amp =0.5, deviation = 0, pch = 1;
        var trig, sig, sampstosecs, devAmt, outsig, env;
        env = EnvGen.kr(Env.new([0, 1,0], [0.1,0.1], \linear, 1));
        trig = Impulse.ar(imp);
        sampstosecs = (SampleRate.ir * BufRateScale.kr(bufnum));
        devAmt = (deviation * LFNoise1.kr( imp) * sampstosecs);
        sig = PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum) * pch, trig, (begin * sampstosecs) + devAmt, 0);
        outsig = Pan2.ar(sig*amp, pan);
        Out.ar(out, [outsig[0], DelayN.ar(outsig[1], 0.002, 0.002)]);
    });

    //contact mic synth for scraping the walls
    scrape = CtkSynthDef(\scrape, { arg bus;
        var signal;
        signal = SoundIn.ar(bus);
        Out.ar(bus,signal*1);
    });

    scrape.new(0,nil).bus_(0).play;
    scrape.new(0,nil).bus_(1).play;

    //FUNCTIONS
    //FUNC :: for use with impulse triggers
    func = {arg duration, amp, rhythms,  imps, begs, pans, dev;
    var start, now, index, dur, imp, beg, rhy, pan1;
    Task({
        start = Main.elapsedTime;
        now = 0;
        index = 0;
        imp = imps;
        while({
        now < duration;
        }, {
        now = Main.elapsedTime - start;
        rhy = rhythms[index%rhythms.size];
        
        imp = imps[index%imps.size];
        beg = begs[index%begs.size];
        pan1 =pans[index%pans.size];
        synth.new(0, nil).bufnum_(noisefloor.bufnum).imp_(4).begin_(beg).pan_(pan1).amp_(amp).deviation_(dev)
            .pch_(controlflex1).play;
        rhy.wait;
        index = index + 1;
        })
    }).play
    };

    //PFUNC :: make my func the pfunc
    pfunc = {arg duration, amp, rhythms, durs, imps, begs, pans, dev;
    var start, now, index, dur, imp, beg, rhy, pan1;
    Task({
        start = Main.elapsedTime;
        now = 0;
        index = 0;
        imp = imps;
        while({
        now < duration;
        }, {
        now = Main.elapsedTime - start;
        rhy = rhythms[index%rhythms.size];
        dur = durs[index%durs.size];
        imp = imps[index%imps.size];
        beg = begs[index%begs.size];
        pan1 =pans[index%pans.size];
        synth.new(0,10).bufnum_(death.bufnum).imp_(4).begin_(beg).pan_(pan1).amp_(amp).deviation_(dev).play;
        (rhy + dur).wait;
        index = index + 1;
        })
    }).play
    };

    //FFUNC :: machine function
    ffunc = {arg duration, amp, rhythms, durs, imps, begs, pans, dev;
    var start, now, index, dur, imp, beg, rhy, pan1;
    Task({
        start = Main.elapsedTime;
        now = 0;
        index = 0;
        imp = imps;
        while({
        now < duration;
        }, {
        now = Main.elapsedTime - start;
        rhy = rhythms[index%rhythms.size];
        dur = durs[index%durs.size];
        imp = imps[index%imps.size];
        beg = begs[index%begs.size];
        pan1 =pans[index%pans.size];
        synth.new(0, 10).bufnum_(machine.bufnum).imp_(4).begin_(beg).pan_(pan1).amp_(amp).deviation_(dev).play;
        (rhy + dur).wait;
        index = index + 1;
        })
    }).play
    };


    //ARDUINO

    //Arduino: Sensors
    //Left Piezo     ”a” “u” lop
    //f.analog[0].active_(1);
    //Left Flex     ”b” “v” lox
    f.analog[1].active_(1);
    //Left Force     ”c” “w” lor
    f.analog[2].active_(1);

    //Right Piezo “g” “x” rop
    //f.analog[3].active_(1);
    //Right Flex     ”h” “y” rox
    f.analog[4].active_(1);
    //Right Force “k” “z” ror
    //f.analog[5].active_(1);
        
    /*    
    //Left Piezo
    a = fork{
            loop{
                var lop;
                f.iterate;
                u= f.analog[0].value;
                ”u:”.post;
                u.postln;
                lop = (1000-u)/100;
                ”lop: imp: “.post;
                lop.postln;
                syn1.imp_(lop);
                0.001.wait;
            }
    };    
    */    
        
    m = 0;
    //Left Flex
    //3030: this is working, starts inactive, is trigger at first bend and cycles through noisefloor functions, the first of which is pitch affected by sensor value
    b = fork{
        t = Main.elapsedTime;
            loop{
                var lox;
                f.iterate;
                v= f.analog[1].value;
                flex1 = v;
                //controlflex1.set((flex1 / 500) - 0.5);
                controlflex1.set((flex1 / 500) - 0.5);
                ”v:”.post;
                v.postln;
                (v > 250).if({
                    (Main.elapsedTime > (t + 1)).if({
                        t = Main.elapsedTime;
                        m = m + 1;
                        m.postln;
                        [        
                        
                        //FUNC:::: duration, amp, rhythms, durs, imps, begs, pans, dev;
                        //duration = duration of the gesture, but may default to 10 seconds
                        //amp = amp
                        //rhythms = space between attacks in the gesture
                        //imps = how ofter the playbuff retriggers; low values = larger chunks o’ audio, inverse relationship means 0.1 = 10 seconds, 10 = .1 seconds
                        //begs = pickup spot
                        //pans = pan
                        //dev    = deviation or randomness on the location of the pickup spot    
                        //duration should be longer than rhytms for more than one note        
                            {func.value(30, 0.5, [1],[0.02],[0.1],[0, 1, -1],[2]).play},
                            {func.value(30, 0.5, [10],[0.4],[0.1],[0, 1, -1],[2]).play},
                        ].wrapAt(m).value;
                        })
                    });
                    
                
                0.001.wait;
            }
    };

    /*

    //Left Force
    c = fork{
            loop{
                var lor;
                f.iterate;
                w= f.analog[2].value;
                ”w:”.post;
                w.postln;
                lor = 100-w;
                ”lor: imp: “.post;
                lor.postln;
                syn3.imp_(lor);
                syn3.pan_(lor-80);
                syn3.imp_(lor+0.1);
                syn3.pan_(lor-90);
                0.001.wait;
            
            }
    };



    //Right Piezo
    g = fork{
            loop{
                var rop;
                f.iterate;
                x= f.analog[3].value;
                ”x:”.post;
                x.postln;
                rop = x-350/100;
                ”imp “.post;
                rop.postln;
                0.001.wait;
            }
    };
    */
    n = 0;
    //Right Flex
    h = fork{
        t = Main.elapsedTime;
            loop{
                var lox;
                f.iterate;
                y= f.analog[4].value;
                flex1 = y;
                controlflex2.set((flex1 / 1000) - 0.5);
                ”y:”.post;
                y.postln;
                (y > 350).if({
                    (Main.elapsedTime > (t + 1)).if({
                        t = Main.elapsedTime;
                        n = n + 1;
                        n.postln;
                        [                        
                            {pfunc.value(2, 0.5, [0.25],[0.03],[3],[1],[0, 1, -1],[2]).play},    
                            {ffunc.value(2, 0.5, [0.25],[0.03],[3],[4,5,6,7,8,9],[0, 1, -1],[2]).play},    
                            {ffunc.value(2, 0.5, [0.25],[1],[5],[4,5,6,7,8,9],[0, 1, -1],[2]).play},    
                            {ffunc.value(2, 0.5, [0.25],[3],[3],[4,5,6,7,8,9],[0, 1, -1],[2]).play}
                        ].wrapAt(m).value;

                        })
                    });
                    
                lox = 250-y;
                
                0.001.wait;
            }
    };

    //Right Force
    //Right Force has a yellow wire conencting to the breadboard in prototype layout
    k= fork{
            loop{
                var ror;
                f.iterate;
                z =0;
                z = f.analog[2].value;
                ”z:”.post;
                z.postln;
                ((z>350) and: {z<1000}).if({
                CmdPeriod.run;
                });
                
                0.001.wait;
            }
    };

      )
    //SEQUENTIAL CLOSE  (this is repeated at the top)
    //close the serial port
    (
    f.close;
    )

  4. six sensor supercollider (by meghatron)
Two breadboard set up with the two sets of sensors: piezo, flex and force, one for each side of the tunnel.

    six sensor supercollider (by meghatron)

    Two breadboard set up with the two sets of sensors: piezo, flex and force, one for each side of the tunnel.

  5. SC+ Arduino: 3 flex sensors

    //Prototype code for the composition KARST, transitioning from a score to a trigger based performance interface
    //this example uses a default SC sample: a11wlk01.wav

    //SERVER
    (
    Server.default = s = Server.internal.boot;
    s.waitForBoot({
    s.scope;
    FreqScope.new(400, 200, 0);
    });
    )

    //SERIAL
    (
    SerialPort.listDevices;
    d = SerialPort.devices[2];
    f = SCPyduino.new(d);  //declares f an SCPyduino object
    )

    (
    //VARIABLES + SCORE
    var score;
    //synths
    var synth;
    //samples
    var voice;
    //function
    var func;

    var syn1, syn2, syn3, syn4, syn5, syn6;

    //score
    score = CtkScore.new;

    //SAMPLE
    voice = CtkBuffer.playbuf(“sounds/a11wlk01.wav”).load;
    score.add(voice);

    //SYNTHDEF
    // trigger synth
    synth = CtkSynthDef(\help_PlayBuf, {arg imp, begin, out = 0, bufnum = 0, pan = 0, amp =0.5, deviation = 0;
        var trig, sig, sampstosecs, devAmt, outsig;
        trig = Impulse.ar(imp);
        sampstosecs = (SampleRate.ir * BufRateScale.kr(bufnum));
        devAmt = (deviation * LFNoise1.kr( imp) * sampstosecs);
        sig = PlayBuf.ar(1, bufnum, BufRateScale.kr(bufnum), trig, (begin * sampstosecs) + devAmt, 0);
        outsig = Pan2.ar(sig*amp, pan);
        Out.ar(out, [outsig[0], DelayN.ar(outsig[1], 0.002, 0.002)]);
    });

    //FUNCTIONS
    //function for use with impulse triggers
    func = {arg starttime, duration, amp, rhythms, durs, imps, begs, pans, dev;
    var now, index, dur, imp, beg, rhy, pan1;
    now = 0;
    index = 0;
    imp = imps;
    while({
    now < duration;
    }, {
    rhy = rhythms[index%rhythms.size];
    dur = durs[index%durs.size];
    imp = imps[index%imps.size];
    beg = begs[index%begs.size];
    pan1 =pans[index%pans.size];
    score.add(synth.new(now+starttime, dur).bufnum_(voice.bufnum).imp_(imp).begin_(beg).pan_(pan1).amp_(amp).deviation_(dev));
    now = now + rhy + dur;
    index = index + 1;
    })};

    /*
    func.value(0.5+(inc*0.5), 5, 0.5, [0.25],[3],[30],[4,5,6,7,8,9],[0, 1, -1],[2]);
    examples from original composition with value examples to aim for with sensor data
    func: starttime, duration, amp, rhythms, durs, imps, begs, pans, dev;
    starttime     0.5
    duration    5
    amp        0.5
    rhythms    [0.25]
    durs        [3]
    imps        [30]
    begs        [4,5,6,7,8,9]
    pans        [0, 1, -1]
    dev        [2]
    */

    //synth instantiations
    syn1 = synth.new(0, 100).bufnum_(voice.bufnum).imp_(0).begin_(0).pan_(0).amp_(4).deviation_(2).play;
    syn2 = synth.new(105, 100).bufnum_(voice.bufnum).imp_(2).begin_(0).pan_(-1).amp_(4).deviation_(2).play;
    syn3 = synth.new(210, 100).bufnum_(voice.bufnum).imp_(4).begin_(0).pan_(-1).amp_(4).deviation_(2).play;

    //arduino
    f.analog[5].active_(1);
    f.analog[2].active_(1);
    f.analog[0].active_(1);

    //right flex sensor
    //controls impulse: sample is heard clearly when sensor is fully flexed
    //posting the value of x as well as the calculated value aids in determining the best calculation to control the loop
    a = fork{
            loop{
                var bob;
                f.iterate;
                x= f.analog[5].value;
                “x:”.post;
                x.postln;
                bob = x-200;
                syn1.imp_(bob);
                “imp A: “.post;
                bob.postln;
                0.001.wait;       
            }
    };

    //center  flex sensor
    //controls panning
    b = fork{
            loop{
                var rob;
                f.iterate;
                y = f.analog[2].value;
                “y:”.post;
                y.postln;
                rob = y-400;
                syn2.pan_(rob);
                “pan B: “.post;
                rob.postln;
                0.001.wait;
           
            }
    };

    //left flex sensor
    c = fork{
            loop{
                var tod;
                f.iterate;
                z = f.analog[0].value;
                “z:”.post;
                z.postln;
                tod = (z-250)/100;
                syn3.begin_(tod);
                “begin C: “.post;
                tod.postln;
                0.001.wait;
           
            }
    };

      )
    //cease reading values
    (
    a.stop;
    b.stop;
    c.stop;
    )
    //close the serial port
    (
    f.close;
    )

  6. SuperCollider + Arduino update

    Problem:

    f.firmataVersion;
    returns
    None

    Solution:

    download  Arduino version 0016
    install Standard Firmata from this version of Arduino

    Further Issues:

    I can’t run version 16 on my machine (solutions posted here don’t fix the java issue) but can upload Standard Firmata from any machine that can.

    Now I can start to tie sensor values to triggers in SuperCollider as with the Max/MSP prototype.

  7. Karst update

    The most significant change to the piece following the first few weeks of prototyping, experimenting and feedback is that the work will be performed walking forward through the tunnel, rather than seated at it’s mouth.  Instead of fabricated wooden forms that bulge out from the terrain, a series of sensors will reach from the back of the tunnel to the front.  This aligns more closely with the inspirational work (Horn walking from the back of the room to the front) as well as Orpheus’s passage up out of Hades.  Additionally, the length of the tunnel maps to a specific temporal event with a natural beginning and end.

    While this piece was first conceived of as being created exclusively from my own voice and breath with significant digital synthesis (after Wishart), the inclusion of the noisefloor of the site has been considered and now, as well, the sound of the exoskeletal form scraping against the wall, again referencing the audio component of Horn’s performance.

    Technical & Logistical work:
    Sensors and speakers have been ordered and shipped and once they arrive I will return to the site to conduct a next round of experiments with sounds and other performance logistics, this time using SuperCollider, rather than Max/MSP, directly with the Arduino and sensors.  Tivon has started an Arduino & SuperCollider wiki that we can use to aggregate strategies.

    I am now considering using a combination of sensors, both trigger and force, and will be focusing on how to weave all of these components together in the composition.