I use an algorithm that is used for image processing and that I found more interesting to use in envelopes and many other things. This does not use exponential functions. I have created the function with the same format as juce::map
template <typename Type>
static Type curveMap(Type sourceValue, Type sourceRangeMin, Type sourceRangeMax, Type targetRangeMin, Type targetRangeMax, Type bend)
jassert (sourceRangeMax != sourceRangeMin); // mapping from a range of zero will produce NaN!
bend = juce::jlimit((Type)0.000001, (Type)0.999999, bend);
sourceValue = juce::jlimit(sourceRangeMin, sourceRangeMax, sourceValue);
Type value0to1 = (sourceValue - sourceRangeMin) / (sourceRangeMax - sourceRangeMin);
return targetRangeMin + ((bend - (Type)1.0) * value0to1) / (((Type)2.0 * bend - (Type)1.0) * value0to1 - bend) * (targetRangeMax - targetRangeMin);
This accepts bend values from 0 to 1, with 0.5 as a straight line.
The difference with respect to exponential functions is that it allows pronounced attacks or decays while maintaining some volume in the rest of the curve. Here you can see the comparison, in blue curveMap, in red pow function. Even if the curve is steeper, its usefulness is maintained throughout its duration, while the exponential curve quickly reaches a value close to 0, leaving 2/3 unused.
(Maybe it is possible to obtain the same result with exponential or logarithmic functions, but I don’t know them)
Here’s a Desmos link showing what I settled on, at least for MSEG/envelope curves:
The ‘s’ variable alters the curve and the ‘t’ variable applies a tension to the curve so that you can get the tighter corner fits.
Code, sans my optimised math functions:
// Get exp curve Y value given the X [-1..1] and curvature [-1..1]
// MaxTension allows more or less corner pinching in the sigmoid
inline float getExpCurveY(float curvature, float x, float maxTension = 16.f)
if (common::is_near(curvature, 0.f)) return x;
float s = curvature * maxTension;
return ((1.f - fast_exp(std::abs(x) * s)) / (1.f - fast_exp(s))) * sgn(x);
Note you have to handle the case where curvature is 0 (‘s’ in the Desmos graph).