KVR Audio is the Internet's number one news and information resource for open standard audio plugins. We report new releases, product announcements and product updates (major and minor) for all VST Plugins, DirectX Plugins and Audio Units Plugins. We manage a fully searchable audio plugin database (updated daily), and offer many free member services including user reviews, product update notifications and a very active discussion forum. We also host official support forums for many plugin developers plus the official Receptor support forum.
Plug-in Database: Virtual
Instruments, Effects & Hosts
Plug-in
Ranks
Banks & Patches
Download & Upload
Plug-in Ratings
by KVR Members
Wiki: Tutorials,
Audio Lexicon, ...
Listen to Music
by KVR Members
Search
KVR

Google Powered Search:

in new window

KVR Powered Plug-in Search:

Author Topic: ADSR - Linear vs. Exponential
Leslie Sanford
KVRian
- profile
- pm
- e-mail
- www
PostPosted: Thu Dec 14, 2006 10:51 am reply with quote
Thanks to everyone who responded to my linear interpolation thread. My oscillator code is coming along well. Next, the ADSR...

I've written an ADSR class that uses a linear approach instead of an exponential one, i.e. the values rise and fall linearly. One thing that I've noticed when I use the ADSR to modulate the amplitude of an oscillator is that when I set the sustain to zero and the decay to some value, e.g. 1.5 seconds, the sound fades out as expected. But at the tail end, the sound seems to drop off too quickly. I've not had a chance to try an exponential ADSR as I'm still exploring ways of implementing one, but could this drop off be related to the ADSR being linear?

I've searched through past posts here for implementing an exponential ADSR. I've come across some very clever solutions. One that I like is using a first order lowpass IIR filter. This solution has been mentioned a few times, at least once by JCJR and a few times by braindoc. It's attractive because of the low computational costs and, from what I understand, it's the way analog synths implement envelopes (with "RC" circuits, I think?).

In examining this approach I noticed that "attack" portion is logarithmic rather than exponential. To see what I mean, check out an excerpt from Steven W. Smith's excellent book, The Scientist and Engineer's Guide to Digital Signal Processing. This shows the attack as logarithmic. However, the decay curve looks exponential. This graphic shows what I'm talking about.

Apparently, this is the way an analog synth's ADSR behaves. For an example, look here.

Now, to calculate how long a segment should last, you use the following equation:


x = e^(-1/d)


Where x is the amount of decay between samples and d is the number of samples for the filter to decay to 36.8%.

From what I understand, the amplitude of the envelope will be in the neighborhood of 0.63 at the end of the attack. If you set up your algorithm thinking that at the end of the attack you'll have an amplitude close to 1, you'll be disappointed. Same goes for targetting the sustain level. I found in testing out some code that if I divide "d" by some value larger than one, it causes the filter to run faster thus getting it closer to the target amplitude. This feels a little kludgey, but seems to be a simple enough solution.
^ Joined: 03 Dec 2006  Member: #131095  
Borogove
KVRAF
- profile
- pm
- e-mail
- www
PostPosted: Thu Dec 14, 2006 11:03 am reply with quote
Leslie Sanford wrote:
But at the tail end, the sound seems to drop off too quickly. I've not had a chance to try an exponential ADSR as I'm still exploring ways of implementing one, but could this drop off be related to the ADSR being linear?


Yes. I find exponential falloff in release (or decay-to-zero as in your case) to sound much more natural.

Quote:
In examining this approach I noticed that "attack" portion is logarithmic rather than exponential.


Actually, it's exponential but upside down, which is, I think, different from log.

Quote:


x = e^(-1/d)


Where x is the amount of decay between samples and d is the number of samples for the filter to decay to 36.8%.

From what I understand, the amplitude of the envelope will be in the neighborhood of 0.63 at the end of the attack. I found in testing out some code that if I divide "d" by some value larger than one, it causes the filter to run faster thus getting it closer to the target amplitude. This feels a little kludgey, but seems to be a simple enough solution.


You can adjust the d scale, which will get you closer to 1 but never quite get there, or simply multiply x by 1/0.63 to get it to hit 1 in one "d" time. Chamberlin (which I think you've referenced here before?) has a nice formula for using an exponential decay to "aim" at a point slightly beyond the target you're trying to hit, and stopping early, with his usual lucid-and-not-too-math-abstracted explanation.
----

Don't do it my way.
^ Joined: 03 Oct 2002  Member: #3996  Location: SF CA USA NA Earth
mystran
KVRAF
- profile
- pm
- e-mail
- www
PostPosted: Thu Dec 14, 2006 12:15 pm reply with quote
My 0.02 euro on envelopes:

Where you wanna stop your attack is a matter of taste (just like linear/exponential really). The exponential envelope is easiest generated by onepole lowpass filter. Now typical onepole lowpass will look something like:

out = out + lag * (in - out)

If you put 1 in place of "in", initialize "out" as 0, and decide attack is over when "out" goes over 0.63, you have the desired(?) attack. "lag" is whatever "x" you had for attack. What happens now is that attack "decays" exponentially upwards.

Alternatively if you wanna hit 1 instead, either multiply the result with 1/0.63, or put 1/0.63 in place of in. You can shape the attack as you like by aiming more (more like linear) or less ("punchier?") over the endpoint.

As for decay, you can start with whatever value out had after attack was over, just switch "in" to sustain level, and "lag" to decay setting. I don't see a point cutting decay for reasons other than possibly saving some computation, and I kinda like it that you can move sustain and all sustained notes will start "decaying" (possibly upwards) to the new setting. I am under the impression that quite a few analog synths actually have this property.

For release, there's some options depending on what you want. One option is to overlay (multiply with) a release envelope on top of the ADS envelope (that setup lets sustain changes have effect even while some notes are already releasing). Alternatively just switch the envelope to another mode which releases to zero from wherever it was. Same onepole setup still. Aim slightly (or more, depends on the shape you want really) below zero (just like with attack, just other direction now) and cut it out when it crosses zero.

As for timing the parts (for GUI representation and/or samplerate independence), I'd solve attack and release exactly and use the same formulas for decay even if it was technically left infinite (as this is the right thing to do for samplerate independence anyway).

Oh and pseudo C code for the loop goes basicly like:


if(gate) {
  if(rising) {
    value += a_coeff * ((1/0.63) - value);
    if(value > 1) {
      value = 1; // clips really snap attacks
      rising = false;
    }
  } else {
    value += d_coeff * (sustain - value);
  }
} else {
  value += r_coeff * (1-(1/0.63) - value);
  if (value < 0) {
    killthisvoice();
  }
}


For modulation envelopes just clip release to 0 instead of killing the voice, ofcourse.

I might also add that I like to have my envelopes go from 1 to 2, and then substract 1 to get the real value. Nicely avoids denormalization problems near zero. YMMV Smile
^ Joined: 11 Feb 2006  Member: #97939  Location: Helsinki, Finland
mistertoast
KVRAF
- profile
- pm
PostPosted: Thu Dec 14, 2006 3:30 pm reply with quote
>>I might also add that I like to have my envelopes go from 1 to 2, and then substract 1 to get the real value. Nicely avoids denormalization problems near zero. YMMV

Good trick.
^ Joined: 15 Sep 2002  Member: #3851  
Robin from www.rs-met.com
KVRAF
- profile
- pm
- e-mail
- www
PostPosted: Thu Dec 14, 2006 3:43 pm reply with quote
Leslie Sanford wrote:

In examining this approach I noticed that "attack" portion is logarithmic rather than exponential. To see what I mean, check out an excerpt from Steven W. Smith's excellent book, The Scientist and Engineer's Guide to Digital Signal Processing. This shows the attack as logarithmic. However, the decay curve looks exponential. This graphic shows what I'm talking about.

it is an exponential saturation curve - just like the RC-loading curve. and i think, it is exactly, what a user expects an attack to look like - at least, i would expect that. and it sounds good.

Leslie Sanford wrote:

From what I understand, the amplitude of the envelope will be in the neighborhood of 0.63 at the end of the attack. If you set up your algorithm thinking that at the end of the attack you'll have an amplitude close to 1, you'll be disappointed. Same goes for targetting the sustain level. I found in testing out some code that if I divide "d" by some value larger than one, it causes the filter to run faster thus getting it closer to the target amplitude. This feels a little kludgey, but seems to be a simple enough solution.

in my aggressor-synth, i decided to make this factor accessible to the user. i simply provide a parameter which scales the RC-time-constant tau by a number between 0.1...1.0. this scaling only affects tau and does not affect the time at which i switch to the next time-constant and target-level.
----
some VST-PlugIns (mostly free):
www.rs-met.com (company website)
www.braindoc.de (private crap-artist website)
^ Joined: 08 Mar 2004  Member: #15959  Location: Berlin, Germany
Leslie Sanford
KVRian
- profile
- pm
- e-mail
- www
PostPosted: Thu Dec 14, 2006 3:46 pm reply with quote
Borogove wrote:
You can adjust the d scale, which will get you closer to 1 but never quite get there, or simply multiply x by 1/0.63 to get it to hit 1 in one "d" time. Chamberlin (which I think you've referenced here before?) has a nice formula for using an exponential decay to "aim" at a point slightly beyond the target you're trying to hit, and stopping early, with his usual lucid-and-not-too-math-abstracted explanation.


I found it, thanks! Can't believe I missed it my other times through the book. It's in chapter 18 of Musical Applications of Microprocessors, if anyone's interested.
^ Joined: 03 Dec 2006  Member: #131095  
Leslie Sanford
KVRian
- profile
- pm
- e-mail
- www
PostPosted: Thu Dec 14, 2006 3:53 pm reply with quote
mystran wrote:
My 0.02 euro on envelopes:


<snip>

Just gotta say that this was a tour de force post for me. I've read through it several times. It's very helpful. I updated my code to use this approach, and it sounds Nice. Smile Thanks for your time and your response. With what I've been learning here, I have a fighting chance of creating a decent softsynth.
^ Joined: 03 Dec 2006  Member: #131095  
Leslie Sanford
KVRian
- profile
- pm
- e-mail
- www
PostPosted: Thu Dec 14, 2006 4:00 pm reply with quote
braindoc wrote:
Leslie Sanford wrote:

In examining this approach I noticed that "attack" portion is logarithmic rather than exponential. To see what I mean, check out an excerpt from Steven W. Smith's excellent book, The Scientist and Engineer's Guide to Digital Signal Processing. This shows the attack as logarithmic. However, the decay curve looks exponential. This graphic shows what I'm talking about.

it is an exponential saturation curve - just like the RC-loading curve.


Yeah, I was mistaken to say it was logaritmic.

Quote:

and i think, it is exactly, what a user expects an attack to look like - at least, i would expect that.


For me, I wasn't sure until today if the attack should be a negative exponential curve or positive (hope I'm using those terms correctly).

Quote:

and it sounds good.


I've just found this out. Smile I'm amazed at how much better this sounds than my linear ADSR. This rocks.

Quote:

in my aggressor-synth, i decided to make this factor accessible to the user. i simply provide a parameter which scales the RC-time-constant tau by a number between 0.1...1.0. this scaling only affects tau and does not affect the time at which i switch to the next time-constant and target-level.


Sounds like a good approach, thanks.
^ Joined: 03 Dec 2006  Member: #131095  
Ben [Camel Audio]
KVRAF
- profile
- e-mail
- www
PostPosted: Fri Dec 15, 2006 4:06 am reply with quote
I have a related issue I'd be very interested to hear peoples opinions on. Having a system such as that described above for ADSR envelopes works well enough - however in the case of graphical volume envelopes, do you think the y axis should have a linear mapping to volume or some other mapping? Similarly, what if you assign an LFO to the volume parameter?

The approach I have taken is that all my modulators just work with values in the range 0-1 and my ADSRs just generate values linearly, however when I apply these volume values to the signal, I cube the values before applying. Cubing the values seemed to result in a mapping which is similar to the way most knobs on synths work. Eg. When you have the volume knob at the half way (0.5) position, you are actually using 0.5^3 = 0.125 as the scaling value.

The other approach I am considering taking is to go back to applying the amplitudes linearly, and instead altering my ADSR envelope to do something similar to that which is described in this thread so far, and scale just the knob values with the x^3 mapping. This would mean that the y axis of any envelope assigned to amplitude would just be linear - ie the half way position would result in multiplying the amplitude value by 0.5. Similarly, if you assigned an LFO to an amplitude destination it would just be applying its values in the linear scaling rather than the x^3 scaling.

This seems to work reasonably well, but I'm not sure if this is really the best approach, and I would be really interested to hear other peoples opinions on this issue. I would be particularly interested to hear from anyone who has made synths with very flexible modulation systems.

Thanks very much in advance for any advice and thoughts Smile
Ben
^ Joined: 17 Sep 2001  Member: #1122  Location: Edinburgh, Scotland
otristan
KVRian
- profile
- pm
PostPosted: Fri Dec 15, 2006 5:20 am reply with quote
Best thing is to generate in linear, then convert to the range and curve of the destination model. This way you could have the same LFO/ADSR connected to the volume/pitch/filter cutoff/ without generating twice the source buffer.

If you're using midi controller as modulation input it will apply the same way, so it's very flexible because you want that CC are applied in a scaled fashion when connected to volume.

HTH
----
Olivier Tristan
Developer - UVI Team - http://www.uvisoundsource.com/
^ Joined: 28 Mar 2005  Member: #63153  
Rock Hardbuns
KVRAF
- profile
- pm
- e-mail
- www
PostPosted: Fri Dec 15, 2006 5:49 am reply with quote
I too simply cube the envelope output. It wont work for -1 to 1 stuff though.
(Edit: Though I suppose you could always transpose it.)


Opps, posted a sine, meant to post this:
^ Joined: 03 Sep 2003  Member: #8788  
Robin from www.rs-met.com
KVRAF
- profile
- pm
- e-mail
- www
PostPosted: Fri Dec 15, 2006 8:52 am reply with quote
[quote="Leslie Sanford]
Quote:

and it sounds good.

I've just found this out. Smile I'm amazed at how much better this sounds than my linear ADSR. This rocks.
[/quote]
yeah. and it resembles the behaviour of analog envelope-genrators. i really tend to think that the envelope-shape has at least the same importance as the oscillator and filter for a good analog emulation.
----
some VST-PlugIns (mostly free):
www.rs-met.com (company website)
www.braindoc.de (private crap-artist website)
^ Joined: 08 Mar 2004  Member: #15959  Location: Berlin, Germany
otristan
KVRian
- profile
- pm
PostPosted: Fri Dec 15, 2006 9:01 am reply with quote
FWIW, you usually use exp envelope for volume (directly of after conversion)
because it gives a linear curve in dB.
Just as you do for filter cutoff in log, because you want linear mods in final results (aka what you hear)
^ Joined: 28 Mar 2005  Member: #63153  
Robin from www.rs-met.com
KVRAF
- profile
- pm
- e-mail
- www
PostPosted: Fri Dec 15, 2006 9:07 am reply with quote
otristan wrote:
FWIW, you usually use exp envelope for volume (directly of after conversion)
because it gives a linear curve in dB.
Just as you do for filter cutoff in log, because you want linear mods in final results (aka what you hear)


this was, what i did before i found the solution with the RC-filter: generate a line-segment envelope in the dB-domain and then converting it to "raw" amplitude values. this was really terribly awkward - in particular for the attack-phase where i additionally had to do some stuff to invert the slope.
----
some VST-PlugIns (mostly free):
www.rs-met.com (company website)
www.braindoc.de (private crap-artist website)
^ Joined: 08 Mar 2004  Member: #15959  Location: Berlin, Germany
mistertoast
KVRAF
- profile
- pm
PostPosted: Fri Dec 15, 2006 9:18 am reply with quote
Speaking of volume. I want to know what you guys do about polyphony that's changing on the fly. Suppose somebody bangs out a big 8-note chord on two hands, then leaves one held down and releases the other 7.

Do you have some sort of built-in compression to handle this?
^ Joined: 15 Sep 2002  Member: #3851  
Reply to topic KVR Forum Index » DSP and Plug-in Development All times are GMT - 8 Hours

Printable version
Page 1 of 2
Goto page 1, 2  Next
Display posts from previous:   
Post new topic
Previous Topic
Next Topic
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
Disclaimer: All communications made available as part of this forum and any opinions, advice, statements, views or other information expressed in this forum are solely provided by, and the responsibility of, the person posting such communication and not of kvraudio.com (unless kvraudio.com is specifically identified as the author of the communication).