How in the {func} do you make a monophonic MIDI synth?

A very common pattern in SuperCollider is to have a responder (GUI, MIDI, OSC, HID…) that controls a resource.

playerThingy = Something(....);

guiControl = AGuiWidget(...)
.action_({ |view|
	playerThingy.setSomeControl(... value based on view...);
});

This is a general pattern that can be used in many contexts… including this one. The resource is the MonoPortaVoicer. The MIDIdefs are the responders.

Also, a useful methodology in cases like this is to be figure out how to control the resource programmatically first – only after you’re sure you can get the desired result by code, then you can put that code into the responders. (A development principle of “solve one problem at a time” – instead of thinking “how do I use MIDI to control this object that I never used before” = two problems muddled into one, you could think “1/ how do I use this object? 2/ how do I do #1 with MIDI” = step by step, solve two problems in turn).

So, MonoPortaVoicer first:

(
SynthDef(\lead, { |out, gate = 1,
	freq = 440, freqlag = 0.1, detun = 1.008, pan = 0,
	ffreq = 1200, ffreqMul = 3, ffreqDcy = 0.15, rq = 0.2,
	atk = 0.005, dcy = 0.2, sus = 0.6, rel = 0.1|
	
	// Voicer uses 'gate' for velocity
	var amp = Latch.kr(gate, gate).linlin(0, 1, 0.05, 0.25);
	
	var freqs = Lag.kr(freq, freqlag) * [1, detun];
	
	var filtEg = EnvGen.kr(Env([ffreqMul, ffreqMul, 1], [0, ffreqDcy], \exp), gate);
	var eg = EnvGen.kr(Env.adsr(atk, dcy, sus, rel), gate, doneAction: 2);
	
	var sig = Saw.ar(freqs).sum;
	
	sig = RLPF.ar(sig, (ffreq * filtEg).clip(20, 20000), rq);
	
	Out.ar(out, Pan2.ar(sig, pan, amp * eg));
}).add;
)

v = MonoPortaVoicer(1, \lead);
v.portaTime = 0.15;

v.trigger(60.midicps, 0.5);  // plays C
v.trigger(64.midicps, 0.5);  // glides to E

// let go of E -- glides back to C
v.release(64.midicps);

// after this, no more keys are held
// so the note fully releases
v.release(60.midicps);

Then for MIDI, all that is necessary is to take the control statements (which were just worked out) and put them into the MIDIdefs:

(
MIDIdef.noteOn(\on, { |vel, num|
	v.trigger(num.midicps, vel/127);
});

MIDIdef.noteOff(\off, { |vel, num|
	v.release(num.midicps);
});
)

hjh

1 Like