Video Tutorials on MIDI based music production with Supercollider, jackd and DAW

I liked this approach so much I created a simple class that encapsulates the setup.

Example:

/////////////////////////////////////////////
// create a synth
(
SynthDef(\sine, {
    var freq = \freq.kr(220);
    var gate = \gate.kr(1);
    var sig = SinOsc.ar(freq);
    var aeg = Env.adsr(
        \atk.kr(0.01),
        \dec.kr(0.3),
        \suslevel.kr(0.5),
        \rel.kr(1),
        curve:\curve.kr(-4)
    ).ar(gate:gate, doneAction:Done.freeSelf);
    sig = Splay.ar(sig) * aeg * \amp.kr(0.3) * \vel.kr(1);
    Out.ar(\out.kr(0), sig);
}).add
)

/////////////////////////////////////////////
// show ui
n = NdefMixer(Server.default)
ProxyMeter.addMixer(n)

/////////////////////////////////////////////
// connect midi
MIDIClient.init()
MIDIIn.connectAll()

/////////////////////////////////////////////
// midi synth setup (see class below)
MidiSynth(\m1).synth(\sine) // primes the ndef with specified synth
MidiSynth(\m1).note(noteChan:2).cc(ctrl:[\rel, \sat], ccNum:[0, 1], ccChan:0)
MidiSynth(\m1).play()
MidiSynth(\m1).objects.first.set(\gate, 0) // silence the primed synth
MidiSynth(\m1).addSpec(\sat, [1, 4])
MidiSynth(\m1).addSpec(\rel, [1, 4])
MidiSynth(\m1).filter(200, {|in| PitchShift.ar(in, 2, 2, 0.01, 0.1)}).set(\wet200, 0.3)
(
MidiSynth(\m1).filter(210, {|in|
    var sat = \sat.kr(1);
    in = (in * sat).tanh * sat.sqrt.reciprocal;
    JPverb.ar(HPF.ar(in, 100), 5, 0, 5)
}).set(\wet210, 0.3)
)
//MidiSynth(\m1).disconnect


/////////////////////////////////////////////
// test with pattern
m = MIDIOut.newByName("IAC Driver", "Bus 1") // or whatever
(
Pdef(\midi,
    Pbind(
        \type, \midi,
        \midiout, m,
        \chan, 2,
        \degree, Pseq([0, 1, 3, 5].pyramid(2), inf),
        \scale, Scale.dorian,
        \dur, 0.5,
        \octave, Pseq([ 6, 5, 5, 6, 5, 5, 6, 5 ], inf),
        \legato, 0.2,
        \tempo, 1,
        \amp, 1)
)
)
Pdef(\midi).play
Pdef(\midi).stop

The class which you can place in Platform.systemExtensionDir

MidiSynth : Ndef {

    var <synthdef, <hasGate, <instrument;
    var <noteonkey, <noteoffkey, <cckey;

    *new {|key|
        var res = Ndef.dictFor(Server.default).envir[key];
        if (res.isNil) {
            res = super.new(key).prInit;
        };
        ^res;
    }

    prInit {
        noteonkey = "%_noteon".format(this.key).asSymbol;
        noteoffkey = "%_noteff".format(this.key).asSymbol;
        cckey = "%_cc".format(this.key).asSymbol;
        ^this;
    }

    note {|noteChan, note|
        MIDIdef.noteOn(noteonkey, {|vel, note, chan|
            if (this.hasGate) {
                this.put(note, instrument, extraArgs:[\freq, note.midicps, \vel, vel/127, \gate, 1])
            } {
                this.put(note, instrument, extraArgs:[\freq, note.midicps, \vel, vel/127])
            }
        }, noteNum:note, chan:noteChan)
        .fix;

        MIDIdef.noteOff(noteoffkey, {|vel, note, chan|
            if (this.hasGate) {
                this.objects[note].set(\gate, 0);
            }
        }, noteNum:note, chan:noteChan)
        .fix;
    }

    synth {|synth|
        synthdef = SynthDescLib.global.at(synth);
        instrument = synth;
        hasGate = synthdef.hasGate;
        this.prime(synth);
    }

    cc {|ctrl, ccNum, ccChan=0|
        var order = Order.newFromIndices(ctrl.asArray, ccNum.asArray);
        MIDIdef.cc(cckey, {|val, num|
            var ctrl = order[num];
            var spec = if (this.getSpec(ctrl).notNil) {
                this.getSpec(ctrl)
            }{
                [0, 1].asSpec;
            };
            var mapped = spec.map(val/127);
            this.set(ctrl, mapped);
        }, ccNum:ccNum, chan:ccChan)
        .fix;
    }

    disconnect {
        MIDIdef.noteOn(noteonkey).permanent_(false).free;
        MIDIdef.noteOff(noteoffkey).permanent_(false).free;
        MIDIdef.cc(cckey).permanent_(false).free;
    }
}
2 Likes