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 public 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 public 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
+
+ some final code cleanup, small code corrections and editorial changes for this year
+ exhaleLib: minor stereo quality tuning for low rates, optional CBR mode via macro
+
+ Version 1.1.7 Aug. 2021
minor tuning at low SBR rates, enabled SBR coding at 22050 Hz input sample rate
exhaleApp: added expert modes for loudness leveling, custom Intra frame interval
@@ -158,7 +163,7 @@
exhaleLib: speed-ups and further quality tuning for difficult signals, as necessary.
-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
|