1919#include " atrac3_bitstream.h"
2020#include " qmf/qmf.h"
2121#include < atrac/atrac_psy_common.h>
22+ #include < atrac/atrac_enc_cache.h>
2223#include < bitstream/bitstream.h>
2324#include < util.h>
2425#include < env.h>
@@ -147,40 +148,82 @@ uint32_t VLCEnc(const uint32_t selector, const int mantissas[TAtrac3Data::MaxSpe
147148 return bitsUsed;
148149}
149150
151+ // Cached per-BFU quantization result reused across the bit-allocation binary
152+ // search. For a fixed (channel, bfu, wordlen) within one frame the quantized
153+ // mantissas and their CLC/VLC costs are deterministic, so we compute them once.
154+ class TAt3SpecUnit : public TUnit {
155+ public:
156+ // TEncCache::TProvideUnit factory: build the unit and quantize `values`.
157+ static TUnit* Provide (size_t /* ch*/ , size_t bfu, size_t wordlen, const float * values, void *) {
158+ auto * u = new TAt3SpecUnit ();
159+ const uint32_t first = TAtrac3Data::BlockSizeTab[bfu];
160+ const uint32_t last = TAtrac3Data::BlockSizeTab[bfu + 1 ];
161+ const uint32_t blockSize = last - first;
162+ const float mul = TAtrac3Data::MaxQuant[std::min ((uint32_t )wordlen, (uint32_t )7 )];
163+
164+ u->Wordlen = wordlen;
165+ u->Multiplier = mul;
166+ u->Mantisas .resize (blockSize);
167+ // `ea` (extended/adaptive rounding) depends only on bfu, so it is
168+ // constant for a given cache key.
169+ u->EnergyErr = QuantMantisas (values, 0 , blockSize, mul, bfu > LOSY_NAQ_START , u->Mantisas .data ());
170+ u->ClcBits = CLCEnc (wordlen, u->Mantisas .data (), blockSize, nullptr );
171+ u->VlcBits = VLCEnc (wordlen, u->Mantisas .data (), blockSize, nullptr );
172+ return u;
173+ }
174+
175+ float EnergyErr = 0 .0f ;
176+ uint32_t ClcBits = 0 ; // CLC spectrum cost (no per-block header bits)
177+ uint32_t VlcBits = 0 ; // VLC spectrum cost (no per-block header bits)
178+ };
179+
180+ // atrac3 has only MS stereo and BFUs carry no channel identity, so the cache
181+ // (reset per channel) is keyed purely on <bfu, wordlen>; `ch` is unused.
182+ static size_t MakeAt3SpecKey (size_t /* ch*/ , size_t bfu, size_t wordlen) {
183+ ASSERT (bfu < 32 );
184+ ASSERT (wordlen < 8 );
185+ return (bfu << 3 ) | wordlen;
186+ }
187+ // Upper bound on MakeAt3SpecKey(): bfu < 32, wordlen < 8.
188+ static constexpr size_t kAt3SpecCacheKeys = 1u << 8 ;
189+
150190std::pair<uint8_t , uint32_t > CalcSpecsBitsConsumption (const TAtrac3BitStreamWriter::TSingleChannelElement& sce,
151191 const vector<uint32_t >& precisionPerEachBlocks,
152192 int * mantisas,
153- vector<float >& energyErr)
193+ vector<float >& energyErr,
194+ TEncCache& cache)
154195{
155196 const vector<TScaledBlock>& scaledBlocks = sce.ScaledBlocks ;
156197 const uint32_t numBlocks = precisionPerEachBlocks.size ();
157198 uint32_t bitsUsed = numBlocks * 3 ;
158199
159- auto lambda = [numBlocks, mantisas, &precisionPerEachBlocks, &scaledBlocks, &energyErr](bool clcMode, bool calcMant) {
160- uint32_t bits = 0 ;
161- for (uint32_t i = 0 ; i < numBlocks; ++i) {
162- if (precisionPerEachBlocks[i] == 0 ) {
163- continue ;
164- }
165- bits += 6 ; // sfi
166- const uint32_t first = TAtrac3Data::BlockSizeTab[i];
167- const uint32_t last = TAtrac3Data::BlockSizeTab[i + 1 ];
168- const uint32_t blockSize = last - first;
169- const float mul = TAtrac3Data::MaxQuant[std::min (precisionPerEachBlocks[i], (uint32_t )7 )];
170- if (calcMant) {
171- const float * values = scaledBlocks[i].Values .data ();
172- energyErr[i] = QuantMantisas (values, first, last, mul, i > LOSY_NAQ_START , mantisas);
173- }
174- bits += clcMode ? CLCEnc (precisionPerEachBlocks[i], mantisas + first, blockSize, nullptr )
175- : VLCEnc (precisionPerEachBlocks[i], mantisas + first, blockSize, nullptr );
200+ // Per-block header (sfi) bits are common to both coding modes; only the
201+ // spectrum cost differs. We accumulate the CLC and VLC spectrum costs from
202+ // the cached units and pick the cheaper mode once at the end.
203+ uint32_t clcSpecBits = 0 ;
204+ uint32_t vlcSpecBits = 0 ;
205+ for (uint32_t i = 0 ; i < numBlocks; ++i) {
206+ if (precisionPerEachBlocks[i] == 0 ) {
207+ continue ;
176208 }
177- return bits;
178- };
209+ bitsUsed += 6 ; // sfi
210+ const uint32_t first = TAtrac3Data::BlockSizeTab[i];
211+ const uint32_t last = TAtrac3Data::BlockSizeTab[i + 1 ];
212+ const uint32_t blockSize = last - first;
213+
214+ auto * unit = static_cast <TAt3SpecUnit*>(
215+ cache.GetOrCompute (0 , i, precisionPerEachBlocks[i], scaledBlocks[i].Values .data ()));
179216
180- const uint32_t clcBits = lambda (true , true );
181- const uint32_t vlcBits = lambda (false , false );
182- const bool mode = clcBits <= vlcBits;
183- return std::make_pair (mode, bitsUsed + (mode ? clcBits : vlcBits));
217+ // Mirror the cached block-local mantissas into the frame-global array
218+ // for the eventual EncodeSpecs() dump.
219+ std::copy_n (unit->GetMantisas ().data (), blockSize, mantisas + first);
220+ energyErr[i] = unit->EnergyErr ;
221+ clcSpecBits += unit->ClcBits ;
222+ vlcSpecBits += unit->VlcBits ;
223+ }
224+
225+ const bool mode = clcSpecBits <= vlcSpecBits;
226+ return std::make_pair (mode, bitsUsed + (mode ? clcSpecBits : vlcSpecBits));
184227}
185228
186229static inline bool CheckBfus (uint16_t * numBfu, const vector<uint32_t >& precisionPerEachBlocks)
@@ -593,7 +636,8 @@ class TAlloc final : public IBitStreamPartEncoder {
593636 ctx->EnergyErr .assign (ctx->NumBfu , 0 .0f );
594637 std::pair<uint8_t , uint32_t > consumption;
595638 do {
596- consumption = CalcSpecsBitsConsumption (*ctx->Sce , tmpAlloc, ctx->Mantissas .data (), ctx->EnergyErr );
639+ consumption = CalcSpecsBitsConsumption (*ctx->Sce , tmpAlloc, ctx->Mantissas .data (),
640+ ctx->EnergyErr , SpecCache);
597641 } while (ConsiderEnergyErr (ctx->EnergyErr , tmpAlloc));
598642
599643 uint32_t totalBits = consumption.second + EncodeTonalComponents (*ctx->Sce , tmpAlloc, nullptr );
@@ -615,11 +659,13 @@ class TAlloc final : public IBitStreamPartEncoder {
615659 }
616660
617661 void Dump (NBitStream::TBitStream& bs) override {
618- if (!Ctx) {
619- return ;
662+ if (Ctx) {
663+ EncodeSpecs (*Ctx->Sce , &bs, Ctx->PrecisionPerBlock , Ctx->CodingMode , Ctx->Mantissas .data ());
664+ Ctx = nullptr ;
620665 }
621- EncodeSpecs (*Ctx->Sce , &bs, Ctx->PrecisionPerBlock , Ctx->CodingMode , Ctx->Mantissas .data ());
622- Ctx = nullptr ;
666+ // The cached quantization results are only valid for the channel/frame
667+ // just finished; drop them before the next channel reuses this part.
668+ SpecCache.Reset ();
623669 }
624670
625671 void Reset () noexcept override {
@@ -632,6 +678,7 @@ class TAlloc final : public IBitStreamPartEncoder {
632678
633679private:
634680 TEncodeCtx* Ctx = nullptr ;
681+ TEncCache SpecCache{kAt3SpecCacheKeys , &TAt3SpecUnit::Provide, &MakeAt3SpecKey};
635682};
636683
637684std::vector<IBitStreamPartEncoder::TPtr> CreateEncParts ()
0 commit comments