diff options
author | Daniil Cherednik <dan.cherednik@gmail.com> | 2025-07-22 22:54:21 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-07-22 22:54:21 +0200 |
commit | 61045345fa43e54fb4fc3eee0e05b25451af9ca5 (patch) | |
tree | e93f417d2a51eb1b9d30909193bc9f84f95124d5 /src/atrac/at3 | |
parent | e9eb8305dcdbdbf5a9cbabf766c6e908e63a19b1 (diff) | |
download | atracdenc-master.tar.gz |
Diffstat (limited to 'src/atrac/at3')
-rw-r--r-- | src/atrac/at3/atrac3.cpp | 56 | ||||
-rw-r--r-- | src/atrac/at3/atrac3.h | 277 | ||||
-rw-r--r-- | src/atrac/at3/atrac3_bitstream.cpp | 673 | ||||
-rw-r--r-- | src/atrac/at3/atrac3_bitstream.h | 95 | ||||
-rw-r--r-- | src/atrac/at3/atrac3_qmf.h | 44 |
5 files changed, 1145 insertions, 0 deletions
diff --git a/src/atrac/at3/atrac3.cpp b/src/atrac/at3/atrac3.cpp new file mode 100644 index 0000000..1e5b8e9 --- /dev/null +++ b/src/atrac/at3/atrac3.cpp @@ -0,0 +1,56 @@ +/* + * This file is part of AtracDEnc. + * + * AtracDEnc is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * AtracDEnc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with AtracDEnc; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "atrac3.h" +#include <algorithm> + +namespace NAtracDEnc { +namespace NAtrac3 { + +constexpr uint32_t TAtrac3Data::BlockSizeTab[33]; +constexpr uint32_t TAtrac3Data::ClcLengthTab[8]; +constexpr float TAtrac3Data::MaxQuant[8]; +constexpr uint32_t TAtrac3Data::BlocksPerBand[4 + 1]; +constexpr uint32_t TAtrac3Data::SpecsPerBlock[33]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable1[HuffTable1Sz]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable2[HuffTable2Sz]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable3[HuffTable3Sz]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable5[HuffTable5Sz]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable6[HuffTable6Sz]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable7[HuffTable7Sz]; +constexpr TAtrac3Data::THuffTablePair TAtrac3Data::HuffTables[7]; + +constexpr TContainerParams TAtrac3Data::ContainerParams[8]; +float TAtrac3Data::EncodeWindow[256] = {0}; +float TAtrac3Data::DecodeWindow[256] = {0}; +float TAtrac3Data::ScaleTable[64] = {0}; +float TAtrac3Data::GainLevel[16]; +float TAtrac3Data::GainInterpolation[31]; + +static const TAtrac3Data Atrac3Data; + +const TContainerParams* TAtrac3Data::GetContainerParamsForBitrate(uint32_t bitrate) { + // Set default to LP2 mode + if (bitrate == 0) { + bitrate = 132300; + } + return std::lower_bound(ContainerParams, ContainerParams+8, bitrate); +} + +} // namespace NAtrac3 +} // namespace NAtracDEnc diff --git a/src/atrac/at3/atrac3.h b/src/atrac/at3/atrac3.h new file mode 100644 index 0000000..558eddc --- /dev/null +++ b/src/atrac/at3/atrac3.h @@ -0,0 +1,277 @@ +/* + * This file is part of AtracDEnc. + * + * AtracDEnc is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * AtracDEnc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with AtracDEnc; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include <math.h> +#include <config.h> +#include <cstdint> +#include <vector> +#include <cassert> +#include <iostream> + +namespace NAtracDEnc { +namespace NAtrac3 { + +struct TContainerParams { + const uint32_t Bitrate; + const uint16_t FrameSz; + const bool Js; +}; + +inline bool operator< (const TContainerParams& x, const TContainerParams& y) +{ + return x.Bitrate < y.Bitrate; +} +inline bool operator> (const TContainerParams& x, const TContainerParams& y) +{ + return x.Bitrate > y.Bitrate; +} +inline bool operator< (const TContainerParams& x, const unsigned int y) +{ + return x.Bitrate < y; +} +inline bool operator> (const TContainerParams& x, const unsigned int y) +{ + return x.Bitrate > y; +} + +class TAtrac3Data { +public: + class TBlockSizeMod { + public: + constexpr bool ShortWin(uint8_t) const noexcept { + return false; + } + }; + + static constexpr uint8_t MaxBfus = 32; + static constexpr uint32_t NumSamples = 1024; + + static const uint32_t MDCTSz = 512; + static float ScaleTable[64]; + static float EncodeWindow[256]; + static float DecodeWindow[256]; + static float GainLevel[16]; + static float GainInterpolation[31]; + static constexpr int32_t ExponentOffset = 4; + static constexpr int32_t LocScale = 3; + static constexpr int32_t LocSz = 1 << LocScale; + static constexpr int32_t GainInterpolationPosShift = 15; + + static constexpr uint32_t NumSpecs = NumSamples; + static const uint32_t frameSz = 152; + static constexpr float MaxQuant[8] = { + 0.0, 1.5, 2.5, 3.5, + 4.5, 7.5, 15.5, 31.5 + }; + static constexpr uint32_t BlockSizeTab[33] = { + 0, 8, 16, 24, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 144, 160, 176, + 192, 224, 256, 288, 320, 352, 384, 416, + 448, 480, 512, 576, 640, 704, 768, 896, + 1024 + }; + static constexpr uint32_t const * const SpecsStartShort = &BlockSizeTab[0]; + + static constexpr uint32_t const * const SpecsStartLong = &BlockSizeTab[0]; + static constexpr uint32_t ClcLengthTab[8] = { 0, 4, 3, 3, 4, 4, 5, 6 }; + static constexpr int NumQMF = 4; + static constexpr uint32_t MaxSpecs = NumSamples; //1024 + static constexpr uint32_t MaxSpecsPerBlock = 128; + + static constexpr uint32_t BlocksPerBand[NumQMF + 1] = {0, 18, 26, 30, 32}; + static constexpr uint32_t SpecsPerBlock[33] = { + 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 64, 64, 64, 64, 128, 128, + 128 + }; + struct THuffEntry { + const uint8_t Code; + const uint8_t Bits; + }; + static constexpr uint8_t HuffTable1Sz = 9; + static constexpr THuffEntry HuffTable1[HuffTable1Sz] = { + { 0x0, 1 }, + { 0x4, 3 }, { 0x5, 3 }, + { 0xC, 4 }, { 0xD, 4 }, + { 0x1C, 5 }, { 0x1D, 5 }, { 0x1E, 5 }, { 0x1F, 5 } + }; + static constexpr uint8_t HuffTable2Sz = 5; + static constexpr THuffEntry HuffTable2[HuffTable2Sz] = { + { 0x0, 1 }, + { 0x4, 3 }, { 0x5, 3 }, { 0x6, 3 }, { 0x7, 3 } + }; + static constexpr uint8_t HuffTable3Sz = 7; + static constexpr THuffEntry HuffTable3[HuffTable3Sz] = { + { 0x0, 1 }, + { 0x4, 3}, { 0x5, 3 }, + { 0xC, 4 }, { 0xD, 4 }, { 0xE, 4 }, { 0xF, 4 } + }; + static constexpr uint8_t HuffTable5Sz = 15; + static constexpr THuffEntry HuffTable5[HuffTable5Sz] = { + { 0x0, 2 }, + { 0x2, 3 }, { 0x3, 3 }, + { 0x8, 4 }, { 0x9, 4 }, { 0xA, 4 }, { 0xB, 4 }, //{ 0xC, 4 }, { 0xD, 4 }, + { 0x1C, 5 }, { 0x1D, 5 }, + { 0x3C, 6 }, { 0x3D, 6 }, { 0x3E, 6 }, { 0x3F, 6}, + { 0xC, 4 }, { 0xD, 4 } //TODO: is it right table??? + }; + static constexpr uint8_t HuffTable6Sz = 31; + static constexpr THuffEntry HuffTable6[HuffTable6Sz] = { + { 0x0, 3 }, + { 0x2, 4 }, { 0x3, 4 }, { 0x4, 4 }, { 0x5, 4 }, { 0x6, 4 }, { 0x7, 4 }, //{ 0x8, 4 }, { 0x9, 4 }, + { 0x14, 5 }, { 0x15, 5 }, { 0x16, 5 }, { 0x17, 5 }, { 0x18, 5 }, { 0x19, 5 }, + { 0x34, 6 }, { 0x35, 6 }, { 0x36, 6 }, { 0x37, 6 }, { 0x38, 6 }, { 0x39, 6 }, { 0x3A, 6 }, { 0x3B, 6 }, + { 0x78, 7 }, { 0x79, 7 }, { 0x7A, 7 }, { 0x7B, 7 }, { 0x7C, 7 }, { 0x7D, 7 }, { 0x7E, 7 }, { 0x7F, 7 }, + { 0x8, 4 }, { 0x9, 4 } //TODO: is it right table??? + }; + static constexpr uint8_t HuffTable7Sz = 63; + static constexpr THuffEntry HuffTable7[HuffTable7Sz] = { + { 0x0, 3 }, + //{ 0x2, 4 }, { 0x3, 4 }, + { 0x8, 5 }, { 0x9, 5 }, { 0xA, 5}, { 0xB, 5 }, { 0xC, 5 }, { 0xD, 5 }, { 0xE, 5}, { 0xF, 5 }, { 0x10, 5 }, + { 0x11, 5 }, + { 0x24, 6 }, { 0x25, 6 }, { 0x26, 6 }, { 0x27, 6 }, { 0x28, 6 }, { 0x29, 6 }, { 0x2A, 6 }, { 0x2B, 6 }, + { 0x2C, 6 }, { 0x2D, 6 }, { 0x2E, 6 }, { 0x2F, 6 }, { 0x30, 6 }, { 0x31, 6 }, { 0x32, 6 }, { 0x33, 6 }, + { 0x68, 7 }, { 0x69, 7 }, { 0x6A, 7 }, { 0x6B, 7 }, { 0x6C, 7 }, { 0x6D, 7 }, { 0x6E, 7 }, + { 0x6F, 7 }, { 0x70, 7 }, { 0x71, 7 }, { 0x72, 7 }, { 0x73, 7 }, { 0x74, 7 }, { 0x75, 7 }, + { 0xEC, 8 }, { 0xED, 8 }, { 0xEE, 8 }, { 0xEF, 8 }, { 0xF0, 8 }, { 0xF1, 8 }, { 0xF2, 8 }, { 0xF3, 8 }, + { 0xF4, 8 }, { 0xF5, 8 }, + { 0xF6, 8 }, { 0xF7, 8 }, { 0xF8, 8 }, { 0xF9, 8 }, { 0xFA, 8 }, { 0xFB, 8 }, { 0xFC, 8 }, { 0xFD, 8 }, + { 0xFE, 8 }, { 0xFF, 8 }, + { 0x2, 4 }, { 0x3, 4 } //TODO: is it right table??? + }; + + struct THuffTablePair { + const THuffEntry* Table; + const uint32_t Sz; + }; + + static constexpr THuffTablePair HuffTables[7] { + { HuffTable1, HuffTable1Sz }, + { HuffTable2, HuffTable2Sz }, + { HuffTable3, HuffTable3Sz }, + { HuffTable1, HuffTable1Sz }, + { HuffTable5, HuffTable5Sz }, + { HuffTable6, HuffTable6Sz }, + { HuffTable7, HuffTable7Sz } + }; +public: + TAtrac3Data() { + if (ScaleTable[0] == 0) { + for (uint32_t i = 0; i < 64; i++) { + ScaleTable[i] = pow(2.0, (double)(i / 3.0 - 21.0)); + } + } + for (int i = 0; i < 256; i++) { + EncodeWindow[i] = (sin(((i + 0.5) / 256.0 - 0.5) * M_PI) + 1.0)/* * 0.5*/; + } + for (int i = 0; i < 256; i++) { + const double a = EncodeWindow[i]; + const double b = EncodeWindow[255-i]; + DecodeWindow[i] = 2.0 * a / (a*a + b*b); + } + for (int i = 0; i < 16; i++) { + GainLevel[i] = pow(2.0, ExponentOffset - i); + } + for (int i = 0; i < 31; i++) { + GainInterpolation[i] = pow(2.0, -1.0 / LocSz * (i - 15)); + } + } + static uint32_t MantissaToCLcIdx(int32_t mantissa) { + assert(mantissa > -3 && mantissa < 2); + const uint32_t mantissa_clc_rtab[4] = { 2, 3, 0, 1}; + return mantissa_clc_rtab[mantissa + 2]; + } + static uint32_t MantissasToVlcIndex(int32_t a, int32_t b) { + assert(a > -2 && a < 2); + assert(b > -2 && b < 2); + const uint32_t mantissas_vlc_rtab[9] = { 8, 4, 7, 2, 0, 1, 6, 3, 5 }; + const uint8_t idx = 3 * (a + 1) + (b + 1); + return mantissas_vlc_rtab[idx]; + } + static constexpr TContainerParams ContainerParams[8] = { + { 66150, 192, true }, + { 93713, 272, true }, + { 104738, 304, false }, + { 132300, 384, false }, + { 146081, 424, false }, + { 176400, 512, false }, + { 264600, 768, false }, + { 352800, 1024, false } + }; + static const TContainerParams* GetContainerParamsForBitrate(uint32_t bitrate); + + struct SubbandInfo { + static const uint32_t MaxGainPointsNum = 8; + struct TGainPoint { + uint32_t Level; + uint32_t Location; + }; + std::vector<std::vector<TGainPoint>> Info; + SubbandInfo() + { + Info.resize(4); + } + void AddSubbandCurve(uint16_t n, std::vector<TGainPoint>&& curve) { + Info[n] = std::move(curve); + } + uint32_t GetQmfNum() const { + return Info.size(); + } + const std::vector<TGainPoint>& GetGainPoints(uint32_t i) const { + return Info[i]; + } + void Reset() + { + for (auto& v : Info) { + v.clear(); + } + } + }; + + struct TTonalVal { + const uint16_t Pos; + const double Val; + const uint8_t Bfu; + }; + typedef std::vector<TTonalVal> TTonalComponents; +}; + + +struct TAtrac3EncoderSettings { + TAtrac3EncoderSettings(uint32_t bitrate, bool noGainControll, + bool noTonalComponents, uint8_t sourceChannels, uint32_t bfuIdxConst) + : ConteinerParams(TAtrac3Data::GetContainerParamsForBitrate(bitrate)) + , NoGainControll(noGainControll) + , NoTonalComponents(noTonalComponents) + , SourceChannels(sourceChannels) + , BfuIdxConst(bfuIdxConst) + { } + const TContainerParams* ConteinerParams; + const bool NoGainControll; + const bool NoTonalComponents; + const uint8_t SourceChannels; + const uint32_t BfuIdxConst; +}; + +} // namespace NAtrac3 +} // namespace NAtracDEnc diff --git a/src/atrac/at3/atrac3_bitstream.cpp b/src/atrac/at3/atrac3_bitstream.cpp new file mode 100644 index 0000000..d039dff --- /dev/null +++ b/src/atrac/at3/atrac3_bitstream.cpp @@ -0,0 +1,673 @@ +/* + * This file is part of AtracDEnc. + * + * AtracDEnc is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * AtracDEnc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with AtracDEnc; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "atrac3_bitstream.h" +#include <atrac/atrac_psy_common.h> +#include <bitstream/bitstream.h> +#include <util.h> +#include <env.h> +#include <algorithm> +#include <iostream> +#include <vector> +#include <cstdlib> + +#include <cstring> + +namespace NAtracDEnc { +namespace NAtrac3 { + +using std::vector; +using std::memset; + +static const uint32_t FixedBitAllocTable[TAtrac3Data::MaxBfus] = { + 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 1, + 0, 0 +}; + +#define EAQ 1 + +#ifdef EAQ + +static constexpr size_t LOSY_NAQ_START = 18; +static constexpr size_t BOOST_NAQ_END = 10; + +#else + +static constexpr size_t LOSY_NAQ_START = 31; +static constexpr size_t BOOST_NAQ_END = 0; + +#endif + +std::vector<float> TAtrac3BitStreamWriter::ATH; +TAtrac3BitStreamWriter::TAtrac3BitStreamWriter(ICompressedOutput* container, const TContainerParams& params, uint32_t bfuIdxConst) + : Container(container) + , Params(params) + , BfuIdxConst(bfuIdxConst) +{ + NEnv::SetRoundFloat(); + if (ATH.size()) { + return; + } + ATH.reserve(TAtrac3Data::MaxBfus); + auto ATHSpec = CalcATH(1024, 44100); + for (size_t bandNum = 0; bandNum < TAtrac3Data::NumQMF; ++bandNum) { + for (size_t blockNum = TAtrac3Data::BlocksPerBand[bandNum]; blockNum < TAtrac3Data::BlocksPerBand[bandNum + 1]; ++blockNum) { + const size_t specNumStart = TAtrac3Data::SpecsStartLong[blockNum]; + float x = 999; + for (size_t line = specNumStart; line < specNumStart + TAtrac3Data::SpecsPerBlock[blockNum]; line++) { + x = fmin(x, ATHSpec[line]); + } + x = pow(10, 0.1 * x); + ATH.push_back(x / 100); //reduce efficiency of ATH, but prevents aliasing problem, TODO: fix it? + } + } +} + +uint32_t TAtrac3BitStreamWriter::CLCEnc(const uint32_t selector, const int mantissas[TAtrac3Data::MaxSpecsPerBlock], + const uint32_t blockSize, NBitStream::TBitStream* bitStream) +{ + const uint32_t numBits = TAtrac3Data::ClcLengthTab[selector]; + const uint32_t bitsUsed = (selector > 1) ? numBits * blockSize : numBits * blockSize / 2; + if (!bitStream) + return bitsUsed; + if (selector > 1) { + for (uint32_t i = 0; i < blockSize; ++i) { + bitStream->Write(NBitStream::MakeSign(mantissas[i], numBits), numBits); + } + } else { + for (uint32_t i = 0; i < blockSize / 2; ++i) { + uint32_t code = TAtrac3Data::MantissaToCLcIdx(mantissas[i * 2]) << 2; + code |= TAtrac3Data::MantissaToCLcIdx(mantissas[i * 2 + 1]); + ASSERT(numBits == 4); + bitStream->Write(code, numBits); + } + } + return bitsUsed; +} + +uint32_t TAtrac3BitStreamWriter::VLCEnc(const uint32_t selector, const int mantissas[TAtrac3Data::MaxSpecsPerBlock], + const uint32_t blockSize, NBitStream::TBitStream* bitStream) +{ + ASSERT(selector > 0); + const TAtrac3Data::THuffEntry* huffTable = TAtrac3Data::HuffTables[selector - 1].Table; + const uint8_t tableSz = TAtrac3Data::HuffTables[selector - 1].Sz; + uint32_t bitsUsed = 0; + if (selector > 1) { + for (uint32_t i = 0; i < blockSize; ++i) { + int m = mantissas[i]; + uint32_t huffS = (m < 0) ? (((uint32_t)(-m)) << 1) | 1 : ((uint32_t)m) << 1; + if (huffS) + huffS -= 1; + ASSERT(huffS < 256); + ASSERT(huffS < tableSz); + bitsUsed += huffTable[huffS].Bits; + if (bitStream) + bitStream->Write(huffTable[huffS].Code, huffTable[huffS].Bits); + } + } else { + ASSERT(tableSz == 9); + for (uint32_t i = 0; i < blockSize / 2; ++i) { + const int ma = mantissas[i * 2]; + const int mb = mantissas[i * 2 + 1]; + const uint32_t huffS = TAtrac3Data::MantissasToVlcIndex(ma, mb); + bitsUsed += huffTable[huffS].Bits; + if (bitStream) + bitStream->Write(huffTable[huffS].Code, huffTable[huffS].Bits); + } + } + return bitsUsed; +} + +std::pair<uint8_t, uint32_t> TAtrac3BitStreamWriter::CalcSpecsBitsConsumption(const TSingleChannelElement& sce, + const vector<uint32_t>& precisionPerEachBlocks, int* mantisas, vector<float>& energyErr) +{ + + const vector<TScaledBlock>& scaledBlocks = sce.ScaledBlocks; + const uint32_t numBlocks = precisionPerEachBlocks.size(); + uint32_t bitsUsed = numBlocks * 3; + + auto lambda = [this, numBlocks, mantisas, &precisionPerEachBlocks, &scaledBlocks, &energyErr](bool clcMode, bool calcMant) { + uint32_t bits = 0; + for (uint32_t i = 0; i < numBlocks; ++i) { + if (precisionPerEachBlocks[i] == 0) + continue; + bits += 6; //sfi + const uint32_t first = TAtrac3Data::BlockSizeTab[i]; + const uint32_t last = TAtrac3Data::BlockSizeTab[i+1]; + const uint32_t blockSize = last - first; + const float mul = TAtrac3Data::MaxQuant[std::min(precisionPerEachBlocks[i], (uint32_t)7)]; + if (calcMant) { + const float* values = scaledBlocks[i].Values.data(); + energyErr[i] = QuantMantisas(values, first, last, mul, i > LOSY_NAQ_START, mantisas); + } + bits += clcMode ? CLCEnc(precisionPerEachBlocks[i], mantisas + first, blockSize, nullptr) : + VLCEnc(precisionPerEachBlocks[i], mantisas + first, blockSize, nullptr); + } + return bits; + }; + const uint32_t clcBits = lambda(true, true); + const uint32_t vlcBits = lambda(false, false); + bool mode = clcBits <= vlcBits; + return std::make_pair(mode, bitsUsed + (mode ? clcBits : vlcBits)); +} + +//true - should reencode +//false - not need to +static inline bool CheckBfus(uint16_t* numBfu, const vector<uint32_t>& precisionPerEachBlocks) +{ + ASSERT(*numBfu); + uint16_t curLastBfu = *numBfu - 1; + //assert(curLastBfu < precisionPerEachBlocks.size()); + ASSERT(*numBfu == precisionPerEachBlocks.size()); + if (precisionPerEachBlocks[curLastBfu] == 0) { + *numBfu = curLastBfu; + return true; + } + return false; +} + +static const std::pair<uint8_t, vector<uint32_t>> DUMMY_ALLOC{1, vector<uint32_t>{0}}; + +bool ConsiderEnergyErr(const vector<float>& err, vector<uint32_t>& bits) +{ + if (err.size() < bits.size()) + abort(); + + bool adjusted = false; + size_t lim = std::min((size_t)BOOST_NAQ_END, bits.size()); + for (size_t i = 0; i < lim; i++) { + float e = err[i]; + if (((e > 0 && e < 0.7) || e > 1.2) & (bits[i] < 7)) { + //std::cerr << "adjust: " << i << " e: " << e << " b: " << bits[i] << std::endl; + bits[i]++; + adjusted = true; + } + } + + return adjusted; +} + +std::pair<uint8_t, vector<uint32_t>> TAtrac3BitStreamWriter::CreateAllocation(const TSingleChannelElement& sce, + const uint16_t targetBits, int mt[TAtrac3Data::MaxSpecs], float laudness) +{ + const vector<TScaledBlock>& scaledBlocks = sce.ScaledBlocks; + if (scaledBlocks.empty()) { + return DUMMY_ALLOC; + } + + float spread = AnalizeScaleFactorSpread(scaledBlocks); + + uint16_t numBfu = BfuIdxConst ? BfuIdxConst : 32; + + // Limit number of BFU if target bitrate is not enough + // 3 bits to write each bfu without data + // 5 bits we need for tonal header + // 32 * 3 + 5 = 101 + if (targetBits < 101) { + uint16_t lim = (targetBits - 5) / 3; + numBfu = std::min(numBfu, lim); + } + + vector<uint32_t> precisionPerEachBlocks(numBfu); + vector<float> energyErr(numBfu); + uint8_t mode; + bool cont = true; + while (cont) { + precisionPerEachBlocks.resize(numBfu); + double maxShift = 20; + double minShift = -8; + for (;;) { + double shift = (maxShift + minShift) / 2; + vector<uint32_t> tmpAlloc = CalcBitsAllocation(scaledBlocks, numBfu, spread, shift, laudness); + energyErr.clear(); + energyErr.resize(numBfu); + std::pair<uint8_t, uint32_t> consumption; + + do { + consumption = CalcSpecsBitsConsumption(sce, tmpAlloc, mt, energyErr); + } while (ConsiderEnergyErr(energyErr, tmpAlloc)); + + auto bitsUsedByTonal = EncodeTonalComponents(sce, tmpAlloc, nullptr); + //std::cerr << consumption.second << " |tonal: " << bitsUsedByTonal << " target: " << targetBits << " shift " << shift << " max | min " << maxShift << " " << minShift << " numBfu: " << numBfu << std::endl; + consumption.second += bitsUsedByTonal; + + if (consumption.second < targetBits) { + if (maxShift - minShift < 0.1) { + precisionPerEachBlocks = tmpAlloc; + mode = consumption.first; + if (numBfu > 1) { + cont = !BfuIdxConst && CheckBfus(&numBfu, precisionPerEachBlocks); + } else { + cont = false; + } + break; + } + maxShift = shift - 0.01; + } else if (consumption.second > targetBits) { + minShift = shift + 0.01; + } else { + precisionPerEachBlocks = tmpAlloc; + mode = consumption.first; + cont = !BfuIdxConst && CheckBfus(&numBfu, precisionPerEachBlocks);; + break; + } + } + } + //std::cerr << "==" << std::endl; + return { mode, precisionPerEachBlocks }; +} + +void TAtrac3BitStreamWriter::EncodeSpecs(const TSingleChannelElement& sce, NBitStream::TBitStream* bitStream, + const std::pair<uint8_t, vector<uint32_t>>& allocation, const int mt[TAtrac3Data::MaxSpecs]) +{ + + const vector<TScaledBlock>& scaledBlocks = sce.ScaledBlocks; + const vector<uint32_t>& precisionPerEachBlocks = allocation.second; + EncodeTonalComponents(sce, precisionPerEachBlocks, bitStream); + const uint32_t numBlocks = precisionPerEachBlocks.size(); //number of blocks to save + const uint32_t codingMode = allocation.first;//0 - VLC, 1 - CLC + + ASSERT(numBlocks <= 32); + bitStream->Write(numBlocks-1, 5); + bitStream->Write(codingMode, 1); + for (uint32_t i = 0; i < numBlocks; ++i) { + uint32_t val = precisionPerEachBlocks[i]; //coding table used (VLC) or number of bits used (CLC) + bitStream->Write(val, 3); + } + for (uint32_t i = 0; i < numBlocks; ++i) { + if (precisionPerEachBlocks[i] == 0) + continue; + bitStream->Write(scaledBlocks[i].ScaleFactorIndex, 6); + } + for (uint32_t i = 0; i < numBlocks; ++i) { + if (precisionPerEachBlocks[i] == 0) + continue; + + const uint32_t first = TAtrac3Data::BlockSizeTab[i]; + const uint32_t last = TAtrac3Data::BlockSizeTab[i+1]; + const uint32_t blockSize = last - first; + + if (codingMode == 1) { + CLCEnc(precisionPerEachBlocks[i], mt + first, blockSize, bitStream); + } else { + VLCEnc(precisionPerEachBlocks[i], mt + first, blockSize, bitStream); + } + } +} + +uint8_t TAtrac3BitStreamWriter::GroupTonalComponents(const std::vector<TTonalBlock>& tonalComponents, + const vector<uint32_t>& allocTable, + TTonalComponentsSubGroup groups[64]) +{ + for (const TTonalBlock& tc : tonalComponents) { + ASSERT(tc.ScaledBlock.Values.size() < 8); + ASSERT(tc.ScaledBlock.Values.size() > 0); + ASSERT(tc.ValPtr->Bfu < allocTable.size()); + auto quant = std::max((uint32_t)2, std::min(allocTable[tc.ValPtr->Bfu] + 1, (uint32_t)7)); + //std::cerr << " | " << tc.ValPtr->Pos << " | " << (int)tc.ValPtr->Bfu << " | " << quant << std::endl; + groups[quant * 8 + tc.ScaledBlock.Values.size()].SubGroupPtr.push_back(&tc); + } + + //std::cerr << "=====" << std::endl; + uint8_t tcsgn = 0; + //for each group + for (uint8_t i = 0; i < 64; ++i) { + size_t start_pos; + size_t cur_pos = 0; + //scan tonal components + while (cur_pos < groups[i].SubGroupPtr.size()) { + start_pos = cur_pos; + ++tcsgn; + groups[i].SubGroupMap.push_back(static_cast<uint8_t>(cur_pos)); + uint8_t groupLimiter = 0; + //allow not grather than 8 components in one subgroup limited by 64 specs + do { + ++cur_pos; + if (cur_pos == groups[i].SubGroupPtr.size()) + break; + if (groups[i].SubGroupPtr[cur_pos]->ValPtr->Pos - (groups[i].SubGroupPtr[start_pos]->ValPtr->Pos & ~63) < 64) { + ++groupLimiter; + } else { + groupLimiter = 0; + start_pos = cur_pos; + } + } while (groupLimiter < 7); + } + } + return tcsgn; +} + +uint16_t TAtrac3BitStreamWriter::EncodeTonalComponents(const TSingleChannelElement& sce, + const vector<uint32_t>& allocTable, + NBitStream::TBitStream* bitStream) +{ + const uint16_t bitsUsedOld = bitStream ? (uint16_t)bitStream->GetSizeInBits() : 0; + const std::vector<TTonalBlock>& tonalComponents = sce.TonalBlocks; + const TAtrac3Data::SubbandInfo& subbandInfo = sce.SubbandInfo; + const uint8_t numQmfBand = subbandInfo.GetQmfNum(); + uint16_t bitsUsed = 0; + + //group tonal components with same quantizer and len + TTonalComponentsSubGroup groups[64]; + const uint8_t tcsgn = GroupTonalComponents(tonalComponents, allocTable, groups); + + ASSERT(tcsgn < 32); + + bitsUsed += 5; + if (bitStream) + bitStream->Write(tcsgn, 5); + + if (tcsgn == 0) { + for (int i = 0; i < 64; ++i) + ASSERT(groups[i].SubGroupPtr.size() == 0); + return bitsUsed; + } + //Coding mode: + // 0 - All are VLC + // 1 - All are CLC + // 2 - Error + // 3 - Own mode for each component + + //TODO: implement switch for best coding mode. Now VLC for all + bitsUsed += 2; + if (bitStream) + bitStream->Write(0, 2); + + uint8_t tcgnCheck = 0; + //for each group of equal quantiser and len + for (size_t i = 0; i < 64; ++i) { + const TTonalComponentsSubGroup& curGroup = groups[i]; + if (curGroup.SubGroupPtr.size() == 0) { + ASSERT(curGroup.SubGroupMap.size() == 0); + continue; + } + ASSERT(curGroup.SubGroupMap.size()); + ASSERT(curGroup.SubGroupMap.size() < UINT8_MAX); + for (size_t subgroup = 0; subgroup < curGroup.SubGroupMap.size(); ++subgroup) { + const uint8_t subGroupStartPos = curGroup.SubGroupMap[subgroup]; + const uint8_t subGroupEndPos = (subgroup < curGroup.SubGroupMap.size() - 1) ? + curGroup.SubGroupMap[subgroup+1] : (uint8_t)curGroup.SubGroupPtr.size(); + ASSERT(subGroupEndPos > subGroupStartPos); + //number of coded values are same in group + const uint8_t codedValues = (uint8_t)curGroup.SubGroupPtr[0]->ScaledBlock.Values.size(); + + //Number of tonal component for each 64spec block. Used to set qmf band flags and simplify band encoding loop + union { + uint8_t c[16]; + uint32_t i[4] = {0}; + } bandFlags; + ASSERT(numQmfBand <= 4); + for (uint8_t j = subGroupStartPos; j < subGroupEndPos; ++j) { + //assert num of coded values are same in group + ASSERT(codedValues == curGroup.SubGroupPtr[j]->ScaledBlock.Values.size()); + uint8_t specBlock = (curGroup.SubGroupPtr[j]->ValPtr->Pos) >> 6; + ASSERT((specBlock >> 2) < numQmfBand); + bandFlags.c[specBlock]++; + } + + ASSERT(numQmfBand == 4); + + tcgnCheck++; + + bitsUsed += numQmfBand; + if (bitStream) { + for (uint8_t j = 0; j < numQmfBand; ++j) { + bitStream->Write((bool)bandFlags.i[j], 1); + } + } + //write number of coded values for components in current group + ASSERT(codedValues > 0); + bitsUsed += 3; + if (bitStream) + bitStream->Write(codedValues - 1, 3); + //write quant index + ASSERT((i >> 3) > 1); + ASSERT((i >> 3) < 8); + bitsUsed += 3; + if (bitStream) + bitStream->Write(i >> 3, 3); + uint8_t lastPos = subGroupStartPos; + uint8_t checkPos = 0; + for (size_t j = 0; j < 16; ++j) { + if (!(bandFlags.i[j >> 2])) { + continue; + } + + const uint8_t codedComponents = bandFlags.c[j]; + ASSERT(codedComponents < 8); + bitsUsed += 3; + if (bitStream) + bitStream->Write(codedComponents, 3); + uint16_t k = lastPos; + for (; k < lastPos + codedComponents; ++k) { + ASSERT(curGroup.SubGroupPtr[k]->ValPtr->Pos >= j * 64); + uint16_t relPos = curGroup.SubGroupPtr[k]->ValPtr->Pos - j * 64; + ASSERT(curGroup.SubGroupPtr[k]->ScaledBlock.ScaleFactorIndex < 64); + + bitsUsed += 6; + if (bitStream) + bitStream->Write(curGroup.SubGroupPtr[k]->ScaledBlock.ScaleFactorIndex, 6); + + ASSERT(relPos < 64); + + bitsUsed += 6; + if (bitStream) + bitStream->Write(relPos, 6); + + ASSERT(curGroup.SubGroupPtr[k]->ScaledBlock.Values.size() < 8); + int mantisas[256]; + const float mul = TAtrac3Data::MaxQuant[std::min((uint32_t)(i>>3), (uint32_t)7)]; + ASSERT(codedValues == curGroup.SubGroupPtr[k]->ScaledBlock.Values.size()); + for (uint32_t z = 0; z < curGroup.SubGroupPtr[k]->ScaledBlock.Values.size(); ++z) { + mantisas[z] = lrint(curGroup.SubGroupPtr[k]->ScaledBlock.Values[z] * mul); + } + //VLCEnc + + ASSERT(i); + bitsUsed += VLCEnc(i>>3, mantisas, curGroup.SubGroupPtr[k]->ScaledBlock.Values.size(), bitStream); + + } + lastPos = k; + checkPos = lastPos; + } + + ASSERT(subGroupEndPos == checkPos); + } + } + ASSERT(tcgnCheck == tcsgn); + if (bitStream) + ASSERT(bitStream->GetSizeInBits() - bitsUsedOld == bitsUsed); + return bitsUsed; +} + +vector<uint32_t> TAtrac3BitStreamWriter::CalcBitsAllocation(const std::vector<TScaledBlock>& scaledBlocks, + const uint32_t bfuNum, + const float spread, + const float shift, + const float loudness) +{ + vector<uint32_t> bitsPerEachBlock(bfuNum); + for (size_t i = 0; i < bitsPerEachBlock.size(); ++i) { + float ath = ATH[i] * loudness; + //std::cerr << "block: " << i << " Loudness: " << loudness << " " << 10 * log10(scaledBlocks[i].MaxEnergy / ath) << std::endl; + if (scaledBlocks[i].MaxEnergy < ath) { + bitsPerEachBlock[i] = 0; + } else { + const uint32_t fix = FixedBitAllocTable[i]; + float x = 6; + if (i < 3) { + x = 2.8; + } else if (i < 10) { + x = 2.6; + } else if (i < 15) { + x = 3.3; + } else if (i <= 20) { + x = 3.6; + } else if (i <= 28) { + x = 4.2; + } + int tmp = spread * ( (float)scaledBlocks[i].ScaleFactorIndex / x) + (1.0 - spread) * fix - shift; + if (tmp > 7) { + bitsPerEachBlock[i] = 7; + } else if (tmp < 0) { + bitsPerEachBlock[i] = 0; + } else if (tmp == 0) { + bitsPerEachBlock[i] = 1; + } else { + bitsPerEachBlock[i] = tmp; + } + } + } + return bitsPerEachBlock; +} + +void WriteJsParams(NBitStream::TBitStream* bs) +{ + bs->Write(0, 1); + bs->Write(7, 3); + for (int i = 0; i < 4; i++) { + bs->Write(3, 2); + } +} + +// 0.5 - M only (mono) +// 0.0 - Uncorrelated +// -0.5 - S only +static float CalcMSRatio(float mEnergy, float sEnergy) { + float total = sEnergy + mEnergy; + if (total > 0) + return mEnergy / total - 0.5; + + // No signal - nothing to shift + return 0; +} + +static int32_t CalcMSBytesShift(uint32_t frameSz, + const vector<TAtrac3BitStreamWriter::TSingleChannelElement>& elements, + const int32_t b[2]) +{ + const int32_t totalUsedBits = 0 - b[0] - b[1]; + ASSERT(totalUsedBits > 0); + + const int32_t maxAllowedShift = (frameSz / 2 - Div8Ceil(totalUsedBits)); + + if (elements[1].ScaledBlocks.empty()) { + return maxAllowedShift; + } else { + float ratio = CalcMSRatio(elements[0].Loudness, elements[1].Loudness); + //std::cerr << ratio << std::endl; + return std::max(std::min(ToInt(frameSz * ratio), maxAllowedShift), -maxAllowedShift); + } +} + +void TAtrac3BitStreamWriter::WriteSoundUnit(const vector<TSingleChannelElement>& singleChannelElements, float laudness) +{ + + ASSERT(singleChannelElements.size() == 1 || singleChannelElements.size() == 2); + + const int halfFrameSz = Params.FrameSz >> 1; + + NBitStream::TBitStream bitStreams[2]; + + int32_t bitsToAlloc[2] = {-6, -6}; // 6 bits used always to write num blocks and coding mode + // See EncodeSpecs + + for (uint32_t channel = 0; channel < singleChannelElements.size(); channel++) { + const TSingleChannelElement& sce = singleChannelElements[channel]; + const TAtrac3Data::SubbandInfo& subbandInfo = sce.SubbandInfo; + + NBitStream::TBitStream* bitStream = &bitStreams[channel]; + + if (Params.Js && channel == 1) { + WriteJsParams(bitStream); + bitStream->Write(3, 2); + } else { + bitStream->Write(0x28, 6); //0x28 - id + } + + const uint8_t numQmfBand = subbandInfo.GetQmfNum(); + ASSERT(numQmfBand > 0); + bitStream->Write(numQmfBand - 1, 2); + + //write gain info + for (uint32_t band = 0; band < numQmfBand; ++band) { + const vector<TAtrac3Data::SubbandInfo::TGainPoint>& GainPoints = subbandInfo.GetGainPoints(band); + ASSERT(GainPoints.size() < TAtrac3Data::SubbandInfo::MaxGainPointsNum); + bitStream->Write(GainPoints.size(), 3); + int s = 0; + for (const TAtrac3Data::SubbandInfo::TGainPoint& point : GainPoints) { + bitStream->Write(point.Level, 4); + bitStream->Write(point.Location, 5); + s++; + ASSERT(s < 8); + } + } + + const int16_t bitsUsedByGainInfoAndHeader = (int16_t)bitStream->GetSizeInBits(); + bitsToAlloc[channel] -= bitsUsedByGainInfoAndHeader; + } + + int mt[2][TAtrac3Data::MaxSpecs]; + std::pair<uint8_t, vector<uint32_t>> allocations[2]; + + const int32_t msBytesShift = Params.Js ? CalcMSBytesShift(Params.FrameSz, singleChannelElements, bitsToAlloc) : 0; // positive - gain to m, negative to s. Must be zero if no joint stereo mode + + bitsToAlloc[0] += 8 * (halfFrameSz + msBytesShift); + bitsToAlloc[1] += 8 * (halfFrameSz - msBytesShift); + + for (uint32_t channel = 0; channel < singleChannelElements.size(); channel++) { + const TSingleChannelElement& sce = singleChannelElements[channel]; + allocations[channel] = CreateAllocation(sce, bitsToAlloc[channel], mt[channel], laudness); + } + + for (uint32_t channel = 0; channel < singleChannelElements.size(); channel++) { + const TSingleChannelElement& sce = singleChannelElements[channel]; + NBitStream::TBitStream* bitStream = &bitStreams[channel]; + + EncodeSpecs(sce, bitStream, allocations[channel], mt[channel]); + + if (!Container) + abort(); + + std::vector<char> channelData = bitStream->GetBytes(); + + if (Params.Js && channel == 1) { + channelData.resize(halfFrameSz - msBytesShift); + OutBuffer.insert(OutBuffer.end(), channelData.rbegin(), channelData.rend()); + } else { + channelData.resize(halfFrameSz + msBytesShift); + OutBuffer.insert(OutBuffer.end(), channelData.begin(), channelData.end()); + } + } + + //No mone mode for atrac3, just make duplicate of first channel + if (singleChannelElements.size() == 1 && !Params.Js) { + int sz = OutBuffer.size(); + ASSERT(sz == halfFrameSz); + OutBuffer.resize(sz << 1); + std::copy_n(OutBuffer.begin(), sz, OutBuffer.begin() + sz); + } + + Container->WriteFrame(OutBuffer); + OutBuffer.clear(); +} + +} // namespace NAtrac3 +} // namespace NAtracDEnc diff --git a/src/atrac/at3/atrac3_bitstream.h b/src/atrac/at3/atrac3_bitstream.h new file mode 100644 index 0000000..17a8548 --- /dev/null +++ b/src/atrac/at3/atrac3_bitstream.h @@ -0,0 +1,95 @@ +/* + * This file is part of AtracDEnc. + * + * AtracDEnc is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * AtracDEnc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with AtracDEnc; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once +#include "atrac3.h" +#include <compressed_io.h> +#include <atrac/atrac_scale.h> +#include <vector> +#include <utility> + +namespace NBitStream { + class TBitStream; +} + +namespace NAtracDEnc { +namespace NAtrac3 { + +struct TTonalBlock { + TTonalBlock(const TAtrac3Data::TTonalVal* valPtr, const TScaledBlock& scaledBlock) + : ValPtr(valPtr) + , ScaledBlock(scaledBlock) + {} + const TAtrac3Data::TTonalVal* ValPtr = nullptr; + TScaledBlock ScaledBlock; +}; + +class TAtrac3BitStreamWriter { +public: + struct TSingleChannelElement { + TAtrac3Data::SubbandInfo SubbandInfo; + std::vector<TTonalBlock> TonalBlocks; + std::vector<TScaledBlock> ScaledBlocks; + float Loudness; + }; +private: + static std::vector<float> ATH; + + struct TTonalComponentsSubGroup { + std::vector<uint8_t> SubGroupMap; + std::vector<const TTonalBlock*> SubGroupPtr; + }; + ICompressedOutput* Container; + const TContainerParams Params; + const uint32_t BfuIdxConst; + std::vector<char> OutBuffer; + + uint32_t CLCEnc(const uint32_t selector, const int mantissas[TAtrac3Data::MaxSpecsPerBlock], + const uint32_t blockSize, NBitStream::TBitStream* bitStream); + + uint32_t VLCEnc(const uint32_t selector, const int mantissas[TAtrac3Data::MaxSpecsPerBlock], + const uint32_t blockSize, NBitStream::TBitStream* bitStream); + + std::vector<uint32_t> CalcBitsAllocation(const std::vector<TScaledBlock>& scaledBlocks, + uint32_t bfuNum, float spread, float shift, float loudness); + + std::pair<uint8_t, std::vector<uint32_t>> CreateAllocation(const TSingleChannelElement& sce, + uint16_t targetBits, int mt[TAtrac3Data::MaxSpecs], float laudness); + + std::pair<uint8_t, uint32_t> CalcSpecsBitsConsumption(const TSingleChannelElement& sce, + const std::vector<uint32_t>& precisionPerEachBlocks, + int* mantisas, std::vector<float>& energyErr); + + void EncodeSpecs(const TSingleChannelElement& sce, NBitStream::TBitStream* bitStream, + const std::pair<uint8_t, std::vector<uint32_t>>&, const int mt[TAtrac3Data::MaxSpecs]); + + uint8_t GroupTonalComponents(const std::vector<TTonalBlock>& tonalComponents, + const std::vector<uint32_t>& allocTable, + TTonalComponentsSubGroup groups[64]); + + uint16_t EncodeTonalComponents(const TSingleChannelElement& sce, + const std::vector<uint32_t>& allocTable, + NBitStream::TBitStream* bitStream); +public: + TAtrac3BitStreamWriter(ICompressedOutput* container, const TContainerParams& params, uint32_t bfuIdxConst); + + void WriteSoundUnit(const std::vector<TSingleChannelElement>& singleChannelElements, float laudness); +}; + +} // namespace NAtrac3 +} // namespace NAtracDEnc diff --git a/src/atrac/at3/atrac3_qmf.h b/src/atrac/at3/atrac3_qmf.h new file mode 100644 index 0000000..6c15846 --- /dev/null +++ b/src/atrac/at3/atrac3_qmf.h @@ -0,0 +1,44 @@ +/* + * This file is part of AtracDEnc. + * + * AtracDEnc is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * AtracDEnc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with AtracDEnc; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once +#include <vector> +#include "../qmf/qmf.h" + +namespace NAtracDEnc { + +class Atrac3AnalysisFilterBank { + const static int nInSamples = 1024; + TQmf<nInSamples> Qmf1; + TQmf<nInSamples / 2> Qmf2; + TQmf<nInSamples / 2> Qmf3; + std::vector<float> Buf1; + std::vector<float> Buf2; +public: + Atrac3AnalysisFilterBank() noexcept { + Buf1.resize(nInSamples); + Buf2.resize(nInSamples); + } + void Analysis(const float* pcm, float* subs[4]) noexcept { + Qmf1.Analysis(pcm, Buf1.data(), Buf2.data()); + Qmf2.Analysis(Buf1.data(), subs[0], subs[1]); + Qmf3.Analysis(Buf2.data(), subs[3], subs[2]); + } +}; + +} //namespace NAtracDEnc |