How to implement portamento/legato in a synthdef

I’m getting a lot of clicking when I play my PmonoArtic pattern. I copied the example synthdef from the docs and made some slight changes but I don’t understand what’s causing the clicking. I also tried an even simpler synthdef that is using Lag.kr to see if that would maybe give me a better clue but I still have the same issue. Overall I’m just trying to replicate what mono synths can usually do and I would like to sequence the legato/slide kind of like a 303 synth. Is there a simple way to do this in SC?

First synthdef I tried:

(
SynthDef.new(\acid, {| out = 1, freq= 100, amp= 3, width=0.5, ffreq = 1400, rez=0.8|
	var plfo, fcurve, sig;
	plfo = SinOsc.kr(6, mul:0, add:1);
	freq = Lag.kr(freq, 0.05) * plfo;
	fcurve = EnvGen.kr(Env.adsr(0.01, 0.2, 0.1, 20), 0.5);
	fcurve = (fcurve - 0.01).madd(0.7, 1) * ffreq;
	sig = Pan2.ar( Pulse.ar(freq, width) );
	sig = RLPFD.ar(sig, fcurve, rez,) *
	EnvGen.kr(Env.adsr(0.04, 0.2, 0.5, 0.1), 0.5, doneAction: Done.freeSelf);
        ///also for some reason the amp argument doesn't do anything here so I just hardcode 3.2
	Out.ar(out, sig * 3.2 );

}).add;
)

(
~acid = PmonoArtic(
    \acid,
	\legato, Pseq([0.25, 1.0], inf),
	\degree, Pxrand(
		[ Pxrand([5, 8, 12, 3, 4, 7, 3, 4]-12, 2),
			Pseq([5, 8, 12, -6, 4, -14, -6, 4], 1)
		], inf),
	\dur, Pseq([0.25, 0.50, 1, 0.25, 0.50, 0.25, 0.25, 0.25, 1],inf)
);

~acid.play();


)

Second simple synthdef I tried using the same PmonoArtic Pattern:

(
SynthDef.new(\acid, {| out = 1, freq= 100, amp= 3, width=0.5, ffreq = 1400, rez=0.8|
	var sig;

	sig = Pan2.ar( Pulse.ar(Lag.kr(freq, 0.2)) );
	sig = RLPFD.ar(sig, 400 , rez);

	Out.ar(out, sig * 3.2 );

}).add;
)

For every note, PmonoArtic does one of two things at the end:

  • Event sustain >= event delta: Connect to the next note.
  • Sustain < delta: Release the current note (and the next event starts a new one).

This means PmonoArtic needs to have control over the note release.

Among the changes you made to the SynthDef, you removed the gate argument and substituted a constant 0.5 for the EnvGen gate.

These SynthDef features are exactly the ones that give PmonoArtic control over the note release!

If PmonoArtic can’t release the note gracefully (by setting gate to 0 and letting the envelope fade out), then – it needs to get rid of the note somehow, so it just brutally /n_frees it – no gentle fade out, just a hard cut off = click.

In short, I can’t think of any audio-rate case where it would ever be legit to use PmonoArtic without a gated envelope (i.e., never delete the gate argument from a synth to use with PmonoArtic).

(
SynthDef.new(\acid, {| out = 1, freq= 100, amp= 3, width = 0.5, ffreq = 1400, rez = 0.8, gate = 1|
	var plfo, fcurve, sig;
	plfo = SinOsc.kr(6, mul:0, add:1);
	freq = Lag.kr(freq, 0.05) * plfo;
	fcurve = EnvGen.kr(Env.adsr(0.01, 0.2, 0.1, 20), 0.5);
	fcurve = (fcurve - 0.01).madd(0.7, 1) * ffreq;
	sig = Pan2.ar( Pulse.ar(freq, width) );
	sig = RLPFD.ar(sig, fcurve, rez,) *
	EnvGen.kr(Env.adsr(0.04, 0.2, 0.5, 0.1), gate, doneAction: Done.freeSelf);
	Out.ar(out, sig * amp );
}).add;
)

(
~acid = PmonoArtic(
    \acid,
	\legato, Pseq([0.25, 1.0], inf),
	\degree, Pxrand(
		[ Pxrand([5, 8, 12, 3, 4, 7, 3, 4]-12, 2),
			Pseq([5, 8, 12, -6, 4, -14, -6, 4], 1)
		], inf),
	\dur, Pseq([0.25, 0.50, 1, 0.25, 0.50, 0.25, 0.25, 0.25, 1],inf),
	\amp, 3  // <<-- yes, the amp arg DOES work! if you specify it
);

~player = ~acid.play();
)

The default amp in an event is 0.1. Since you originally didn’t put an amp into the pattern, then the event default amp is used. No need to hardcode in the SynthDef.

Also… If you simply do ~acid.play then you lose programmatic access to the EventStreamPlayer – so then the only way you can stop it is cmd-dot. That’s not really a sustainable programming practice. Always save the player in a variable (same as always save a synth = { ... }.play in a variable).

hjh

3 Likes

It’s working great now. I have learned my lesson on how important it is to use arguments correctly. I thought I could just hardcode values in place and it would be ok.

SynthDef argument rates might be of interest here. in short:
SynthDef.new(name, ugenGraphFunc, rates, prependArgs, variants, metadata)