Skip to main content
Topic: ReplayGain peak magnitude->dB implementation (Read 2072 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

ReplayGain peak magnitude->dB implementation

I couldn't find any canned scripts to do this, so I broke down and wrote one myself. (Ugh. I'd never imagine I'd be doing something this barbaric in a Windows app.)

This foobar2000 titleformat code receives %replaygain_track_peak% as input, which is assumed to be a string representing a positive, real-valued number to 6 decimal places*, and approximates its conversion from units of magnitude to units of decibels, as per 20*log10(input). The output is a string representing a real-valued number to 2 decimal places.

The algorithm is as documented on Wikipedia as derived from the Taylor series expansion of ln(1+x), to order 7, with the input normalized to the range [0.5, 5.0) for max efficiency. Calculations are carried out to the 3rd decimal place which is rounded to 2 places. With light empirical testing and light analysis of the code, the output error appears to be +- 0.01, but that's fairly sensitive to the chosen precision of 'normVal' and 'frac' - there are pretty tight bounds imposed by the (presumed) 32-bit signed math environment, and that considerably more than 4 digits of precision are necessary to ensure 4 digits of precision in the output. It's pretty easy to tweak the precision in one direction or the other and see the error jump up to +-0.1 or so.

This has been pretty-printed for clarity, but you'll need to remove the newlines/leading spaces to use in foobar2000; I use the following regex in jEdit which should translate to any suitably advanced text editor: s/\n *//

Code: [Select]
$if(%replaygain_track_peak%,%replaygain_track_peak% '('
    $puts(valInt,$replace(%replaygain_track_peak%,.,))
    $puts(numericVal,$add($get(valInt),0))
    $puts(numOfZeroes,$sub($len($get(valInt)),$len($get(numericVal))))
    $puts(isGe5,$ifgreater($substr($get(numericVal),0,1),4,-1,))
    $puts(normVal,$cut($pad($ifequal($get(isGe5),-1,0,)$get(numericVal),5,0),5))
    $puts(vMinus1,$sub($get(normVal),10000))
    $puts(vPlus1,$add($get(normVal),10000))
    $puts(frac,$div($get(vMinus1)0000,$get(vPlus1)))
    $puts(frac3,$div($mul($get(frac),$get(vMinus1),$get(vMinus1)),$mul($get(vPlus1),$get(vPlus1),3)))
    $puts(frac5,$div($mul($get(frac3),$get(vMinus1),$get(vMinus1),3),$mul($get(vPlus1),$get(vPlus1),5)))
    $puts(frac7,$div($mul($get(frac5),$get(vMinus1),$get(vMinus1),5),$mul($get(vPlus1),$get(vPlus1),7)))
    $puts(logSum,$add($get(frac),$get(frac3),$get(frac5),$get(frac7)))
    $puts(logApprox,$muldiv($get(logSum),1737,100))
    $puts(resultInt,$div($sub($get(logApprox),$mul($add($get(numOfZeroes),$get(isGe5)),200000)),10))
    $puts(isNegative,$strchr($get(resultInt),-))
    $puts(resultInt,$pad_right($replace($get(resultInt),-,),3,0))
    $puts(resultInt,$add($left($get(resultInt),$sub($len($get(resultInt)),1)),$ifgreater($right($get(resultInt),1),4,1,0)))
    $puts(resultInt,$pad_right($get(resultInt),3,0))
    $puts(rhs,$right($get(resultInt),2))
    $puts(difflen,$sub($len($get(resultInt)),$len($get(rhs))))
    $puts(outstr,$substr($get(resultInt),0,$get(difflen)).$get(rhs))
    $ifequal($get(isNegative),1,-,)$ifequal($get(difflen),0,0,)$get(outstr) dB')',N/A)


Example output:
Code: [Select]
0.068097 (-23.34 dB)
1.033155 (0.28dB)


* Due to computation precision restrictions, only 4 decimal places of the input are used. No rounding is done with that, so the max error is 20*ln(1.0001)/ln(10) = 0.000869 << 0.01, so this is fine.

ReplayGain peak magnitude->dB implementation

Reply #1
good work

I did something similar here: http://www.hydrogenaudio.org/forums/index....showtopic=76874 when I didn't know about that formula (I did actually but not in practical sense)

but it looks much simpler then yours, and it's more limited - I interpolated by hand: 10^(x/20) ~ 0.01*x^2 + 0.12*x +1 which suited fine some range of [-5, 15] I think

ReplayGain peak magnitude->dB implementation

Reply #2
Ah - yeah, I didn't see that, although that appears to be an approximation to exponentiation and not the logarithm.

I've been known to take RG peak values and quote them verbatim in forum posts, plug them into audio processing steps in Audacity, etc, so getting the result accurate across a wide range is important.

Stripping the exponent out to get the input value into a fixed-decade range is really, really important for convergence within reasonable time. Getting ln(5) to within 1% requires 4 terms. Getting ln(50) to within 1% requires 27 terms 

ReplayGain peak magnitude->dB implementation

Reply #3
My goodness, Axon. I... approve... I think. Reminds me of my HSL -> RGB conversion code, only that didn't require a Taylor series expansion.

 
SimplePortal 1.0.0 RC1 © 2008-2019