Skip to content

Instantly share code, notes, and snippets.

@scztt
Last active November 4, 2019 01:03

Revisions

  1. scztt revised this gist May 4, 2017. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion twister-example.scd
    Original file line number Diff line number Diff line change
    @@ -2,9 +2,13 @@
    // classvar <endpointDevice="Midi Fighter Twister %", <endpointName="Midi Fighter Twister";
    // Where "Midi Fighter Twister 1" is TwisterDevice(\default) and "Midi Fighter Twister 2" is TwisterDevice(\secondary)

    // A twister device. These are singletons - there is only ever one registered per device.
    // A twister device. These are singletons - there is only ever one registered per device. It works ala Ndef, Pdef, etc, see Singleton help file.
    ~device = TwisterDevice(\default);

    // If your MIDI Fighter is named something other than the above, you can register it via:
    this.registerDevice(\myDevice, "endpoint device", "endpoint name");
    // And then access it via TwisterDevice(\myDevice)

    // Set state directly on device
    ~device.knobs[0].ledHue = 0.25;
    ~device.knobs[0].ledBrightness = 0.75;
  2. scztt created this gist May 4, 2017.
    153 changes: 153 additions & 0 deletions twister-example.scd
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,153 @@
    // Twister devices are expected to be named in this form:
    // classvar <endpointDevice="Midi Fighter Twister %", <endpointName="Midi Fighter Twister";
    // Where "Midi Fighter Twister 1" is TwisterDevice(\default) and "Midi Fighter Twister 2" is TwisterDevice(\secondary)

    // A twister device. These are singletons - there is only ever one registered per device.
    ~device = TwisterDevice(\default);

    // Set state directly on device
    ~device.knobs[0].ledHue = 0.25;
    ~device.knobs[0].ledBrightness = 0.75;
    ~device.knobs[0].ringBrightness = 0.8;
    ~device.knobs[0].value = 0.5;


    // Twister is a front-end connection to a particular device. You can have multiple Twister objects for a given device, but only one
    // connected at a time. This allows for multiple performance setups / "pages" that can be swapped on a single device. Twister objects
    // are still functional when they are not connected to a device, and will keep their state. Specifying a device or device name
    //

    ~t1 = Twister(); // to connect automatically, you could have used Twister(\default)
    ~t1.connect(\default); // connect

    // Accessing knobs
    ~t1.knobs[7]; // 8th knob
    ~t1.rows[0]; // top row of knobs
    ~t1.rows(2, 2); // knob at row 2, column 2 (zero indexed, so nominally the third row/col)
    ~t1.cols(1); // knobs in column 1


    ~t1.rows(0, 0).enable;
    ~t1.rows(0, 0).disable;

    // Knobs are normally enabled / disabled if they have a value attached to them.
    // These are set via knobCV and buttonCV setters. NumericControlValue or BusControlValue
    // are generally used here, but these should be backwards compatible with the CV
    // class from the Conductor quark as well.


    // Example: Connect to a Synth and some UI
    (
    ~c1 = NumericControlValue(spec:ControlSpec(20, 1000, \exp, 10));

    ~t2 = Twister(\default);
    ~t2.rows(0, 0).knobCV = ~c1;

    Server.default.waitForBoot {
    // a sound
    ~synth = {
    |lpFreq=100|
    LPF.ar(LFSaw.ar(200), lpFreq);
    }.play;

    // a number box
    ~view = View(bounds:Rect(200, 200, 100, 40)).layout_(HLayout(
    ~num = NumberBox().scroll_step_(10);
    )).front;

    // connect them up to ~c1
    ~c1.signal(\value).connectTo(~synth.argSlot(\lpFreq)).freeAfter(~synth).trace;
    ~c1.signal(\value).connectTo(~num.valueSlot).freeAfter(~view).trace;
    ~num.signal(\value).connectTo(~c1.valueSlot).freeAfter(~view).trace;
    ~synth.freeAfter(~view);
    }
    )




    // Similar example, but use buttonCV to trigger also
    (
    Server.default.waitForBoot {
    ~t3 = Twister(\default);

    ~synths = ();

    ~t3.rows.reverse.flatten.do {
    |knob, i|

    knob.knobCV = BusControlValue(0.2, spec:ControlSpec(100, 1000, \exp, 10));
    knob.buttonCV = OnOffControlValue();
    knob.ledColor = Color.hsv(1.0.rand, 1, 1);
    knob.toggle = true; // This means knob state is toggled between on/off once per button push. toggle==false means down is on, up is off.

    knob.buttonCV.signal(\on).connectTo({
    "Playing note %".format(i).postln;

    ~synths[i] = {
    |gate=1|

    LPF.ar(
    in: LFSaw.ar(\freq.kr),
    freq: \lpFreq.kr(lag:3)
    ) * Env.adsr().kr(gate:gate, doneAction:2);

    }.play(args:[
    \freq, (50 + i).midicps,
    \lpFreq, knob.knobCV.asMap
    ]);
    });

    knob.buttonCV.signal(\off).connectTo({
    "Stopping note %".format(i).postln;
    ~synths[i] !? { |n| n.set(\gate, 0) };
    })
    };
    }
    )


    // If you still have the window from the earlier example open, we can swap between each twister configuration by connecting them:
    ~t2.connect(\default);
    ~t3.connect(\default);



    // Random sequencer example
    (
    ~t4 = Twister(\default);
    ~t4.knobs.do({
    |k|
    k.knobCV = NumericControlValue(spec:[0, 1]);
    k.toggle = true;
    });

    SynthDef(\wn, {
    |vel=0|
    var sig;

    sig = WhiteNoise.ar(vel.linexp(0.01, 1, 0.3, 1));
    sig = sig * Env.perc(0.01, vel.linlin(0, 1, 0.05, 1)).kr(doneAction:2);

    Out.ar(0, sig);
    }).add;

    Pdef(\wn, Pbind(
    \instrument, \wn,
    \index, Pseries(),
    \dur, 0.125,
    \vel, Pfunc({
    |e|
    var knob = ~t4.knobs.wrapAt(e.index);

    { knob.ledColor = Color.green }.defer(s.latency);
    { knob.ledColor = Color.blue }.defer(s.latency + 0.2);

    if (knob.buttonCV.value == \on) {
    knob.knobCV.value
    } {
    Rest(0)
    }
    })
    )).play;
    )