diff options
author | Daniil Cherednik <dan.cherednik@gmail.com> | 2015-10-31 02:14:04 +0300 |
---|---|---|
committer | Daniil Cherednik <dan.cherednik@gmail.com> | 2015-10-31 02:14:04 +0300 |
commit | 592b4bd68f3eb9d1cdbcda74feaffc7b1d5f0485 (patch) | |
tree | a7e1b93335631a8ff969f6d395e08ec24281fe62 | |
parent | 4bfe046e9620644dda130aaf3f9d9078251ffd59 (diff) | |
download | atracdenc-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/Makefile | 3 | ||||
-rw-r--r-- | src/aea.cpp | 102 | ||||
-rw-r--r-- | src/aea.h | 48 | ||||
-rw-r--r-- | src/atrac/atrac1.cpp | 10 | ||||
-rw-r--r-- | src/atrac/atrac1.h | 71 | ||||
-rw-r--r-- | src/atrac/atrac1_bitalloc.cpp | 155 | ||||
-rw-r--r-- | src/atrac/atrac1_bitalloc.h | 35 | ||||
-rw-r--r-- | src/atrac/atrac1_dequantiser.cpp | 48 | ||||
-rw-r--r-- | src/atrac/atrac1_dequantiser.h | 12 | ||||
-rw-r--r-- | src/atrac/atrac1_qmf.h | 45 | ||||
-rw-r--r-- | src/atrac/atrac1_scale.cpp | 55 | ||||
-rw-r--r-- | src/atrac/atrac1_scale.h | 22 | ||||
-rw-r--r-- | src/atracdenc.cpp | 213 | ||||
-rw-r--r-- | src/atracdenc.h | 35 | ||||
-rw-r--r-- | src/bitstream/Makefile | 6 | ||||
-rw-r--r-- | src/bitstream/bitstream.cpp | 55 | ||||
-rw-r--r-- | src/bitstream/bitstream.h | 29 | ||||
-rw-r--r-- | src/bitstream/bitstream_ut.cpp | 55 | ||||
-rw-r--r-- | src/main.cpp | 125 | ||||
-rw-r--r-- | src/pcmengin.h | 106 | ||||
-rw-r--r-- | src/qmf/qmf.cpp | 1 | ||||
-rw-r--r-- | src/qmf/qmf.h | 81 | ||||
-rw-r--r-- | src/wav.cpp | 85 | ||||
-rw-r--r-- | src/wav.h | 126 |
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); + } + } + }); +} + + |