Discussion:
[music-dsp] variations on exponential curves
Frank Sheeran
2018-10-01 07:21:05 UTC
Permalink
Sali Andre,

I'm just now seeing your answer, thanks! It seems a lot more
complicated--but probably far more thorough--an explanation than I have.

The solution I hit upon to generate coefficients "multiplier" and "delta"
for the sample-by-sample calculation

current = previous * multiplier + delta

was to do the following calculations ONCE, AT INITIALIZATION TIME:

1) multiplier = pow( startLevel / endLevel, curve / secs / sampleRate )

This can be thought of as "curve" being approximately how steep the curve
should start, relative to linear, than exponential. curve=2 will be twice
as steep initially (for falling segment) or finally (for rising segment).
curve=.5 will split the distance between the linear and exponential curve's
initial (final) slope, and so on. Pretty intuitive.

2) solve for delta with a binary search over [-1,1] (can be much more
optimized), to find a delta that reaches endLevel in the right number of
samples. (Seems to take at most 40 iterations for 1/2 second curves).

Curve of 0 gives a multiplier of 1, so you get a linear "curve." For this
special and very common curve, just calculate:

delta = ( endLevel - startLevel ) / sec / sampleRate

Curve of 1 will give an exponential curve that needs no delta. For this
special and very common curve, just calculate:

delta = 0.

Otherwise the iterative method finds a delta for curve values about
[-6.6,6.6] for a half-second curve rising from 0+-47dB to 1+-47dB. (-47dB
is a default value in my envelope for an epsilon shift, allowing
exponential curves to reach 0.) Negative curve values will produce the
exact same curve as the corresponding positive, except reversed top to
bottom and left to right.

It looks like your delta calculation is going to be much faster than my
iteration :-D But one little advantage of the iterative search is that it
will find a delta that actually works (if there is one), even if floating
point rounding errors make the theoretical number not work.

----------------

Reading your paper, it looks like you have a definition of "bend" than my
"curve". Your bend seems to be (0,1) with .5=linear. My curve is
theoretically infinite range but in practice more like [-5,5] with
0=linear. Since your delta calculation refers to your bend, I won't be
able to use your formula directly. However seeing that you found a
solution for your "bend" gives me hope that there's a solution for my
"curve."

Gruss, Frank
STEFFAN DIEDRICHSEN
2018-10-01 08:53:00 UTC
Permalink
A very simple parametric curve is

y = (1 - x) / (1 + a*x)

With a = 0, you get a line thru 0,1 and 1,0
With increasing a, you bend the line to almost a sharp angle.

Best,

Steffan
Post by Frank Sheeran
Sali Andre,
I'm just now seeing your answer, thanks! It seems a lot more complicated--but probably far more thorough--an explanation than I have.
The solution I hit upon to generate coefficients "multiplier" and "delta" for the sample-by-sample calculation
current = previous * multiplier + delta
1) multiplier = pow( startLevel / endLevel, curve / secs / sampleRate )
This can be thought of as "curve" being approximately how steep the curve should start, relative to linear, than exponential. curve=2 will be twice as steep initially (for falling segment) or finally (for rising segment). curve=.5 will split the distance between the linear and exponential curve's initial (final) slope, and so on. Pretty intuitive.
2) solve for delta with a binary search over [-1,1] (can be much more optimized), to find a delta that reaches endLevel in the right number of samples. (Seems to take at most 40 iterations for 1/2 second curves).
delta = ( endLevel - startLevel ) / sec / sampleRate
delta = 0.
Otherwise the iterative method finds a delta for curve values about [-6.6,6.6] for a half-second curve rising from 0+-47dB to 1+-47dB. (-47dB is a default value in my envelope for an epsilon shift, allowing exponential curves to reach 0.) Negative curve values will produce the exact same curve as the corresponding positive, except reversed top to bottom and left to right.
It looks like your delta calculation is going to be much faster than my iteration :-D But one little advantage of the iterative search is that it will find a delta that actually works (if there is one), even if floating point rounding errors make the theoretical number not work.
----------------
Reading your paper, it looks like you have a definition of "bend" than my "curve". Your bend seems to be (0,1) with .5=linear. My curve is theoretically infinite range but in practice more like [-5,5] with 0=linear. Since your delta calculation refers to your bend, I won't be able to use your formula directly. However seeing that you found a solution for your "bend" gives me hope that there's a solution for my "curve."
Gruss, Frank
_______________________________________________
dupswapdrop: music-dsp mailing list
https://lists.columbia.edu/mailman/listinfo/music-dsp
André Michelle
2018-10-01 09:05:41 UTC
Permalink
Hi Frank,


the solution Werner solved is working the same way. The multiplier and delta gets computed once when receiving all necessary data. For each sample you then only have to run one multiplication and one addition plus reassignment. The math behind it looks a bit complicated but for very long (envelope) phases you might need to update the current value manually because floating errors will add up. There is Java code at the end of the article which should be easily portable to any other language. We are heavily make use of this code in many devices in our online DAW.

Greetings

~
André
https://www.audiotool.com <https://www.audiotool.com/>
Post by Frank Sheeran
The solution I hit upon to generate coefficients "multiplier" and "delta" for the sample-by-sample calculation
current = previous * multiplier + delta
Frank Sheeran
2018-10-01 11:52:39 UTC
Permalink
Post by STEFFAN DIEDRICHSEN
A very simple parametric curve is
y = (1 - x) / (1 + a*x)
With a = 0, you get a line thru 0,1 and 1,0
With increasing a, you bend the line to almost a sharp angle.
Hello Stefan.

Indeed, that's a simple parametric, but for generating envelopes we have
the freedom to depend on the previous sample's output. So, while an
exponential curve parametric-style requires a pow(), the iterative solution
is simply current = previous * multiplier.

Adding a range of curves can be done by adding a delta, as Andre/Werner and
I have independently discovered (along with probably many others).

And "current = previous * multiplier + delta" is going to be fewer
calculations than a parametric formula such as yours. So I suggest the
Andre/Werner/my method seems faster for the specific application of
envelopes, as well as offering true exponential curves.

My synthesizer is a programming language, not a graphic synth, and includes
curve and s-curve functions that are parametric, and indeed use the exact
function you mention!

http://moselle-synth.com/

Thanks, Frank
Vadim Zavalishin
2018-10-01 12:12:07 UTC
Permalink
Post by Frank Sheeran
Indeed, that's a simple parametric, but for generating envelopes we have
the freedom to depend on the previous sample's output.  So, while an
exponential curve parametric-style requires a pow(), the iterative
solution is simply current = previous * multiplier.
Adding a range of curves can be done by adding a delta, as Andre/Werner
and I have independently discovered (along with probably many others).
And "current = previous * multiplier + delta" is going to be fewer
calculations than a parametric formula such as yours.  So I suggest the
Andre/Werner/my method seems faster for the specific application of
envelopes, as well as offering true exponential curves.
With multiplier values >> 1 there can be noticeable incremental
precision losses. They can be reduced by offsetting the whole curve so
that its starting position is zero (which means that that the "output"
value is current + offset). In principle IIRC the same rule applies for
multiplier < 1, but there the losses are not too large. This also
manifests at multiplier = 1 by having the "best offset" so that the
curve's middle is at zero.

Regards,
Vadim
--
Vadim Zavalishin
Reaktor Application Architect
Native Instruments GmbH
+49-30-611035-0

www.native-instruments.com
Vadim Zavalishin
2018-10-01 12:14:03 UTC
Permalink
Post by Vadim Zavalishin
In principle IIRC the same rule applies for
multiplier < 1, but there the losses are not too large. This also
manifests at multiplier = 1 by having the "best offset" so that the
curve's middle is at zero.
Sorry, I meant to say that for multiplier < 1 you need the opposite
rule, the curve should end at zero. Therefore at mult = 1 you position
the curve halfway ;)

Regards,
Vadim
--
Vadim Zavalishin
Reaktor Application Architect
Native Instruments GmbH
+49-30-611035-0

www.native-instruments.com
Frank Sheeran
2018-10-01 11:58:32 UTC
Permalink
Post by André Michelle
The math behind it looks a bit complicated but for very long (envelope)
phases you might need to update the current value manually because floating
errors will add up

Sali Andre,

I thought this would be a problem, but for exponential curves up to 5
seconds, ranging from 2^(1/12) start to 1 finish, I found my pow( end /
start, curve / time / sampleRate ) method to never be off by a sample. For
10 seconds, sometims off by 1-3. (This all at 44.1kHz.)

I calculate with doubles in C++, by the way. I had no idea it would be so
accurate.

For curves other than 0 and 1, I discover a delta that will work to the
exact number of samples iteratively because I am too stupid to figure out
an equation for delta. Werner is much better at math than I am!

Please take a look at my soft synth if you're bored. I think there's
nothing like it except Csound and Max/MSP/PureData, but I think my language
is the easiest to read and write for more complicated patches.
http://moselle-synth.com
Vadim Zavalishin
2018-10-01 12:19:32 UTC
Permalink
Post by Frank Sheeran
For curves other than 0 and 1, I discover a delta that will work to the
exact number of samples iteratively because I am too stupid to figure
out an equation for delta.  Werner is much better at math than I am!
This is quite a smart way to work around the precision loss issues!
Although I'm somewhat concerned about its reliability. I guess it can be
proven that the final value is a monotonic function of delta, but
possibly with large multipliers the output value step (for the smallest
change of delta) will be quite large, so the search will not fully converge.

Regards,
Vadim
--
Vadim Zavalishin
Reaktor Application Architect
Native Instruments GmbH
+49-30-611035-0

www.native-instruments.com
David Olofson
2018-10-01 13:07:58 UTC
Permalink
On Mon, Oct 1, 2018 at 1:58 PM Frank Sheeran <***@gmail.com> wrote:
[...]
Post by Frank Sheeran
Please take a look at my soft synth if you're bored. I think there's
nothing like it except Csound and Max/MSP/PureData, but I think
my language is the easiest to read and write for more complicated
patches. http://moselle-synth.com
[emerges from deep lurk mode]

Cool! As for similar things, there's also SuperCollider, and possibly
my own tiny, obscure contribution, Audiality 2, although that one sort
of started out in the other end of the control-vs-processing spectrum;
sub-sample accurate scripted control of simple DSP units:
https://github.com/olofson/audiality2

As for the original topic, I'm currently using linear ramping per
fragment on the Unit API level (Max 64 samples, with splitting down to
single samples as needed for event timing.) Envelopes can be scripted
at arbitrary resolution (that was the only way to do it originally),
but there's also an 'env' unit that deals in segments that can be
linear, spline, or varying degrees of "tapered exponential."
(Superimposed over linear functions, to hit the exact targets.) These
are all implemented by transforming linear ramps via interpolated
LUTs. Perhaps not optimal (minor issue as it's not running at the full
audio rate), but simple and flexible (you can throw any curve into a
LUT), and makes it trivial to deal with the variable control rates.

Oh, and never mind the DSP code! ;-) For historical reasons, it's
integer/fixed point. I'll most likely move to floating point in the
near future, though.
--
//David Olofson - Consultant, Developer, Artist, Open Source Advocate

.--- Games, examples, libraries, scripting, sound, music, graphics ---.
| http://consulting.olofson.net http://olofsonarcade.com |
'---------------------------------------------------------------------'
Sound of L.A. Music and Audio
2018-10-21 22:58:19 UTC
Permalink
Hi all
Post by Frank Sheeran
current = previous * multiplier + delta
current = previous * multiplier + delta
Im a using this multiplication with offset to sequentially generate and
detune the frequencies for music channels:

Playing with Commodore VIC20 and C-64 I found the relation of 185/196 as
a good approximation for the relative frequency distance of two notes.
For this kind of tuning, I am using a cyclic iteration to generate the
frequency values for organ like instruments in order to prepare the
values for all channels in sequence leading to a logarithmic scaling (12
notes per ratio 2). Is is very close to the temperted tuning:
http://www.96khz.org/oldpages/musicfrequencygeneration.htm

Now, regarding the topic, adding an offset during multiplication like
mentioned here, helps to detune this logarithmic scaling that way, that
frequencies at the lower end can be lower than math "says", while higher
frequencies can be slightly higher. This is necessary when coming to
piano tuning to fullfill the spreading issue often observed with real
piano frequency timmings.

Also this tuning can help to handle the psychoacoustic non linearity
issue decribed here:
https://de.wikipedia.org/wiki/Mel

Around 2005, I implemented such a detailled adaptive logarithmic scaled
curve to create measurement systems to drive and test industrial hearing
systems / cochlear implants for a customer.

After having dealed with this topics, i started to try around to use
that in music apps by dublicating MIDI channels and using individual
harmonics with detuned frequencies rather than creating harmonics by non
linear circuits wich automatically created harmonics hard linked to the
base frequency.

You may tryout yourself about the difference.

Jürgen
www.96khz.org

Continue reading on narkive:
Loading...