From: <man...@us...> - 2013-06-14 11:13:54
|
Revision: 2367 http://sourceforge.net/p/modplug/code/2367 Author: manxorist Date: 2013-06-14 11:13:43 +0000 (Fri, 14 Jun 2013) Log Message: ----------- [Fix] Reduce quantization noise by correcting the rounding and scaling in floating point conversion and float or bigint normalization in SampleFormatConverters.h. [Fix] Do not tolerate off-by-one in the least significant bit in sample normalization test cases. [Ref] Do integer normalization without floating point code. [Ref] Avoid per-sample division in floating point normalization. Modified Paths: -------------- trunk/OpenMPT/common/misc_util.h trunk/OpenMPT/soundlib/SampleFormatConverters.h trunk/OpenMPT/test/test.cpp Modified: trunk/OpenMPT/common/misc_util.h =================================================================== --- trunk/OpenMPT/common/misc_util.h 2013-06-14 10:18:18 UTC (rev 2366) +++ trunk/OpenMPT/common/misc_util.h 2013-06-14 11:13:43 UTC (rev 2367) @@ -373,6 +373,13 @@ return static_cast<int32>( ( static_cast<int64>(a) * b + ( c / 2 ) ) / c ); } + inline int32 muldivrfloor(int64 a, uint32 b, uint32 c) + { + a *= b; + a += c/2; + return (a >= 0) ? (int32)(a / c) : (int32)((a - (c - 1)) / c); + } + template<typename T, std::size_t n> class fixed_size_queue { private: Modified: trunk/OpenMPT/soundlib/SampleFormatConverters.h =================================================================== --- trunk/OpenMPT/soundlib/SampleFormatConverters.h 2013-06-14 10:18:18 UTC (rev 2366) +++ trunk/OpenMPT/soundlib/SampleFormatConverters.h 2013-06-14 11:13:43 UTC (rev 2367) @@ -187,12 +187,12 @@ forceinline int16 operator() (const void *sourceBuffer) { const uint8 *inBuf = static_cast<const uint8 *>(sourceBuffer); - const uint32 in32 = inBuf[loLoByteIndex] | (inBuf[loHiByteIndex] << 8) | (inBuf[hiLoByteIndex] << 16) | (inBuf[hiHiByteIndex] << 24); - const bool negative = (in32 & 0x80000000) != 0; - - float val = *reinterpret_cast<const float *>(&in32); + float val = DecodeFloatLE(uint8_4(inBuf[loLoByteIndex], inBuf[loHiByteIndex], inBuf[hiLoByteIndex], inBuf[hiHiByteIndex])); Limit(val, -1.0f, 1.0f); - return static_cast<int16>(val * (negative ? -int16_min : int16_max)); + val *= 32768.0f; + // MSVC with x87 floating point math calls floor for the more intuitive version + // return mpt::saturate_cast<int16>(static_cast<int>(std::floor(val + 0.5f))); + return mpt::saturate_cast<int16>(static_cast<int>(val * 2.0f + 1.0f) >> 1); } }; @@ -200,15 +200,14 @@ ////////////////////////////////////////////////////// // Sample normalization + conversion functors -// Signed integer PCM (up to 32-Bit) to 16-Big signed sample conversion with normalization. A second sample conversion functor is used for actual sample reading. +// Signed integer PCM (up to 32-Bit) to 16-Bit signed sample conversion with normalization. A second sample conversion functor is used for actual sample reading. template <typename SampleConversion> struct ReadBigIntToInt16PCMandNormalize : SampleConversionFunctor<typename SampleConversion::input_t, int16, conversionHasNoState> { - uint32 maxCandidate; - double maxVal; + uint32 maxVal; SampleConversion sampleConv; - ReadBigIntToInt16PCMandNormalize() : maxCandidate(0) + ReadBigIntToInt16PCMandNormalize() : maxVal(0) { static_assert(SampleConversion::hasState == false, "Implementation of this conversion functor is stateless"); static_assert(sizeof(typename SampleConversion::output_t) <= 4, "Implementation of this conversion functor is only suitable for 32-Bit integers or smaller"); @@ -222,30 +221,28 @@ { if(val == int32_min) { - maxCandidate = static_cast<uint32>(-int32_min); + maxVal = static_cast<uint32>(-int32_min); return; } val = -val; } - if(static_cast<uint32>(val) > maxCandidate) + if(static_cast<uint32>(val) > maxVal) { - maxCandidate = static_cast<uint32>(val); + maxVal = static_cast<uint32>(val); } } - bool IsSilent() + bool IsSilent() const { - maxVal = static_cast<double>(maxCandidate); - return (maxCandidate == 0); + return maxVal == 0; } forceinline int16 operator() (const void *sourceBuffer) { int32 val = sampleConv(sourceBuffer); - const double NC = (val < 0) ? 32768.0 : 32767.0; // Normalization Constant - const double roundBias = (val < 0) ? -0.5 : 0.5; - return static_cast<int16>((static_cast<double>(val) / maxVal * NC) + roundBias); + val = Util::muldivrfloor(val, 32768, maxVal); + return mpt::saturate_cast<int16>(val); } }; @@ -254,9 +251,14 @@ template <size_t loLoByteIndex, size_t loHiByteIndex, size_t hiLoByteIndex, size_t hiHiByteIndex> struct ReadFloat32to16PCMandNormalize : SampleConversionFunctor<float, int16, conversionHasNoState> { - FloatInt32 maxVal; + uint32 intMaxVal; + float maxValInv; - ReadFloat32to16PCMandNormalize() { maxVal.i = 0; } + ReadFloat32to16PCMandNormalize() + { + intMaxVal = 0; + maxValInv = 0.0f; + } forceinline void FindMax(const void *sourceBuffer) { @@ -266,27 +268,34 @@ // IEEE float values are lexicographically ordered and can be compared when interpreted as integers. // So we won't bother with loading the float into a floating point register here if we already have it in an integer register. - if(val > maxVal.i) + if(val > intMaxVal) { - ASSERT(*reinterpret_cast<float *>(&val) > maxVal.f); - maxVal.i = val; + ASSERT(*reinterpret_cast<float *>(&val) > DecodeFloatLE(uint8_4().SetLE(intMaxVal))); + intMaxVal = val; } } - bool IsSilent() const + bool IsSilent() { - return (maxVal.i == 0); + if(intMaxVal == 0) + { + return true; + } else + { + maxValInv = 1.0f / DecodeFloatLE(uint8_4().SetLE(intMaxVal)); + return false; + } } forceinline int16 operator() (const void *sourceBuffer) { const uint8 *inBuf = static_cast<const uint8 *>(sourceBuffer); - const uint32 in32 = inBuf[loLoByteIndex] | (inBuf[loHiByteIndex] << 8) | (inBuf[hiLoByteIndex] << 16) | (inBuf[hiHiByteIndex] << 24); - const bool negative = (in32 & 0x80000000) != 0; - - const float val = (*reinterpret_cast<const float *>(&in32) / maxVal.f) * (negative ? 32768.0f : 32767.0f); - ASSERT(val >= -32768.0f && val <= 32767.0f); - return static_cast<int16>(val); + float val = DecodeFloatLE(uint8_4(inBuf[loLoByteIndex], inBuf[loHiByteIndex], inBuf[hiLoByteIndex], inBuf[hiHiByteIndex])); + val *= maxValInv; + val *= 32768.0f; + // MSVC with x87 floating point math calls floor for the more intuitive version + // return mpt::saturate_cast<int16>(static_cast<int>(std::floor(val + 0.5f))); + return mpt::saturate_cast<int16>(static_cast<int>(val * 2.0f + 1.0f) >> 1); } }; Modified: trunk/OpenMPT/test/test.cpp =================================================================== --- trunk/OpenMPT/test/test.cpp 2013-06-14 10:18:18 UTC (rev 2366) +++ trunk/OpenMPT/test/test.cpp 2013-06-14 11:13:43 UTC (rev 2367) @@ -1855,11 +1855,7 @@ for(size_t i = 0; i < 65536; i++) { - int16 normValue = static_cast<const int16 *>(sample.pSample)[i]; - if(abs(normValue - static_cast<int16>(i)) > 1) - { - VERIFY_EQUAL_QUIET_NONCONT(true, false); - } + VERIFY_EQUAL_QUIET_NONCONT(static_cast<const int16 *>(sample.pSample)[i], static_cast<int16>(i)); VERIFY_EQUAL_QUIET_NONCONT(truncated16[i], static_cast<int16>(i)); } } @@ -1869,13 +1865,11 @@ uint8 *source32 = sourceBuf; for(size_t i = 0; i < 65536; i++) { - FloatInt32 val; - - val.f = (static_cast<float>(i) / 65536.0f) - 0.5f; - source32[i * 4 + 0] = static_cast<uint8>(val.i >> 24); - source32[i * 4 + 1] = static_cast<uint8>(val.i >> 16); - source32[i * 4 + 2] = static_cast<uint8>(val.i >> 8); - source32[i * 4 + 3] = static_cast<uint8>(val.i >> 0); + uint8_4 floatbits = EncodeFloatBE((static_cast<float>(i) / 65536.0f) - 0.5f); + source32[i * 4 + 0] = floatbits.x[0]; + source32[i * 4 + 1] = floatbits.x[1]; + source32[i * 4 + 2] = floatbits.x[2]; + source32[i * 4 + 3] = floatbits.x[3]; } int16 *truncated16 = static_cast<int16 *>(targetBuf); @@ -1889,11 +1883,7 @@ for(size_t i = 0; i < 65536; i++) { - int16 normValue = static_cast<const int16 *>(sample.pSample)[i]; - if(abs(normValue - static_cast<int16>(i- 0x8000u)) > 1) - { - VERIFY_EQUAL_QUIET_NONCONT(true, false); - } + VERIFY_EQUAL_QUIET_NONCONT(static_cast<const int16 *>(sample.pSample)[i], static_cast<int16>(i - 0x8000u)); if(abs(truncated16[i] - static_cast<int16>((i - 0x8000u) / 2)) > 1) { VERIFY_EQUAL_QUIET_NONCONT(true, false); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |