W3C home > Mailing lists > Public > public-audio@w3.org > July to September 2014

RE: Having some problems with envelopes

From: Richard Atkinson <richard@atkinsoft.com>
Date: Tue, 5 Aug 2014 22:30:49 +0100
To: "'Chris Wilson'" <cwilso@google.com>, "'Ray Bellis'" <ray@bellis.me.uk>
Cc: <public-audio@w3.org>
Message-ID: <14c101cfb0f4$877b0950$96711bf0$@com>
I thought I’d write up what I know about envelopes and other situations where linear or exponential ramps might be useful. I’ve studied a fair few analogue and digital hardware synthesizers over the years so I will make reference to examples I have studied.

 

First of all, it’s important to establish whether the parameter an envelope (or other ramp waveform) is driving has a linear or exponential response. Both types of response are possible, and indeed a device may have both a linear control input and an exponential control input.

 

Taking the example of ADSR envelopes, let’s assume for a moment we have a linear control input on a voltage controlled amplifier and we want to drive that amplifier with an envelope.

 

The easiest envelopes to make in analogue hardware are what I will call e^-x envelopes and (1 – e^-x) envelopes. This is where a control voltage is stored on a capacitor and to change the control voltage a resistor is connected (by means of an analogue switch) to a fixed potential. The resistor might be an attack, decay or release slider or it might be a voltage controlled element such as an operational transconductance amplifier (OTA) being used as a variable resistor.

 

If there is a positive voltage on the capacitor at present and the capacitor is connected through a resistor to ground, the voltage on that capacitor will decay following an e^-x curve as the charge flows through the resistor to ground. The higher the voltage, the higher the current, the lower the voltage the lower the current. This is a good model for the decay or release of a musical instrument provided the voltage is controlling a linear input on a voltage controlled amplifier. Most envelope generators in analogue synthesizers work this way. Service manuals for many of the old classic analogue synthesizers are available online which show the envelope generator hardware, whether it is implemented in discrete devices such as transistors, op-amps, resistors and capacitors or in integrated devices such as a dedicated envelope generator chip. A good example is the Yamaha CS5 synthesizer which has a discrete envelope generator, and the Yamaha CS10 (its slightly newer, bigger brother) which has a pair of envelope generator ICs.

 

That’s good for sounds which decay to zero after they have reached their maximum value (e.g. plucked sounds) or sustained sounds once the key has been released. For sustained sounds while the key is still being held down (i.e. gate still on), there is also a decay curve between the maximum value and the sustain level.

 

Some envelope generators, especially ones working in the digital domain, still use an e^-x curve on the decay part of the ADSR cycle, so they decay asymptotically to zero until they reach their sustain level, whereupon they are frozen at that value until the key is released (gate off). An example of an envelope generator like this is the type in the Commodore MOS Technology 6581 SID chip used in the Commodore 64. This uses a piecewise linear approximation of an exponential decay, driving a digital to analogue convertor and then a linear voltage controlled amplifier input. The envelope generator can be decaying quite fast (depending on the decay time), and as soon as it reaches the sustain level it is held there.

 

Analogue envelope generators are more likely to decay asymptotically to that sustain level (not to zero), thus taking “forever” to reach the sustain level, although on short decay times for all intents and purposes it will sound as if it has reached it. (There is also the question of noise in the circuit to consider, and the discrete nature of charge (individual electrons) at small currents.) This type of decay follows a (s + e^-x) curve where s is the sustain level. The envelope generator in the Yamaha CS5 works this way; other examples include the Roland SH series of synthesizers and American classics like the Minimoog and ARP Odyssey.

 

For attack, this is where the most variation can be found. Analogue synthesizers using envelope capacitors and variable resistors use a (1 – e^-x) curve on attack cycles – the envelope asymptotically reaches some maximum value – e.g. 5V. There will be a comparator which compares the voltage on the capacitor with a fixed potential, the level at which the decay cycle begins, and the output of that comparator is used to switch envelope states so that the attack resistor (connected to the maximum potential) is disconnected and the decay resistor (connected to the sustain level potential) is connected.

 

That comparison level can be the same as the maximum value, which in theory would never be reached in a (1 – e^-x) curve, but in practice will be reached due to noise in the circuit. This gives somewhat varying attack times at longer attack settings. Alternatively, the comparison voltage can be lower than the maximum value, producing a shorter segment of the (1 – e^-x) curve. As the comparison voltage gets lower the shape of the attack curve approaches a linear ramp.

 

Digital synthesizers using linear amplifiers (or numerical amplification in the form of multiplication) often have linear attacks because they are easy to implement in hardware – just a counter. The Commodore MOS Technology 6581 is an example here, it has an 8 bit up down counter for the envelope generator and in attack cycle there is no piecewise exponential approximation so it is simply a linear attack.

 

The next situation occurs when a device such as an amplifier has an exponential response to a control input instead of a linear response. Other digital synthesizers may have an exponential attack – following an e^x curve. An example here is Yamaha’s series of digital FM synthesizer chips, the YM3526 OPL, YM2151 OPM and later versions. These use an exponential lookup table for their output so they are in effect driving a final amplifier with an exponential response. Their envelope generators count up and down using linear ramps (attack to a maximum value, decay down to a sustain value, release to a small non-zero release value) but because the envelopes control an exponential control input the result is an exponential attack (e^x) and exponential decay and release (e^-x).

 

These different attack shapes tend not to make much difference in practice since attack times are usually short. If a longer attack time is required, it would be usual to adjust the attack time by ear, and thereby compensate for different shapes with attack times that suit a particular attack curve. For example, a (1 – e^-x) curve has the majority of the volume level change in the first part of the curve, so it sounds like it builds up quicker. To compensate for this you might increase the attack time. A linear attack ramp also has the majority of the volume level change at the front of the attack (although less so than (1 – e^-x) ) so attack time might be increased but less than for (1 – e^-x). An exponential (e^x) attack ramp sounds like the same level of volume change throughout the attack until the maximum value is reached, so in comparison with the others it might appear to take longer to reach the maximum value.

 

One proposal I would like to suggest for exponential decays and releases is that instead of decaying or releasing to a small final value (e.g. 0.001) it would be good if the decay or release rate could be set instead, and the envelope generator simply continues decaying or releasing asymptotically to zero, taking forever or as long as the floating point maths can provide smaller and smaller values. In practice of course the next gate on signal usually comes long soon enough and the envelope generator enters a new attack cycle. 

 

Richard Atkinson

 

From: Chris Wilson [mailto:cwilso@google.com] 
Sent: 24 July 2014 16:34
To: Ray Bellis
Cc: public-audio@w3.org
Subject: Re: Having some problems with envelopes

 

I think these are related.  You would currently need to keep track of the calculations yourself to determine the current value, but if we exposed the computedValue at the last time, you could use that.  You will of course need to calculate the attack time yourself (i.e. the attack segment of the second note starts from non-zero, but you want to keep the slope of the ramp the same, IIUC).

 

Can you tell me, in your experience do "typical ADSR" envelopes use Linear ramps or exponential?  My understanding has been that attacks are typically linear, but decays are exponential, but I've not found very definitive information.

 

setValueCurve does not interpolate, by definition, so I don't know that it would be a good model.

 

On Thu, Jul 24, 2014 at 6:39 AM, Ray Bellis <ray@bellis.me.uk> wrote:

On 23/07/2014 16:57, Chris Wilson wrote:
> I'd been toying with the idea of suggesting the idea that
> "cancelScheduledValues should explicitly set a "previously scheduled
> parameter value" of the current value, at that time.  WDYT?

I'm not sure how it would help.

The fundamental difficulty in correctly emulating an ADSR EG is that the
separate phases are triggered by a combination of the note on/off "gate"
and the current level of the EG itself.

Take this, for example, representing a key being pressed twice without
the first note completing.

    /\     /\
   /  \___/  \_____
  /                \
 /                  \

 X       OX       O

The first attack phase lasts 4 units, but because the second note is
starting with the EG already above zero its attack phase only lasts 2
units, and its decay phase (and subsequent sustain phase) start
commensurately sooner relative to the second "note on" event.

This may not fit with the "WebAudio way" of creating a new chain of
modules for each note (with the EG implicitly starting at zero for each
note) but it's what's needed to emulate a typical monosynth - probably
even more so for the filter EG than for the volume EG.

I didn't try it yet, but the only API call I can see that might help is
to exclusively use setValueCurveAtTime() and manually calculate every
single step of the envelope manually.

kind regards,

Ray

 
Received on Wednesday, 6 August 2014 09:20:08 UTC

This archive was generated by hypermail 2.3.1 : Tuesday, 6 January 2015 21:50:14 UTC