Old 06-11-2019, 09:13 AM   #1
DarkStar
Human being with feelings
 
DarkStar's Avatar
 
Join Date: May 2006
Location: Surrey, UK
Posts: 19,681
Default JSFX: convert amplitude to dB?

I thought that I would use
Code:
dB_level = floor(20 * log10((abs(spl0) + abs(spl1))/2));
to convert the average audio amplitude to dB.

I looked in some other JSFX and saw:
Code:
sc=6/log(2); // 8.656 
dB_level2 = floor(log((abs(spl0) + abs(spl1))/2)*sc); // dB
What am I getting wrong? If they give the same result, which is more efficient? And why?
__________________
DarkStar ... interesting, if true. . . . Inspired by ...

Last edited by DarkStar; 06-11-2019 at 11:31 AM.
DarkStar is offline   Reply With Quote
Old 06-11-2019, 10:37 AM   #2
sai'ke
Human being with feelings
 
sai'ke's Avatar
 
Join Date: Aug 2009
Location: NL
Posts: 1,458
Default

They're not the same, no.
log10(x) = log(x)/log(10)

So if you want option 2 to match option one, you'd have to do something like:

sc=20/log(10);
dB_level_new = floor(log((abs(spl0) + abs(spl1))/2)*sc);

Not sure about the performance of either. I know that exp is significantly faster than pow($exp, blah); but I don't know if something similar holds for log.
__________________
[Tracker Plugin: Thread|Github|Reapack] | [Routing Plugin: Thread|Reapack] | [More JSFX: Thread|Descriptions|Reapack]
sai'ke is offline   Reply With Quote
Old 06-12-2019, 08:18 AM   #3
DarkStar
Human being with feelings
 
DarkStar's Avatar
 
Join Date: May 2006
Location: Surrey, UK
Posts: 19,681
Default

Ah ha - I see where I was going wrong.

I was reading "log" as "log to the base 10", but it is actually "log to the base e, or natural log". With that understood, the 2 equations yield very similar results. Here they both are in MS Excel, with a range of values:



With amplitudes of 0.50, the equations evaluate to -6.021 and -6.000 respectively, so the second equations looks the better of the two.
__________________
DarkStar ... interesting, if true. . . . Inspired by ...
DarkStar is offline   Reply With Quote
Old 06-12-2019, 08:31 AM   #4
sai'ke
Human being with feelings
 
sai'ke's Avatar
 
Join Date: Aug 2009
Location: NL
Posts: 1,458
Default

sc should be 20/log(10);

That brings your error < 1e-10;
__________________
[Tracker Plugin: Thread|Github|Reapack] | [Routing Plugin: Thread|Reapack] | [More JSFX: Thread|Descriptions|Reapack]
sai'ke is offline   Reply With Quote
Old 06-12-2019, 10:12 AM   #5
DarkStar
Human being with feelings
 
DarkStar's Avatar
 
Join Date: May 2006
Location: Surrey, UK
Posts: 19,681
Default

Even better, thank you. And this is what I am working on:

__________________
DarkStar ... interesting, if true. . . . Inspired by ...
DarkStar is offline   Reply With Quote
Old 06-12-2019, 07:22 PM   #6
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,295
Default

Quote:
Originally Posted by DarkStar View Post
With amplitudes of 0.50, the equations evaluate to -6.021 and -6.000 respectively, so the second equations looks the better of the two.
???
-6.021 is closer to correct. 6db for a doubling/halving is an approximation.
ashcat_lt is online now   Reply With Quote
Old 06-18-2019, 07:58 AM   #7
DarkStar
Human being with feelings
 
DarkStar's Avatar
 
Join Date: May 2006
Location: Surrey, UK
Posts: 19,681
Default

OK, thank you. back to my original calculation then,
__________________
DarkStar ... interesting, if true. . . . Inspired by ...
DarkStar is offline   Reply With Quote
Old 11-15-2020, 09:51 PM   #8
IonianStreams
Human being with feelings
 
IonianStreams's Avatar
 
Join Date: May 2019
Location: Front Range Colorado
Posts: 17
Default

I found this thread as I had the same question as DarkStar. From sai'ke and ashcat_lt's comments, it looks like the 20 * log10(spl) approach is more accurate than the 6/log(2) * log(spl) approach. But why? Which is theoretically, physically correct? I looked into it a bit more. I'm no expert -- corrections, clarifications are welcome.

First, clear up log semantics. There are two types of logs here: base-10 log and natural log (base e). As DarkStar notes, it's easy to get confused which is which, especially when dealing with the semantics of differing programming languages. Reaper's JSFX uses "log()" for the natural log and "log10()" for base-10 log (in contrast for example, Microsoft Excel uses "ln()" for natural log).

Base-10 logs deal with powers of 10; e.g. 10^2 = 100 and log10(10^2) = 2. Natural log deals with powers of "e" which is the value 2.7182; e.g. 2.7182^5 = 148.4 and log(2.7182^5) = 5. As sai'ke shows, to convert from natural to base-10 log use log10(x) = log(x)/log(10), (that is, divide by the natural log of the value 10, which illustrates another way to get confused between the two logs!).

The decibel (dB) scale is based on powers of ten (another clue that the 20 * log10(spl) approach is more appropriate). A "decibel" is one-tenth of a "bel". A measurement of a sound in "bels" is the base-10 log of a measure of the sound's "acoustic pressure". This value is then multiplied by 10 to get the equivalent "decibels" value (this 10-factor is solely for the convenience of making the resulting values bigger; it has nothing to do with acoustical physics).

So if P is the sound's "acoustic pressure" (in units such as "watts per square meter") then log10(P) is the pressure in bel units, and 10 * log10(P) is the pressure in decibel (dB) units.

bel = log10(P)
decibel = 10 * log10(P)

But -- if I have this right -- in Reaper we deal with digital samples ("spl") that measure the sound wave's amplitude, not its pressure. These sample values range from -1 to 1 (or 0 to 1 in absolute terms, with the sign indicating phase). To express these amplitude sample values in decibels which depend on pressure, we need to know how amplitude relates to pressure: a sound's pressure relates to the square of the sound's amplitude; that is, P = spl^2.

So...
decibel (dB) = 10 * log10(P)
= 10 * log10(spl^2)
= 10 * [2 * log10(spl)]
= 20 * log10(spl)
[ Math reminder: log(X^n) = n * log(X) ]
in short...
dB = 20 * log10(spl)

There's the origin of the first equation. Reaper gives us a sample of a sound's amplitude. To convert it to dB, we first convert amplitude to pressure, which (ultimately) results in a 2-factor. We take the base-10 log of the pressure to get bels. Then we multiply that by 10 to get decibels dB. The 10 and the 2 combine to form the 20-factor. Only the 2-factor, however, is physically significant as it links amplitude to pressure; the 10-factor is just for convenience.


What about the other equation, 6/log(2) * log(spl)? Converting from the log10 form to a natural log form, we get...

dB = 20 * log10(spl)
dB = 20 * log(spl) / log(10) 'using log10 to log conversion from sai'ke above
dB = 20 * log(spl) / 2.302585
dB = 20 / 2.302585 * log(spl)
dB = 8.68589 * log(spl)
dB = 6.0206/log(2) * log(spl)
dB ~ 6/log(2) * log(spl)

Note that the factor 8.68589 is exact because it's derived directly from the log(10) conversion factor. If we compute 6/log(2) we get 8.65617, which is close but not exact. To get the two equations to agree exactly we should use 6.0206/log(2) which equals 8.68589. This is what sai'ke said above: "sc should be 20/log(10)". This is what ashcat_lt said above: "-6.021 is closer to correct."

Conclusion: Either the log10 or natural log approach can be used. But the log10 approach using 20 * log10(spl) is more accurate and also reflects the underlying nature of the decibel scale. The log approach using 6/log(2) * log(spl) can be used as an approximation; maybe it's needed if the log10 function is unavailable or the natural log is desired for some other reason. To improve its accuracy, use 6.0206/log(2) * log(spl).

[Note: I have ignored the fact that sound pressure is properly treated as a ratio to a "threshold of hearing" reference pressure.]


SIDEBAR: ashcat_lt also said above, "6db for a doubling/halving is an approximation." This refers to the fact that a 6 dB boost corresponds to a doubling of the amplitude, and a 6 dB cut means a halving of the amplitude (note that 20*log10(2)=6.0206). However, that does not mean that our human perception of the volume is doubled or halved (apparently that takes more than a 6 dB change). I'm fuzzy on whether -- and if so how -- the 6/log(2) term represents this relationship.


BUT WAIT, THERE'S MORE...
In some JS programs I've also seen the reverse procedure -- taking a value in dB and converting to a sample "spl" value. The code seen is (e.g. JS Volume Adjustment)...
spl = 2^(dB/6)

Converting backward to confirm that this is the same thing just in reverse...
spl = 2^(dB/6)
log(spl) = log[2^(dB/6)]
log(spl) = dB/6 * log(2)
log(spl) / log(2) = dB/6
log(spl) / log(2) * 6 = dB
dB = 6/log(2) * log(spl)
...which confirms that we're back where we started.

However, we've seen that using "6" is close but not exact. To convert dB to spl exactly use 6.0206...
spl = 2^(dB/6.0206)

...or use the log10 equivalent...
dB = 20 * log10(spl)
dB/20 = log10(spl)
10^(dB/20) = 10^(log10(spl))
spl = 10^(dB/20)


Summary:
Using log10()...
dB = 20 * log10(spl)
spl = 10^(dB/20)
Using natural log()...
dB = 6.0206/log(2) * log(spl)
spl = 2^(dB/6.0206)

Examples:
spl = 0.384
dB = 20 * log10(0.384) = -8.3 dB

spl = 0.9999 'nearly the maximum spl value of 1.0
dB = 20 * log10(0.9999) = -0.00087 dB 'nearly the maximum dB value of 0

spl = 0.0001 'nearly the minimum spl value of 0
dB = 20 * log10(0.0001) = -80 dB 'nearly the minimum dB value; inaudible

Notice that these dB equations yield negative values from 0 dB downward toward -96 dB. This is the "dB Full Scale" or "dBFS".

References:
https://www.dsprelated.com/freebooks/mdft/Decibels.html
http://legacy.earlham.edu/~tobeyfo/m...ibel_edit.html
https://docs.cycling74.com/max5/tuto...italaudio.html
__________________
I understand. At least I think I understand, which is the same thing... I think.
My music made with Reaper: https://www.youtube.com/channel/UC5y...G03G5UQBR22Yfw
IonianStreams is offline   Reply With Quote
Old 11-16-2020, 01:22 AM   #9
Heart Doctor
Human being with feelings
 
Join Date: Jan 2015
Location: Munich
Posts: 241
Default More on this topic

This discussion comes up every once in a while.

See also the following REAPER forum thread:

https://forum.cockos.com/showthread.php?t=228775
Heart Doctor is offline   Reply With Quote
Old 01-21-2022, 07:30 PM   #10
cohler
Banned
 
Join Date: Dec 2018
Posts: 642
Default

Quote:
Originally Posted by IonianStreams View Post
These sample values range from -1 to 1 (or 0 to 1 in absolute terms, with the sign indicating phase).
Then why does
Code:
reaper.NF_GetMediaItemMaxPeak
return numbers like -20.3765432?

Can't find any documentation anywhere on what these values are?
cohler is offline   Reply With Quote
Old 01-21-2022, 08:08 PM   #11
schwa
Administrator
 
schwa's Avatar
 
Join Date: Mar 2007
Location: NY
Posts: 15,823
Default

Functions like NF_whatever are extension functions, not native REAPER functions, so you'd have to look to the extension developer for documentation.


Re the various JSFX dB calculations,


Exactly correct: dB = 20*log(spl)/log(10)

Exactly correct: 8.6858896380650365530225783783321*log(spl)

Exactly correct: 6.0205999132796239042747778944899*log(spl)/log(2)

Accurate to 7 decimal places: 8.68589*log(spl)

Accurate to 4 decimal places: 6.02*log(spl)/log(2)

Accurate to 2 decimal places : 6*log(spl)/log(2)


There is no performance difference given that either the compiler or the programmer can precalculate the constant log term. The last calculation might be easier to remember given that -6 dB is very close to half the amplitude. But really the reason I think the versions with 6 have persisted for so long is that people tend to copy code that works.
schwa is offline   Reply With Quote
Old 01-21-2022, 08:47 PM   #12
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,295
Default

Quote:
Originally Posted by cohler View Post
Then why does
Code:
reaper.NF_GetMediaItemMaxPeak
return numbers like -20.3765432?
I’d guess that’s a dB value, though it is actually possible for sample values to go beyond +/-1. Like way beyond. Like into the billions at least. We usually work below 0dbFS, though, and if you follow some the math above, you’ll soon see why that falls between -1 and 1.

Last edited by ashcat_lt; 01-21-2022 at 08:53 PM.
ashcat_lt is online now   Reply With Quote
Old 01-22-2022, 06:32 AM   #13
cohler
Banned
 
Join Date: Dec 2018
Posts: 642
Default

Quote:
Originally Posted by ashcat_lt View Post
I’d guess that’s a dB value, though it is actually possible for sample values to go beyond +/-1. Like way beyond. Like into the billions at least. We usually work below 0dbFS, though, and if you follow some the math above, you’ll soon see why that falls between -1 and 1.
So are you saying that @IonianStreams is incorrect? I quoted him where he said, that REAPER samples, "range from -1 to 1 (or 0 to 1 in absolute terms, with the sign indicating phase)."
cohler is offline   Reply With Quote
Old 01-22-2022, 08:45 AM   #14
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,295
Default

No. I’m saying they were simplifying for the sake of clarity. Most of the samples we work with will have absolute value less than 1. Reaper can handle much bigger values. It ultimately doesn’t change the math, though. A sample value of 1 is 0dBFS. Most of what we do is at negative dBFSlevels, but it can go over into positive values. Those positive dBFS values when converted back to sample values will give you numbers bigger than 1.
ashcat_lt is online now   Reply With Quote
Old 01-22-2022, 04:57 PM   #15
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,110
Default

Quote:
Originally Posted by ashcat_lt View Post
I’d guess that’s a dB value, though it is actually possible for sample values to go beyond +/-1.
Yes, NF_GetMediaItemMaxPeak() returns in dbFS (and yes, it's missing in the API doc, will correct that).

Last edited by nofish; 01-23-2022 at 10:48 AM.
nofish is offline   Reply With Quote
Old 01-22-2022, 04:59 PM   #16
cohler
Banned
 
Join Date: Dec 2018
Posts: 642
Default

Quote:
Originally Posted by ashcat_lt View Post
No. I’m saying they were simplifying for the sake of clarity. Most of the samples we work with will have absolute value less than 1. Reaper can handle much bigger values. It ultimately doesn’t change the math, though. A sample value of 1 is 0dBFS. Most of what we do is at negative dBFSlevels, but it can go over into positive values. Those positive dBFS values when converted back to sample values will give you numbers bigger than 1.
My understanding (based on actual testing) is that ALL REAPER amplitude samples are between 1 and -1, with 1 or -1 representing 0dB and anything in between representing a negative dB amplitude according to the formula (LUA):

Code:
dB = 20*math.log(math.abs(sample),10)
cohler is offline   Reply With Quote
Old 01-22-2022, 05:05 PM   #17
schwa
Administrator
 
schwa's Avatar
 
Join Date: Mar 2007
Location: NY
Posts: 15,823
Default

Sample values outside of [-1, 1] represent positive dBFS values. A sample value of 2 (or -2) is about +6dB, etc.
schwa is offline   Reply With Quote
Old 01-22-2022, 05:10 PM   #18
cohler
Banned
 
Join Date: Dec 2018
Posts: 642
Default

Quote:
Originally Posted by schwa View Post
Sample values outside of [-1, 1] represent positive dBFS values. A sample value of 2 (or -2) is about +6dB, etc.
I see what you are saying. So dBFS refers to the fact that the samples are floating point and therefore can go positive in terms of dB?
cohler is offline   Reply With Quote
Old 01-22-2022, 05:15 PM   #19
schwa
Administrator
 
schwa's Avatar
 
Join Date: Mar 2007
Location: NY
Posts: 15,823
Default

FS means "full scale". 0 dbFS is the loudest signal that a given converter can process without loss of resolution. Internally REAPER processes everything in floating point, so sample values can be much higher than 0 dBFS. When rendered to a fixed point format or sent to a DA converter, anything over 0 dBFS is truncated (aka clipped).
schwa is offline   Reply With Quote
Old 01-22-2022, 05:18 PM   #20
cohler
Banned
 
Join Date: Dec 2018
Posts: 642
Default

Quote:
Originally Posted by schwa View Post
FS means "full scale". 0 dbFS is the loudest signal that a given converter can process without loss of resolution. Internally REAPER processes everything in floating point, so sample values can be much higher than 0 dBFS. When rendered to a fixed point format or sent to a DA converter, anything over 0 dBFS is truncated (aka clipped).
Yup. I understand. Thanks for the explanation.
cohler is offline   Reply With Quote
Old 09-12-2022, 08:19 PM   #21
IonianStreams
Human being with feelings
 
IonianStreams's Avatar
 
Join Date: May 2019
Location: Front Range Colorado
Posts: 17
Default As relates to YouTube "content loudness"

Hey All,

I got drawn back to this thread in regard to YouTube's "content loudness" normalization.

When looking at YouTube's "stats for nerds" (right-click on a video), you'll see something like "Volume / Normalized: 100% / 48% (content loudness 6.3dB)".

> The first number is the value for the YouTube volume slider under the little speaker icon; for the purpose at hand, set this to 100%.
> The second number is the YouTube normalized volume which is given relative to the first number (this is why you want to set the first slider number to 100% so that the second number makes sense in absolute terms).
> The third number is how much YouTube deems the original audio's volume exceeds YouTube's standard reference volume. If this number is positive, it means the original audio is too loud compared to what YouTube wants and YouTube will "normalize" the volume by turning it down on playback.

(NOTE: A negative (or zero) content loudness value indicates that the original audio is below YouTube's expected volume. But YouTube does not turn UP the volume of audio that is lower than the YouTube standard; it is not "normalized" upward. For this reason, the first two values (Volume / Normalized) both show 100% (assuming you have the volume slider set to 100%).)

How does YouTube calculate these numbers? YouTube uses the rule that a +6dB change in volume corresponds to a doubling of amplitude and, conversely, a -6dB change corresponds to half the amplitude. So, in the example above, the content loudness is +6.3 meaning the audio is too loud by a bit more than 6dB and needs to be normalized down by a bit more than -6dB. This means YouTube will turn it down by a bit more than half -- that is, for this example, down to 48%.

We can calculate these numbers using the formulas earlier in this thread. Volume (measured in dB) is related to amplitude (as expressed in a sample spl value) as:

spl = 10^(dB/20).

Remember, spl amplitude values range from 0 to 1 (assuming we stay within <0 dBFS values as schwa and ashcat_lt expertly pointed out). So amplitudes can be expressed as a percentage with 1 = 100%, 0.5 = 50%, 0.33 = 33%, and so on.

So, in our example where the audio is 6.3dB too loud, YouTube needs a -6.3dB normalization. Using the equation...
spl = 10^(-6.3/20) = 0.484 = 48.4%. YouTube will turn down the volume to 48% or just over halfway.

Streaming in YouTube the Billy Gibbons "She's On Fire" video we see "Volume / Normalized: 100% / 42% (content loudness 7.6dB)". Here, YouTube will turn down the too-loud song by -7.6dB. Thus, spl = 10^(-7.6/20) = 0.417 = 42%. YouTube will turn down the volume to 42%.

Maroon 5 "She Will Be Loved" -- content loudness = 2.7dB, spl = 10^(-2.7/20) = 0.733. YouTube will play it back at 73% volume.

(Remember to keep your YouTube volume slider at 100% to see these numbers.)

But "too loud" is a relative term -- too loud compared to what? Well, compared to YouTube's standard reference volume, whatever that is. The general thinking is that -14 LUFS Integrated is the YouTube "target." But there's a lot of discussion around this issue and I won't get into it here or how loud your songs should be mastered (see links below).

But for the sake of argument to wrap this up, let's assume that -14 LUFS Integrated is what YouTube uses. Under this assumption, if your song as delivered to YouTube is measuring -10.5 LUFS Integrated then it's 3.5dB "too loud" and YouTube will play it back quieter at 67% of your original volume:

spl = 10^(-3.5/20) = 67%

Your comments, corrections & clarifications are welcome. Especially, how do you do a similar exercise in other streaming platforms?

See also...
https://productionadvice.co.uk/stats-for-nerds/
https://productionadvice.co.uk/how-loud/
https://forum.cockos.com/showthread.php?t=254748
__________________
I understand. At least I think I understand, which is the same thing... I think.
My music made with Reaper: https://www.youtube.com/channel/UC5y...G03G5UQBR22Yfw

Last edited by IonianStreams; 09-12-2022 at 08:50 PM.
IonianStreams is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 01:00 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.