diff options
author | Daniil Cherednik <dan.cherednik@gmail.com> | 2016-07-17 17:50:38 +0300 |
---|---|---|
committer | Daniil Cherednik <dan.cherednik@gmail.com> | 2016-07-17 17:50:38 +0300 |
commit | 842bd06107983bf1775e9c1bbafc8cbe43ffb164 (patch) | |
tree | 4808bed3ae63572f092686f2e93ff44a2676abaa /src/atrac3denc.cpp | |
parent | 1151d5831f19a9f24dd0c545a4968606712a62d2 (diff) | |
download | atracdenc-842bd06107983bf1775e9c1bbafc8cbe43ffb164.tar.gz |
Experimental implementation of gain control.atrac3_gaincontrol
Diffstat (limited to 'src/atrac3denc.cpp')
-rw-r--r-- | src/atrac3denc.cpp | 231 |
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 {}; } |