aboutsummaryrefslogtreecommitdiffstats
path: root/src/atrac3denc.cpp
diff options
context:
space:
mode:
authorDaniil Cherednik <dan.cherednik@gmail.com>2016-07-17 17:50:38 +0300
committerDaniil Cherednik <dan.cherednik@gmail.com>2016-07-17 17:50:38 +0300
commit842bd06107983bf1775e9c1bbafc8cbe43ffb164 (patch)
tree4808bed3ae63572f092686f2e93ff44a2676abaa /src/atrac3denc.cpp
parent1151d5831f19a9f24dd0c545a4968606712a62d2 (diff)
downloadatracdenc-842bd06107983bf1775e9c1bbafc8cbe43ffb164.tar.gz
Experimental implementation of gain control.atrac3_gaincontrol
Diffstat (limited to 'src/atrac3denc.cpp')
-rw-r--r--src/atrac3denc.cpp231
1 files changed, 217 insertions, 14 deletions
diff --git a/src/atrac3denc.cpp b/src/atrac3denc.cpp
index 432fb18..b0ecc48 100644
--- a/src/atrac3denc.cpp
+++ b/src/atrac3denc.cpp
@@ -11,18 +11,35 @@ using namespace NMDCT;
using namespace NAtrac3;
using std::vector;
-void TAtrac3MDCT::Mdct(TFloat specs[1024], TFloat* bands[4], TGainModulatorArray gainModulators) {
+
+static void hpFilter(const TFloat* in, TFloat* out, uint32_t n)
+{
+ TFloat t0 = 0;
+ TFloat t1 = 0;
+ for (uint32_t i = 0; i < n; ++i) {
+ TFloat x = in[i] / 4.0f;
+ TFloat y = t0 + x;
+ t0 = t1 + y - 2.0f * x;
+ t1 = x - .5f * y;
+ out[i] = y;
+ }
+}
+
+void TAtrac3MDCT::Mdct(TFloat specs[1024], TFloat* bands[4], TFloat maxLevels[4], TGainModulatorArray gainModulators)
+{
for (int band = 0; band < 4; ++band) {
TFloat* srcBuff = bands[band];
TFloat* const curSpec = &specs[band*256];
TGainModulator modFn = gainModulators[band];
vector<TFloat> tmp(512);
+ TFloat maxOverlapGain = 0.0;
memcpy(&tmp[0], &srcBuff[256], 256 * sizeof(TFloat));
if (modFn) {
modFn(tmp.data(), srcBuff);
}
for (int i = 0; i < 256; i++) {
srcBuff[256+i] = TAtrac3Data::EncodeWindow[i] * srcBuff[i];
+ maxOverlapGain = std::max(maxOverlapGain, std::abs(srcBuff[256+i]));
srcBuff[i] = TAtrac3Data::EncodeWindow[255-i] * srcBuff[i];
}
memcpy(&tmp[256], &srcBuff[0], 256 * sizeof(TFloat));
@@ -32,10 +49,12 @@ void TAtrac3MDCT::Mdct(TFloat specs[1024], TFloat* bands[4], TGainModulatorArray
if (band & 1) {
SwapArray(curSpec, 256);
}
+ maxLevels[band] = maxOverlapGain;
}
}
-void TAtrac3MDCT::Midct(TFloat specs[1024], TFloat* bands[4], TGainDemodulatorArray gainDemodulators) {
+void TAtrac3MDCT::Midct(TFloat specs[1024], TFloat* bands[4], TGainDemodulatorArray gainDemodulators)
+{
for (int band = 0; band < 4; ++band) {
TFloat* dstBuff = bands[band];
TFloat* curSpec = &specs[band*256];
@@ -70,7 +89,8 @@ TAtrac3Processor::TAtrac3Processor(TCompressedIOPtr&& oma, TAtrac3EncoderSetting
TAtrac3Processor::~TAtrac3Processor()
{}
-TAtrac3MDCT::TGainModulatorArray TAtrac3MDCT::MakeGainModulatorArray(const TAtrac3Data::SubbandInfo& si) {
+TAtrac3MDCT::TGainModulatorArray TAtrac3MDCT::MakeGainModulatorArray(const TAtrac3Data::SubbandInfo& si)
+{
switch (si.GetQmfNum()) {
case 1:
{
@@ -100,7 +120,8 @@ TAtrac3MDCT::TGainModulatorArray TAtrac3MDCT::MakeGainModulatorArray(const TAtra
}
//TODO:
-TAtrac3Data::TTonalComponents TAtrac3Processor::ExtractTonalComponents(TFloat* specs, TTonalDetector fn) {
+TAtrac3Data::TTonalComponents TAtrac3Processor::ExtractTonalComponents(TFloat* specs, TTonalDetector fn)
+{
TAtrac3Data::TTonalComponents res;
const float thresholds[TAtrac3Data::NumQMF] = { 0.9, 2.4, 2.8, 3.2 };
for (uint8_t bandNum = 0; bandNum < this->NumQMF; ++bandNum) {
@@ -117,9 +138,8 @@ TAtrac3Data::TTonalComponents TAtrac3Processor::ExtractTonalComponents(TFloat* s
TFloat absValue = std::abs(specs[n]);
if (absValue > 65535.0) {
TFloat shift = (specs[n] > 0) ? 65535.0 : -65535.0;
-
- std::cerr << "shift overflowed value " << specs[n] << " " << specs[n] - shift << " " << shift << std::endl;
- res.push_back({n, specs[n] - shift});
+ std::cerr << "overflow: " << specs[n] << " at: " << n << std::endl;
+ //res.push_back({n, specs[n] - shift});
specs[n] = shift;
} else if (log10(std::abs(specs[n])) - log10(level) > thresholds[bandNum]) {
res.push_back({n, specs[n]/* - level*/});
@@ -133,7 +153,8 @@ TAtrac3Data::TTonalComponents TAtrac3Processor::ExtractTonalComponents(TFloat* s
}
return res;
}
-std::vector<TTonalComponent> TAtrac3Processor::MapTonalComponents(const TTonalComponents& tonalComponents) {
+std::vector<TTonalComponent> TAtrac3Processor::MapTonalComponents(const TTonalComponents& tonalComponents)
+{
vector<TTonalComponent> componentMap;
for (uint16_t i = 0; i < tonalComponents.size();) {
const uint16_t startPos = i;
@@ -152,12 +173,190 @@ std::vector<TTonalComponent> TAtrac3Processor::MapTonalComponents(const TTonalCo
return componentMap;
}
-TAtrac3Data::SubbandInfo TAtrac3Processor::CreateSubbandInfo(TFloat* in[4], uint32_t channel, TTransientDetector* transientDetector) {
- assert(false); //not implemented
- return {};
+
+TFloat TAtrac3Processor::LimitRel(TFloat x)
+{
+ return std::min(std::max(x, GainLevel[15]), GainLevel[0]);
+}
+
+uint32_t TAtrac3Processor::CheckLevelOverflow(const TFloat probe, uint32_t levelIdx)
+{
+ //std::cout << "CheckLevelOverflow: " << probe << " start idx: " << levelIdx << std::endl;
+ while (probe / GainLevel[levelIdx] > 65535) {
+ if (levelIdx == 0) {
+ std::cerr << "level too hi" << std::endl;
+ break;
+ }
+ levelIdx--;
+ }
+ return levelIdx;
+}
+
+vector<TAtrac3Data::SubbandInfo::TGainPoint> TAtrac3Processor::FilterCurve(const vector<SubbandInfo::TGainPoint>& curve,
+ const int threshold)
+{
+ if (curve.empty())
+ return curve;
+
+#ifndef NDEBUG
+ int prev = -1;
+ for (auto v : curve) {
+ assert((int)v.Location > prev);
+// std::cout << "in: " << v.Level << " " << v.Location << " threshold: " << threshold << std::endl;
+ prev = v.Location;
+ }
+#endif
+
+ std::vector<TAtrac3Data::SubbandInfo::TGainPoint> res;
+ res.push_back(curve[curve.size() - 1]);
+ for (int32_t i = curve.size() - 1; i >=0;) {
+ uint32_t minSeenVal = curve[i].Level;
+ uint32_t maxSeenVal = curve[i].Level;
+
+ int32_t j = i;
+ for (;;) {
+ minSeenVal = std::min(curve[j].Level, minSeenVal);
+ maxSeenVal = std::max(curve[j].Level, maxSeenVal);
+
+ uint32_t curVal = curve[j].Level;
+/*
+ std::cout << "i: " << i
+ << " j: " << j
+ << " minSeenVal: " << minSeenVal
+ << " maxSeenVal: " << maxSeenVal
+ << " curVal: " << curVal
+ << std::endl;
+*/
+ if ((j == 0 && (curve[0].Level != curve[1].Level)) ||
+ (curVal - minSeenVal > threshold) ||
+ (maxSeenVal - curVal > threshold) )
+ {
+ res.push_back(curve[j]);
+ break;
+ }
+ if (j == 0)
+ break;
+ j--;
+ }
+ i = j;
+ if (i == 0)
+ break;
+ }
+ std::reverse(res.begin(), res.end());
+
+// for (auto v : res)
+// std::cout << "out: " << v.Level << " " << v.Location << std::endl;
+
+ if (res.size() < TAtrac3Data::SubbandInfo::MaxGainPointsNum) {
+ return res;
+ }
+ return FilterCurve(res, threshold + 1);
+}
+
+//TODO: implement real transient detector
+bool checkTransient(TFloat cur, TFloat prev)
+{
+ TFloat x = (cur > prev) ? cur / prev : prev / cur;
+ if (x > 6)
+ return true;
+
+ return false;
}
-TPCMEngine<TFloat>::TProcessLambda TAtrac3Processor::GetEncodeLambda() {
+std::vector<TFloat> TAtrac3Processor::CalcBaseLevel(const TFloat prev, const std::vector<TFloat>& gain) {
+
+ TFloat maxRel = 1.0;
+ bool done = false;
+ //TODO: recheck it. It looks like we realy need to compare only prev and last point
+ for (int i = gain.size() - 1; i < gain.size(); ++i) {
+ if (prev > gain[i] && prev / gain[i] > maxRel) {
+ maxRel = prev / gain[i];
+ done = true;
+ }
+ }
+
+ TFloat val0 = gain[gain.size() - 1];
+ if (done) {
+ const TFloat rel = LimitRel(maxRel);
+ uint32_t relIdx = 15 - Log2FloatToIdx(rel, 2048);
+ val0 = prev / GainLevel[relIdx];
+ }
+
+ TFloat val1 = gain[gain.size() - 1];
+ std::vector<TFloat> baseLine(gain.size());
+
+ baseLine[0] = val0;
+ baseLine[baseLine.size() - 1] = val1;
+ TFloat a = (baseLine[baseLine.size() - 1] - baseLine[0]) / baseLine.size();
+
+ for (int i = 1; i < baseLine.size() - 1; i++) {
+ baseLine[i] = i * a + baseLine[0];
+ }
+ return baseLine;
+}
+
+TAtrac3Data::SubbandInfo TAtrac3Processor::CreateSubbandInfo(TFloat* in[4],
+ uint32_t channel,
+ TTransientDetector* transientDetector)
+{
+ TAtrac3Data::SubbandInfo siCur;
+ for (int band = 0; band < 4; ++band) {
+
+ const TFloat* srcBuff = in[band];
+ TFloat* const lastLevel = &LastLevels[channel][band];
+ TFloat* const lastHPLevel = &LastHPLevels[channel][band];
+ TFloat* const lastMax = &PrevPeak[channel][band];
+
+ std::vector<TAtrac3Data::SubbandInfo::TGainPoint> curve;
+ //RMS gain
+ std::vector<TFloat> gain = AnalyzeGain(srcBuff, 256, 32, true);
+ //std::cout << "gain prev: " << *lastLevel << std::endl;
+ //for ( auto vvv : gain ) {
+ // std::cout << " gain: " << vvv << std::endl;
+ //}
+ int32_t gainPos = gain.size() - 2;
+ bool hasTransient = false;
+
+ std::vector<TFloat> base = CalcBaseLevel(*lastLevel, gain);
+
+ TFloat hpSig[256];
+ hpFilter(srcBuff, &hpSig[0], 256);
+ //Peak gain
+ std::vector<TFloat> hpGain = AnalyzeGain(&hpSig[0], 256, 32, false);
+
+ for (; gainPos >= 0; --gainPos) {
+ const TFloat val = (gainPos == 0) ? *lastLevel : gain[gainPos];
+
+ const TFloat hpval = (gainPos == 0) ? *lastHPLevel : hpGain[gainPos];
+ if (!hasTransient && checkTransient(hpval, hpGain[gainPos + 1])) {
+ //std::cout << "hasTransient true at: " << gainPos << " base: " << base[gainPos] << std::endl;
+ hasTransient = true;
+ }
+
+ const TFloat rel = LimitRel(val / base[gainPos]);
+ uint32_t scaleIdx = 15 - Log2FloatToIdx(rel, 2048);
+
+ curve.push_back({scaleIdx, (uint32_t)gainPos /*+ !!gainPos*/});
+ }
+
+
+ *lastLevel = gain[gain.size() -1];
+ *lastHPLevel = hpGain[gain.size() -1];
+ if (hasTransient) {
+ std::reverse(curve.begin(), curve.end());
+ auto t = CheckLevelOverflow(*lastMax, curve[0].Level);
+ //std::cout << "overflow: " << curve[0].Level << " new: " << t << " max: " << *lastMax << std::endl;
+ curve[0].Level = t;
+ siCur.AddSubbandCurve(band, std::move(FilterCurve(curve, 0)));
+ }
+
+ }
+ return siCur;
+}
+
+
+TPCMEngine<TFloat>::TProcessLambda TAtrac3Processor::GetEncodeLambda()
+{
TOma* omaptr = dynamic_cast<TOma*>(Oma.get());
if (!omaptr) {
std::cerr << "Wrong container" << std::endl;
@@ -169,6 +368,7 @@ TPCMEngine<TFloat>::TProcessLambda TAtrac3Processor::GetEncodeLambda() {
for (uint32_t channel=0; channel < 2; channel++) {
vector<TFloat> specs(1024);
TFloat src[NumSamples];
+
for (int i = 0; i < NumSamples; ++i) {
src[i] = data[meta.Channels == 1 ? i : (i * 2 + channel)] / 4.0; //no mono mode in atrac3. //TODO we can TFloat frame after encoding
}
@@ -179,7 +379,9 @@ TPCMEngine<TFloat>::TProcessLambda TAtrac3Processor::GetEncodeLambda() {
TAtrac3Data::SubbandInfo siCur = Params.NoGainControll ?
TAtrac3Data::SubbandInfo() : CreateSubbandInfo(p, channel, &TransientDetectors[channel*4]); //4 detectors per band
- Mdct(specs.data(), p, MakeGainModulatorArray(siCur));
+ TFloat* maxOverlapLevels = PrevPeak[channel];
+
+ Mdct(specs.data(), p, maxOverlapLevels, MakeGainModulatorArray(siCur));
TTonalComponents tonals = Params.NoTonalComponents ?
TAtrac3Data::TTonalComponents() : ExtractTonalComponents(specs.data(), [](const TFloat* spec, uint16_t len) {
std::vector<TFloat> magnitude(len);
@@ -203,7 +405,8 @@ TPCMEngine<TFloat>::TProcessLambda TAtrac3Processor::GetEncodeLambda() {
};
}
-TPCMEngine<TFloat>::TProcessLambda TAtrac3Processor::GetDecodeLambda() {
+TPCMEngine<TFloat>::TProcessLambda TAtrac3Processor::GetDecodeLambda()
+{
abort();
return {};
}