Math behind [function] (curve)

hoowdie's icon

Dear all,

for a Synthesizer I need a lot of mappings/functions to interpolate values depending on a given input(0-1).

For performance reasons I don't wanna use lots and lots of function objects,
my approach is to have a "function-editor", where you have one function object, write your graph and just save a minimal amount of data in a dict and use some math to "recover" the interpolation later.

For linear interpolation this should be no problem ( I came up with [expr ((x-x1)*y1) + ((x2- (x2 - x1))*y2)] and it gives me the same result as the function-object ;) ) - but the curved function is still a problem.

I didn't find any useful information on how excactly the curve value "scales" or "transforms" the graph despite of "logarithmically and exponentially" in the helpfiles / forums.

I thought - in a perfect world - when I have a function from (0,0) to (1,1) with a curve of 0.5 it would be a nice exponentially (f(x)=x2) function, where x=0.5 would return y=0.25, atleast thats what it looks like, but when I input x=0.5 i get y = 0.196434.

Anyone any ideas or knowledge of how I can achieve my goals with the least amount of data/processing needed? :)

Thanks in advance!

Hoowdie

Roman Thilenius's icon

" where x=0.5 would return y=0.25"

that sounds more like sqrt(), but i guess in most cases log/exp is indeed more useful for music applications.

you want file 3121 from here:
https://cycling74.com/forums/re-mapping-a-range-of-numbers-add-your-tricks-here

as for performance, you could use formulas like mine, or scale, or mtof, or a function, to write 1000 or 1024 values into a coll, and then later do

0. - 1.
[* 1000]
[coll]
[line~ 10]

in many cases without loosing too much accuracy.

hoowdie's icon

Hey Roman, thank you for your answer, that looks very helpful and I will check it out later!

I thought about the coll/dict method with "sampling" the function ( with an uzi for example) and save 1000 values in a coll, but that doesnt look very elegant/efficient, having to save overall 100.000 numbers in colls, instead of having one expr object interpolating from few point-values or am I wrong here?

best regards

Hoowdie

Roman Thilenius's icon

no, it makes a table of 1000 indexes with 1000 floats each, thats all.

then you can point to this transition table from other locations by using the same name (coll foo).

looking up a single number in a table is for sure the most CPU efficient solution whenever you are expecting dozens of parameters beeing modulated at the same time in your patch.

(for mouse input only, taking care about the efficiency of calculations would be futile.)

hoowdie's icon

Hey Roman,

thank you again for your fast reply, you're probably right, that just looking up a float from a table is way more efficient in the end than doing some sqrt-calculations all the time.

I just saw those gigantic lists in my dict and got scared and looked for an elegant solution with just a few numbers/objects ;)

Thank you very much for the advices!

best regards

Hoowdie

Oren Shoham's icon

Hey, for anyone who comes across this thread in the future, I found an accurate interpolation function that matches the behavior of the function object with curves enabled by looking through the JavaScript source code for Emmanuel Jourdan's ej.function object: http://www.e--j.com/index.php/ej-function/

Here's my lightly-adapted version of Emmanuel's JavaScript code:

function interpolate(x, startX, startY, endX, endY, curve) {
    var tmpRange = endY - startY;
    var tmpDomain = endX - startX;
    
    if (Math.abs(curve) < 0.001) {
        return ((x - startX) / tmpDomain) * tmpRange + startY;
    }

    if (curve < 0.0) {
        var gx = (endX - x) / tmpDomain;
        var hp = Math.pow((1e-20 - curve) * 1.2, 0.41) * 0.91;
        var fp = hp / (1.0 - hp);
        var gp = (Math.exp(fp * gx) - 1.0) / (Math.exp(fp) - 1.0);
        
        return endY - gp * tmpRange;
    } else {
        var gx = (x - startX) / tmpDomain;
        var hp = Math.pow((curve + 1e-20) * 1.2, 0.41) * 0.91;
        var fp = hp / (1.0 - hp);
        var gp = (Math.exp(fp * gx) - 1.0) / (Math.exp(fp) - 1.0);
        
        return gp * tmpRange + startY;
    }
}
Eldar Sadykov's icon

OREN SHOHAM, thanks a lot! Needed it for gen~ calculations.

sousastep's icon

Thanks Shoham :D

I implemented this in gen~ for two curved lines. might have to (clear) the function and redraw the lines to make it work.

Max Patch
Copy patch and select New From Clipboard in Max.

Eldar Sadykov's icon

MARTIN.JIRSAK,
If x = startX, the output is startY;
if x = endX, the output is endY;
if x is somewhere in-between startX and endX, the output is somewhere in-between startY and endY depending on the curve parameter.

So, it's just an interpolation function, there are only two points: the starting one and the ending one. Chaining several of them into a multi-point sequence is another story. Let me know if you need any help with that!

Martin.Jirsak's icon

Hi Eldar,

Thank you very much! I got it, now. The X is the input value. Now it's working like a charm. Thank you very much, great work!