I've been looking at the pops and clicks evident in the PlaySynthMusic example, in the situation where the envelope is
force-released. Typical waveform you see is:
Click image for larger version. 

Name:	synth_discontinuities1.png 
Views:	9 
Size:	8.2 KB 
ID:	21126

The first discontinuity stems from successive notes having different amplitudes, and the code has to pass this to the
waveform generator as envelope doesn't have an amplitude parameter. Thus there is an amplitude and frequency
discontinuity at that point (but phase-continuous, so the frequency change isn't a problem in itself). The amplitude
discontinuity is 1st-order and often quite audible as a click.

The second discontinuity is second-order due to the envelope fading down (forced release) and immediately up again (attack)
In this example it's happened near a waveform maximum so it is very visible (basically its pot-luck if this is audible, depending
on timing, so its inconsistent). It tends to be quite a high energy event as the force-release and attack slopes are typically
large.

A 2nd-order discontinuity is less intrusive than a 1st-order one, but still creates a broadband pulse of energy. This
fade-down-fade-up discontinuity is easily fixed by inverting the phase of the envelope after a forced release.
However the linear ADSR has other 2nd-order discontinuities and would need the corners rounded-off to get rid of
these as well.

To my mind a good envelope should be handling anything to do with amplitude, and should be careful not to introduce
discontinuities (certainly not first-order ones) - except perhaps using a "harshness/smoothness" parameter.

I suggest an improved envelope with an amplitude parameter to noteOn().
It would have corner-softening (a simple digital filter on the envelope value could give this).
It would switch polarity when force-release changes to attack.

A further improvement might be a mechanism for the envelope to signal to the waveform generator when to change
frequency, at the point at the end of force-release - this is not as straightforward, but could perhaps be a change-delay
parameter passed back from noteOn() that could be written to the waveform generator explicitly?

I'd also suggest a selectable alternate forced-release behaviour, which goes straight to attack without the fade
out - for when a less stucatto sound is wanted?