diff options
author | Daniil Cherednik <dan.cherednik@gmail.com> | 2017-08-06 00:28:18 +0300 |
---|---|---|
committer | Daniil Cherednik <dan.cherednik@gmail.com> | 2017-10-17 01:08:53 +0300 |
commit | 43e508b0b607054d2a720e8d9c3a11947d6f798d (patch) | |
tree | c9d70e031a21ec9dcf45ab1d18727f267724c30f | |
parent | fcb5986d9d21d923db2480be5089168fc1d1fcd2 (diff) | |
download | atracdenc-43e508b0b607054d2a720e8d9c3a11947d6f798d.tar.gz |
Simplest gain control - scale first part of frame if the attack transient was detected
-rw-r--r-- | src/atrac3denc.cpp | 191 | ||||
-rw-r--r-- | src/atrac3denc.h | 31 | ||||
-rw-r--r-- | src/atrac3denc_ut.cpp | 32 | ||||
-rw-r--r-- | src/gain_processor.h | 2 | ||||
-rw-r--r-- | src/main.cpp | 6 | ||||
-rw-r--r-- | src/transient_detector.cpp | 2 | ||||
-rw-r--r-- | src/util.h | 6 |
7 files changed, 167 insertions, 103 deletions
diff --git a/src/atrac3denc.cpp b/src/atrac3denc.cpp index b5b838b..893733a 100644 --- a/src/atrac3denc.cpp +++ b/src/atrac3denc.cpp @@ -18,7 +18,6 @@ #include "atrac3denc.h" #include "transient_detector.h" -#include "util.h" #include <assert.h> #include <algorithm> #include <iostream> @@ -94,6 +93,7 @@ TAtrac3Processor::TAtrac3Processor(TCompressedIOPtr&& oma, TAtrac3EncoderSetting , Params(std::move(encoderSettings)) , TransientDetectors(2 * 4, TTransientDetector(8, 256)) //2 - channels, 4 - bands , SingleChannelElements(Params.SourceChannels) + , TransientParamsHistory(Params.SourceChannels, std::vector<TTransientParam>(4)) {} TAtrac3Processor::~TAtrac3Processor() @@ -188,54 +188,77 @@ TFloat TAtrac3Processor::LimitRel(TFloat x) return std::min(std::max(x, GainLevel[15]), GainLevel[0]); } +void TAtrac3Processor::ResetTransientParamsHistory(int channel, int band) +{ + TransientParamsHistory[channel][band] = {-1 , 1, -1, 1, -1, 1}; +} + +void TAtrac3Processor::SetTransientParamsHistory(int channel, int band, const TTransientParam& params) +{ + TransientParamsHistory[channel][band] = params; +} + +const TAtrac3Processor::TTransientParam& TAtrac3Processor::GetTransientParamsHistory(int channel, int band) const +{ + return TransientParamsHistory[channel][band]; +} + TAtrac3Processor::TTransientParam TAtrac3Processor::CalcTransientParam(const std::vector<TFloat>& gain, const TFloat lastMax) { - int32_t attackLocation = 0; - TFloat attackRelation = 1; - - const TFloat attackThreshold = 4; - //pre-echo searching - TFloat tmp; - TFloat q = lastMax; //std::max(lastMax, gain[0]); - tmp = gain[0] / q; - if (tmp > attackThreshold) { - attackRelation = tmp; - } else { - for (uint32_t i = 0; i < gain.size() -1; ++i) { - q = std::max(q, gain[i]); - tmp = gain[i+1] / q; + int32_t attack0Location = -1; // position where gain is risen up, -1 - no attack + TFloat attack0Relation = 1; + + const TFloat attackThreshold = 2; + + { + // pre-echo searching + // relative to previous half frame + for (uint32_t i = 0; i < gain.size(); i++) { + const TFloat tmp = gain[i] / lastMax; if (tmp > attackThreshold) { - attackRelation = tmp; - attackLocation = i; + attack0Relation = tmp; + attack0Location = i; break; } + } + } + + int32_t attack1Location = -1; + TFloat attack1Relation = 1; + { + // pre-echo searching + // relative to previous subsamples block + TFloat q = gain[0]; + for (uint32_t i = 1; i < gain.size(); i++) { + const TFloat tmp = gain[i] / q; + if (tmp > attackThreshold) { + attack1Relation = tmp; + attack1Location = i; + } + q = std::max(q, gain[i]); } } - int32_t releaseLocation = 0; + int32_t releaseLocation = -1; // position where gain is fallen down, -1 - no release TFloat releaseRelation = 1; - const TFloat releaseTreshold = 4; - //post-echo searching - q = 0; - for (uint32_t i = gain.size() - 1; i > 0; --i) { - q = std::max(q, gain[i]); - tmp = gain[i-1] / q; - if (tmp > releaseTreshold) { - releaseRelation = tmp; - releaseLocation = i; - break; - } - } - if (releaseLocation == 0) { - q = std::max(q, gain[0]); - tmp = lastMax / q; - if (tmp > releaseTreshold) { - releaseRelation = tmp; + const TFloat releaseTreshold = 2; + { + // post-echo searching + // relative to current frame + TFloat q = gain.back(); + for (uint32_t i = gain.size() - 2; i > 0; --i) { + const TFloat tmp = gain[i] / q; + if (tmp > releaseTreshold) { + releaseRelation = tmp; + releaseLocation = i; + break; + } + q = std::max(q, gain[i]); } } - return {attackLocation, attackRelation, releaseLocation, releaseRelation}; + return {attack0Location, attack0Relation, attack1Location, attack1Relation, releaseLocation, releaseRelation}; } void TAtrac3Processor::CreateSubbandInfo(TFloat* in[4], @@ -243,72 +266,65 @@ void TAtrac3Processor::CreateSubbandInfo(TFloat* in[4], TTransientDetector* transientDetector, TAtrac3Data::SubbandInfo* subbandInfo) { + + auto relToIdx = [this](TFloat rel) { + rel = 1.0/rel; + return (uint32_t)(RelationToIdx(rel)); + }; + for (int band = 0; band < 4; ++band) { - TFloat invBuf[256]; - if (band & 1) { - memcpy(invBuf, in[band], 256*sizeof(TFloat)); - InvertSpectrInPlase<256>(invBuf); - } - const TFloat* srcBuff = (band & 1) ? invBuf : in[band]; + const TFloat* srcBuff = in[band]; const TFloat* const lastMax = &PrevPeak[channel][band]; std::vector<TAtrac3Data::SubbandInfo::TGainPoint> curve; - std::vector<TFloat> gain = AnalyzeGain(srcBuff, 256, 32, false); + const std::vector<TFloat> gain = AnalyzeGain(srcBuff, 256, 32, false); auto transientParam = CalcTransientParam(gain, *lastMax); - bool hasTransient = (transientParam.AttackRelation != 1.0 || transientParam.ReleaseRelation != 1.0); - - //combine attack and release - TFloat relA = 1; - TFloat relB = 1; - TFloat relC = 1; - uint32_t loc1 = 0; - uint32_t loc2 = 0; - if (transientParam.AttackLocation < transientParam.ReleaseLocation) { - //Peak like transient - relA = transientParam.AttackRelation; - loc1 = transientParam.AttackLocation; - relB = 1; - loc2 = transientParam.ReleaseLocation; - relC = transientParam.ReleaseRelation; - } else if (transientParam.AttackLocation > transientParam.ReleaseLocation) { - //Hole like transient - relA = transientParam.AttackRelation; - loc1 = transientParam.ReleaseLocation; - relB = transientParam.AttackRelation * transientParam.ReleaseRelation; - loc2 = transientParam.AttackLocation; - relC = transientParam.ReleaseRelation; - } else { - //??? - //relA = relB = relC = transientParam.AttackRelation * transientParam.ReleaseRelation; - //loc1 = loc2 = transientParam.ReleaseLocation; - hasTransient = false; - } - //std::cout << "loc: " << loc1 << " " << loc2 << " rel: " << relA << " " << relB << " " << relC << std::endl; + bool hasTransient = false; - if (relC != 1) { - relA /= relC; - relB /= relC; - relC = 1.0; + if (transientParam.Attack0Location == -1 && transientParam.Attack1Location == -1 && transientParam.ReleaseLocation == -1) { + // No transient + ResetTransientParamsHistory(channel, band); + continue; } - auto relToIdx = [this](TFloat rel) { - rel = LimitRel(1/rel); - return (uint32_t)(15 - Log2FloatToIdx(rel, 2048)); - }; - curve.push_back({relToIdx(relA), loc1}); - if (loc1 != loc2) { - curve.push_back({relToIdx(relB), loc2}); + if (transientParam.Attack0Location == -1 && transientParam.Attack1Location == -1) { + // Release only in current frame - PostEcho. Not implemented yet. + // Note: "Hole like" transient also possible (if value is grather in next frame), + // but we keep peak value of this frame, so in next frame we will use this peak value + // for searching attack. + // Handling "Hole like" transients also not implemented. But it should be masked + SetTransientParamsHistory(channel, band, transientParam); + continue; } - if (loc2 != 31) { - curve.push_back({relToIdx(relC), 31}); + + auto transientParamHistory = GetTransientParamsHistory(channel, band); + + if (transientParamHistory.Attack0Location == -1 && transientParamHistory.Attack1Location == -1 && transientParamHistory.ReleaseLocation == -1 && + transientParam.Attack0Location != -1 /*&& transientParam.Attack1Location == -1 && transientParam.ReleaseLocation == -1*/) { + // No transient at previous frame, but transient (attack) at border of first and second half - simplest way, just scale the first half. + + //std::cout << "CASE 1: " << transientParam.Attack0Location << " " << transientParam.Attack0Relation << std::endl; + auto idx = relToIdx(transientParam.Attack0Relation); + //std::cout << "PREV PEAK: " << *lastMax << " " << idx << std::endl; + curve.push_back({idx, (uint32_t)transientParam.Attack0Location}); + hasTransient = true; } + //std::cout << "transient params band: " << band << " atack0loc: " << transientParam.Attack0Location << " atack0rel: " << transientParam.Attack0Relation << + // " atack1loc: " << transientParam.Attack1Location << " atack1rel: " << transientParam.Attack1Relation << + // " releaseloc: " << transientParam.ReleaseLocation << " releaserel: "<< transientParam.ReleaseRelation << std::endl; + + //for (int i = 0; i < 256; i++) { + // std::cout << i << " " << srcBuff[i] << " | " << srcBuff[i-256] << std::endl; + //} + + + SetTransientParamsHistory(channel, band, transientParam); if (hasTransient) { subbandInfo->AddSubbandCurve(band, std::move(curve)); } - } } @@ -347,7 +363,8 @@ TPCMEngine<TFloat>::TProcessLambda TAtrac3Processor::GetEncodeLambda() sce->SubbandInfo.Reset(); if (!Params.NoGainControll) { - //CreateSubbandInfo(p, channel, &TransientDetectors[channel*4], &sce->SubbandInfo); //4 detectors per band + TFloat* p[4] = {PcmBuffer.GetSecond(channel), PcmBuffer.GetSecond(channel+2), PcmBuffer.GetSecond(channel+4), PcmBuffer.GetSecond(channel+6)}; + CreateSubbandInfo(p, channel, &TransientDetectors[channel*4], &sce->SubbandInfo); //4 detectors per band } TFloat* maxOverlapLevels = PrevPeak[channel]; diff --git a/src/atrac3denc.h b/src/atrac3denc.h index 95cbaf0..7c9e239 100644 --- a/src/atrac3denc.h +++ b/src/atrac3denc.h @@ -25,6 +25,7 @@ #include "atrac/atrac3_qmf.h" #include "transient_detector.h" #include "delay_buffer.h" +#include "util.h" #include "atrac/atrac3_bitstream.h" #include "atrac/atrac_scale.h" @@ -33,8 +34,23 @@ #include <functional> #include <array> +#include <cmath> namespace NAtracDEnc { +/////////////////////////////////////////////////////////////////////////////// + +inline uint16_t RelationToIdx(TFloat x) { + if (x <= 0.5) { + x = 1.0 / std::max(x, 0.00048828125); + return 4 + GetFirstSetBit(std::trunc(x)); + } else { + x = std::min(x, 16.0); + return 4 - GetFirstSetBit(std::trunc(x)); + } +} + +/////////////////////////////////////////////////////////////////////////////// + class TAtrac3MDCT : public NAtrac3::TAtrac3Data { NMDCT::TMDCT<512> Mdct512; NMDCT::TMIDCT<512> Midct512; @@ -80,13 +96,15 @@ class TAtrac3Processor : public IProcessor<TFloat>, public TAtrac3MDCT { typedef std::array<uint8_t, NumSpecs> TonalComponentMask; public: struct TTransientParam { - const int32_t AttackLocation; - const TFloat AttackRelation; - const int32_t ReleaseLocation; - const TFloat ReleaseRelation; + int32_t Attack0Location; // Attack position relative to previous frame + TFloat Attack0Relation; + int32_t Attack1Location; // Attack position relative to previous sample + TFloat Attack1Relation; + int32_t ReleaseLocation; + TFloat ReleaseRelation; }; private: - + std::vector<std::vector<TTransientParam>> TransientParamsHistory; #ifdef ATRAC_UT_PUBLIC public: #endif @@ -95,6 +113,9 @@ public: void CreateSubbandInfo(TFloat* in[4], uint32_t channel, TTransientDetector* transientDetector, TAtrac3Data::SubbandInfo* subbandInfo); + void ResetTransientParamsHistory(int channel, int band); + void SetTransientParamsHistory(int channel, int band, const TTransientParam& params); + const TTransientParam& GetTransientParamsHistory(int channel, int band) const; TonalComponentMask AnalyzeTonalComponent(TFloat* specs); TTonalComponents ExtractTonalComponents(TFloat* specs, TTonalDetector fn); diff --git a/src/atrac3denc_ut.cpp b/src/atrac3denc_ut.cpp index a80db5a..7813210 100644 --- a/src/atrac3denc_ut.cpp +++ b/src/atrac3denc_ut.cpp @@ -345,6 +345,38 @@ TEST(TAtrac3MDCT, TAtrac3MDCTSignalWithGainCompensationAndManualTransient) { } } +TEST(AtracGainControl, RelToIdxTest) { + + EXPECT_EQ(4, RelationToIdx(1)); + EXPECT_EQ(4, RelationToIdx(1.8)); + EXPECT_EQ(3, RelationToIdx(2)); + EXPECT_EQ(3, RelationToIdx(3)); + EXPECT_EQ(3, RelationToIdx(3.5)); + EXPECT_EQ(2, RelationToIdx(4)); + EXPECT_EQ(2, RelationToIdx(7)); + EXPECT_EQ(1, RelationToIdx(8)); + EXPECT_EQ(1, RelationToIdx(15)); + EXPECT_EQ(0, RelationToIdx(16)); + EXPECT_EQ(0, RelationToIdx(9999)); + + EXPECT_EQ(4, RelationToIdx(0.8)); + EXPECT_EQ(5, RelationToIdx(0.5)); + EXPECT_EQ(5, RelationToIdx(0.4)); + EXPECT_EQ(5, RelationToIdx(0.3)); + EXPECT_EQ(6, RelationToIdx(0.25)); + EXPECT_EQ(6, RelationToIdx(0.126)); + EXPECT_EQ(7, RelationToIdx(0.125)); + EXPECT_EQ(7, RelationToIdx(0.0626)); + EXPECT_EQ(8, RelationToIdx(0.0625)); + EXPECT_EQ(8, RelationToIdx(0.03126)); + EXPECT_EQ(9, RelationToIdx(0.03125)); + EXPECT_EQ(9, RelationToIdx(0.015626)); + EXPECT_EQ(10, RelationToIdx(0.015625)); + EXPECT_EQ(13, RelationToIdx(0.001953125)); + EXPECT_EQ(15, RelationToIdx(0.00048828125)); + EXPECT_EQ(15, RelationToIdx(0.00000048828125)); +} + TEST(TAtrac3MDCT, TAtrac3MDCTWindow) { TWindowTest test; test.RunTest(); diff --git a/src/gain_processor.h b/src/gain_processor.h index 998ee31..66d6b1c 100644 --- a/src/gain_processor.h +++ b/src/gain_processor.h @@ -114,7 +114,7 @@ public: } for (; pos < T::MDCTSz/2; pos++) { - //std::cout << "mod pos: " << pos << " scale: " << scale << " bufCur: " << bufCur[pos] << std::endl; + //std::cout << "mod pos: " << pos << " scale: " << scale << " bufCur: " << bufCur[pos] << " new value: " << bufCur[pos] / scale<<std::endl; bufCur[pos] /= scale; } }; diff --git a/src/main.cpp b/src/main.cpp index b74dea1..ee8f862 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -213,7 +213,7 @@ int main(int argc, char* const* argv) { "notransient", optional_argument, NULL, O_NOTRANSIENT}, { "nostdout", no_argument, NULL, O_NOSTDOUT}, { "notonal", no_argument, NULL, O_NOTONAL}, - { "gaincontrol", no_argument, NULL, O_GAINCONTROL}, + { "nogaincontrol", no_argument, NULL, O_GAINCONTROL}, { NULL, 0, NULL, 0} }; @@ -224,7 +224,7 @@ int main(int argc, char* const* argv) uint32_t bfuIdxConst = 0; //0 - auto, no const bool fastBfuNumSearch = false; bool noStdOut = false; - bool noGainControl = true; + bool noGainControl = false; bool noTonalComponents = false; NAtrac1::TAtrac1EncodeSettings::EWindowMode windowMode = NAtrac1::TAtrac1EncodeSettings::EWindowMode::EWM_AUTO; uint32_t winMask = 0; //0 - all is long @@ -281,7 +281,7 @@ int main(int argc, char* const* argv) noTonalComponents = true; break; case O_GAINCONTROL: - noGainControl = false; + noGainControl = true; break; default: printUsage(myName); diff --git a/src/transient_detector.cpp b/src/transient_detector.cpp index 8059e8c..660b776 100644 --- a/src/transient_detector.cpp +++ b/src/transient_detector.cpp @@ -42,7 +42,7 @@ static TFloat calculatePeak(const TFloat* in, uint32_t n) { if (absVal > s) s = absVal; } - return s; + return s; } void TTransientDetector::HPFilter(const TFloat* in, TFloat* out) { @@ -62,12 +62,6 @@ inline uint16_t GetFirstSetBit(uint32_t x) { } template<class T> -inline uint16_t Log2FloatToIdx(T x, uint16_t shift) { - T t = x * shift; - return GetFirstSetBit(std::trunc(t)); -} - -template<class T> inline T CalcMedian(T* in, uint32_t len) { std::vector<T> tmp(in, in+len); std::sort(tmp.begin(), tmp.end()); |