Calculate logarithmic fade out?
-
- KVRAF
- Topic Starter
- 2550 posts since 13 Mar, 2004
I'm looking for a way (function) I can periodically call (via a timer) that sets a variable from say 100 (full volume) to 0 silence in given amount of time.
I have it working linear but rather would like to do it logarithmic (since the device I'm controlling has linear volume adjustment, a log fade would then be perceived as linear.)
Since I'm not sure it's clear what I want, another use case of this is a play position indicator in an audio player, goes from 0 (left / song start) to 100 (right / song end) continously in given amount of time (song duration), I practically want this reversed and logarithmic.
I have it working linear but rather would like to do it logarithmic (since the device I'm controlling has linear volume adjustment, a log fade would then be perceived as linear.)
Since I'm not sure it's clear what I want, another use case of this is a play position indicator in an audio player, goes from 0 (left / song start) to 100 (right / song end) continously in given amount of time (song duration), I practically want this reversed and logarithmic.
-
- Banned
- 12368 posts since 30 Apr, 2002 from i might peeramid
set a threshold, you'll never get to zero
reference RT60 calcs, where teh thresh is .001
reference RT60 calcs, where teh thresh is .001
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.
- KVRAF
- 11001 posts since 15 Apr, 2019 from Nowhere
Are you looking to emulate an audio taper pot? You don't need to use logs to get that, as it's based on the math for putting a fixed value resistor in parallel with a variable resistor.
If:
v = current volume level
k = a constant value used to tweak the non-linear response
a = % of attenuation where 1.0 = maximum volume and 0.0 = zero volume
instantaneous value of volume (v) = a / (1 + (1 - a) * k)
If the response is not what you expect, then you can vary k until it works for you. Maybe start with a value around 0.25 or so.
If:
v = current volume level
k = a constant value used to tweak the non-linear response
a = % of attenuation where 1.0 = maximum volume and 0.0 = zero volume
instantaneous value of volume (v) = a / (1 + (1 - a) * k)
If the response is not what you expect, then you can vary k until it works for you. Maybe start with a value around 0.25 or so.
Last edited by Forgotten on Sat Jul 27, 2019 1:51 am, edited 1 time in total.
- KVRist
- 323 posts since 19 Jul, 2008
If you want it to be linear in decibels, start with 1 and exponentially decay toward zero. Or use a power function like x^alpha from x=1 to 0 to approximate exponential decay with the benefit of having a finite support.
VCV Rack, the Eurorack simulator
-
- KVRian
- 653 posts since 4 Apr, 2010
OK, you want an exponential fade, right? That would be the equivalent of a linear fade on a log (dB) scale. For an exponential fade, simply start with 1.0 and multiply it by a rate value less than 1 every update period (every sample, for instance). For example, if the multiplier is 0.5, you'd get 1.0, 0.5, 0.25, 0.125, 0.625...corresponding to (approximately) 0 dB, -6 dB, -12 dB, -18 dB, -24 dB...No_Use wrote: ↑Fri Jul 26, 2019 1:06 am I'm looking for a way (function) I can periodically call (via a timer) that sets a variable from say 100 (full volume) to 0 silence in given amount of time.
I have it working linear but rather would like to do it logarithmic (since the device I'm controlling has linear volume adjustment, a log fade would then be perceived as linear.)
Since I'm not sure it's clear what I want, another use case of this is a play position indicator in an audio player, goes from 0 (left / song start) to 100 (right / song end) continously in given amount of time (song duration), I practically want this reversed and logarithmic.
It will never get to zero, but you choose a target value (after which you can set it to zero). The math is not so obvious, but you can calculate the rate multiplier exactly (within floating point limits). Check out my article series with code for the details, including sample-accurate fade time (discussed in part 2 and the code):
https://www.earlevel.com/main/category/ ... enerators/
My audio DSP blog: earlevel.com
-
- KVRAF
- Topic Starter
- 2550 posts since 13 Mar, 2004
Oh, either reply notification didn't work or I forgot to turn it on err..., sorry for the late reply.
Fade time needn't be sample accurate at all (it's not for an audio application / plugin, just for a sleep timer app with volume fade function), so rough approximation will do.
Will check out the linked articles.
Thanks for the answers.
edit:
To give further details, the doc for the device in question says:
Yep that's it, it's a good starting point to realize that I want an exponential fade.
Fade time needn't be sample accurate at all (it's not for an audio application / plugin, just for a sleep timer app with volume fade function), so rough approximation will do.
Will check out the linked articles.
Thanks for the answers.
edit:
To give further details, the doc for the device in question says:
So essentailly what I'd like to achieve is an exponential (linear in dB) fade out from [current level] to (approx.) 0 over (roughly) a given amount of time. Any advice how to do that with the simplest math possible?Specifying Volume in the Level Scale
The level scale specifies volume in a linear scale. It ranges from 0 to 100, where 0 represents silence and 100 represents the highest volume. The mapping for producing a linear multiplicative value is implementation dependent.
-
- KVRian
- 653 posts since 4 Apr, 2010
Simple, just start with a variable at 1.0. It’s the gain value that you use to multiply your audio by, so your audio is played at its full level. When it’s time to fade, go into a new state where you multiply the gain by a rate factor, at each sample period. A rate factor slightly less than 1 would be a very slow fade. Smaller values are faster. More details in my envelope generator article, but that’s the gist of it.
A good idea to set a limit, so once the gain is below a certain level, you set it to zero, or at least stop updating it. Otherwise you hit denormals. Every decimal point is another 20 dB, so 0.1 is -20 dB, 0.00001 is -100 dB.
A good idea to set a limit, so once the gain is below a certain level, you set it to zero, or at least stop updating it. Otherwise you hit denormals. Every decimal point is another 20 dB, so 0.1 is -20 dB, 0.00001 is -100 dB.
My audio DSP blog: earlevel.com
-
- KVRAF
- Topic Starter
- 2550 posts since 13 Mar, 2004
Thanks.
I get it this far. What I'm stumbling on is how to calculate the gain factor so it reaches the limit in a given amount of time.
This is what I have currently in my app as the crude linear 'fade' (periodically reducing volume in steps of 5 over roughly a minute) via a timer (Java):
I then check periodically if volume has reached <= 0 to trigger a shutdown.
It works sufficently ok for my purpose.
I get it this far. What I'm stumbling on is how to calculate the gain factor so it reaches the limit in a given amount of time.
This is what I have currently in my app as the crude linear 'fade' (periodically reducing volume in steps of 5 over roughly a minute) via a timer (Java):
Code: Select all
private void startVolumeFade() {
int vol = vc.getLevel();
int steps = vol / 5; // reduce vol. in steps of 5
int oneStepDurationMillisec = 60 / steps * 1000;
vc.setFadeLevel(vc.getLevel()); // start fading at current volume
fadeOutTimer = new Timer();
TimerTask task = new TimerTask() {
public void run() {
vc.setFadeLevel(vc.getFadeLevel() - 5);
}
};
fadeOutTimer.schedule(task, 0, oneStepDurationMillisec);
}
It works sufficently ok for my purpose.
-
- KVRian
- 653 posts since 4 Apr, 2010
OK, you said roughly—I thought that meant you would plug in some numbers and see what you get.
As I said, the math is explained in my article series, and is in the code. The idea is that for a linear fade, and wanting to set a given number of steps to get there, you'd divide the distance you need to move by the number of steps to get the increment, right? For an exponential move, you essentially need to warp the move to linear (by taking the log), doing the same thing you did for a linear move, then warping it back (exp).
Grab calcCoef from my code. targetRatio is a number like 0.0001 (-80 dB), rate is the number of steps to get there. Note that if you set targetRatio too low, it will sound like the fade finishes earlier, because the tail gets too quiet.
You can grab the iterative code from the release.
Note that I set a target below zero and stop when it gets to zero. That means the base needs to be below zero, and that's what releaseBase is about. You can grab its calculation from my code too. Or, you can omit it as many people do (because it's not as hard to figure out), and just kill it or let it continue to run (but watch out for denormals).
As I said, the math is explained in my article series, and is in the code. The idea is that for a linear fade, and wanting to set a given number of steps to get there, you'd divide the distance you need to move by the number of steps to get the increment, right? For an exponential move, you essentially need to warp the move to linear (by taking the log), doing the same thing you did for a linear move, then warping it back (exp).
Grab calcCoef from my code. targetRatio is a number like 0.0001 (-80 dB), rate is the number of steps to get there. Note that if you set targetRatio too low, it will sound like the fade finishes earlier, because the tail gets too quiet.
Code: Select all
float ADSR::calcCoef(float rate, float targetRatio) {
return (rate <= 0) ? 0.0 : exp(-log((1.0 + targetRatio) / targetRatio) / rate);
}
Code: Select all
case env_release:
output = releaseBase + output * releaseCoef;
if (output <= 0.0) {
output = 0.0;
state = env_idle;
}
My audio DSP blog: earlevel.com
- KVRAF
- 7890 posts since 12 Feb, 2006 from Helsinki, Finland
This is rather trivial if you take the problem into log-domain and think about it in terms of decibels, since we're just adding numbers.
Let's say we want -100dB = 10^(100/20) fade over 5 seconds and we're updating 30 times a second. So we have 5*20=150 updates, each of which should reduce the gain by -100dB/150 = -0.75dB. That's it.
The coefficient formula then becomes:
10^(dBtotal/(20*timeSec*updatesPerSec))
= exp(log(10)/20 * dBtotal/(timeSec*updatesPerSec))
-
- KVRian
- 607 posts since 6 Mar, 2005 from USA
Another way of saying what mystran said:
Code: Select all
// outside of your audio loop initialize these variables
int N = timeSec*updatesPerSec;
int i = 0;
float k = 10^(dBtotal/(20*N)); // calculate this just once
gain = 1; // max gain
// inside your audio loop, every updatesPerSec call this code to calculate gain
gain = (++i > N ? 0 : gain*k) // fast way to update gain; no powers
-
- Banned
- 12368 posts since 30 Apr, 2002 from i might peeramid
from the xoxos dsp enema freely offered:
DECAY
The gain of a recursive system at recursion n is derivable using the pow function:
gain_at_n_recursions = pow(gain, n);
Eg. a variable multiplied by 0.5 on every sample is obviously 0.0625 of its original value on the 4th iteration:
0.0625 = pow(0.5, 4);
If we intend to have a specific gain at a specific number of recursions, we can calculate a gain coefficient:
sought coefficient = pow(intended_gain_at_n_recursions, inverse_of_n);
eg: 0.5 = pow(0.0625, 1/4);
Given any two of these variables we can derive the third using the identities:
x = pow(b, y);
b = pow(x, 1 / y);
y = log(x) / log(b);
eg. 4 = log(0.0625) / log(0.5);
DECAY
The gain of a recursive system at recursion n is derivable using the pow function:
gain_at_n_recursions = pow(gain, n);
Eg. a variable multiplied by 0.5 on every sample is obviously 0.0625 of its original value on the 4th iteration:
0.0625 = pow(0.5, 4);
If we intend to have a specific gain at a specific number of recursions, we can calculate a gain coefficient:
sought coefficient = pow(intended_gain_at_n_recursions, inverse_of_n);
eg: 0.5 = pow(0.0625, 1/4);
Given any two of these variables we can derive the third using the identities:
x = pow(b, y);
b = pow(x, 1 / y);
y = log(x) / log(b);
eg. 4 = log(0.0625) / log(0.5);
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.
- KVRist
- 243 posts since 24 Aug, 2014
Try this formula: https://www.desmos.com/calculator/p7jao8ae2i
-
- KVRAF
- Topic Starter
- 2550 posts since 13 Mar, 2004
Just wanted to say thanks for all the replies, I'm still following.
Though it will be a bit until getting back to this project, that's why I haven't reported back in more detail yet..
Though it will be a bit until getting back to this project, that's why I haven't reported back in more detail yet..