# S-shaped attack/release curves | Algorithm Design | Forum

guest

— Forum Scope —

— Match —

— Forum Options —

Minimum search word length is 3 characters - maximum search word length is 84 characters

S-shaped attack/release curves
Leiden, The Netherlands
November 19, 2014 - 9:53 pm
Member Since: November 5, 2014
Forum Posts: 80
Offline

Hi Will, how are you?

Iâ€™m slowly starting to get the hang of it, and Iâ€™m trying to modify my compressor again in a subtle way. What I want to do is modify the attack/release curves in a way so that they donâ€™t have a linear or exponential (gain reduction) curve, but an S-curve (sigmoid). I realize this might seem like a subtle modification, but I did some tests and Iâ€™m quite certain that it makes some of the compression techniques I use during my mastering sessions a bit smoother. So Iâ€™m curious if something like this is possible without being super heavy on the cpu.

I figured out two ways of implementing this modification. By actually changing the envelope detector, or by scaling the release and attack times before they get to the detector. I am working on a very flexible formula where I can shape the sigmoid curve as I wish, giving me a lot of options, but for now Iâ€™m doing some tests with a simple sine function that changes a linear value between 0 and 1 to a sigmoid value between 0 and 1. Here it is: Y(x) = 0.5(sin(x*pi-pi/2)+1)

At this point Iâ€™m just curious if it can be done, or if it would require an envelope detector that works fundamentally different. Do you have any thoughts on this?

All the best,

JD

November 20, 2014 - 5:29 am
Member Since: January 29, 2017
Forum Posts: 690
Offline

Hi JD, all good.

You can just apply the waveshaper to the output of the detector. There are a bunch of sigmoid waveshapers out there; I list several in the synth book, but a google of "sigmoid functions" should get you plenty more.

- Will

Leiden, The Netherlands
November 20, 2014 - 6:14 am
Member Since: November 5, 2014
Forum Posts: 80
Offline

Mmm, didnâ€™t expect it to be that simple But I think I get it. Maybe itâ€™s time to learn some more about envelope detectors in generalâ€¦ Still some difficulties to wrap my head around it. Anyway, thanks very much for the fast reply, Will! Back to work

November 20, 2014 - 5:26 pm
Member Since: January 29, 2017
Forum Posts: 690
Offline

Just remember that these envelope detectors do not output a linear shape (like the ADSR figures you sometimes see, which almost always are not linear either). These detectors mimic the exponential charge and discharge of a capacitor, so you are really waveshaping *those* curves.

- WP

Leiden, The Netherlands
November 20, 2014 - 6:24 pm
Member Since: November 5, 2014
Forum Posts: 80
Offline

Ah thanks:) I was just re-reading a part of the book and trying to figure that out! I'll get to that as soon as possible, but I'm still struggling with one other thing. I donâ€™t want to take up too much of your time, but it would be great if you had time for some last advise!

It seems that if I want to use the same S-curve for the attack and release, I can just end EnvelopeDetector::detect(float fInput) with the following, temporarily using my test sine function (like you explained in your last message):

m_fEnvelope = 0.5*(sin(m_fEnvelope*pi-pi/2)+1);
return m_fEnvelope;

But what if I would like to have a different curve for the attack and release. Then I would have to branch the function into an attack and a release part. I came up with this, but I might have missed something.

if(fInput> m_fEnvelope)
{
m_fEnvelope = m_fAttackTime * (m_fEnvelope - fInput) + fInput;
if(m_fEnvelope > 0.0 && m_fEnvelope < FLT_MIN_PLUS) m_fEnvelope = 0;
if(m_fEnvelope FLT_MIN_MINUS) m_fEnvelope = 0;

// bound them; can happen when using pre-detector gains of more than 1.0
m_fEnvelope = min(m_fEnvelope, 1.0);
m_fEnvelope = max(m_fEnvelope, 0.0);

//16-bit scaling!
if(m_bLogDetector)
{
if(m_fEnvelope 0.0 && m_fEnvelope < FLT_MIN_PLUS) m_fEnvelope = 0;
if(m_fEnvelope FLT_MIN_MINUS) m_fEnvelope = 0;

// bound them; can happen when using pre-detector gains of more than 1.0
m_fEnvelope = min(m_fEnvelope, 1.0);
m_fEnvelope = max(m_fEnvelope, 0.0);

//16-bit scaling!
if(m_bLogDetector)
{
if(m_fEnvelope <= 0)
return -96.0; // 16 bit noise floor

return 20*log10(m_fEnvelope);
}

m_fEnvelope = 0.5*(sin(m_fEnvelope*pi-pi/2)+1); // S-curve for release
return m_fEnvelope;
}

(hope the code wonâ€™t get truncated after posting)

Leiden, The Netherlands
November 20, 2014 - 6:27 pm
Member Since: November 5, 2014
Forum Posts: 80
Offline

November 21, 2014 - 6:41 pm
Member Since: January 29, 2017
Forum Posts: 690
Offline

Hmmm. Well, yeah that's going to be a problem. The main issue is that unlike a synth EG, the envelope detector is "attacking" when it is increasing and "releasing" when it is decreasing, one after the other, after the other... forever. So you will need to detect the current direction to know which curve to apply. Within that is the direction issue itself; how do you decide that the direction has flipped? One sample or two samples or... 100 samples all going in the same direction, increasing or decreasing? That implies that the detector needs memory but you already have with your window-of-samples so you could start there.

What you are doing is interesting - nonlinear or "distorted detection" and I'd be curious to hear comparisons with the normal detector.

WP

Leiden, The Netherlands
November 21, 2014 - 7:06 pm
Member Since: November 5, 2014
Forum Posts: 80
Offline

Thanks for the advice Will! I'll look into it If I succeed, I'll let you know.

All the best,

JD

Leiden, The Netherlands
November 24, 2014 - 3:22 pm
Member Since: November 5, 2014
Forum Posts: 80
Offline

HI Will:)

It seems to me that making the envelope detection linear first, is key for accurate waveshaping. So Iâ€™m just thinking, in your envelope follower you do the following calculation:
m_fAttackTime = exp(DIGITAL_TC/( attack_in_ms * m_fSampleRate * 0.001));

Would it be linear if I changed it to this?

m_fAttackTime = 1/(1+(1/( attack_in_ms * m_fSampleRate * 0.001)));

All the best,

JD

November 25, 2014 - 4:22 am
Member Since: January 29, 2017
Forum Posts: 690
Offline

Yes, that's correct regarding "start with linear" and then waveshape (I'm doing a similar thing with the sawtooth oscillator in my synth book). I'm not sure about your equation without simulating it for sure, but it is a linear mapping. We are on holiday here so my time is pretty limited.

- Will

Leiden, The Netherlands
November 25, 2014 - 1:46 pm
Member Since: November 5, 2014
Forum Posts: 80
Offline

December 3, 2014 - 11:40 pm
Member Since: January 29, 2017
Forum Posts: 690
Offline

Hi JD

just checking to see if you made any progress on your interesting Detector algorithm?

WP

Leiden, The Netherlands
December 5, 2014 - 5:15 pm
Member Since: November 5, 2014
Forum Posts: 80
Offline

Hi Will,

Thanks for your message! Getting the detector to act linear seems to have worked out, but it sounds a bit grainy, even with some S-shaped curves. Iâ€™m not sure if itâ€™s the â€˜cheaperâ€™ linear sound, or I messed up the formula, but it has been very insightful anyway.

But I must admit â€“ after going through the envelope follower code many times â€“ that I still donâ€™t get it a 100%. The code I get, but the inner workings still seem a bit vague to me (for compressors in general). So Iâ€™m reading everything I can about the subject at the moment. I also ordered the new Joshua D. Reiss book you mentioned; looking forward to reading that as well:) I guess to me itâ€™s important to be able to visualize these concepts in order to make the right calls implementing them. But Iâ€™m still having a lot of fun exploring these subjects!

It comes down to making this linear:

exp(ANALOG_TC/( attack_in_ms * m_fSampleRate * 0.001));

And understanding this piece, which seems to take in account the angle of the wave as well:

if(fInput> m_fEnvelope)
m_fEnvelope = m_fAttackTime * (m_fEnvelope - fInput) + fInput;
else
m_fEnvelope = m_fReleaseTime * (m_fEnvelope - fInput) + fInput;

Iâ€™ll give it another go as soon as possible

All the best,

JD

Leiden, The Netherlands
December 11, 2014 - 9:39 pm
Member Since: November 5, 2014
Forum Posts: 80
Offline

Hi Will, how are you?

I think Iâ€™ve got my detector working the way I want. I branched out the attack and release part (without the need of a memory aliment so far) and tried a different approach on the linear formula calculating m_fAttackTime and m_fReleaseTime. In graph form it seems to be exactly the same, but without the danger of dividing by zero: 1-(1/(S+1)). (with â€˜Sâ€™ being the attack or release in samples). I was wondering why there are no â€˜divided by zeroâ€™ issues in your algorithm, with the attack or release set to zero? Iâ€™m fiddling around with some S-curves now and it does sound quite nice.

Iâ€™m trying to make a brickwall limiting version as well. Iâ€™ve coded it to limit like in your book and implemented a look-ahead into the algorithm. Now Iâ€™m trying to find a way to link it to the attack control in a way to catch every peak in time while having a nice controllable â€˜preâ€™-attack. I had hoped because of the now linear detector this would be as easy as setting the attack an look-ahead to the same value, but this doesnâ€™t work. Iâ€™m thinking of hard-coding a static attack curve in the algorithm, while setting the detectors attack to zero. But maybe you have a trick up your sleeve you want to share?

All the best,

JD

December 16, 2014 - 6:35 pm
Member Since: January 29, 2017
Forum Posts: 690
Offline

Hmmm. Not sure how the hardcoded attack would work there, but I am very interested in hearing your compressor. Did you notice the thread on this forum

http://www.willpirkle.com/foru.....uency-cut/

Alex found a very old typo in the code/FX book that resulted in the wrong analog and digital TCs. Ironically, if I had left the older (less efficient) code in place, the error would not be there. This small change does make quite an audible difference in the brief tests I have run.

- Will

Leiden, The Netherlands
December 17, 2014 - 12:56 am
Member Since: November 5, 2014
Forum Posts: 80
Offline

Thanks for the link! I will try the new time constants tomorrow. Very curious:) About the hard coded attack - maybe Iâ€™m not using the right terminology. I was just wondering how to implement a sort of pre-attack in the brickwall limiting version Iâ€™ve build now. Because just linking the look ahead and attack doesnâ€™t work, I was thinking about another circular buffer to â€˜fade inâ€™ the gain reduction that is needed. Is this a good idea?

December 17, 2014 - 3:02 am
Member Since: January 29, 2017
Forum Posts: 690
Offline

Well, the gain reduction fade-in I get in the brickwall case. I think a table (or buffer) with a preset attack curve would work, the main thing is getting the trigger to work correctly. But, you have to be careful when you keep adding more bandages to the plugin - maybe there is a more elegant solution.

- Will

Leiden, The Netherlands
December 17, 2014 - 3:10 am
Member Since: November 5, 2014
Forum Posts: 80
Offline

Thanks! Iâ€™ll give it a go and try to keep it simple

Leiden, The Netherlands
December 17, 2014 - 5:31 pm
Member Since: November 5, 2014
Forum Posts: 80
Offline

Hi Will,

The new time constants work fine! Especially the ANALOG_TC sounds very smooth. And the DIGITAL_TC, yeah, sounds digital

JD

Forum Timezone: America/New_York

Most Users Ever Online: 152

Currently Online:
4 Guest(s)

1 Guest(s)

Top Posters:

Chaes: 56

Skyler: 48

StevieD: 46

Derek: 46

Frodson: 45

Peter: 43

TheSmile: 43

Nickolai: 43

clau_ste: 39

jeanlecode: 37

Member Stats:

Guest Posters: 1

Members: 775

Moderators: 1