Bugzilla – Bug 2557
SB2 dB volume control causes digital round errors
Last modified: 2008-09-15 14:38:10 UTC
Please see this thread for background info and for a patch http://forums.slimdevices.com/showthread.php?t=17269 I have been running this patch for the past two weeks. It works really well and fixes what I consider to be a show-stopping problem with using the SB2 volume control and ripped CD files that was introduced with Firmware V22. The difference is clearly audiable, and has been confirmed by several other users. Here's the patch (with thanks to Steinar) Index: server/Slim/Player/Squeezebox2.pm ================================================== ================= --- server/Slim/Player/Squeezebox2.pm (revision 4942) +++ server/Slim/Player/Squeezebox2.pm (working copy) @@ -272,7 +272,12 @@ # Map a floating point dB value to a 16.16 fixed point value to # send as a new style volume to SB2 (FW 22+). my $floatmult = 10 ** ($db/20); - return int(($floatmult * (1 << 16)) + 0.5); + if ($db >= -35 && $db <= 0) { + return int($floatmult * (1 << 8) + 0.5) * (1 << 8); + } + else { + return int(($floatmult * (1 << 16)) + 0.5); + } } sub volume {
This really should be in 6.2.1 - you will loose audiophile sales if people evaluate using the released software - the patch is there all ready for you!
Dean I've done this fairly quickly and it's a difficult thing to explain simply, but here goes: Imagine the following two systems: system 1 -------- ADC (3-bit res) -> add 2 more lsbs -> divide by 4 -> DAC (5-bit res) -> *4 analogue gain system 2 -------- ADC (3-bit res) -> add 1 more lsbs -> divide by 4 -> DAC (4-bit res) -> *4 analogue gain In system 1, the input S/N is 18dB. Because there is no rounding, no resolution is lost, there is no additional error and the output S/N remains 18dB. Note that the S/N is not 30db even despite the 5-bit resolution of the DAC - it can never be better than the S/N at sampling time. input input as % of fs add 2 lsbs integerised div 4 dac op as % of fs analogue gain x4 ERROR 7 87.50% 28 7 21.88% 87.50% 0.00% 6 75.00% 24 6 18.75% 75.00% 0.00% 5 62.50% 20 5 15.63% 62.50% 0.00% 4 50.00% 16 4 12.50% 50.00% 0.00% 3 37.50% 12 3 9.38% 37.50% 0.00% 2 25.00% 8 2 6.25% 25.00% 0.00% 1 12.50% 4 1 3.13% 12.50% 0.00% 0 0.00% 0 0 0.00% 0.00% 0.00% Peak Error 0.00% Input S/N 18.06 Total S/N 18.06 In system 2, the input S/N is 18dB. Because there is rounding, resolution is lost and there is an additional pp error of 12.5%. Output S/N is therefore reduced to 12db - IE AS IF THE INPUT WAS SAMPLED WITH 2-BITS RATHER THAN 3. Note that the S/N is not 24db even despite the 4-bit resolution of the DAC - it can never be better than the S/N at sampling time. input input as % of fs add 1 lsbs integerised div 4 dac op as % of fs analogue gain x4 ERROR 7 87.50% 14 3 18.75% 75.00% 12.50% 6 75.00% 12 3 18.75% 75.00% 0.00% 5 62.50% 10 2 12.50% 50.00% 12.50% 4 50.00% 8 2 12.50% 50.00% 0.00% 3 37.50% 6 1 6.25% 25.00% 12.50% 2 25.00% 4 1 6.25% 25.00% 0.00% 1 12.50% 2 0 0.00% 0.00% 12.50% 0 0.00% 0 0 0.00% 0.00% 0.00% Peak Error 12.50% Input S/N 18.06 Output S/N 12.04 So the principle is that providing you never loose any digital resolution in the DSP path, you don't harm the original S/N (but you can't improve it either). But as soon as you start to loose resolution, you affect the S/N at the lsb of the originaly sampled signal. HTH - I'm off to bed now.
I think this may also help illustrate the problem graphically, to show the errors lie not at the 24 bit, but at the 16th: - Open Windows Calculator and select 'Scientific' mode and then the 'Bin' radio button to work in Binary. Enter binary 111111111111111100000000 to represent the 16 audio bits (1's) and the 8 additional bits to make it into a 24 bit signal. Now divide the above number by binary 111, then multiply the result by binary 111. The result is 111111111111111011111100, the data has changed due to a rounding error and as can be easily seen there's an error at the 16th bit, which is now a 0, not a 1. If you repeat the above using divisor / multipliers which give a mathematical result that doesn't result in rounding (try 110, 100 etc.) you'll see the division / multiplication process is lossless (i.e. the original number is returned). Although the actual math is different in Slimserver, I think this demonstrates what is happening AND that the error is at the 16th bit and below, not the 24th. HTH, Andy.
Below is a list of the gain coefficients in binary format for "trunk" and "patch" for gains ranging from 0 to -50dB. Note that for the patch, the 8 LSB bits are always zero. For 16 bits CD data put into a 24 bits word, the 8 LSB bits are zero. Multiplying two such numbers result in a 40 bit word with the 16 LSB bits all zero. By using the 24 MSB bits of this 40 bits word as the 24 bit result, only zeros are thrown away. Steinar dB | ------Trunk------ | ------Patch------ -0 | 10000000000000000 | 10000000000000000 -1 | 01110010000101001 | 01110010000000000 -2 | 01100101101011001 | 01100101100000000 -3 | 01011010100111100 | 01011010100000000 -4 | 01010000110000110 | 01010001000000000 -5 | 01000111111110110 | 01001000000000000 -6 | 01000000001001110 | 01000000000000000 -7 | 00111001001011010 | 00111001000000000 -8 | 00110010111101010 | 00110011000000000 -9 | 00101101011010101 | 00101101100000000 -10 | 00101000011110100 | 00101000100000000 -11 | 00100100000100111 | 00100100000000000 -12 | 00100000001001110 | 00100000000000000 -13 | 00011100101010000 | 00011100100000000 -14 | 00011001100010100 | 00011001100000000 -15 | 00010110110000110 | 00010111000000000 -16 | 00010100010010011 | 00010100100000000 -17 | 00010010000101001 | 00010010000000000 -18 | 00010000000111010 | 00010000000000000 -19 | 00001110010111001 | 00001110100000000 -20 | 00001100110011010 | 00001101000000000 -21 | 00001011011010001 | 00001011100000000 -22 | 00001010001010110 | 00001010000000000 -23 | 00001001000100000 | 00001001000000000 -24 | 00001000000100111 | 00001000000000000 -25 | 00000111001100101 | 00000111000000000 -26 | 00000110011010101 | 00000110100000000 -27 | 00000101101101111 | 00000101100000000 -28 | 00000101000110001 | 00000101000000000 -29 | 00000100100010101 | 00000100100000000 -30 | 00000100000011000 | 00000100000000000 -31 | 00000011100110111 | 00000011100000000 -32 | 00000011001101110 | 00000011000000000 -33 | 00000010110111011 | 00000011000000000 -34 | 00000010100011100 | 00000010100000000 -35 | 00000010010001101 | 00000010100000000 -36 | 00000010000001111 | 00000010000000000 -37 | 00000001110011110 | 00000010000000000 -38 | 00000001100111001 | 00000001100000000 -39 | 00000001011011111 | 00000001100000000 -40 | 00000001010001111 | 00000001100000000 -41 | 00000001001001000 | 00000001000000000 -42 | 00000001000001001 | 00000001000000000 -43 | 00000000111010000 | 00000001000000000 -44 | 00000000110011110 | 00000001000000000 -45 | 00000000101110001 | 00000000100000000 -46 | 00000000101001000 | 00000000100000000 -47 | 00000000100100101 | 00000000100000000 -48 | 00000000100000101 | 00000000100000000 -49 | 00000000011101001 | 00000000100000000 -50 | 00000000011001111 | 00000000100000000
Below I have listed the resulting gain values in dB for the calculations in trunk, and for the proposed patch. I think the small nonlinearity in the dB values for the patch is insignificant down to -30 dB, and the patch should be used for 0 to -30dB range in order to guarantee bit-perfect gain multiplications for those gain settings. Steinar dB | ------Trunk------ | ------Patch------ | Trunk dB | Patch dB -0 | 10000000000000000 | 10000000000000000 | 0 | 0 -1 | 01110010000101001 | 01110010000000000 | -1.0000 | -1.0061 -2 | 01100101101011001 | 01100101100000000 | -2.0000 | -2.0149 -3 | 01011010100111100 | 01011010100000000 | -3.0000 | -3.0112 -4 | 01010000110000110 | 01010001000000000 | -4.0001 | -3.9745 -5 | 01000111111110110 | 01001000000000000 | -4.9999 | -4.9975 -6 | 01000000001001110 | 01000000000000000 | -5.9999 | -6.0206 -7 | 00111001001011010 | 00111001000000000 | -7.0000 | -7.0267 -8 | 00110010111101010 | 00110011000000000 | -8.0001 | -7.9928 -9 | 00101101011010101 | 00101101100000000 | -9.0000 | -8.9840 -10 | 00101000011110100 | 00101000100000000 | -10.0001 | -9.9951 -11 | 00100100000100111 | 00100100000000000 | -10.9998 | -11.0181 -12 | 00100000001001110 | 00100000000000000 | -11.9999 | -12.0412 -13 | 00011100101010000 | 00011100100000000 | -12.9998 | -13.0473 -14 | 00011001100010100 | 00011001100000000 | -14.0001 | -14.0134 -15 | 00010110110000110 | 00010111000000000 | -15.0001 | -14.9096 -16 | 00010100010010011 | 00010100100000000 | -15.9998 | -15.9091 -17 | 00010010000101001 | 00010010000000000 | -17.0002 | -17.0387 -18 | 00010000000111010 | 00010000000000000 | -18.0005 | -18.0618 -19 | 00001110010111001 | 00001110100000000 | -19.0003 | -18.9168 -20 | 00001100110011010 | 00001101000000000 | -19.9995 | -19.8653 -21 | 00001011011010001 | 00001011100000000 | -20.9999 | -20.9302 -22 | 00001010001010110 | 00001010000000000 | -21.9995 | -22.1442 -23 | 00001001000100000 | 00001001000000000 | -22.9992 | -23.0593 -24 | 00001000000100111 | 00001000000000000 | -24.0001 | -24.0824 -25 | 00000111001100101 | 00000111000000000 | -25.0008 | -25.2422 -26 | 00000110011010101 | 00000110100000000 | -25.9989 | -25.8859 -27 | 00000101101101111 | 00000101100000000 | -27.0011 | -27.3369 -28 | 00000101000110001 | 00000101000000000 | -28.0001 | -28.1648 -29 | 00000100100010101 | 00000100100000000 | -29.0011 | -29.0799 -30 | 00000100000011000 | 00000100000000000 | -30.0018 | -30.1030 -31 | 00000011100110111 | 00000011100000000 | -31.0003 | -31.2628 -32 | 00000011001101110 | 00000011000000000 | -32.0010 | -32.6018 -33 | 00000010110111011 | 00000011000000000 | -33.0010 | -32.6018 -34 | 00000010100011100 | 00000010100000000 | -33.9974 | -34.1854 -35 | 00000010010001101 | 00000010100000000 | -35.0031 | -34.1854 -36 | 00000010000001111 | 00000010000000000 | -35.9973 | -36.1236 -37 | 00000001110011110 | 00000010000000000 | -36.9974 | -36.1236 -38 | 00000001100111001 | 00000001100000000 | -38.0005 | -38.6224 -39 | 00000001011011111 | 00000001100000000 | -39.0039 | -38.6224 -40 | 00000001010001111 | 00000001100000000 | -40.0048 | -38.6224 -41 | 00000001001001000 | 00000001000000000 | -41.0013 | -42.1442 -42 | 00000001000001001 | 00000001000000000 | -41.9928 | -42.1442 -43 | 00000000111010000 | 00000001000000000 | -42.9992 | -42.1442 -44 | 00000000110011110 | 00000001000000000 | -43.9896 | -42.1442 -45 | 00000000101110001 | 00000000100000000 | -44.9891 | -48.1648 -46 | 00000000101001000 | 00000000100000000 | -46.0121 | -48.1648 -47 | 00000000100100101 | 00000000100000000 | -46.9922 | -48.1648 -48 | 00000000100000101 | 00000000100000000 | -47.9968 | -48.1648 -49 | 00000000011101001 | 00000000100000000 | -48.9825 | -48.1648 -50 | 00000000011001111 | 00000000100000000 | -50.0102 | -48.1648
Thanks for the work, I understand the math. Two thoughts on your tables: 1. The user controls are not on integer decibel values (there are 11 steps in the web interface and 40 in the player interface). 2. Note the bottom of the table where from -32 to -50 the steps usually have no effect. This is not acceptable. Also, remember that the data lost is _below_ the 24bit resolution of the output, at 144dB down. The multiplier values are interesting, but at those levels it is nearly immeasurable unless turn the output of the Squeezebox down very low and the amplifier up very high, thereby amplifying any background noise in the system.
> 1. The user controls are not on integer decibel values (there are 11 steps in > the web interface and 40 in the player interface). I don't think that matters - the math in the patch would product a "correct" multiplier no matter what the floating point db is (above the -30db threshold, that is). But I assume the highest (0db) volume is actually exactly $db=0.0 for both web interface (11) and player interface (max), right? I would hope that at full volume, no multiplication is effectively being done (i.e. bit-perfect output). > 2. Note the bottom of the table where from -32 to -50 the steps usually have > no effect. This is not acceptable. Dean, if you are looking at the tables in the examples in this bug report, the -30db threshold is not being applied (note lower 8 bits are zero in all values), therefore multipliers are duplicated at the low volumes - I think this is just for illustration. If the -30db threshold were used, as suggested, this would not happen. -Joe
"Also, remember that the data lost is _below_ the 24bit resolution of the output, at 144dB down. The multiplier values are interesting, but at those levels it is nearly immeasurable unless turn the output of the Squeezebox down very low and the amplifier up very high, thereby amplifying any background noise in the system." Dean, I don't understand what you mean here - please can you explain?
Regarding: >2. Note the bottom of the table where from -32 to -50 the steps usually have >no effect. This is not acceptable. I appologize for confusing you here. Your remark is exactly why the patch switch over to the original coefficients below -30dB. This was not clear in the listings I generated. Take a look at Sean's response to this at: http://forums.slimdevices.com/showpost.php?p=60460&postcount=11 Steinar
Patch committed for tonight's nightly. Leaving bug open to get feedback on distortion, etc.
There has been a lot back and forth here, and I know that I have contributed to the confusion... But based on the table in comment #5 above, it looks like -30dB is a better threshold than -35dB that you used in the patch. Just a minor detail, but...
This is fixed in the latest nightlies for 6.2.2 and 6.5