Transient Aphasia




(

var width= Window.screenBounds.width, height= Window.screenBounds.height;

var fps= 60;
var buffersize= 512;
var scale= Window.screenBounds.width;
var perspective= 1;
var distance= 1;

var window = Window("Article Codes: Triggered Euclidean Sequencer", Rect(100,height, width, Window.screenBounds.height, false));


var can= Canvas3D(window, Rect(480,  0, width-480, height)).scale_(scale).perspective_(perspective).distance_(distance)
.background_(Color.green(0.1,0.8));

var itemWave= Canvas3DItem.new
.color_(Color.new255(200,200,200,210))
.transforms = [
    Canvas3D.mTranslate(0,0,-10),
];

/* Paths */
var path  = [];
/* Buffers */
var buffer;
var rate = 0.005;

/* Add items */
can.add(itemWave);


s = Server.default;

s.boot;

/* Synthdefs */
s.waitForBoot({
  SynthDef(\SOSkick,
    { arg  amp = 0, out = 0,freq = 70;
      var hatosc, hatenv, hatoutput;

      hatenv = EnvGen.ar(Env.perc(0.003,0.3),doneAction:2);
      hatoutput = (PMOsc.ar([MouseX.kr(freq,freq+6,'exponential'), MouseX.kr(MouseY.kr(freq-5,freq,\exponential),freq+5,'exponential')],VOsc3.ar(freq/1000,20,30,40,0.3),1,6)* hatenv);
      Out.ar([0,2],Pan2.ar(hatoutput, PMOsc.ar(18,freq/12000,6), amp));
    }
  ).add;

  SynthDef(\snare909,{ arg out =0,amp=0,mul=1,velocity=1, freq = 8040;
    var env = 0.5*EnvGen.ar(Env.perc(0.02,0.5),doneAction:2);
    var output = (PMOsc.ar([MouseX.kr(freq -10 ,freq,'exponential'), MouseX.kr(MouseY.kr(freq-8,freq-6,\exponential),freq,'exponential')],freq/300,3,4)* env);
    Out.ar([0],0.25*amp*Rotate2.ar(output[0],output[1],LFNoise0.kr(0.2)));
    Out.ar([4],1.5*amp*Rotate2.ar(output[0],output[1],LFNoise0.kr(0.2)));

  }).add;

  SynthDef(\closedhat, { arg amp = 0, freq=8000;
    var hatosc, hatenv, hatnoise, hatoutput;

    hatenv = {Line.ar(1, 0, 1, doneAction:2)};
    hatoutput = (PMOsc.ar(freq,MouseX.kr(freq -5 ,freq,'exponential'),2,4,1)*SinOsc.ar([6000,MouseX.kr(5998,5999,\exponential)]) * hatenv*0.6*amp);
    Out.ar([0],0.1*Pan2.ar(hatoutput,LFTri.kr(0.01)));
    Out.ar([6],0.6*Pan2.ar(hatoutput,LFTri.kr(0.01)));

  }).add;

  SynthDef(\openhat, {arg amp = 0, freq=4000;
      var hatosc, hatenv, hatoutput;
    var excitation = LPF.ar(WhiteNoise.ar(1), freq, 1) * (0.1);

    var env_perc = EnvGen.kr(Env.perc(0.0005,0.3),doneAction:2);
    var membrane = excitation * env_perc * amp;

    var env_gen;
    env_gen = EnvGen.kr(Env.perc,amp,timeScale: 0.4, doneAction: 0);
    Out.ar([0,10], 0.2*Pan2.ar(MembraneHexagon.ar(env_gen * PMOsc.ar(freq,2,4,2,0.2), freq/40000 + 0.012, MouseY.kr(0.9992, 0.999, 1)),LFNoise0.kr(0.2),0.25));

      /*hatenv = EnvGen.ar(Env.perc(0.003,1.5),doneAction:2);
      hatoutput = (PMOsc.ar([MouseX.kr(freq,freq+6,'exponential'), MouseX.kr(MouseY.kr(freq-5,freq,\exponential),freq+5,'exponential')],VOsc3.ar(freq/1000,20,30,40,0.3),1,6)* hatenv);
      Out.ar([0,10],Pan2.ar(hatoutput, PMOsc.ar(18,freq/12000,6), amp));*/
    }
  ).add;


  // Create the pattern maker and set tempo.
  t = TempoClock(130/60.0);
  ~velocity = 0.5;
  ~patternMaker = { arg name, f, s, v, freq = [400];
    Pbind(
      \instrument, name,
      \dur, Prand([1, 0.2, 0.5, 3], inf),
      \amp, v*Pseq(Bjorklund(f,s),inf),
      \freq, Pseq(freq,inf);
    ).asStream;
  };

  //OSC
  SynthDef(\clockTrigger,{ |clock|
    SendTrig.kr(Impulse.kr(clock*4),0,1);
  }).add;

  s.sync;

  // register to receive this message
  o = OSCFunc({ arg msg, time;
    //[time, msg].postln;
    switch(msg[2])
    {0}{
      ~kick.next(Event.default).play(t);
      ~snare.next(Event.default).play(t);
      ~closedhat.next(Event.default).play(t);
      ~openhat.next(Event.default).play(t);
    };
  },'/tr', s.addr);

  //Default values
  ~kick      = ~patternMaker.value(\SOSkick, 0, 16, ~velocity, [70]);
  ~closedhat = ~patternMaker.value(\closedhat, 0, 21, ~velocity, [8040]);
  ~openhat   = ~patternMaker.value(\openhat, 0, 33, ~velocity, [8000]);
  ~snare     = ~patternMaker.value(\snare909, 0, 18, ~velocity, [4000]);

  w = Synth(\clockTrigger, (["clock", t.tempo]));

  // UI Function
  ~generateUI = { arg window, instr, cc1, cc2, note;
    var slider, updater, model, setValueFunction, setValueFunction2, setValueFunction3, updater2, updater3, model2, model3, button, numberbox, numberbox2, ctrlSpec, ctrlSpec2;
    var control1, control2, triggernote;
    var numSliders, multislider;
    var o;

    /*** MVC ***/
    //model
    model = (myValue: 0);
    setValueFunction = {|value|
      model [\myValue] = value;
      model.changed(\value, value);
    };

    //updater
    updater = {|theChanger, what, val|
      {
        if(what == \value, {
          numberbox.string_(val);
          slider.value_(val/numberbox2.value);
        });
      }.defer;
    };
    model.addDependant(updater);

    //model2
    model2 = (myValue: 0);
    setValueFunction2 = {|value|
      model2 [\myValue] = value;
      model2.changed(\value, value);
    };

    //updater2
    updater2 = {|theChanger, what, val|
      {
        if(what == \value, {
          ctrlSpec2 = ControlSpec(0, val, \lin, 1, 0, );
          ctrlSpec = ControlSpec(0, ctrlSpec2.map(val), \lin, 1, 0, );
          if(numberbox.string.asInteger > val){
            numberbox.string_(val);
            slider.doAction;
            slider.value_(ctrlSpec.map(val));
          };
          numberbox2.value_(val);
          numSliders=numberbox2.value;
        });
      }.defer;
    };
    model2.addDependant(updater2);

    //model3
    model3 = (myValue: 0);
    setValueFunction3 = {|value|
      model3 [\myValue] = value;
      model3.changed(\value, value);
    };

    //updater3
    updater3 = {|theChanger, what, val|
      {
        if(what == \value, {
          val.postln;
          switch(instr)
          {\kick}{
            ~kick = ~patternMaker.value(\SOSkick,
              numberbox.string.asInteger, numberbox2.value, ~velocity,[50,13000, \exp, 1].asSpec.map(multislider.value));}

          {\snare}{
            ~snare = ~patternMaker.value(\snare909,
              numberbox.string.asInteger, numberbox2.value, ~velocity,[50,13000, \exp, 1].asSpec.map(multislider.value));}

          {\closedhat}{
            ~closedhat = ~patternMaker.value(\closedhat,
              numberbox.string.asInteger, numberbox2.value, ~velocity,[50,13000, \exp, 1].asSpec.map(multislider.value));}

          {\openhat}{
            ~openhat = ~patternMaker.value(\openhat,
              numberbox.string.asInteger, numberbox2.value, ~velocity,[50,13000, \exp, 1].asSpec.map(multislider.value));};
        });
      }.defer;
    };
    model3.addDependant(updater3);

    /*** GUI ***/
    //view
    ctrlSpec = ControlSpec(0, 0, \lin, 1, 0, "");
    ctrlSpec2 = ControlSpec(0, 64, \lin, 1, 16, "" );

    //Slider
    slider = Slider(window, Rect(110,0, 200, 40))
    .value_(model[\myValue])
    .action_({|view| setValueFunction.value(ctrlSpec.map(view.value))});

    //Number
    numberbox = StaticText(window, Rect(320, 0, 40,40))
    .string_(model[\myValue])
    .stringColor_(Color.white)
    .font_(Font("Arial Rounded MT Bold", 20))
    .action_({|view| setValueFunction.value(ctrlSpec.map(view.value))});

    //Number
    numberbox2 = EZNumber(window,  110@40, label:"Length", controlSpec: ctrlSpec2 )
    .setColors(Color.new255(60,140,60),Color.white,background: Color.new255(60,140,60), numTypingColor:Color.red)
    .font_(Font("Arial Rounded MT Bold", 14))
    .action_({|view|
      ctrlSpec2 = ControlSpec(0, view.value, \lin, 1, 0, );
      ctrlSpec = ControlSpec(0, ctrlSpec2.map(view.value), \lin, 1, 0, );
    });
    numberbox2.doAction;

    //Button
    button = Button(window, Rect(340,0, 50, 40))
    .states_([["Trigger"], ["Trigger"]])
    .font_(Font("Arial Rounded MT Bold", 12))
    .action_({|v|
      if(v.value==0){
      }{
        switch(instr)
        {\kick}{
          ~kick = ~patternMaker.value(\SOSkick,
            numberbox.string.asInteger, numberbox2.value, ~velocity, [50,13000, \exp, 1].asSpec.map(multislider.value));}

        {\snare}{
          ~snare = ~patternMaker.value(\snare909,
            numberbox.string.asInteger, numberbox2.value, ~velocity, [50,13000, \exp, 1].asSpec.map(multislider.value));}

        {\closedhat}{
          ~closedhat = ~patternMaker.value(\closedhat,
            numberbox.string.asInteger, numberbox2.value, ~velocity, [50,13000, \exp, 1].asSpec.map(multislider.value));}

        {\openhat}{
          ~openhat = ~patternMaker.value(\openhat,
            numberbox.string.asInteger, numberbox2.value, ~velocity, [50,13000, \exp, 1].asSpec.map(multislider.value));};
        button.value_(0);
      }
    });

    //Multislider
    multislider = MultiSliderView(window,Rect(10,150,480-20,110));
    multislider.gap_(500/(numberbox2.value*2)); // width in pixels of each stick
    multislider.value=Array.fill(numberbox2.value, {|v| 0.5});
    multislider.colors_(Color.new255(150,0,0,255),Color.new255(150,0,0,255));
    multislider.action = { arg q; // size is set automatically when you set the value
      [50,13000, \exp, 1].asSpec.map(q.value).postln;
    };
    multislider.isFilled_(true);
    multislider.elasticMode_(1);
    //multislider.showIndex = true; // cursor mode


    control1 = CCResponder({ |src,chan,num,value|
      setValueFunction.value(ctrlSpec.map((value/127)));
      },
      nil, // any source
      nil, // any channel
      cc1, // CC number cc1
    );

    control2 = CCResponder({ |src,chan,num,value|
      setValueFunction2.value(value/2);
      },
      nil, // any source
      nil, // any channel
      cc2, // CC number cc2
    );

    triggernote = NoteOnResponder({ |src,chan,note,vel|
      setValueFunction3.value(note);
      },
      nil, // any source
      nil, // any channel
      note, // note number
      nil // any vel
    );
    //Cleanup
/*  window.onClose_({model.removeDependant(updater); model2.removeDependant(updater2); model3.removeDependant(updater3); control1.remove; control2.remove; triggernote.remove; numberbox.remove;});*/
  };

  //window.addFlowLayout( 10@10, 20@5 );
  window.view.decorator = FlowLayout( Rect(0,0,480,height), 10@10, 20@5 );

  window.view.background_(Color.new255(50,50,50));

  //Title
  StaticText(window, Rect(0, 0, 500, 30))
    .string_("Triggered Euclidean Sequencer")
    .align_(\center)
    .stringColor_(Color.white)
    .font_(Font("Arial Rounded MT Bold", 20));

  // Generate UI for each timbre with Trigger and CC control numbers
  ~row1 = ~generateUI.value(window, \kick,      1, 2, 40);
  ~row2 = ~generateUI.value(window, \snare,     3, 4, 42);
  ~row3 = ~generateUI.value(window, \closedhat, 5, 6, 36);
  ~row4 = ~generateUI.value(window, \openhat,   7, 8, 38);

  /* TEMPO */
  ~tempoSpec = ControlSpec(20, 400, \lin, 1, 130, "" );
  ~tempoControl = EZNumber(window,  110@40, label:"Tempo", controlSpec: ~tempoSpec )
    .setColors(Color.new255(60,140,60),Color.white,background: Color.new255(60,140,60), numTypingColor:Color.red)
    .font_(Font("Arial Rounded MT Bold", 14))
    .action_({|view|
    t.tempo = view.value/60.0;
    t.tempo.postln;
    });
  ~tempoControl.doAction;

  /*** MVC ***/
  //model
  ~tempoModel = (myValue: 0);
  ~tempoSetValueFunction = {|value|
    ~tempoModel [\myValue] = value;
    ~tempoModel.changed(\value, value);
  };

  //updater
  ~tempoUpdater = {|theChanger, what, val|
    {
      if(what == \value, {
        ~tempoControl.value_(val);
        ~tempoControl.doAction;
      });
    }.defer;
  };
  ~tempoModel.addDependant(~tempoUpdater);

  ~tempoCC = CCResponder({ |src,chan,num,value|
    ~tempoSetValueFunction.value(value.linlin(0,127,20,400));
      },
      nil, // any source
      nil, // any channel
      9, // CC number 9
    );

  /* Tempo Trigger */
  //Button
  ~tempTrigger = Button(window, 50@40)
    .states_([["Trigger"], ["Trigger"]])
    .font_(Font("Arial Rounded MT Bold", 12))
    .action_({|v|
    w.free;
    w = Synth(\clockTrigger, (["clock", t.tempo]));
    });

  ~tempoMidiTrigger = NoteOnResponder({ |src,chan,note,vel|
    [src,chan,note,vel].postln;
      w.free;
        w = Synth(\clockTrigger, (["clock", t.tempo]));
      },
        nil, // any source
      nil, // any channel
      35, // note number
      nil // any vel
    );

  /* Visualization */
   buffer  = Buffer.alloc(s, buffersize, 2);

    s.sync;
    {
        RecordBuf.ar(In.ar(0,2), buffer);
    Silent.ar;
    }.play;
    s.sync;
    can.animate(fps, {|frame|

    buffer.getn(0, 1023, {|data| path= data.clump(3)});
        itemWave.paths= [path];

    can.transforms = [
            Canvas3D.mRotateY(frame/100 + (sin(frame/70)*2) %2pi),
            Canvas3D.mRotateX(frame/100%2pi),
            Canvas3D.mScale(sin(frame/80*4)*0.3+1)
        ];
        itemWave.transforms = [
            Canvas3D.mRotateY(frame/60%2pi)
        ];


    });

   can.mouseMoveAction = {|v,x,y, mod|

        can.transforms = [
            Canvas3D.mRotateY(x / -200 % 2pi),
            Canvas3D.mRotateX(y / 200 % 2pi)
        ];
        can.refresh;
    };


    CmdPeriod.doOnce({window.close; buffer.free;NoteOnResponder.removeAll; CCResponder.removeAll;});

  // ~bus = Bus.audio(s,2);

  window.front;
  window.onClose_({
    w.free;z.free;s.freeAll;o.free;
    ~row1.free; ~row2.free; ~row3.free; ~row4.free; NoteOnResponder.removeAll; CCResponder.removeAll;
  });
});

//s.scope;
)

s.makeWindow;