Hi,
I am looking for a way to lag one synth’s parameter according to the previous setting. For instance, we set the amp of a synth at 0.1, then put it to 0.6 which the lag time should be longer than for 0.3, but proportionally. I am thinking in the GUI context when sometimes on the slider you jump from one value to another one.
My idea is to store the previous settings in a variable and then compute the time lag according to this previous value.
Am I in the right direction, or does there exist some other way, or trick to manage that maybe more efficiently?
You are trying to write a stateful context. A very simple way to implement it would be to store the old and new states and set the lag accordingly:
(
~amp = 0.1; // Initial amplitude value
~lag = 0.1; // Initial lag time value
SynthDef(\asynth, {
|freq=440, ampB=0.1, lagB=0.1|
var sig = SinOsc.ar(freq) * Lag.kr(ampB, lagB).poll(1)
Out.ar(0, sig ! 2);
}).add;
~setAmp = {
|newAmp|
var diff = (newAmp - ~amp).abs;
~lag = diff * 20;
~amp = newAmp;
~synth.set(\ampB, ~amp, \lagB, ~lag.postln);
};
)
~synth = Synth(\asynth, [\freq, 440, \ampB, ~amp, \lagB, ~lag]);
~setAmp.(0.192);
~setAmp.(0.622);
~setAmp.(0.511);
Yes, thanks for confirming, that was what I thought.
1 Like
You can also do it in the language with some interpolation functions and a routine. Something like this ( not tested, double check it before using it):
(
~linearInterp = { |start, end, t| start + (t * (end - start)) };
~expInterp = { |start, end, t| start * ((end / start) ** t) };
~sineInterp = { |start, end, t| start + ((end - start) * sin(t * pi / 2)) };
SynthDef(\gsynth, { |out=0, freq=440, amp=0.1|
var sig = SinOsc.ar(freq) * amp;
Out.ar(out, sig ! 2);
}).add;
~currentAmp = 0.1;
~targetAmp = 0.1;
~interpFunc = \linear;
~interpDuration = 1;
~updateRate = 20;
~interpRoutine = nil;
~startInterpolation = { |target, duration, func=\linear|
var start = ~currentAmp;
~targetAmp = target;
~interpDuration = duration;
~interpFunc = func;
~interpRoutine.stop;
~interpRoutine = Routine({
var steps = ~interpDuration * ~updateRate;
var stepDuration = 1 / ~updateRate;
var interpFunction = switch(func,
\linear, { ~linearInterp },
\exponential, { ~expInterp },
\sine, { ~sineInterp }
);
steps.do { |i|
var t = (i + 1) / steps;
~currentAmp = interpFunction.(start, target, t);
~synth.set(\amp, ~currentAmp);
stepDuration.wait;
};
"Interpolation OK. Current AMP: %\n".postf(~currentAmp);
}).play;
};
~synth = Synth(\gsynth, [\freq, 440, \amp, ~currentAmp]);
~setAmp = { |newAmp, duration, curve=\linear|
~startInterpolation.(newAmp, duration, curve);
};
~setAmp.(0.8, 2, \linear);
)
~setAmp.(0.2, 3, \exponential);
~setAmp.(0.6, 4, \sine);
~setAmp.(0.1, 2, \linear);
~updateRate = 50; // new update rate
~setAmp.(0.9, 2, \exponential);
Yes, it works, but the first solution is more straightforward and can be modulated with VarLag.
This said, it depends on the context.
You can accomplish something similar with Slew
. With this you are not controlling the lag time, but rather the lag rate in units per second:
(
~slewDb = {
var sig = Saw.ar(220);
var slew = \dbRate.kr(3); // rate of change in dB per second
var db = Slew.kr(\db.kr(-6), slew, slew).poll;
sig * db.dbamp ! 2;
}.play;
)
~slewDb.set(\db, -12); // change db
~slewDb.set(\db, -9); // change less (happens faster)
~slewDb.set(\db, -15, \dbRate, 12); // change faster