diff --git a/CMakeLists.txt b/CMakeLists.txt index 3eb4842..215974e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}") endif() -project(exhale VERSION 1.1.7 LANGUAGES CXX) +project(exhale VERSION 1.1.8 LANGUAGES CXX) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE Release diff --git a/README.md b/README.md index 87de6ed..b8292f4 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ exhale is being made available under an open-source license which is based on the 3-clause BSD license but modified to address particular aspects dictated by the nature and the output of this application. -The license text and release notes for the current version 1.1.7 can +The license text and release notes for the current version 1.1.8 can be found in the `include` subdirectory of the exhale distribution. diff --git a/include/Release.htm b/include/Release.htm index 09188b1..b909b6d 100644 --- a/include/Release.htm +++ b/include/Release.htm @@ -25,9 +25,14 @@


exhale - ecodis extended high-efficiency and low-complexity encoder

Software Release Notes, Version History, Known Issues, Upcoming Feature Roadmap


-

   The version of this distribution of the «exhale» software release is 1.1.7 (official pub­lic minor release) from August 2021. Please check www.ecodis.de regularly for new versions of this software. A summary of each version up to this release, a list of known issues with this release, and a roadmap of additional functionality are provided below.

+

   The version of this distribution of the «exhale» software release is 1.1.8 (official pub­lic minor release) from October 2021. Please check www.ecodis.de regularly for new versions of this software. A summary of each version up to this release, a list of known issues with this release, and a roadmap of additional functionality are provided below.


Chronological Version History

-

   Version 1.1.7  Aug. 2021, this release

+

   Version 1.1.8  Oct. 2021, this release

+ +

   Version 1.1.7  Aug. 2021


-

Written by C. R. Helmrich for exhale 1.1.7, Aug. 2021. Available at www.ecodis.de/exhale/release.htm.

+

Written by C. R. Helmrich for exhale 1.1.8, Oct. 2021. Available at www.ecodis.de/exhale/release.htm.

diff --git a/include/version.h b/include/version.h index 29348e9..60cd565 100644 --- a/include/version.h +++ b/include/version.h @@ -15,5 +15,5 @@ # define EXHALELIB_VERSION_MINOR "1" #endif #ifndef EXHALELIB_VERSION_BUGFIX -# define EXHALELIB_VERSION_BUGFIX ".7" // "RC" or ".0", ".1", ... +# define EXHALELIB_VERSION_BUGFIX ".8" // "RC" or ".0", ".1", ... #endif diff --git a/src/app/exhaleApp.rc b/src/app/exhaleApp.rc index fa8fdc4..d980a50 100644 --- a/src/app/exhaleApp.rc +++ b/src/app/exhaleApp.rc @@ -13,7 +13,7 @@ 0 ICON "exhaleApp.ico" VS_VERSION_INFO VERSIONINFO -FILEVERSION 1,1,7,1 +FILEVERSION 1,1,8 BEGIN BLOCK "StringFileInfo" BEGIN diff --git a/src/lib/bitAllocation.cpp b/src/lib/bitAllocation.cpp index fad3a03..159ba87 100644 --- a/src/lib/bitAllocation.cpp +++ b/src/lib/bitAllocation.cpp @@ -107,11 +107,12 @@ uint16_t BitAllocator::getRateCtrlFac (const int32_t rateRatio, const unsigned s #if BA_MORE_CBR const int32_t ratioFac = rateRatio * (40 - 5 * m_rateIndex); const uint32_t brRatio = __max ((prevEightShorts ? (ratioFac * ratioFac + (1 << 16)) >> 17 : 0) - SHRT_MIN, __min (USHRT_MAX, ratioFac)) - - (m_rateIndex == 2 ? (1 << 11) : 0); + (m_rateIndex == 2 ? 1 << 12 : 0); // rate tuning + const uint16_t mSfmSqr = (m_rateIndex <= 2 && samplingRate >= 27713 ? (specFlatness * specFlatness) >> m_rateIndex : 0); #else const uint32_t brRatio = __max (1 << 15, __min (USHRT_MAX, rateRatio * (36 - 9 * m_rateIndex))); -#endif const uint16_t mSfmSqr = (m_rateIndex < 2 && samplingRate >= 27713 ? (specFlatness * specFlatness) >> m_rateIndex : 0); +#endif const uint16_t mSfmFac = 256 - (((32 + m_rateIndex) * (specFlatness << 4) - mSfmSqr + (1 << 9)) >> 10); return uint16_t ((brRatio * mSfmFac + (1 << 7)) >> 8); diff --git a/src/lib/exhaleEnc.cpp b/src/lib/exhaleEnc.cpp index 34e2c90..e7285a1 100644 --- a/src/lib/exhaleEnc.cpp +++ b/src/lib/exhaleEnc.cpp @@ -1324,7 +1324,7 @@ unsigned ExhaleEncoder::quantizationCoding () // apply MDCT quantization and en } } // for el #if !RESTRICT_TO_AAC - m_rateFactor = samplingRate; // for RC + m_rateFactor = samplingRate; // rate ctrl #endif return (errorValue > 0 ? 0 : m_outStream.createAudioFrame (m_elementData, m_entropyCoder, m_mdctSignals, m_mdctQuantMag, m_indepFlag, m_numElements, m_numSwbShort, (uint8_t* const) m_tempIntBuf, @@ -1389,9 +1389,8 @@ unsigned ExhaleEncoder::spectralProcessing () // complete ics_info(), calc TNS const uint8_t meanSpecFlat = (((m_specAnaCurr[ci] >> 16) & UCHAR_MAX) + ((m_specAnaCurr[ci + 1] >> 16) & UCHAR_MAX) + 1) >> 1; const uint16_t* const swbo = swbOffsetsL[m_swbTableIdx]; const uint16_t nSamplesMax = (useMaxBandwidth ? nSamplesInFrame : swbo[brModeAndFsToMaxSfbLong (m_bitRateMode, samplingRate)]); - const int16_t steAnaStats = m_specAnalyzer.stereoSigAnalysis (m_mdctSignals[ci], m_mdctSignals[ci + 1], - m_mdstSignals[ci], m_mdstSignals[ci + 1], nSamplesMax, - nSamplesInFrame, eightShorts, coreConfig.stereoDataCurr); + const int16_t steAnaStats = m_specAnalyzer.stereoSigAnalysis (m_mdctSignals[ci], m_mdctSignals[ci + 1], m_mdstSignals[ci], m_mdstSignals[ci + 1], + nSamplesMax, nSamplesInFrame, eightShorts, coreConfig.stereoDataCurr); if (steAnaStats == SHRT_MIN) errorValue = 1; if ((s = abs (steAnaStats)) * m_perCorrHCurr[el] == 0) // transition to/from silence @@ -1455,7 +1454,7 @@ unsigned ExhaleEncoder::spectralProcessing () // complete ics_info(), calc TNS { icsCurr.maxSfb = __min (icsCurr.maxSfb, brModeAndFsToMaxSfbShort (m_bitRateMode, samplingRate)); } -#if SA_OPT_WINDOW_GROUPING + if (ch > 0 && coreConfig.commonWindow) // resynchronize the scale_factor_grouping { if (icsCurr.windowGrouping != coreConfig.icsInfoCurr[0].windowGrouping) @@ -1471,15 +1470,15 @@ unsigned ExhaleEncoder::spectralProcessing () // complete ics_info(), calc TNS } } memcpy (grpData.windowGroupLength, windowGroupingTable[icsCurr.windowGrouping], NUM_WINDOW_GROUPS * sizeof (uint8_t)); -#endif + findActualBandwidthShort (&icsCurr.maxSfb, grpSO, m_mdctSignals[ci], nChannels < 2 ? nullptr : m_mdstSignals[ci], nSamplesInShort); errorValue |= eightShortGrouping (grpData, grpSO, m_mdctSignals[ci], nChannels < 2 ? nullptr : m_mdstSignals[ci]); } // if EIGHT_SHORT // compute and quantize optimal TNS coefficients, then find optimal TNS filter order - s /*max. pred gain*/ = getOptParCorCoeffs (grpData, icsCurr.maxSfb, tnsData, ci, - ch > 0 && coreConfig.commonWindow ? coreConfig.tnsData[0].firstTnsWindow : 0); + s = getOptParCorCoeffs (grpData, icsCurr.maxSfb, tnsData, ci, (ch > 0 && coreConfig.commonWindow ? coreConfig.tnsData[0].firstTnsWindow : 0)); + for (uint16_t n = 0, gr = 0; gr < grpData.numWindowGroups; gr++) { if (grpData.windowGroupLength[gr] == 1) @@ -1780,6 +1779,7 @@ unsigned ExhaleEncoder::temporalProcessing () // determine time-domain aspects o memcpy (coreConfig.stereoDataPrev, &coreConfig.stereoDataCurr[lastGrpOffset], __min (60 - lastGrpOffset, maxSfbStePrev) * sizeof (uint8_t)); } + coreConfig.stereoDataCurr[0] = (m_bitRateMode <= 1 ? m_tempAnalyzer.stereoPreAnalysis (&m_timeSignals[ci - 2], &m_specFlatPrev[ci - 2], nSamplesInFrame) : 0); } // if nrChannels > 1 } diff --git a/src/lib/specAnalysis.cpp b/src/lib/specAnalysis.cpp index 6069d71..f1e79b2 100644 --- a/src/lib/specAnalysis.cpp +++ b/src/lib/specAnalysis.cpp @@ -179,7 +179,6 @@ unsigned SpecAnalyzer::initSigAnaMemory (LinearPredictor* const linPredictor, co return 0; // no error } -#if SA_OPT_WINDOW_GROUPING unsigned SpecAnalyzer::optimizeGrouping (const unsigned channelIndex, const unsigned prefBandwidth, const unsigned prefGroupingIndex) { const uint32_t* meanAbsValCurr = m_meanAbsValue[channelIndex]; @@ -226,7 +225,6 @@ unsigned SpecAnalyzer::optimizeGrouping (const unsigned channelIndex, const unsi return __min (grpIdxCurr, prefGroupingIndex); // final optimized grouping index } -#endif // SA_OPT_WINDOW_GROUPING unsigned SpecAnalyzer::spectralAnalysis (const int32_t* const mdctSignals[USAC_MAX_NUM_CHANNELS], const int32_t* const mdstSignals[USAC_MAX_NUM_CHANNELS], @@ -349,14 +347,12 @@ unsigned SpecAnalyzer::spectralAnalysis (const int32_t* const mdctSignals[USAC_M // --- spectral analysis statistics for frame b = 1; -#if SA_IMPROVED_FILT_CALC if (samplingRate < 27713) sumAvgBand -= m_meanAbsValue[ch][b++]; -#endif + while (((unsigned) b + 1 < lpcStopBand16k) && ((uint64_t) m_meanAbsValue[ch][b] * (m_numAnaBands[ch] - 1) > sumAvgBand)) b++; b = __min (m_bandwidthOff[ch], b << SA_BW_SHIFT); -#if SA_IMPROVED_FILT_CALC if (samplingRate < 27713) sumAvgBand += m_meanAbsValue[ch][1]; -#endif + // obtain prediction gain across spectrum m_tnsPredGains[ch] = m_tnsPredictor->calcParCorCoeffs (&chMdct[b], __min (m_bandwidthOff[ch], lpcStopBand16k << SA_BW_SHIFT) - b, MAX_PREDICTION_ORDER, m_parCorCoeffs[ch]); @@ -378,7 +374,7 @@ unsigned SpecAnalyzer::spectralAnalysis (const int32_t* const mdctSignals[USAC_M int16_t SpecAnalyzer::stereoSigAnalysis (const int32_t* const mdctSignal1, const int32_t* const mdctSignal2, const int32_t* const mdstSignal1, const int32_t* const mdstSignal2, const unsigned nSamplesMax, const unsigned nSamplesInFrame, const bool shortTransforms, - uint8_t* const stereoCorrValue /*= nullptr*/) // per-band perceptual correlation data + uint8_t* const stereoCorrValue) // per-band LR correlation { const uint64_t anaBwOffset = SA_BW >> 1; const uint16_t numAnaBands = (shortTransforms ? nSamplesInFrame : nSamplesMax) >> SA_BW_SHIFT; @@ -392,7 +388,8 @@ int16_t SpecAnalyzer::stereoSigAnalysis (const int32_t* const mdctSignal1, const } else { - uint16_t currPC = 0, numPC = 0; // frame-average correlation + const uint16_t tempPreAnaPC = stereoCorrValue[0]; + uint16_t currPC = 0, numPC = 0; uint64_t sumReM = 0, sumReS = 0;// mid-side RMS distribution for (b = numAnaBands - 1; b >= 0; b--) @@ -457,6 +454,8 @@ int16_t SpecAnalyzer::stereoSigAnalysis (const int32_t* const mdctSignal1, const if (numPC > 1) currPC = (currPC + (numPC >> 1)) / numPC; // frame's perceptual correlation + if (currPC < tempPreAnaPC) currPC = (currPC + tempPreAnaPC + 1) >> 1; + b = (int16_t) currPC * (sumReS * 2 > sumReM * 3 ? -1 : 1); // negation implies side > mid } diff --git a/src/lib/specAnalysis.h b/src/lib/specAnalysis.h index bcccdc5..e7af357 100644 --- a/src/lib/specAnalysis.h +++ b/src/lib/specAnalysis.h @@ -19,8 +19,6 @@ #define SA_BW (1 << SA_BW_SHIFT) #define SA_EPS 1024 #define SA_EXACT_COMPLEX_ABS 0 -#define SA_IMPROVED_FILT_CALC 1 -#define SA_OPT_WINDOW_GROUPING 1 // spectral signal analysis class class SpecAnalyzer @@ -52,9 +50,7 @@ public: void getSpecAnalysisStats (uint32_t avgSpecAnaStats[USAC_MAX_NUM_CHANNELS], const unsigned nChannels); void getSpectralBandwidth (uint16_t bandwidthOffset[USAC_MAX_NUM_CHANNELS], const unsigned nChannels); unsigned initSigAnaMemory (LinearPredictor* const linPredictor, const unsigned nChannels, const unsigned maxTransfLength); -#if SA_OPT_WINDOW_GROUPING unsigned optimizeGrouping (const unsigned channelIndex, const unsigned preferredBandwidth, const unsigned preferredGrouping); -#endif unsigned spectralAnalysis (const int32_t* const mdctSignals[USAC_MAX_NUM_CHANNELS], const int32_t* const mdstSignals[USAC_MAX_NUM_CHANNELS], const unsigned nChannels, const unsigned nSamplesInFrame, const unsigned samplingRate, @@ -62,7 +58,7 @@ public: int16_t stereoSigAnalysis (const int32_t* const mdctSignal1, const int32_t* const mdctSignal2, const int32_t* const mdstSignal1, const int32_t* const mdstSignal2, const unsigned nSamplesMax, const unsigned nSamplesInFrame, const bool shortTransforms, - uint8_t* const stereoCorrValue = nullptr); // per-band perceptual correlation data + uint8_t* const stereoCorrValue); // per-band LR correlation }; // SpecAnalyzer #endif // _SPEC_ANALYSIS_H_ diff --git a/src/lib/tempAnalysis.cpp b/src/lib/tempAnalysis.cpp index 2b7616d..7cbe0ed 100644 --- a/src/lib/tempAnalysis.cpp +++ b/src/lib/tempAnalysis.cpp @@ -126,6 +126,46 @@ void TempAnalyzer::getTransientAndPitch (int16_t transIdxAndPitch[USAC_MAX_NUM_C memcpy (transIdxAndPitch, m_transientLoc, nChannels * sizeof (int16_t)); } +uint8_t TempAnalyzer::stereoPreAnalysis (const int32_t* const timeSignals[2], const uint8_t specFlatness[2], const unsigned nSamplesInSig) +{ + const double offsetSfmLR = __max (0.0, ((double) specFlatness[0] + specFlatness[1] - 256.0) * 0.5); + const int32_t* const sigL = timeSignals[0] + (nSamplesInSig >> 1); + const int32_t* const sigLM1 = sigL - 1; + const int32_t* const sigR = timeSignals[1] + (nSamplesInSig >> 1); + const int32_t* const sigRM1 = sigR - 1; + int64_t hpNextL = sigL[nSamplesInSig] - sigLM1[nSamplesInSig]; + int64_t hpNextR = sigR[nSamplesInSig] - sigRM1[nSamplesInSig]; + int64_t sumSqrL = hpNextL * hpNextL, sumSqrR = hpNextR * hpNextR; + int64_t sumPC00 = (hpNextL * hpNextR) >> 1, sumPC01 = 0, sumPC10 = 0; + double d; + + for (int s = nSamplesInSig - 1; s >= 0; s--) + { + // compute correlation between high-pass channel signals with and without 1 smp time delay + const int64_t hpL = sigL[s] - sigLM1[s]; + const int64_t hpR = sigR[s] - sigRM1[s]; + + sumSqrL += hpL * hpL; + sumSqrR += hpR * hpR; + sumPC00 += hpL * hpR; + sumPC01 += hpL * hpNextR; + sumPC10 += hpR * hpNextL; + + hpNextL = hpL; + hpNextR = hpR; + } + + if (sumSqrL < nSamplesInSig || sumSqrR < nSamplesInSig) return 0; // stop on low-level input + + sumPC00 = abs (sumPC00); + sumPC01 = abs (sumPC01); + sumPC10 = abs (sumPC10); + + d = 256.0 * __max (sumPC00, __max (sumPC01, sumPC10)); // max. corr. regardless of the delay + + return (uint8_t) __max (0.0, d / sqrt ((double) sumSqrL * sumSqrR) - offsetSfmLR); +} + unsigned TempAnalyzer::temporalAnalysis (const int32_t* const timeSignals[USAC_MAX_NUM_CHANNELS], const unsigned nChannels, const int nSamplesInFrame, const unsigned lookaheadOffset, const uint8_t sbrShift, int32_t* const lrCoreTimeSignals[USAC_MAX_NUM_CHANNELS] /*= nullptr*/, // if using SBR diff --git a/src/lib/tempAnalysis.h b/src/lib/tempAnalysis.h index 73b1d78..9d0f07f 100644 --- a/src/lib/tempAnalysis.h +++ b/src/lib/tempAnalysis.h @@ -1,5 +1,5 @@ /* tempAnalysis.h - header file for class providing temporal analysis of PCM signals - * written by C. R. Helmrich, last modified in 2020 - see License.htm for legal notices + * written by C. R. Helmrich, last modified in 2021 - see License.htm for legal notices * * The copyright in this software is being made available under the exhale Copyright License * and comes with ABSOLUTELY NO WARRANTY. This software may be subject to other third- @@ -40,6 +40,7 @@ public: // public functions void getTempAnalysisStats (uint32_t avgTempAnaStats[USAC_MAX_NUM_CHANNELS], const unsigned nChannels); void getTransientAndPitch (int16_t transIdxAndPitch[USAC_MAX_NUM_CHANNELS], const unsigned nChannels); + uint8_t stereoPreAnalysis (const int32_t* const timeSignals[2], const uint8_t specFlatness[2], const unsigned nSamplesInSig); unsigned temporalAnalysis (const int32_t* const timeSignals[USAC_MAX_NUM_CHANNELS], const unsigned nChannels, const int nSamplesInFrame, const unsigned lookaheadOffset, const uint8_t sbrShift, int32_t* const lrCoreTimeSignals[USAC_MAX_NUM_CHANNELS] = nullptr, // if using SBR