aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniil Cherednik <dan.cherednik@gmail.com>2015-10-31 02:14:04 +0300
committerDaniil Cherednik <dan.cherednik@gmail.com>2015-10-31 02:14:04 +0300
commit592b4bd68f3eb9d1cdbcda74feaffc7b1d5f0485 (patch)
treea7e1b93335631a8ff969f6d395e08ec24281fe62
parent4bfe046e9620644dda130aaf3f9d9078251ffd59 (diff)
downloadatracdenc-592b4bd68f3eb9d1cdbcda74feaffc7b1d5f0485.tar.gz
experimental first implementation of ATRAC encoder
current limitations: - only long window - naive MDCT (O(n^2)) - bad table of fixed bit allocation - bad usage of CBR - time accuracy is not guaranteed - dirty, not optimized code
-rw-r--r--src/Makefile3
-rw-r--r--src/aea.cpp102
-rw-r--r--src/aea.h48
-rw-r--r--src/atrac/atrac1.cpp10
-rw-r--r--src/atrac/atrac1.h71
-rw-r--r--src/atrac/atrac1_bitalloc.cpp155
-rw-r--r--src/atrac/atrac1_bitalloc.h35
-rw-r--r--src/atrac/atrac1_dequantiser.cpp48
-rw-r--r--src/atrac/atrac1_dequantiser.h12
-rw-r--r--src/atrac/atrac1_qmf.h45
-rw-r--r--src/atrac/atrac1_scale.cpp55
-rw-r--r--src/atrac/atrac1_scale.h22
-rw-r--r--src/atracdenc.cpp213
-rw-r--r--src/atracdenc.h35
-rw-r--r--src/bitstream/Makefile6
-rw-r--r--src/bitstream/bitstream.cpp55
-rw-r--r--src/bitstream/bitstream.h29
-rw-r--r--src/bitstream/bitstream_ut.cpp55
-rw-r--r--src/main.cpp125
-rw-r--r--src/pcmengin.h106
-rw-r--r--src/qmf/qmf.cpp1
-rw-r--r--src/qmf/qmf.h81
-rw-r--r--src/wav.cpp85
-rw-r--r--src/wav.h126
24 files changed, 1523 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..46afbde
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,3 @@
+
+all:
+ g++ -std=c++11 -g main.cpp wav.cpp aea.cpp atracdenc.cpp bitstream/bitstream.cpp atrac/atrac1.cpp atrac/atrac1_dequantiser.cpp atrac/atrac1_scale.cpp atrac/atrac1_bitalloc.cpp -o atracdenc
diff --git a/src/aea.cpp b/src/aea.cpp
new file mode 100644
index 0000000..fc041cb
--- /dev/null
+++ b/src/aea.cpp
@@ -0,0 +1,102 @@
+#include "aea.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+using std::string;
+
+
+TAea::TMeta TAea::ReadMeta(const string& filename) {
+ FILE* fp = fopen(filename.c_str(), "r");
+ if (!fp)
+ throw TAeaIOError("Can't open file to read", errno);
+ std::array<char, AeaMetaSize> buf;
+ if (fread(&buf[0], TAea::AeaMetaSize, 1, fp) != 1) {
+ const int errnum = errno;
+ fclose(fp);
+ throw TAeaIOError("Can't read AEA header", errnum);
+ }
+
+ if ( buf[0] == 0x00 && buf[1] == 0x08 && buf[2] == 0x00 && buf[3] == 0x00 && buf[264] < 3 ) {
+ return {fp, buf};
+ }
+ throw TAeaFormatError();
+
+}
+
+TAea::TMeta TAea::CreateMeta(const string& filename, const string& title, int channelNum) {
+ FILE* fp = fopen(filename.c_str(), "w");
+ if (!fp)
+ throw TAeaIOError("Can't open file to write", errno);
+ std::array<char, AeaMetaSize> buf;
+ memset(&buf[0], 0, AeaMetaSize);
+ buf[0] = 0x00;
+ buf[1] = 0x08;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ strncpy(&buf[4], title.c_str(), 16);
+ buf[19] = 0;
+// buf[210] = 0x08;
+ buf[264] = (char)channelNum;
+ if (fwrite(&buf[0], TAea::AeaMetaSize, 1, fp) != 1) {
+ const int errnum = errno;
+ fclose(fp);
+ throw TAeaIOError("Can't read AEA header", errnum);
+ }
+ return {fp, buf};
+}
+
+
+TAea::TAea(const string& filename)
+ : Meta(ReadMeta(filename)) {
+ }
+
+TAea::TAea(const string& filename, const string& title, int channelNum)
+ : Meta(CreateMeta(filename, title, channelNum)) {
+ }
+
+TAea::~TAea() {
+ fclose(Meta.AeaFile);
+}
+
+std::string TAea::GetName() const {
+ return string(&Meta.AeaHeader[4]);
+}
+
+std::unique_ptr<TAea::TFrame> TAea::ReadFrame() {
+ std::unique_ptr<TAea::TFrame>frame(new TFrame);
+ if(fread(frame.get(), frame->size(), 1, Meta.AeaFile) != 1) {
+ const int errnum = errno;
+ fclose(Meta.AeaFile);
+ throw TAeaIOError("Can't read AEA frame", errnum);
+ }
+ return frame;
+}
+/*
+void TAea::WriteFrame(std::unique_ptr<TAea::TFrame>&& frame) {
+ if (fwrite(frame.get(), frame->size(), 1, Meta.AeaFile) != 1) {
+ const int errnum = errno;
+ fclose(Meta.AeaFile);
+ throw TAeaIOError("Can't write AEA frame", errnum);
+ }
+}
+*/
+void TAea::WriteFrame(std::vector<char> data) {
+ data.resize(212);
+ if (fwrite(data.data(), data.size(), 1, Meta.AeaFile) != 1) {
+ const int errnum = errno;
+ fclose(Meta.AeaFile);
+ throw TAeaIOError("Can't write AEA frame", errnum);
+ }
+}
+
+int TAea::GetChannelNum() const {
+ return Meta.AeaHeader[264];
+}
+uint32_t TAea::GetLengthInSamples() const {
+ const int fd = fileno(Meta.AeaFile);
+ struct stat sb;
+ fstat(fd, &sb);
+ const uint32_t nChannels = Meta.AeaHeader[264] ? Meta.AeaHeader[264] : 1;
+ return 512 * ((sb.st_size - TAea::AeaMetaSize) / 212 / nChannels - 5);
+}
diff --git a/src/aea.h b/src/aea.h
new file mode 100644
index 0000000..3499579
--- /dev/null
+++ b/src/aea.h
@@ -0,0 +1,48 @@
+#pragma once
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <array>
+#include <memory>
+
+
+
+class TAeaIOError : public std::exception {
+ const int ErrNum = 0;
+ const char* Text;
+public:
+ TAeaIOError(const char* str, int err)
+ : ErrNum(err)
+ , Text(str)
+ {}
+ virtual const char* what() const throw() {
+ return Text;
+ }
+};
+
+class TAeaFormatError {
+};
+
+class TAea {
+ static constexpr uint32_t AeaMetaSize = 2048;
+ struct TMeta {
+ FILE* AeaFile;
+ std::array<char, AeaMetaSize> AeaHeader;
+ } Meta;
+ static TAea::TMeta ReadMeta(const std::string& filename);
+ static TAea::TMeta CreateMeta(const std::string& filename, const std::string& title, int numChannel);
+public:
+ typedef std::array<char, 212> TFrame;
+ TAea(const std::string& filename);
+ TAea(const std::string& filename, const std::string& title, int numChannel);
+ ~TAea();
+ std::unique_ptr<TFrame> ReadFrame();
+// void WriteFrame(std::unique_ptr<TAea::TFrame>&& frame);
+ void WriteFrame(std::vector<char> data);
+ std::string GetName() const;
+ int GetChannelNum() const;
+ uint32_t GetLengthInSamples() const;
+};
+
+typedef std::unique_ptr<TAea> TAeaPtr;
+
diff --git a/src/atrac/atrac1.cpp b/src/atrac/atrac1.cpp
new file mode 100644
index 0000000..26d8218
--- /dev/null
+++ b/src/atrac/atrac1.cpp
@@ -0,0 +1,10 @@
+#include "atrac1.h"
+
+constexpr uint32_t TAtrac1Data::BlocksPerBand[QMF_BANDS + 1];
+constexpr uint32_t TAtrac1Data::SpecsPerBlock[MAX_BFUS];
+constexpr uint32_t TAtrac1Data::SpecsStartLong[MAX_BFUS];
+constexpr uint32_t TAtrac1Data::SpecsStartShort[MAX_BFUS];
+constexpr uint32_t TAtrac1Data::BfuAmountTab[8];
+double TAtrac1Data::ScaleTable[64] = {0};
+double TAtrac1Data::SineWindow[32] = {0};
+
diff --git a/src/atrac/atrac1.h b/src/atrac/atrac1.h
new file mode 100644
index 0000000..10cfd0a
--- /dev/null
+++ b/src/atrac/atrac1.h
@@ -0,0 +1,71 @@
+#pragma once
+#include <cstdint>
+#include <array>
+#include <map>
+#include <math.h>
+#include "../bitstream/bitstream.h"
+const int QMF_BANDS = 3;
+const int MAX_BFUS = 52;
+
+class TAtrac1Data {
+protected:
+ static constexpr uint32_t SpecsPerBlock[MAX_BFUS] = {
+ 8, 8, 8, 8, 4, 4, 4, 4, 8, 8, 8, 8, 6, 6, 6, 6, 6, 6, 6, 6, // low band
+ 6, 6, 6, 6, 7, 7, 7, 7, 9, 9, 9, 9, 10, 10, 10, 10, // middle band
+ 12, 12, 12, 12, 12, 12, 12, 12, 20, 20, 20, 20, 20, 20, 20, 20 // high band
+ };
+ static constexpr uint32_t BlocksPerBand[QMF_BANDS + 1] = {0, 20, 36, 52};
+ static constexpr uint32_t SpecsStartLong[MAX_BFUS] = {
+ 0, 8, 16, 24, 32, 36, 40, 44, 48, 56, 64, 72, 80, 86, 92, 98, 104, 110, 116, 122,
+ 128, 134, 140, 146, 152, 159, 166, 173, 180, 189, 198, 207, 216, 226, 236, 246,
+ 256, 268, 280, 292, 304, 316, 328, 340, 352, 372, 392, 412, 432, 452, 472, 492,
+ };
+ static constexpr uint32_t SpecsStartShort[MAX_BFUS] = {
+ 0, 32, 64, 96, 8, 40, 72, 104, 12, 44, 76, 108, 20, 52, 84, 116, 26, 58, 90, 122,
+ 128, 160, 192, 224, 134, 166, 198, 230, 141, 173, 205, 237, 150, 182, 214, 246,
+ 256, 288, 320, 352, 384, 416, 448, 480, 268, 300, 332, 364, 396, 428, 460, 492
+ };
+ static const uint32_t SoundUnitSize = 212;
+ static constexpr uint32_t BfuAmountTab[8] = {20, 28, 32, 36, 40, 44, 48, 52};
+ static const uint32_t BitsPerBfuAmountTabIdx = 3;
+ static const uint32_t BitsPerIDWL = 4;
+ static const uint32_t BitsPerIDSF = 6;
+ static const uint32_t NumSamples = 512;
+
+ static double ScaleTable[64];
+ static double SineWindow[32];
+public:
+ TAtrac1Data() {
+ if (ScaleTable[0] == 0) {
+ for (uint32_t i = 0; i < 64; i++) {
+ ScaleTable[i] = pow(2.0, (double)(i - 15.0) / 3.0);
+ }
+ }
+ if (SineWindow[0] == 0) {
+ for (uint32_t i = 0; i < 32; i++) {
+ SineWindow[i] = sin((i + 0.5) * (M_PI / (2.0 * 32.0)));
+ }
+ }
+ }
+};
+
+class TBlockSize {
+ static std::array<int, QMF_BANDS> Parse(NBitStream::TBitStream* stream) {
+ std::array<int,QMF_BANDS> tmp;
+ tmp[0] = 2 - stream->Read(2);
+ tmp[1] = 2 - stream->Read(2);
+ tmp[2] = 3 - stream->Read(2);
+ stream->Read(2); //skip unused 2 bits
+ return tmp;
+ }
+public:
+ TBlockSize(NBitStream::TBitStream* stream)
+ : LogCount(Parse(stream))
+ {}
+ TBlockSize()
+ : LogCount({0,0,0}) //windows are long
+ {}
+ const std::array<int,QMF_BANDS> LogCount;
+};
+
+
diff --git a/src/atrac/atrac1_bitalloc.cpp b/src/atrac/atrac1_bitalloc.cpp
new file mode 100644
index 0000000..7193b1a
--- /dev/null
+++ b/src/atrac/atrac1_bitalloc.cpp
@@ -0,0 +1,155 @@
+#include "atrac1_bitalloc.h"
+#include "atrac1_scale.h"
+#include "atrac1.h"
+#include <math.h>
+#include "../bitstream/bitstream.h"
+namespace NAtrac1 {
+
+using std::vector;
+using std::cerr;
+using std::endl;
+
+static const uint32_t FixedBitAllocTableLong[MAX_BFUS] = {
+ 6, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6,
+ 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 5,
+ 5, 4, 4, 3, 3, 3, 3, 2, 0, 0, 0, 0, 0, 0, 0
+};
+
+//returns 1 for tone-like, 0 - noise-like
+static double AnalizeSpread(const std::vector<TScaledBlock>& scaledBlocks) {
+ double s = 0.0;
+ for (int i = 0; i < scaledBlocks.size(); ++i) {
+ s += scaledBlocks[i].ScaleFactorIndex;
+ }
+ s /= scaledBlocks.size();
+ double sigma = 0.0;
+ double xxx = 0.0;
+ for (int i = 0; i < scaledBlocks.size(); ++i) {
+ xxx = (scaledBlocks[i].ScaleFactorIndex - s);
+ xxx *= xxx;
+ sigma += xxx;
+ }
+ sigma /= scaledBlocks.size();
+ sigma = sqrt(sigma);
+ if (sigma > 14.0)
+ sigma = 14.0;
+ return sigma/14.0;
+}
+
+vector<uint32_t> TAtrac1SimpleBitAlloc::CalcBitsAllocation(const std::vector<TScaledBlock>& scaledBlocks, const double spread, const double shift) {
+ vector<uint32_t> bitsPerEachBlock;
+ bitsPerEachBlock.resize(scaledBlocks.size());
+ for (int i = 0; i < scaledBlocks.size(); ++i) {
+ int tmp = spread * ( (double)scaledBlocks[i].ScaleFactorIndex/5.2) + (1.0 - spread) * (FixedBitAllocTableLong[i] + 1) - shift;
+ if (tmp > 16) {
+ bitsPerEachBlock[i] = 16;
+ } else if (tmp < 2) {
+ bitsPerEachBlock[i] = 0;
+ } else {
+ bitsPerEachBlock[i] = tmp;
+ }
+ }
+ return bitsPerEachBlock;
+}
+
+uint32_t TAtrac1SimpleBitAlloc::Write(const std::vector<TScaledBlock>& scaledBlocks) {
+ uint32_t bfuIdx = 7;
+ vector<uint32_t> bitsPerEachBlock;
+ double spread = AnalizeSpread(scaledBlocks);
+ bitsPerEachBlock.resize(scaledBlocks.size());
+ const uint32_t bitsAvaliablePerBfus = SoundUnitSize * 8 - BitsPerBfuAmountTabIdx - 32 - 2 - 3 - bitsPerEachBlock.size() * (BitsPerIDWL + BitsPerIDSF);
+ double maxShift = 4;
+ double minShift = -2;
+ double shift = 3.0;
+ const uint32_t maxBits = bitsAvaliablePerBfus;
+ const uint32_t minBits = bitsAvaliablePerBfus - 110;
+
+ for(;;) {
+ const vector<uint32_t>& tmpAlloc = CalcBitsAllocation(scaledBlocks, spread, shift);
+ uint32_t bitsUsed = 0;
+ for (int i = 0; i < tmpAlloc.size(); i++) {
+ bitsUsed += SpecsPerBlock[i] * tmpAlloc[i];
+ }
+
+ if (bitsUsed < minBits) {
+ maxShift = shift;
+ shift -= (shift - minShift) / 2;
+ } else if (bitsUsed > maxBits) {
+ minShift = shift;
+ shift += (maxShift - shift) / 2;
+ } else {
+ bitsPerEachBlock = tmpAlloc;
+ break;
+ }
+ }
+ WriteBitStream(bitsPerEachBlock, scaledBlocks, bfuIdx);
+ return BfuAmountTab[bfuIdx];
+}
+
+void TAtrac1BitStreamWriter::WriteBitStream(const vector<uint32_t>& bitsPerEachBlock, const std::vector<TScaledBlock>& scaledBlocks, uint32_t bfuAmountIdx) {
+ NBitStream::TBitStream bitStream;
+ int bitUsed = 0;
+ if (bfuAmountIdx >= (1 << BitsPerBfuAmountTabIdx)) {
+ cerr << "Wrong bfuAmountIdx (" << bfuAmountIdx << "), frame skiped" << endl;
+ return;
+ }
+ bitStream.Write(0x2, 2);
+ bitUsed+=2;
+
+ bitStream.Write(0x2, 2);
+ bitUsed+=2;
+
+ bitStream.Write(0x3, 2);
+ bitStream.Write(0, 2);
+ bitUsed+=4;
+
+ bitStream.Write(bfuAmountIdx, BitsPerBfuAmountTabIdx);
+ bitUsed += BitsPerBfuAmountTabIdx;
+
+ bitStream.Write(0, 2);
+ bitStream.Write(0, 3);
+ bitUsed+= 5;
+
+ for (const auto wordLength : bitsPerEachBlock) {
+ const auto tmp = wordLength ? (wordLength - 1) : 0;
+ bitStream.Write(tmp, 4);
+ bitUsed+=4;
+ }
+ for (int i = 0; i < bitsPerEachBlock.size(); ++i) {
+ bitStream.Write(scaledBlocks[i].ScaleFactorIndex, 6);
+ bitUsed+=6;
+ }
+ for (int i = 0; i < bitsPerEachBlock.size(); ++i) {
+ const auto wordLength = bitsPerEachBlock[i];
+ if (wordLength == 0 || wordLength == 1)
+ continue;
+
+ const double multiple = ((1 << (wordLength - 1)) - 1);
+ for (const double val : scaledBlocks[i].Values) {
+ const int tmp = val * multiple;
+ const int testwl = bitsPerEachBlock[i] ? (bitsPerEachBlock[i] - 1) : 0;
+ const int a = !!testwl + testwl;
+ if (a != wordLength) {
+ cerr << "wordlen error " << a << " " << wordLength << endl;
+ abort();
+ }
+ bitStream.Write(NBitStream::MakeSign(tmp, wordLength), wordLength);
+ bitUsed+=wordLength;
+ }
+ }
+
+ bitStream.Write(0x0, 8);
+ bitStream.Write(0x0, 8);
+
+ bitUsed+=16;
+ bitStream.Write(0x0, 8);
+
+ bitUsed+=8;
+ if (bitUsed > SoundUnitSize * 8) {
+ cerr << "ATRAC1 bitstream corrupted, used: " << bitUsed << " exp: " << SoundUnitSize * 8 << endl;
+ abort();
+ }
+ Container->WriteFrame(bitStream.GetBytes());
+}
+
+}
diff --git a/src/atrac/atrac1_bitalloc.h b/src/atrac/atrac1_bitalloc.h
new file mode 100644
index 0000000..01b0821
--- /dev/null
+++ b/src/atrac/atrac1_bitalloc.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "atrac1_scale.h"
+#include "../bitstream/bitstream.h"
+#include "../aea.h"
+#include "../atrac/atrac1.h"
+#include <vector>
+#include <cstdint>
+
+namespace NAtrac1 {
+
+class IAtrac1BitAlloc {
+public:
+ IAtrac1BitAlloc() {};
+ virtual ~IAtrac1BitAlloc() {};
+ virtual uint32_t Write(const std::vector<TScaledBlock>& scaledBlocks) = 0;
+};
+
+class TAtrac1BitStreamWriter : public TAtrac1Data {
+ TAea* Container;
+public:
+ explicit TAtrac1BitStreamWriter(TAea* container)
+ : Container(container)
+ {};
+ void WriteBitStream(const std::vector<uint32_t>& bitsPerEachBlock, const std::vector<TScaledBlock>& scaledBlocks, uint32_t bfuAmountIdx);
+};
+
+class TAtrac1SimpleBitAlloc : public TAtrac1BitStreamWriter, public IAtrac1BitAlloc {
+ std::vector<uint32_t> CalcBitsAllocation(const std::vector<TScaledBlock>& scaledBlocks, const double spread, const double shift);
+public:
+ using TAtrac1BitStreamWriter::TAtrac1BitStreamWriter;
+ ~TAtrac1SimpleBitAlloc() {};
+ uint32_t Write(const std::vector<TScaledBlock>& scaledBlocks) override;
+};
+
+}
diff --git a/src/atrac/atrac1_dequantiser.cpp b/src/atrac/atrac1_dequantiser.cpp
new file mode 100644
index 0000000..281a884
--- /dev/null
+++ b/src/atrac/atrac1_dequantiser.cpp
@@ -0,0 +1,48 @@
+#include "atrac1_dequantiser.h"
+
+
+namespace NAtrac1 {
+using namespace NBitStream;
+
+TAtrac1Dequantiser::TAtrac1Dequantiser() {
+}
+
+void TAtrac1Dequantiser::Dequant(TBitStream* stream, const TBlockSize& bs, double specs[512]) {
+ uint32_t wordLens[MAX_BFUS];
+ uint32_t idScaleFactors[MAX_BFUS];
+ const uint32_t numBFUs = BfuAmountTab[stream->Read(3)];
+ stream->Read(2);
+ stream->Read(3);
+
+ for (uint32_t i = 0; i < numBFUs; i++) {
+ wordLens[i] = stream->Read(4);
+ }
+
+ for (uint32_t i = 0; i < numBFUs; i++) {
+ idScaleFactors[i] = stream->Read(6);
+ }
+ for (uint32_t i = numBFUs; i < MAX_BFUS; i++) {
+ wordLens[i] = idScaleFactors[i] = 0;
+ }
+
+ for (uint32_t bandNum = 0; bandNum < QMF_BANDS; bandNum++) {
+ for (uint32_t bfuNum = BlocksPerBand[bandNum]; bfuNum < BlocksPerBand[bandNum + 1]; bfuNum++) {
+ const uint32_t numSpecs = SpecsPerBlock[bfuNum];
+ const uint32_t wordLen = !!wordLens[bfuNum] + wordLens[bfuNum];
+ const double scaleFactor = ScaleTable[idScaleFactors[bfuNum]];
+ const uint32_t startPos = bs.LogCount[bandNum] ? SpecsStartShort[bfuNum] : SpecsStartLong[bfuNum];
+ if (wordLen) {
+ double maxQuant = 1.0 / (double)((1 << (wordLen - 1)) - 1);
+ //cout << "BFU ("<< bfuNum << ") :" << "wordLen " << wordLen << " maxQuant " << maxQuant << " scaleFactor " << scaleFactor << " id " << idScaleFactors[bfuNum] << " num Specs " << numSpecs << " short: "<< (int)bs.LogCount[bandNum] << endl;
+ for (uint32_t i = 0; i < numSpecs; i++ ) {
+ specs[startPos + i] = scaleFactor * maxQuant * MakeSign(stream->Read(wordLen), wordLen);
+ }
+ } else {
+ memset(&specs[startPos], 0, numSpecs * sizeof(double));
+ }
+ }
+
+ }
+}
+
+}
diff --git a/src/atrac/atrac1_dequantiser.h b/src/atrac/atrac1_dequantiser.h
new file mode 100644
index 0000000..112fc8b
--- /dev/null
+++ b/src/atrac/atrac1_dequantiser.h
@@ -0,0 +1,12 @@
+#pragma once
+#include "atrac1.h"
+
+
+namespace NAtrac1 {
+
+class TAtrac1Dequantiser : public TAtrac1Data {
+public:
+ TAtrac1Dequantiser();
+ void Dequant(NBitStream::TBitStream* stream, const TBlockSize& bs, double specs[512]);
+};
+}
diff --git a/src/atrac/atrac1_qmf.h b/src/atrac/atrac1_qmf.h
new file mode 100644
index 0000000..fa2dcd9
--- /dev/null
+++ b/src/atrac/atrac1_qmf.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "../qmf/qmf.h"
+
+template<class TIn>
+class Atrac1SplitFilterBank {
+ const static int nInSamples = 512;
+ TQmf<TIn, nInSamples> Qmf1;
+ TQmf<TIn, nInSamples / 2> Qmf2;
+ std::vector<double> MidLowTmp;
+ std::vector<double> DelayBuf;
+public:
+ Atrac1SplitFilterBank() {
+ MidLowTmp.resize(512);
+ DelayBuf.resize(23 + 512);
+ }
+ void Split(TIn* pcm, double* low, double* mid, double* hi) {
+ memcpy(&DelayBuf[0], &DelayBuf[256], sizeof(double) * 23);
+ Qmf1.Split(pcm, &MidLowTmp[0], &DelayBuf[23]);
+ Qmf2.Split(&MidLowTmp[0], low, mid);
+ memcpy(hi, &DelayBuf[0], sizeof(double) * 256);
+
+ }
+};
+template<class TOut>
+class Atrac1SynthesisFilterBank {
+ const static int nInSamples = 512;
+ TQmf<TOut, nInSamples> Qmf1;
+ TQmf<TOut, nInSamples / 2> Qmf2;
+ std::vector<double> MidLowTmp;
+ std::vector<double> DelayBuf;
+public:
+ Atrac1SynthesisFilterBank() {
+ MidLowTmp.resize(512);
+ DelayBuf.resize(23 + 512);
+ }
+ void Synthesis(TOut* pcm, double* low, double* mid, double* hi) {
+ memcpy(&DelayBuf[0], &DelayBuf[256], sizeof(double) * 23);
+ memcpy(&DelayBuf[23], hi, sizeof(double) * 256);
+ Qmf2.Merge(&MidLowTmp[0], &low[0], &mid[0]);
+ Qmf1.Merge(&pcm[0], &MidLowTmp[0], &DelayBuf[0]);
+ }
+};
+
+
diff --git a/src/atrac/atrac1_scale.cpp b/src/atrac/atrac1_scale.cpp
new file mode 100644
index 0000000..461da3a
--- /dev/null
+++ b/src/atrac/atrac1_scale.cpp
@@ -0,0 +1,55 @@
+#include "atrac1_scale.h"
+#include <cmath>
+#include <iostream>
+#include <algorithm>
+namespace NAtrac1 {
+using std::vector;
+using std::map;
+
+using namespace std;
+map<uint32_t, uint8_t> TScaler::ScaleIndex;
+static const uint32_t MAX_SCALE = 65536;
+
+static bool absComp(double a, double b) {
+ return abs(a) < abs(b);
+}
+
+TScaler::TScaler() {
+ if (ScaleIndex.empty()) {
+ for (int i = 0; i < 64; i++) {
+ ScaleIndex[ScaleTable[i] * 256] = i;
+ }
+ }
+}
+
+vector<TScaledBlock> TScaler::Scale(const vector<double>& specs) {
+ vector<TScaledBlock> scaledBlocks;
+ for (uint8_t bandNum = 0; bandNum < QMF_BANDS; ++bandNum) {
+ for (uint8_t blockNum = BlocksPerBand[bandNum]; blockNum < BlocksPerBand[bandNum + 1]; ++blockNum) {
+ const uint16_t specNumStart = SpecsStartLong[blockNum];
+ const uint16_t specNumEnd = SpecsStartLong[blockNum] + SpecsPerBlock[blockNum];
+ double maxAbsSpec = 0;
+ for (uint16_t curSpec = specNumStart; curSpec < specNumEnd; ++curSpec) {
+ const double absSpec = abs(specs[curSpec]);
+ if (absSpec > maxAbsSpec) {
+ if (absSpec > MAX_SCALE) {
+ cerr << "got " << absSpec << " value - overflow" << endl;
+ maxAbsSpec = MAX_SCALE;
+ } else {
+ maxAbsSpec = absSpec;
+ }
+ }
+ }
+ const map<uint32_t, uint8_t>::const_iterator scaleIter = ScaleIndex.lower_bound(maxAbsSpec * 256);
+ const double scaleFactor = scaleIter->first / 256.0;
+ const uint8_t scaleFactorIndex = scaleIter->second;
+ scaledBlocks.push_back(TScaledBlock(scaleFactorIndex));
+ for (uint16_t specNum = specNumStart; specNum < specNumEnd; ++specNum) {
+ const double scaledValue = specs[specNum] / scaleFactor;
+ scaledBlocks.back().Values.push_back(scaledValue);
+ }
+ }
+ }
+ return scaledBlocks;
+}
+}
diff --git a/src/atrac/atrac1_scale.h b/src/atrac/atrac1_scale.h
new file mode 100644
index 0000000..1b41172
--- /dev/null
+++ b/src/atrac/atrac1_scale.h
@@ -0,0 +1,22 @@
+#pragma once
+#include <vector>
+#include <map>
+#include <cstdint>
+
+#include "atrac1.h"
+
+namespace NAtrac1 {
+
+struct TScaledBlock {
+ TScaledBlock(uint8_t sfi) : ScaleFactorIndex(sfi) {}
+ const uint8_t ScaleFactorIndex = 0;
+ std::vector<double> Values;
+};
+
+class TScaler : public TAtrac1Data {
+ static std::map<uint32_t, uint8_t>ScaleIndex;
+public:
+ TScaler();
+ std::vector<TScaledBlock> Scale(const std::vector<double>& specs);
+};
+}
diff --git a/src/atracdenc.cpp b/src/atracdenc.cpp
new file mode 100644
index 0000000..3ec2026
--- /dev/null
+++ b/src/atracdenc.cpp
@@ -0,0 +1,213 @@
+#include <vector>
+
+#include "atracdenc.h"
+#include "bitstream/bitstream.h"
+#include "atrac/atrac1.h"
+#include "atrac/atrac1_dequantiser.h"
+#include "atrac/atrac1_qmf.h"
+#include "atrac/atrac1_bitalloc.h"
+
+namespace NAtracDEnc {
+using namespace std;
+using namespace NBitStream;
+using namespace NAtrac1;
+TAtrac1Processor::TAtrac1Processor(TAeaPtr&& aea, bool mono)
+ : MixChannel(mono)
+ , Aea(std::move(aea))
+{
+}
+
+static void vector_fmul_window(double *dst, const double *src0,
+ const double *src1, const double *win, int len)
+{
+ int i, j;
+
+ dst += len;
+ win += len;
+ src0 += len;
+
+ for (i = -len, j = len - 1; i < 0; i++, j--) {
+ double s0 = src0[i];
+ double s1 = src1[j];
+ double wi = win[i];
+ double wj = win[j];
+ dst[i] = s0 * wj - s1 * wi;
+ dst[j] = s0 * wi + s1 * wj;
+ }
+}
+
+vector<double> mdct(double* x, int N) {
+ vector<double> res;
+ for (int k = 0; k < N; k++) {
+ double sum = 0;
+ for (int n = 0; n < 2 * N; n++) {
+ sum += x[n]* cos((M_PI/N) * ((double)n + 0.5 + N/2) * ((double)k + 0.5));
+ }
+ res.push_back(sum);
+ }
+ return res;
+}
+
+vector<double> midct(double* x, int N) {
+ vector<double> res;
+ for (int n = 0; n < 2 * N; n++) {
+ double sum = 0;
+ for (int k = 0; k < N; k++) {
+ sum += (x[k] * cos((M_PI/N) * ((double)n + 0.5 + N/2) * ((double)k + 0.5)));
+ }
+ sum = sum / N;
+ res.push_back(sum);
+ }
+ return res;
+}
+
+
+void TAtrac1Processor::Mdct(double Specs[512], double* low, double* mid, double* hi) {
+ uint32_t pos = 0;
+ for (uint32_t band = 0; band < QMF_BANDS; band++) {
+ double* srcBuf = (band == 0) ? low : (band == 1) ? mid : hi;
+ uint32_t bufSz = (band == 2) ? 256 : 128;
+ uint32_t xxx = (band == 2) ? 112 : 48;
+ double tmp[512];
+ //if (band == 2) {
+ // for (uint32_t i = 0; i < bufSz; i++) {
+ // srcBuf[i] = srcBuf[i] * 2;
+ // }
+ //}
+ for (uint32_t i = 0; i < 512; i++)
+ tmp[i] = 0;
+ memcpy(&tmp[xxx], &srcBuf[bufSz], 32 * sizeof(double));
+ for (int i = 0; i < 32; i++) {
+ srcBuf[bufSz + i] = TAtrac1Data::SineWindow[i] * srcBuf[bufSz - 32 + i];
+ srcBuf[bufSz - 32 + i] = TAtrac1Data::SineWindow[31 - i] * srcBuf[bufSz - 32 + i];
+ }
+ memcpy(&tmp[xxx+32], &srcBuf[0], bufSz * sizeof(double));
+ const vector<double>& sp = mdct(&tmp[0], bufSz);
+ if (band) {
+ for (uint32_t j = 0; j < sp.size()/2; j++) {
+ Specs[pos+j] = sp[bufSz - 1 -j];
+ Specs[pos + bufSz - 1 -j] = sp[j];
+ }
+ } else {
+ for (uint32_t i = 0; i < sp.size(); i++) {
+ Specs[pos + i] = sp[i];
+ }
+ }
+ /*
+ if (band == 2) {
+ for (int i = 0; i < bufSz * 2; i++) {
+ cout << "tmp " << i << " " << tmp[i] << endl;
+ }
+ for (int i = 0; i < sp.size(); ++i ) {
+ cout << "band2 " << i << " " << Specs[pos + i] << " " << endl;
+ }
+ }
+ */
+ pos += bufSz;
+ }
+}
+void TAtrac1Processor::IMdct(double Specs[512], const TBlockSize& mode, double* low, double* mid, double* hi) {
+ uint32_t pos = 0;
+ for (uint32_t band = 0; band < QMF_BANDS; band++) {
+ const uint32_t numMdctBlocks = 1 << mode.LogCount[band];
+ const uint32_t blockSize = (numMdctBlocks == 1) ? ((band == 2) ? 256 : 128) : 32;
+ //if (blockSize == 32) {
+ // cout << "SHORT: " << numMdctBlocks << " band: " << band << endl;
+ //}
+ uint32_t start = 0;
+
+ double* dstBuf = (band == 0) ? low : (band == 1) ? mid : hi;
+
+
+ vector<double> invBuf;
+ invBuf.resize(512);
+ double* prevBuf = &dstBuf[blockSize*2 - 16];
+ static uint32_t fc;
+ for (uint32_t block = 0; block < numMdctBlocks; block++) {
+
+ // cout << "fc counter: " << fc++ << endl;
+ if (band) {
+ for (uint32_t j = 0; j < blockSize/2; j++) {
+ double tmp = Specs[pos+j];
+ Specs[pos+j] = Specs[pos + blockSize - 1 -j];
+ Specs[pos + blockSize - 1 -j] = tmp;
+ }
+ }
+ vector<double> inv = midct(&Specs[pos], blockSize);
+ for (int i = 0; i < (inv.size()/2); i++) {
+
+ invBuf[start+i] = ((blockSize == 32) ? 32.0 : 128.0) * inv[i + inv.size()/4];
+ }
+
+ vector_fmul_window(dstBuf + start, prevBuf, &invBuf[start], &TAtrac1Data::SineWindow[0], 16);
+
+ prevBuf = &invBuf[start+16];
+ start += blockSize;
+ pos += blockSize;
+ }
+ if (numMdctBlocks == 1)
+ memcpy(dstBuf + 32, &invBuf[16], ((band == 2) ? 240 : 112) * sizeof(double));
+
+ for (int j = 0; j < 16; j++) {
+ dstBuf[blockSize*2 - 16 + j] = invBuf[blockSize - 16 + j];
+ }
+ }
+}
+TPCMEngine<double>::TProcessLambda TAtrac1Processor::GetDecodeLambda() {
+ return [this](vector<double>* data) {
+ double sum[512];
+ const uint32_t srcChannels = Aea->GetChannelNum();
+ for (uint32_t channel = 0; channel < srcChannels; channel++) {
+ std::unique_ptr<TAea::TFrame> frame(Aea->ReadFrame());
+ TBitStream bitstream(&(*frame.get())[0], frame->size());
+ // cout << "frame size: " << bitstream.GetBufSize() << endl;
+
+ TBlockSize mode(&bitstream);
+ TAtrac1Dequantiser dequantiser;
+ vector<double> specs;
+ specs.resize(512);;
+ dequantiser.Dequant(&bitstream, mode, &specs[0]);
+
+ IMdct(&specs[0], mode, &PcmBufLow[channel][0], &PcmBufMid[channel][0], &PcmBufHi[channel][0]);
+ SynthesisFilterBank[channel].Synthesis(&sum[0], &PcmBufLow[channel][0], &PcmBufMid[channel][0], &PcmBufHi[channel][0]);
+ const int numChannel = data[0].size();
+ for (int i = 0; i < NumSamples; ++i) {
+ data[i][srcChannels - 1-channel] = sum[i];
+ }
+ }
+
+ };
+}
+
+
+TPCMEngine<double>::TProcessLambda TAtrac1Processor::GetEncodeLambda() {
+ const uint32_t srcChannels = Aea->GetChannelNum();
+ //TODO: should not be here
+ //vector<char> dummy;
+ //dummy.resize(212 * srcChannels);
+ //Aea->WriteFrame(dummy);
+ //cout << "Encode, channels: " << srcChannels << endl;
+ vector<IAtrac1BitAlloc*> bitAlloc;
+ for (int i = 0; i < srcChannels; i++)
+ bitAlloc.push_back(new TAtrac1SimpleBitAlloc(Aea.get()));
+ //bitAlloc.push_back(new TAtrac1PsyBitAlloc(Aea.get()));
+
+ return [this, srcChannels, bitAlloc](vector<double>* data) {
+ for (uint32_t channel = 0; channel < srcChannels; channel++) {
+ double src[NumSamples];
+ double sum[512];
+ vector<double> specs;
+ specs.resize(512);
+ for (int i = 0; i < NumSamples; ++i) {
+ src[i] = data[i][channel] / 256.0;
+ }
+ SplitFilterBank[channel].Split(&src[0], &PcmBufLow[channel][0], &PcmBufMid[channel][0], &PcmBufHi[channel][0]);
+
+ Mdct(&specs[0], &PcmBufLow[channel][0], &PcmBufMid[channel][0], &PcmBufHi[channel][0]);
+ bitAlloc[channel]->Write(Scaler.Scale(specs));
+ }
+ };
+}
+
+
+}
diff --git a/src/atracdenc.h b/src/atracdenc.h
new file mode 100644
index 0000000..2fb114e
--- /dev/null
+++ b/src/atracdenc.h
@@ -0,0 +1,35 @@
+#pragma once
+#include "pcmengin.h"
+#include "aea.h"
+#include "atrac/atrac1.h"
+#include "atrac/atrac1_qmf.h"
+#include "atrac/atrac1_scale.h"
+
+namespace NAtracDEnc {
+
+enum EMode {
+ E_ENCODE = 1,
+ E_DECODE = 2
+};
+
+class TAtrac1Processor : public TAtrac1Data {
+ const bool MixChannel;
+ TAeaPtr Aea;
+
+ double PcmBufLow[2][256 + 16];
+ double PcmBufMid[2][256 + 16];
+ double PcmBufHi[2][512 + 16];
+
+ Atrac1SynthesisFilterBank<double> SynthesisFilterBank[2];
+ Atrac1SplitFilterBank<double> SplitFilterBank[2];
+ void IMdct(double specs[512], const TBlockSize& mode, double* low, double* mid, double* hi);
+ void Mdct(double specs[512], double* low, double* mid, double* hi);
+ NAtrac1::TScaler Scaler;
+
+public:
+ TAtrac1Processor(TAeaPtr&& aea, bool mono = false);
+ TPCMEngine<double>::TProcessLambda GetDecodeLambda();
+
+ TPCMEngine<double>::TProcessLambda GetEncodeLambda();
+};
+}
diff --git a/src/bitstream/Makefile b/src/bitstream/Makefile
new file mode 100644
index 0000000..f87c233
--- /dev/null
+++ b/src/bitstream/Makefile
@@ -0,0 +1,6 @@
+test:
+ g++ -std=c++11 bitstream_ut.cpp bitstream.cpp -I ../../3rd/gtest-1.7.0/include/ ../../3rd/gtest-1.7.0/src/gtest-all.o ../../3rd/gtest-1.7.0/src/gtest_main.o -o bitstream_ut
+ ./bitstream_ut
+
+clean:
+ rm ./bitstream_ut
diff --git a/src/bitstream/bitstream.cpp b/src/bitstream/bitstream.cpp
new file mode 100644
index 0000000..7de4873
--- /dev/null
+++ b/src/bitstream/bitstream.cpp
@@ -0,0 +1,55 @@
+#include "bitstream.h"
+namespace NBitStream {
+
+union TMix {
+ unsigned long long ull = 0;
+ uint8_t bytes[8];
+};
+
+TBitStream::TBitStream(const char* buf, int size)
+ : Buf(buf, buf+size)
+{}
+TBitStream::TBitStream()
+{}
+
+void TBitStream::Write(unsigned long long val, int n) {
+ if (n > 23 || n < 0)
+ abort();
+ const int bitsLeft = Buf.size() * 8 - BitsUsed;
+ const int bitsReq = n - bitsLeft;
+ const int bytesPos = BitsUsed / 8;
+ const int overlap = BitsUsed % 8;
+
+ if (bitsReq > 0) {
+ Buf.resize(Buf.size() + (bitsReq / 8 + (overlap ? 2 : 1 )), 0);
+ }
+ TMix t;
+ t.ull = val;
+ t.ull = (t.ull << (64 - n) >> overlap);
+
+ for (int i = 0; i < n/8 + (overlap ? 2 : 1); ++i) {
+ Buf[bytesPos+i] |= t.bytes[7-i];
+ }
+
+ BitsUsed += n;
+}
+unsigned long long TBitStream::Read(int n) {
+ if (n >23 || n < 0)
+ abort();
+ const int bytesPos = ReadPos / 8;
+ const int overlap = ReadPos % 8;
+ TMix t;
+ for (int i = 0; i < n/8 + (overlap ? 2 : 1); ++i) {
+ t.bytes[7-i] = (uint8_t)Buf[bytesPos+i];
+ }
+
+ t.ull = (t.ull << overlap >> (64 - n));
+ ReadPos += n;
+ return t.ull;
+}
+
+unsigned long long TBitStream::GetSizeInBits() const {
+ return BitsUsed;
+}
+
+}
diff --git a/src/bitstream/bitstream.h b/src/bitstream/bitstream.h
new file mode 100644
index 0000000..eb813cc
--- /dev/null
+++ b/src/bitstream/bitstream.h
@@ -0,0 +1,29 @@
+#pragma once
+#include <vector>
+
+#include <iostream>
+
+namespace NBitStream {
+
+static inline int MakeSign(int val, unsigned bits) {
+ unsigned shift = 8 * sizeof(int) - bits;
+ union { unsigned u; int s; } v = { (unsigned) val << shift };
+ return v.s >> shift;
+}
+
+class TBitStream {
+ std::vector<char> Buf;
+ int BitsUsed = 0;
+ int ReadPos = 0;
+ public:
+ TBitStream(const char* buf, int size);
+ TBitStream();
+ void Write(unsigned long long val, int n);
+ unsigned long long Read(int n);
+ unsigned long long GetSizeInBits() const;
+ uint32_t GetBufSize() const { return Buf.size(); };
+ const std::vector<char>& GetBytes() const {
+ return Buf;
+ }
+};
+} //NBitStream
diff --git a/src/bitstream/bitstream_ut.cpp b/src/bitstream/bitstream_ut.cpp
new file mode 100644
index 0000000..109570b
--- /dev/null
+++ b/src/bitstream/bitstream_ut.cpp
@@ -0,0 +1,55 @@
+#include "bitstream.h"
+#include <gtest/gtest.h>
+
+using namespace NBitStream;
+
+TEST(TBitStream, DefaultConstructor) {
+ TBitStream bs;
+ EXPECT_EQ(0u, bs.GetSizeInBits());
+}
+
+TEST(TBitStream, SimpleWriteRead) {
+ TBitStream bs;
+ bs.Write(5, 3);
+ EXPECT_EQ(3, bs.GetSizeInBits());
+ EXPECT_EQ(5, bs.Read(3));
+}
+
+TEST(TBisStream, OverlapWriteRead) {
+ TBitStream bs;
+ bs.Write(101, 22);
+ EXPECT_EQ(22, bs.GetSizeInBits());
+
+ bs.Write(212, 22);
+ EXPECT_EQ(44, bs.GetSizeInBits());
+
+ bs.Write(323, 22);
+ EXPECT_EQ(66, bs.GetSizeInBits());
+
+ EXPECT_EQ(101, bs.Read(22));
+ EXPECT_EQ(212, bs.Read(22));
+ EXPECT_EQ(323, bs.Read(22));
+}
+TEST(TBisStream, OverlapWriteRead2) {
+ TBitStream bs;
+ bs.Write(2, 2);
+ bs.Write(7, 4);
+ bs.Write(10003, 16);
+
+ EXPECT_EQ(2, bs.Read(2));
+ EXPECT_EQ(7, bs.Read(4));
+ EXPECT_EQ(10003, bs.Read(16));
+}
+
+TEST(TBisStream, SignWriteRead) {
+ TBitStream bs;
+ bs.Write(MakeSign(-2, 3), 3);
+ bs.Write(MakeSign(-1, 3), 3);
+ bs.Write(MakeSign(1, 2), 2);
+ bs.Write(MakeSign(-7, 4), 4);
+ EXPECT_EQ(-2, MakeSign(bs.Read(3), 3));
+ EXPECT_EQ(-1, MakeSign(bs.Read(3), 3));
+ EXPECT_EQ(1, MakeSign(bs.Read(2), 2));
+ EXPECT_EQ(-7, MakeSign(bs.Read(4), 4));
+}
+
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..71c8fb2
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,125 @@
+#include <iostream>
+#include <string>
+
+#include <getopt.h>
+
+#include "pcmengin.h"
+#include "wav.h"
+#include "aea.h"
+#include "atracdenc.h"
+
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::string;
+using std::unique_ptr;
+using std::move;
+
+using namespace NAtracDEnc;
+
+
+static void printUsage(const char* myName) {
+ cout << "\tusage: " << myName << " <-e|-d> <-i input> <-o output>\n" << endl;
+ cout << "-e encode mode (PCM -> ATRAC), -i wav file, -o aea file" << endl;
+ cout << "-d decode mode (ATRAC -> PCM), -i aea file, -o wav file" << endl;
+}
+
+int main(int argc, char* const* argv) {
+ const char* myName = argv[0];
+ static struct option longopts[] = {
+ { "encode", no_argument, NULL, 'e' },
+ { "decode", no_argument, NULL, 'd' },
+ { "numbfus", required_argument, NULL, 1},
+ { "mono", no_argument, NULL, 'm'},
+ { NULL, 0, NULL, 0}
+ };
+
+ int ch = 0;
+ string inFile;
+ string outFile;
+ uint32_t mode = 0;
+ bool mono = false;
+ while ((ch = getopt_long(argc, argv, "edi:o:m", longopts, NULL)) != -1) {
+ switch (ch) {
+ case 'e':
+ cout << "encode " << endl;
+ mode |= E_ENCODE;
+ break;
+ case 'd':
+ cout << "decode" << endl;
+
+ mode |= E_DECODE;
+ break;
+ case 'i':
+ inFile = optarg;
+ break;
+ case 'o':
+ outFile = optarg;
+ cout << "out: " << outFile<< endl;
+ break;
+ case 'm':
+ mono = true;
+ break;
+ case 1:
+ cout << "numbfus" << optarg << endl;
+ break;
+
+ default:
+ printUsage(myName);
+ }
+
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (inFile.empty()) {
+ cout << "No input file" << endl;
+ return 1;
+ }
+ if (outFile.empty()) {
+ cout << "No out file" << endl;
+ return 1;
+ }
+
+ TWavPtr wavIO;
+ TAeaPtr aeaIO;
+ TPCMEngine<double>* pcmEngine = nullptr;
+ uint64_t totalSamples = 0;
+ if (mode == E_ENCODE) {
+ wavIO = TWavPtr(new TWav(inFile));
+ const TWavHeader& wavHeader = wavIO->GetWavHeader();
+ const int numChannels = wavHeader.NumOfChan;
+ aeaIO = TAeaPtr(new TAea(outFile, "test", numChannels));
+ pcmEngine = new TPCMEngine<double>(4096, numChannels, TPCMEngine<double>::TReaderPtr(wavIO->GetPCMReader<double>()));
+ totalSamples = wavHeader.ChunkSize;
+ cout << "Input file: " << inFile << "Channels: " << numChannels << "SampleRate: " << wavHeader.SamplesPerSec << "TotalSamples: " << totalSamples << endl;
+ } else if (mode == E_DECODE) {
+ aeaIO = TAeaPtr(new TAea(inFile));
+ totalSamples = 4 * aeaIO->GetLengthInSamples();
+ uint32_t length = aeaIO->GetLengthInSamples();
+ cout << "Name: " << aeaIO->GetName() << "\n Channels: " << aeaIO->GetChannelNum() << "\n Length: " << length << endl;
+ wavIO = TWavPtr(new TWav(outFile, TWav::CreateHeader(aeaIO->GetChannelNum(), length)));
+ pcmEngine = new TPCMEngine<double>(4096, aeaIO->GetChannelNum(), TPCMEngine<double>::TWriterPtr(wavIO->GetPCMWriter<double>()));
+ } else {
+ cout << "Processing mode was not specified" << endl;
+ return 1;
+ }
+
+ TAtrac1Processor atrac1Processor(move(aeaIO), mono);
+ auto atracLambda = (mode == E_DECODE) ? atrac1Processor.GetDecodeLambda() : atrac1Processor.GetEncodeLambda();
+ uint64_t processed = 0;
+ try {
+ while (totalSamples/4 > (processed = pcmEngine->ApplyProcess(512, atracLambda)))
+ {
+ cout << "pos: " << processed << " " << totalSamples/4 << endl;
+ }
+ }
+ catch (TAeaIOError err) {
+ cerr << "Aea IO fatal error: " << err.what() << endl;
+ exit(1);
+ }
+ catch (TWavIOError err) {
+ cerr << "Wav IO fatal error: " << err.what() << endl;
+ exit(1);
+ }
+}
diff --git a/src/pcmengin.h b/src/pcmengin.h
new file mode 100644
index 0000000..8ea58a6
--- /dev/null
+++ b/src/pcmengin.h
@@ -0,0 +1,106 @@
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <exception>
+#include <functional>
+
+#include <assert.h>
+
+class TPCMBufferTooSmall : public std::exception {
+ virtual const char* what() const throw() {
+ return "PCM buffer too small";
+ }
+};
+
+class TWrongReadBuffer : public std::exception {
+ virtual const char* what() const throw() {
+ return "PCM buffer too small";
+ }
+};
+
+
+class TEndOfRead : public std::exception {
+ virtual const char* what() const throw() {
+ return "End of reader";
+ }
+};
+
+template <class T>
+class IPCMWriter {
+ public:
+ virtual void Write(const std::vector<std::vector<T>>& data , const uint32_t size) const = 0;
+ IPCMWriter() {};
+ virtual ~IPCMWriter() {};
+};
+
+template <class T>
+class IPCMReader {
+ public:
+ virtual void Read(std::vector<std::vector<T>>& data , const uint32_t size) const = 0;
+ IPCMReader() {};
+ virtual ~IPCMReader() {};
+};
+
+
+
+template<class T>
+class TPCMEngine {
+ std::vector<std::vector<T>> Buffers;
+public:
+ typedef std::unique_ptr<IPCMWriter<T>> TWriterPtr;
+ typedef std::unique_ptr<IPCMReader<T>> TReaderPtr;
+private:
+ TWriterPtr Writer;
+ TReaderPtr Reader;
+ uint64_t Processed = 0;
+public:
+ TPCMEngine(const int32_t bufSize, const int32_t numChannels) {
+ InitBuffers(bufSize, numChannels);
+ }
+ TPCMEngine(const int32_t bufSize, const int32_t numChannels, TWriterPtr&& writer)
+ : Writer(std::move(writer)) {
+ InitBuffers(bufSize, numChannels);
+ }
+ TPCMEngine(const int32_t bufSize, const int32_t numChannels, TReaderPtr&& reader)
+ : Reader(std::move(reader)) {
+ InitBuffers(bufSize, numChannels);
+ }
+ TPCMEngine(const int32_t bufSize, const int32_t numChannels, TWriterPtr&& writer, TReaderPtr&& reader)
+ : Writer(std::move(writer))
+ , Reader(std::move(reader)) {
+ InitBuffers(bufSize, numChannels);
+ }
+ typedef std::function<void(std::vector<T>* data)> TProcessLambda;
+
+ uint64_t ApplyProcess(int step, TProcessLambda lambda) {
+ if (step > Buffers.size()) {
+ throw TPCMBufferTooSmall();
+ }
+ if (Reader) {
+ const uint32_t sizeToRead = Buffers.size();
+ Reader->Read(Buffers, sizeToRead);
+ }
+ int32_t lastPos = 0;
+ for (int i = 0; i + step <= Buffers.size(); i+=step) {
+ lambda(&Buffers[i]);
+ lastPos = i + step;
+ }
+ assert(lastPos == Buffers.size());
+ if (Writer) {
+ Writer->Write(Buffers, lastPos);
+ }
+ Processed += lastPos;
+ return Processed;
+
+ }
+
+
+private:
+ void InitBuffers(const int32_t bufSize, const int32_t numChannels) {
+ Buffers.resize(bufSize);
+ for (std::vector<T>& channel : Buffers) {
+ channel.resize(numChannels);
+ }
+ }
+};
diff --git a/src/qmf/qmf.cpp b/src/qmf/qmf.cpp
new file mode 100644
index 0000000..856054a
--- /dev/null
+++ b/src/qmf/qmf.cpp
@@ -0,0 +1 @@
+#include "qmf.h"
diff --git a/src/qmf/qmf.h b/src/qmf/qmf.h
new file mode 100644
index 0000000..c935baa
--- /dev/null
+++ b/src/qmf/qmf.h
@@ -0,0 +1,81 @@
+#pragma once
+
+
+template<class TPCM, int nIn>
+class TQmf {
+ static const float TapHalf[24];
+ double QmfWindow[48];
+ TPCM PcmBuffer[nIn + 46];
+ double PcmBufferMerge[nIn + 46];
+ double DelayBuff[46];
+public:
+ TQmf() {
+ const int sz = sizeof(QmfWindow)/sizeof(QmfWindow[0]);
+
+ for (int i = 0 ; i < sz/2; i++) {
+ QmfWindow[i] = QmfWindow[ sz - 1 - i] = TapHalf[i] * 2.0;
+ }
+ for (int i = 0; i < sizeof(PcmBuffer)/sizeof(PcmBuffer[0]); i++) {
+ PcmBuffer[i] = 0;
+ PcmBufferMerge[i] = 0;
+ }
+ }
+
+ void Split(TPCM* in, double* lower, double* upper) {
+ double temp;
+ static double last;
+ for (int i = 0; i < 46; i++)
+ PcmBuffer[i] = PcmBuffer[nIn + i];
+
+ for (int i = 0; i < nIn; i++)
+ PcmBuffer[46+i] = in[i];
+
+ for (int j = 0; j<nIn; j+=2) {
+ lower[j/2] = upper[j/2] = 0.0;
+ for (int i = 0; i < 24; i++) {
+ lower[j/2] += QmfWindow[2*i] * PcmBuffer[48-1+j-(2*i)];
+ upper[j/2] += QmfWindow[(2*i)+1] * PcmBuffer[48-1+j-(2*i)-1];
+ }
+ temp = upper[j/2];
+ upper[j/2] = lower[j/2] - upper[j/2];
+ lower[j/2] += temp;
+ }
+ }
+
+ void Merge(TPCM* out, double* lower, double* upper) {
+ memcpy(&PcmBufferMerge[0], &DelayBuff[0], 46*sizeof(double));
+ double* newPart = &PcmBufferMerge[46];
+ for (int i = 0; i < nIn; i+=4) {
+ newPart[i+0] = lower[i/2] + upper[i/2];
+ newPart[i+1] = lower[i/2] - upper[i/2];
+ newPart[i+2] = lower[i/2 + 1] + upper[i/2 + 1];
+ newPart[i+3] = lower[i/2 + 1] - upper[i/2 + 1];
+ }
+
+ double* winP = &PcmBufferMerge[0];
+ for (int j = nIn/2; j != 0; j--) {
+ double s1 = 0;
+ double s2 = 0;
+ for (int i = 0; i < 48; i+=2) {
+ s1 += winP[i] * QmfWindow[i];
+ s2 += winP[i+1] * QmfWindow[i+1];
+ }
+ out[0] = s2;
+ out[1] = s1;
+ winP += 2;
+ out += 2;
+ }
+ memcpy(&DelayBuff[0], &PcmBufferMerge[nIn], 46*sizeof(double));
+ }
+};
+
+template<class TPCM, int nIn>
+const float TQmf<TPCM, nIn>::TapHalf[24] = {
+ -0.00001461907, -0.00009205479, -0.000056157569, 0.00030117269,
+ 0.0002422519, -0.00085293897, -0.0005205574, 0.0020340169,
+ 0.00078333891, -0.0042153862, -0.00075614988, 0.0078402944,
+ -0.000061169922, -0.01344162, 0.0024626821, 0.021736089,
+ -0.007801671, -0.034090221, 0.01880949, 0.054326009,
+ -0.043596379, -0.099384367, 0.13207909, 0.46424159
+};
+
diff --git a/src/wav.cpp b/src/wav.cpp
new file mode 100644
index 0000000..755a7eb
--- /dev/null
+++ b/src/wav.cpp
@@ -0,0 +1,85 @@
+#include <functional>
+#include <memory>
+#include <cerrno>
+
+#include <sys/stat.h>
+
+#include "wav.h"
+#include "pcmengin.h"
+
+
+using namespace std;
+
+TWavHeader TWav::CreateHeader(int channels, uint32_t length) {
+ TWavHeader header;
+ uint32_t dataLen = length * 2 * channels;
+ const char dataString[] = "data";
+ header.RIFF[0] = 0x52;
+ header.RIFF[1] = 0x49;
+ header.RIFF[2] = 0x46;
+ header.RIFF[3] = 0x46;
+ header.ChunkSize = dataLen + sizeof(TWavHeader) - 8;;
+ header.WAVE[0] = 0x57;
+ header.WAVE[1] = 0x41;
+ header.WAVE[2] = 0x56;
+ header.WAVE[3] = 0x45;
+ header.fmt[0] = 0x66;
+ header.fmt[1] = 0x6d;
+ header.fmt[2] = 0x74;
+ header.fmt[3] = 0x20;
+ header.Subchunk1Size = 16;
+ header.AudioFormat = 1;
+ header.NumOfChan = channels;
+ header.SamplesPerSec = 44100; //samplerate;
+ header.bytesPerSec = 44100 * ((16 * channels) / 8);
+ header.blockAlign = 2 * channels;
+ header.bitsPerSample = 16;
+ strncpy(&header.Subchunk2ID[0], dataString, sizeof(dataString));
+ header.Subchunk2Size = dataLen;
+
+ return header;
+}
+
+TWav::TMeta TWav::ReadWavHeader(const string& filename) {
+ FILE* fd = fopen(filename.c_str(), "rb");
+ if (!fd)
+ throw TWavIOError("Can't open file to read", errno);
+ TWavHeader headerBuf;
+ if (fread(&headerBuf, sizeof(TWavHeader), 1, fd) != 1) {
+ const int errnum = errno;
+ fclose(fd);
+ throw TWavIOError("Can't read WAV header", errnum);
+ }
+ return {fd, headerBuf};
+}
+
+TWav::TMeta TWav::CreateFileAndHeader(const string& filename, const TWavHeader& header, bool overwrite) {
+ (void)overwrite;
+
+ FILE* fd = fopen(filename.c_str(), "wb");
+ if (!fd)
+ throw TWavIOError("Can't open file to write", errno);
+ if (fwrite(&header, sizeof(TWavHeader), 1, fd) != 1) {
+ const int errnum = errno;
+ fclose(fd);
+ throw TWavIOError("Can't write WAV header", errnum);
+ }
+ return {fd, header};
+}
+
+TWav::TWav(const string& filename)
+ : Meta(ReadWavHeader(filename)) {
+}
+
+TWav::TWav(const string& filename, const TWavHeader& header, bool overwrite)
+ : Meta(CreateFileAndHeader(filename, header, overwrite)) {
+}
+TWav::~TWav() {
+ fclose(Meta.WavFile);
+}
+
+const TWavHeader& TWav::GetWavHeader() const {
+ return Meta.Header;
+}
+
+
diff --git a/src/wav.h b/src/wav.h
new file mode 100644
index 0000000..fa979c7
--- /dev/null
+++ b/src/wav.h
@@ -0,0 +1,126 @@
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "pcmengin.h"
+
+typedef struct WAV_HEADER {
+ char RIFF[4]; // RIFF Header Magic header
+ unsigned int ChunkSize; // RIFF Chunk Size
+ char WAVE[4]; // WAVE Header
+ char fmt[4]; // FMT header
+ unsigned int Subchunk1Size; // Size of the fmt chunk
+ unsigned short AudioFormat; // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM
+ unsigned short NumOfChan; // Number of channels 1=Mono 2=Sterio
+ unsigned int SamplesPerSec; // Sampling Frequency in Hz
+ unsigned int bytesPerSec; // bytes per second
+ unsigned short blockAlign; // 2=16-bit mono, 4=16-bit stereo
+ unsigned short bitsPerSample; // Number of bits per sample
+ char Subchunk2ID[4]; // "data" string
+ unsigned int Subchunk2Size; // Sampled data length
+} __attribute__((packed)) TWavHeader;
+
+class TFileAlreadyExists : public std::exception {
+
+};
+
+class TWavIOError : public std::exception {
+ const int ErrNum = 0;
+ const char* Text;
+public:
+ TWavIOError(const char* str, int err)
+ : ErrNum(err)
+ , Text(str)
+ {}
+ virtual const char* what() const throw() {
+ return Text;
+ }
+};
+
+template<class T>
+class TWavPcmReader : public IPCMReader<T> {
+public:
+ typedef std::function<void(std::vector<std::vector<T>>& data, const uint32_t size)> TLambda;
+ TLambda Lambda;
+ TWavPcmReader(TLambda lambda)
+ : Lambda(lambda)
+ {}
+ void Read(std::vector<std::vector<T>>& data , const uint32_t size) const override {
+ Lambda(data, size);
+ }
+};
+
+template<class T>
+class TWavPcmWriter : public IPCMWriter<T> {
+public:
+ typedef std::function<void(const std::vector<std::vector<T>>& data, const uint32_t size)> TLambda;
+ TLambda Lambda;
+ TWavPcmWriter(TLambda lambda)
+ : Lambda(lambda)
+ {}
+ void Write(const std::vector<std::vector<T>>& data , const uint32_t size) const override {
+ Lambda(data, size);
+ }
+};
+
+class TWav {
+ struct TMeta {
+ FILE* WavFile;
+ const TWavHeader Header;
+ } Meta;
+ static TWav::TMeta ReadWavHeader(const std::string& filename);
+ static TWav::TMeta CreateFileAndHeader(const std::string& filename, const TWavHeader& header, bool overwrite);
+public:
+ static TWavHeader CreateHeader(int channels, uint32_t length);
+ enum Mode {
+ E_READ,
+ E_WRITE
+ };
+ TWav(const std::string& filename); // reading
+ TWav(const std::string& filename, const TWavHeader& header, bool overwrite = false); //writing
+ ~TWav();
+ uint32_t GetChannelNum() const;
+ uint32_t GetSampleRate() const;
+ const TWavHeader& GetWavHeader() const;
+ template<class T>
+ IPCMReader<T>* GetPCMReader() const;
+
+ template<class T>
+ IPCMWriter<T>* GetPCMWriter();
+};
+
+typedef std::unique_ptr<TWav> TWavPtr;
+
+template<class T>
+IPCMReader<T>* TWav::GetPCMReader() const {
+ return new TWavPcmReader<T>([this](std::vector<std::vector<T>>& data, const uint32_t size) {
+ if (data[0].size() != Meta.Header.NumOfChan)
+ throw TWrongReadBuffer();
+ uint32_t dataRead = 0;
+ for (uint32_t i = 0; i < size; i++) {
+ for (uint32_t c = 0; c < Meta.Header.NumOfChan; c++) {
+ short buf;
+ dataRead += fread(&buf, Meta.Header.bitsPerSample/8, 1, Meta.WavFile);
+ data[i][c] = (T)buf;
+ }
+ }
+ });
+}
+
+template<class T>
+IPCMWriter<T>* TWav::GetPCMWriter() {
+ return new TWavPcmWriter<T>([this](const std::vector<std::vector<T>>& data, const uint32_t size) {
+ if (data[0].size() != Meta.Header.NumOfChan)
+ throw TWrongReadBuffer();
+ uint32_t dataWrite = 0;
+ for (uint32_t i = 0; i < size; i++) {
+ for (uint32_t c = 0; c < Meta.Header.NumOfChan; c++) {
+ short buf = (short)data[i][c];
+ dataWrite += fwrite(&buf, Meta.Header.bitsPerSample/8, 1, Meta.WavFile);
+ }
+ }
+ });
+}
+
+