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;
}
}