aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniil Cherednik <dan.cherednik@gmail.com>2017-08-06 00:28:18 +0300
committerDaniil Cherednik <dan.cherednik@gmail.com>2017-10-17 01:08:53 +0300
commit43e508b0b607054d2a720e8d9c3a11947d6f798d (patch)
treec9d70e031a21ec9dcf45ab1d18727f267724c30f
parentfcb5986d9d21d923db2480be5089168fc1d1fcd2 (diff)
downloadatracdenc-43e508b0b607054d2a720e8d9c3a11947d6f798d.tar.gz
Simplest gain control - scale first part of frame if the attack transient was detected
-rw-r--r--src/atrac3denc.cpp191
-rw-r--r--src/atrac3denc.h31
-rw-r--r--src/atrac3denc_ut.cpp32
-rw-r--r--src/gain_processor.h2
-rw-r--r--src/main.cpp6
-rw-r--r--src/transient_detector.cpp2
-rw-r--r--src/util.h6
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) {
diff --git a/src/util.h b/src/util.h
index 91b3bf8..e0994f6 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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());