Yeah I’m mostly using the specs for midi/GUI controls, but at that point it becomes convenient to be able to use them as the modulation spec as well… So the modulation is equivalent to automatically turning the knob at audio rate.
Here is my latest attempt which is not very elegant but does work correctly as far as I have tested (though actually now realizing I haven’t fully implemented the “step” control):
(
var warpModFuncs = ();
[CosineWarp, SineWarp, LinearWarp, ExponentialWarp].do { |warp|
warpModFuncs[warp] = { |val, modVal, minval, maxval, step|
var spec = ControlSpec(minval, maxval, warp, step);
var unmappedVal = spec.unmap(val);
var moddedVal = unmappedVal + modVal;
spec.map(moddedVal);
};
};
warpModFuncs[DbFaderWarp] = { |val, modVal, minval, maxval, step|
var valAmp = val.dbamp;
var minvalAmp = minval.dbamp;
var rangeAmp = maxval.dbamp - minvalAmp;
var unmappedVal = Select.kr(rangeAmp > 0, [
1 - sqrt(1 - ((valAmp - minvalAmp) / rangeAmp)),
((valAmp - minvalAmp) / rangeAmp).sqrt
]);
var moddedVal = (unmappedVal + modVal).clip(0, 1);
Select.ar(K2A.ar(rangeAmp > 0), [
(1 - (1 - moddedVal).squared) * rangeAmp + minvalAmp,
moddedVal.squared * rangeAmp + minvalAmp
]).ampdb;
};
warpModFuncs[FaderWarp] = { |val, modVal, minval, maxval, step|
var range = maxval - minval;
var unmappedVal = Select.kr(range > 0, [
1 - sqrt(1 - ((val - minval) / range)),
((val - minval) / range).sqrt
]);
var moddedVal = (unmappedVal + modVal).clip(0, 1);
Select.ar(K2A.ar(range > 0), [
(1 - (1 - moddedVal).squared) * range + minval,
moddedVal.squared * range + minval
]);
};
warpModFuncs.keysValuesDo { |warp, func|
var defName = ("modulate" ++ warp.asString).asSymbol;
SynthDef(defName, { |out, in, amp, val, minval, maxval, step|
var modVal = In.ar(in) * amp;
Out.ar(out, func.(val, modVal, minval, maxval, step));
}).add;
};
SynthDef(\modulateCurveWarp, { |out, in, amp, val, minval, maxval, step, curve|
var range = maxval - minval;
var grow = exp(curve);
var a = range / (1.0 - grow);
var b = minval + a;
var modVal = In.ar(in) * amp;
var unmappedVal = log((b - val) / a) / curve;
var moddedVal = unmappedVal + modVal;
Out.ar(out, b - (a * pow(grow, moddedVal)));
}).add;
)
Basically used like this:
~bus = Bus.audio(s)
~bus2 = Bus.audio(s)
~spec = \freq.asSpec;
x = { SinOsc.ar(\freq.kr(440)) }.play
y = { SinOsc.ar(1) }.play(x, ~bus, 0, \addBefore);
z = Synth(\modulateExponentialWarp, [out: ~bus2, in: ~bus, amp: 1, val: 440, minval: ~spec.minval, maxval: ~spec.maxval, step: ~spec.step], y, \addAfter);
x.set(\freq, ~bus2.asMap)
z.set(\amp, 0.1)