Calculate decibels from a ratio or range of bits
MarcG
Posts: 14
I want to display, in decibels, the 14bit value that I am sending to an audio MDAC digital attenuator.
To calculate the gain of my MDAC in dB, I use the formula log10(<MDAC value>/16383)*20
My dB reading only needs to display an accuracy of 1 decimal point (ex -5.5dB). I am trying to save cogs and maintain speed and I thought that this might best be accomplished, in spin, utilizing the log table. I have modified my formula to use log base 2, and get answers that are accurate enough for my application.
log2(<MDAC value>/16383)*6 This results in an answer that is +/- a tenth dB.
I can do this with a lookup table, but I would like to save memory.
To calculate the gain of my MDAC in dB, I use the formula log10(<MDAC value>/16383)*20
My dB reading only needs to display an accuracy of 1 decimal point (ex -5.5dB). I am trying to save cogs and maintain speed and I thought that this might best be accomplished, in spin, utilizing the log table. I have modified my formula to use log base 2, and get answers that are accurate enough for my application.
log2(<MDAC value>/16383)*6 This results in an answer that is +/- a tenth dB.
I can do this with a lookup table, but I would like to save memory.
Comments
dB := ( ( >| value ) - 14 ) * 6
should do the trick. The -14 is, of course, the division. Also, you may want to add in 0.707 * 16384 to value 1st, to do a sort of rounding in pre-log space.
dB := ( ( >| (value + 11585) ) - 14 ) * 6
I'm not sure of operator precedence, so you may not need some of the parens.
Jonathan
dB := f32.FMul( f32.Log10( f32.FFloat( val ) ), 20.0 )
You can do a fairly simple integer approximation to return milliBells (dB * 100):
dB := >|val - 1
frac := val >< dB >< 16
dB *= scale_dB
dB += frac / scale_frac
where scale_frac = 109 and scale_dB = 602. Still just an approximation, of course, good to the 1st 2 decimal places or so. This does not divide by 16384...you can simple subtract 8429 [milliBells] at the end.
Jonathan
Z := >| x - 1 ' Highest bit of the number, 0 to 15, do not allow x=0. Integer part of log2(x)
R := x - |< Z ' This is the remainder when the greatest power of 2 less than x is subtracted from x
M := R >> ((Z #> 8) - 8) << (8 - (Z <# 8)) ' This shifts the remainder over to fill one byte.
' shift right for large numbers, left for small numbers (based on Z). 8 is the turning point
' this is the fractional part of log2(x), but as linear interpolation between 2^z and 2^(z+1)
' The bitLog function stops there, with a the integer value and a linear interpolation to z bits
Now, on the propeller, you have the log table in rom. One more step to turn the linear interpolation into more accurate log interpolation. Justify the remainder R into 12 bits (instead of 8) and use that as the basis of the lookup into the log table at $C000 to $CFFF.
M := R >> ((Z #> 12) - 12) << (12 - (Z <# 12)) ' Justifies the remainder into 12 bits.
L := WORD[$C000][M & $FFE] ' fractional part of log2(x), 16 bits mantissa, lsb=0 for word address
Subtract the 14 from the characteristic (integer part) at the very end.
Tracy, There seems to be a gap in my knowledge. I know that if I take (Z-13)*6, I get my dB readings in powers of 2 (i.e. 0,-6,-12,-18, etc.). How do I combine Z & L in such a fashion to get full resolution. Also, since I am working with integers in spin, I will need the dB value in a form where I can extract the decimal integer side as one integer, and the decimal fractional side as another integer. Convert them both to text strings, and combine them together for my display. Example -6.5dB. Or is there a better approach?
20 * log10(value / 16383)
Base 10 log is 0.30103 times the base 2 log:
20 *0.30103 * log2( value /16383)
and 16384 is a power of two so it becomes:
6.0206 * (log2(value) - 14)
Johnathan rounded off to 6. I guess the 0.707 has to do with RMS conversion, but I'm not sure.
If you have an integer part Z and a 16 bit mantissa M, the two can be combined as
Z << 16 + M
That is a fixed point number with the binary radix implied in front of the mantissa.
Now subtract 14 and multiply times 6.0206 to give decibels with 16 binary digits of fractional part to play with.
dB := (Z - 14) * 6 ' in binary
(It is possible to do 6.0206 too)
@Tracy: Actually, the 0.707 was an error on my part...the idea was to add in a value to pre-round...it should have actually been 0.5858 because:
log2( 1 ) = 0
log2( 1.414 ) = 0.5 (you want this to round up to 1)
log2( 2 ) = 1
Jonathan
Tracy, Right after you stated that the integer and mantissa can be combined by Z << 16 + M. Then you stated dB := (Z - 14) * 6 ' in binary. Is this after or before left shifting Z by 16? Also, when you refer to mantissa M, are you actually referring to L := WORD[$C000][M & $FFE] ' fractional part of log2(x), 16 bits mantissa, lsb=0 for word address? Or is the Log Table not being used?
Thanks again, for your help.
' get the "integer" portion of the log2 computation
dB := >|val - 1
' get the fractional portion, scaled to 16 bits
frac := val >< dB >< 16
' reconstruct
dB *= 602
dB += (1386 - frac / 181) * frac / 111465
Just a hint where the constants came from: fit a quadratic through 3 points to approximate the log2 function: 0,0 & 0.414,0.5 & 1,1, but scaled to 16-bits. I juggled a few terms to keep the intermediate values from overflowing a 31-bit number.
So, the results are still in mB, but are much more accurate.
Jonathan
I've attached a little program that runs through how to use the log table in ROM to calculate dB. I hope this is less confusing, better signal to noise ratio! The main pub is here: Once you have the fractional bits, the first mantissa line, it is one more easy step to look up the value in the hub table. Then combine the integer and fractional parts back into one long, fixed point, then multiply it times the constant that converts it from base 2 to base 10. The attached program goes on to the ratio of two values, which is what you want to do for dB, and printing it out. I used the old math terms "characteristic" and "mantissa" for the integer and fractional parts.
I adopted the way that Jonathan used the >< operator to justify the bits, much slicker than the way I had been doing it before. The quadratic approximation is neat, too, but I figure since you have the log table, you might as well use it.
Jonathan
P.S. If it helps anyone at all....0vu (at a ref of +4) = 1.228 volts. This is considered "professional" scale. The issue of db then becomes weighting (A,C, Full Scale?)
Since dB is a measure of specific frequency ranges, it would be smart to use a weighting that is similar to the fletcher munson curves (within the human ear). The task of building an accurate meter is no easy one, and has baffled (and caused much argument within) the pro-audio community for many decades....
Another tidbit 0vu = -16 (to -18) dbfs....
I'm building a digitally controlled analog level control. The MDAC's I am using are 14 bit. This is good enough for my application, which are for cue only.
Ahh, I see....for cues coarse level should be fine....still the problem remains.....what scale do you intend the meter ans logical steps to read? VU, PPm, dbFS? also how are you going to translate the output voltage to 1.228v? What is your ref. voltage and are you employing a comparator for accuracy? I could see an error in the digital world causing a blast of full scale noise and a very angry session player....
Curious about how you are going to implement this....John Oram built a console a while back that was 100% digitally controlled analog components.....it was a bit of a disaster (although a fine console)
I think a digitally controlled pot would be simpler and then you could use a standard VU meter on a paralleled leg.....since resolution isn't that important.....
you want a cue out with no meter or relative (industry standard scale)? *scratches head*
the digital pot is still the way to go....this has been beat to death on most "hi-fi" and DIY audio forums...there is really no way an ADC can match the linearity of a digital pot, feeding a transistor....unless you aren't trying to achieve "analog gain"......
maybe that's my own fault for assuming you are trying to build a cue pre-amp?
I am working in concert with a product manufacturer, and in order to maintain consistency with their product, I am using their circuit standards.
Tracy,
I've checked out the decibel.spin. Very nice. This is a useful learning tool for the Log Table and how to utilize exponents. Thanks