aboutsummaryrefslogtreecommitdiffstats
path: root/src/atrac3denc_ut.cpp
diff options
context:
space:
mode:
authorDaniil Cherednik <dan.cherednik@gmail.com>2016-03-13 09:49:33 +0300
committerDaniil Cherednik <dan.cherednik@gmail.com>2016-09-02 21:21:28 +0300
commitcfaa2cd39b7256a868a4f5cd83aac207df6bd1b3 (patch)
tree75efff26584e046566d17cd308d45b6b0fd5abfc /src/atrac3denc_ut.cpp
parentb4df8a7c2dd12eea27c8cc52bd52a1bb8c00943f (diff)
downloadatracdenc-cfaa2cd39b7256a868a4f5cd83aac207df6bd1b3.tar.gz
Dirty implementation of atrac3 encoder:
- no JS mode - constant quantiser for tonal components - gain controll implemented but produces some artifacts with real signals. - etc...
Diffstat (limited to 'src/atrac3denc_ut.cpp')
-rw-r--r--src/atrac3denc_ut.cpp335
1 files changed, 335 insertions, 0 deletions
diff --git a/src/atrac3denc_ut.cpp b/src/atrac3denc_ut.cpp
new file mode 100644
index 0000000..e0602a2
--- /dev/null
+++ b/src/atrac3denc_ut.cpp
@@ -0,0 +1,335 @@
+#define ATRAC_UT_PUBLIC
+
+#include "atrac3denc.h"
+#include <gtest/gtest.h>
+
+#include <vector>
+#include <cmath>
+using std::vector;
+using namespace NAtracDEnc;
+using namespace NAtrac3;
+
+static void GenerateSignal(TFloat* buf, size_t n, TFloat f, TFloat a) {
+ for (size_t i = 0; i < n; ++i) {
+ buf[i] = a * sin((M_PI/2) * i * f);
+ }
+}
+
+static void GenerateSignalWithTransient(TFloat* buf, size_t n, TFloat f, TFloat a,
+ size_t transientPos, size_t transientLen, TFloat transientLev) {
+ assert(transientPos + transientLen < n);
+ GenerateSignal(buf, n, f, a);
+ GenerateSignal(buf+transientPos, transientLen, f, transientLev);
+// for (size_t i = transientPos; i < transientPos + transientLen; ++i) {
+// buf[i] += (i & 1) ? transientLev : - transientLev;
+// }
+}
+
+class TWindowTest : public TAtrac3Data {
+public:
+ void RunTest() {
+ for (size_t i = 0; i < 256; i++) {
+ const TFloat ha1 = EncodeWindow[i] / 2.0; //compensation
+ const TFloat hs1 = DecodeWindow[i];
+ const TFloat hs2 = DecodeWindow[255-i];
+ const TFloat res = hs1 / (hs1 * hs1 + hs2 * hs2);
+ EXPECT_NEAR(ha1, res, 0.000000001);
+ }
+ }
+};
+
+template<class T>
+class TAtrac3MDCTWorkBuff {
+ T* Buffer;
+public:
+ static const size_t BandBuffSz = 256;
+ static const size_t BandBuffAndOverlapSz = BandBuffSz * 2;
+ static const size_t BuffSz = BandBuffAndOverlapSz * (4 + 4);
+ T* const Band0;
+ T* const Band1;
+ T* const Band2;
+ T* const Band3;
+ T* const Band0Res;
+ T* const Band1Res;
+ T* const Band2Res;
+ T* const Band3Res;
+ TAtrac3MDCTWorkBuff()
+ : Buffer(new T[BuffSz])
+ , Band0(Buffer)
+ , Band1(Band0 + BandBuffAndOverlapSz)
+ , Band2(Band1 + BandBuffAndOverlapSz)
+ , Band3(Band2 + BandBuffAndOverlapSz)
+ , Band0Res(Band3 + BandBuffAndOverlapSz)
+ , Band1Res(Band0Res + BandBuffAndOverlapSz)
+ , Band2Res(Band1Res + BandBuffAndOverlapSz)
+ , Band3Res(Band2Res + BandBuffAndOverlapSz)
+ {
+ memset(Buffer, 0, sizeof(T)*BuffSz);
+ }
+ ~TAtrac3MDCTWorkBuff()
+ {
+ delete[] Buffer;
+ }
+};
+
+
+TEST(TAtrac3MDCT, TAtrac3MDCTZeroOneBlock) {
+ TAtrac3MDCT mdct;
+ TAtrac3MDCTWorkBuff<TFloat> buff;
+ size_t workSz = TAtrac3MDCTWorkBuff<TFloat>::BandBuffSz;
+
+ vector<TFloat> specs(1024);
+
+ TFloat* p[4] = { buff.Band0, buff.Band1, buff.Band2, buff.Band3 };
+
+ mdct.Mdct(specs.data(), p);
+ for(auto s: specs)
+ EXPECT_NEAR(s, 0.0, 0.0000000001);
+
+ TFloat* t[4] = { buff.Band0Res, buff.Band1Res, buff.Band2Res, buff.Band3Res };
+ mdct.Midct(specs.data(), p);
+
+ for(size_t i = 0; i < workSz; ++i)
+ EXPECT_NEAR(buff.Band0Res[i], 0.0, 0.0000000001);
+
+ for(size_t i = 0; i < workSz; ++i)
+ EXPECT_NEAR(buff.Band1Res[i], 0.0, 0.0000000001);
+
+ for(size_t i = 0; i < workSz; ++i)
+ EXPECT_NEAR(buff.Band2Res[i], 0.0, 0.0000000001);
+
+ for(size_t i = 0; i < workSz; ++i)
+ EXPECT_NEAR(buff.Band3Res[i], 0.0, 0.0000000001);
+
+
+}
+
+TEST(TAtrac3MDCT, TAtrac3MDCTSignal) {
+ TAtrac3MDCT mdct;
+ TAtrac3MDCTWorkBuff<TFloat> buff;
+ size_t workSz = TAtrac3MDCTWorkBuff<TFloat>::BandBuffSz;
+
+ const size_t len = 1024;
+ vector<TFloat> signal(len);
+ vector<TFloat> signalRes(len);
+ GenerateSignal(signal.data(), signal.size(), 0.25, 32768);
+
+ for (size_t pos = 0; pos < len; pos += workSz) {
+ vector<TFloat> specs(1024);
+ memcpy(buff.Band0, signal.data() + pos, workSz * sizeof(TFloat));
+
+ TFloat* p[4] = { buff.Band0, buff.Band1, buff.Band2, buff.Band3 };
+ mdct.Mdct(specs.data(), p);
+
+ TFloat* t[4] = { buff.Band0Res, buff.Band1Res, buff.Band2Res, buff.Band3Res };
+ mdct.Midct(specs.data(), t);
+
+ memcpy(signalRes.data() + pos, buff.Band0Res, workSz * sizeof(TFloat));
+ }
+
+ for (int i = workSz; i < len; ++i)
+ EXPECT_NEAR(signal[i - workSz], signalRes[i], 0.00000001);
+}
+
+TEST(TAtrac3MDCT, TAtrac3MDCTSignalWithGainCompensation) {
+ TAtrac3MDCT mdct;
+ TAtrac3MDCTWorkBuff<TFloat> buff;
+ size_t workSz = TAtrac3MDCTWorkBuff<TFloat>::BandBuffSz;
+
+ const size_t len = 4096;
+ vector<TFloat> signal(len, 8000);
+ vector<TFloat> signalRes(len);
+ GenerateSignal(signal.data() + 1024, signal.size()-1024, 0.25, 32768);
+
+ for (size_t pos = 0; pos < len; pos += workSz) {
+ vector<TFloat> specs(1024);
+ memcpy(buff.Band0, signal.data() + pos, workSz * sizeof(TFloat));
+
+ TFloat* p[4] = { buff.Band0, buff.Band1, buff.Band2, buff.Band3 };
+
+ if (pos == 256) { //apply gain modulation
+ TAtrac3Data::SubbandInfo siCur;
+ siCur.AddSubbandCurve(0, {{3, 2}});
+
+ mdct.Mdct(specs.data(), p, { mdct.GainProcessor.Modulate(siCur.GetGainPoints(0)),
+ TAtrac3MDCT::TGainModulator(), TAtrac3MDCT::TGainModulator(), TAtrac3MDCT::TGainModulator()});
+ } else if (pos == 1024) {
+ TAtrac3Data::SubbandInfo siCur;
+ std::vector<TAtrac3Data::SubbandInfo::TGainPoint> curve = {{3, 2}, {2, 5}};
+ siCur.AddSubbandCurve(0, std::move(curve));
+
+ mdct.Mdct(specs.data(), p, { mdct.GainProcessor.Modulate(siCur.GetGainPoints(0)),
+ TAtrac3MDCT::TGainModulator(), TAtrac3MDCT::TGainModulator(), TAtrac3MDCT::TGainModulator()});
+ } else if (pos == 1024 + 256) {
+ TAtrac3Data::SubbandInfo siCur;
+ siCur.AddSubbandCurve(0, {{1, 0}});
+
+ mdct.Mdct(specs.data(), p, { mdct.GainProcessor.Modulate(siCur.GetGainPoints(0)),
+ TAtrac3MDCT::TGainModulator(), TAtrac3MDCT::TGainModulator(), TAtrac3MDCT::TGainModulator()});
+ } else if (pos == 2048) {
+ TAtrac3Data::SubbandInfo siCur;
+ std::vector<TAtrac3Data::SubbandInfo::TGainPoint> curve = {{4, 2}, {1, 5}};
+ siCur.AddSubbandCurve(0, std::move(curve));
+
+ mdct.Mdct(specs.data(), p, { mdct.GainProcessor.Modulate(siCur.GetGainPoints(0)),
+ TAtrac3MDCT::TGainModulator(), TAtrac3MDCT::TGainModulator(), TAtrac3MDCT::TGainModulator()});
+ } else {
+ mdct.Mdct(specs.data(), p);
+ }
+
+ TFloat* t[4] = { buff.Band0Res, buff.Band1Res, buff.Band2Res, buff.Band3Res };
+
+ if (pos == 256) { //restore gain modulation
+ TAtrac3Data::SubbandInfo siCur;
+ TAtrac3Data::SubbandInfo siNext;
+ siNext.AddSubbandCurve(0, {{3, 2}});
+
+ mdct.Midct(specs.data(), t, {mdct.GainProcessor.Demodulate(siCur.GetGainPoints(0), siNext.GetGainPoints(0)),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator()});
+ } else if (pos == 512) {
+ TAtrac3Data::SubbandInfo siNext;
+ TAtrac3Data::SubbandInfo siCur;
+ siCur.AddSubbandCurve(0, {{3, 2}});
+
+ mdct.Midct(specs.data(), t, {mdct.GainProcessor.Demodulate(siCur.GetGainPoints(0), siNext.GetGainPoints(0)),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator()});
+ } else if (pos == 1024) {
+ TAtrac3Data::SubbandInfo siCur;
+ TAtrac3Data::SubbandInfo siNext;
+ std::vector<TAtrac3Data::SubbandInfo::TGainPoint> curve = {{3, 2}, {2, 5}};
+ siNext.AddSubbandCurve(0, std::move(curve));
+
+ mdct.Midct(specs.data(), t, {mdct.GainProcessor.Demodulate(siCur.GetGainPoints(0), siNext.GetGainPoints(0)),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator()});
+ } else if (pos == 1024 + 256) {
+ TAtrac3Data::SubbandInfo siNext;
+ TAtrac3Data::SubbandInfo siCur;
+ siNext.AddSubbandCurve(0, {{1, 0}});
+ std::vector<TAtrac3Data::SubbandInfo::TGainPoint> curve = {{3, 2}, {2, 5}};
+ siCur.AddSubbandCurve(0, std::move(curve));
+
+ mdct.Midct(specs.data(), t, {mdct.GainProcessor.Demodulate(siCur.GetGainPoints(0), siNext.GetGainPoints(0)),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator()});
+ } else if (pos == 1024 + 256 + 256) {
+ TAtrac3Data::SubbandInfo siNext;
+ TAtrac3Data::SubbandInfo siCur;
+ siCur.AddSubbandCurve(0, {{1, 0}});
+
+ mdct.Midct(specs.data(), t, {mdct.GainProcessor.Demodulate(siCur.GetGainPoints(0), siNext.GetGainPoints(0)),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator()});
+ } else if (pos == 2048) {
+ TAtrac3Data::SubbandInfo siCur;
+ TAtrac3Data::SubbandInfo siNext;
+ std::vector<TAtrac3Data::SubbandInfo::TGainPoint> curve = {{4, 2}, {1, 5}};
+ siNext.AddSubbandCurve(0, std::move(curve));
+
+ mdct.Midct(specs.data(), t, {mdct.GainProcessor.Demodulate(siCur.GetGainPoints(0), siNext.GetGainPoints(0)),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator()});
+ } else if (pos == 2048 + 256) {
+ TAtrac3Data::SubbandInfo siNext;
+ TAtrac3Data::SubbandInfo siCur;
+ std::vector<TAtrac3Data::SubbandInfo::TGainPoint> curve = {{4, 2}, {1, 5}};
+ siCur.AddSubbandCurve(0, std::move(curve));
+
+ mdct.Midct(specs.data(), t, {mdct.GainProcessor.Demodulate(siCur.GetGainPoints(0), siNext.GetGainPoints(0)),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator()});
+ } else {
+ mdct.Midct(specs.data(), t);
+ }
+ memcpy(signalRes.data() + pos, buff.Band0Res, workSz * sizeof(TFloat));
+ }
+ for (int i = workSz; i < len; ++i) {
+ //std::cout << "res: " << i << " " << signalRes[i] << std::endl;
+ EXPECT_NEAR(signal[i - workSz], signalRes[i], 0.00000001);
+ }
+}
+
+TEST(TAtrac3MDCT, TAtrac3MDCTSignalWithGainCompensationAndManualTransient) {
+ TAtrac3MDCT mdct;
+ TAtrac3MDCTWorkBuff<TFloat> buff;
+ size_t workSz = TAtrac3MDCTWorkBuff<TFloat>::BandBuffSz;
+
+ const size_t len = 1024;
+ vector<TFloat> signal(len);
+ vector<TFloat> signalRes(len);
+ GenerateSignalWithTransient(signal.data(), signal.size(), 0.03125, 512.0,
+ 640, 64, 32768.0);
+ const std::vector<TAtrac3Data::SubbandInfo::TGainPoint> curve1 = {{6, 13}, {4, 14}};
+
+ for (size_t pos = 0; pos < len; pos += workSz) {
+ vector<TFloat> specs(1024);
+ memcpy(buff.Band0, signal.data() + pos, workSz * sizeof(TFloat));
+
+ TFloat* p[4] = { buff.Band0, buff.Band1, buff.Band2, buff.Band3 };
+ //for (int i = 0; i < 256; i++) {
+ // std::cout << i + pos << " " << buff.Band0[i] << std::endl;
+ //}
+
+ if (pos == 512) { //apply gain modulation
+ TAtrac3Data::SubbandInfo siCur;
+ siCur.AddSubbandCurve(0, std::vector<TAtrac3Data::SubbandInfo::TGainPoint>(curve1));
+
+ for (int i = 0; i < 256; i++) {
+ std::cout << i << " " << buff.Band0[i] << std::endl;
+ }
+
+ mdct.Mdct(specs.data(), p, { mdct.GainProcessor.Modulate(siCur.GetGainPoints(0)),
+ TAtrac3MDCT::TGainModulator(), TAtrac3MDCT::TGainModulator(), TAtrac3MDCT::TGainModulator()});
+ } else {
+ mdct.Mdct(specs.data(), p);
+ }
+
+ for (int i = 0; i < specs.size(); ++i) {
+ if (i > 240 && i < 256)
+ specs[i] /= 1.9;
+ }
+ TFloat* t[4] = { buff.Band0Res, buff.Band1Res, buff.Band2Res, buff.Band3Res };
+ if (pos == 512) { //restore gain modulation
+ TAtrac3Data::SubbandInfo siCur;
+ TAtrac3Data::SubbandInfo siNext;
+ siNext.AddSubbandCurve(0, std::vector<TAtrac3Data::SubbandInfo::TGainPoint>(curve1));
+ mdct.Midct(specs.data(), t, {mdct.GainProcessor.Demodulate(siCur.GetGainPoints(0), siNext.GetGainPoints(0)),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator()});
+ } else if (pos == 768) {
+ TAtrac3Data::SubbandInfo siNext;
+ TAtrac3Data::SubbandInfo siCur;
+ siCur.AddSubbandCurve(0, std::vector<TAtrac3Data::SubbandInfo::TGainPoint>(curve1));
+
+ mdct.Midct(specs.data(), t, {mdct.GainProcessor.Demodulate(siCur.GetGainPoints(0), siNext.GetGainPoints(0)),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator(),
+ TAtrac3MDCT::TAtrac3GainProcessor::TGainDemodulator()});
+ } else {
+ mdct.Midct(specs.data(), t);
+ }
+
+ memcpy(signalRes.data() + pos, buff.Band0Res, workSz * sizeof(TFloat));
+ }
+ for (int i = workSz; i < len; ++i) {
+ //std::cout << "res: " << i << " " << signalRes[i] << std::endl;
+ EXPECT_NEAR(signal[i - workSz], signalRes[i], 10);
+ }
+}
+
+TEST(TAtrac3MDCT, TAtrac3MDCTWindow) {
+ TWindowTest test;
+ test.RunTest();
+}
+
+