diff options
author | Daniil Cherednik <dan.cherednik@gmail.com> | 2025-05-31 22:39:38 +0200 |
---|---|---|
committer | Daniil Cherednik <dan.cherednik@gmail.com> | 2025-05-31 22:39:38 +0200 |
commit | 1c7f2f821fb965af468cdf2a14df3ff75cc1c352 (patch) | |
tree | 1bc92237122b75c67afc326af207cf3cc9eb3d6c /src | |
parent | 272af27a3d148bd13e8f15640e53ca70c64ccb9b (diff) | |
parent | 6dfc60e9d4791c3385908c61ad75c4a0093ea1eb (diff) | |
download | atracdenc-1c7f2f821fb965af468cdf2a14df3ff75cc1c352.tar.gz |
Merge branch 'at3plus-dev'
It looks like we are able to encode ATRAC3PLUS compatible bitstream so we can merge at3p development branch in to the main branch.
Current limitation for AT3P mode:
- Only 352 Kbps (proper bit allocation and some psychoacoustic must be implemented)
- GHA sometime works with error (but huge bitrate hide it)
- No VLC, VQ, delta encoding
- No noise substitution
- No gain control
- No window shape switching
Diffstat (limited to 'src')
37 files changed, 6420 insertions, 28 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 57a5e41..17f35b3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,10 +1,10 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.1...3.26) -set (CMAKE_CXX_STANDARD 11) +set (CMAKE_CXX_STANDARD 14) set (CMAKE_C_STANDARD 11) #add_definitions( "-Wall -O2 -g -Rpass-analysis=loop-vectorize" ) -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address -fno-omit-frame-pointer") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address -fno-omit-frame-pointer") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE) @@ -59,11 +59,19 @@ include_directories( "." "lib" "lib/liboma/include" + "lib/fft/kissfft_impl" ) -set(SOURCE_FFT_LIB lib/fft/kissfft_impl/kiss_fft.c) +set(SOURCE_FFT_LIB + lib/fft/kissfft_impl/kiss_fft.c + lib/fft/kissfft_impl/tools/kiss_fftr.c +) + +set_source_files_properties(${SOURCE_FFT_LIB} PROPERTIES COMPILE_FLAGS -Dkiss_fft_scalar=float) +add_library(fft_impl STATIC ${SOURCE_FFT_LIB}) -set_source_files_properties(lib/fft/kissfft_impl/kiss_fft.c PROPERTIES COMPILE_FLAGS -Dkiss_fft_scalar=float) +set(GHA_FFT_LIB fft_impl) +add_subdirectory(lib/libgha) set(SOURCE_OMA_LIB lib/liboma/src/liboma.c) set(SOURCE_BITSTREAM_LIB lib/bitstream/bitstream.cpp) @@ -84,11 +92,17 @@ set(SOURCE_ATRACDENC_IMPL atrac3denc.cpp atrac/atrac3.cpp atrac/atrac3_bitstream.cpp + atrac/atrac3plus_pqf/atrac3plus_pqf.c + atrac/at3p/ff/atrac3plusdsp.c + atrac/at3p/at3p.cpp + atrac/at3p/at3p_bitstream.cpp + atrac/at3p/at3p_gha.cpp + atrac/at3p/at3p_mdct.cpp + atrac/at3p/at3p_tables.cpp lib/mdct/mdct.cpp + lib/bs_encode/encode.cpp ) -add_library(fft_impl STATIC ${SOURCE_FFT_LIB}) - add_library(pcm_io STATIC ${SOURCE_PCM_IO_LIB}) if (NOT WIN32) target_link_libraries(pcm_io ${SNDFILE_LIBRARIES}) @@ -97,7 +111,7 @@ endif() add_library(oma STATIC ${SOURCE_OMA_LIB}) add_library(bitstream STATIC ${SOURCE_BITSTREAM_LIB}) add_library(atracdenc_impl STATIC ${SOURCE_ATRACDENC_IMPL}) -target_link_libraries(atracdenc_impl fft_impl pcm_io oma bitstream ${SNDFILE_LIBRARIES}) +target_link_libraries(atracdenc_impl fft_impl pcm_io oma bitstream ${SNDFILE_LIBRARIES} gha) set(SOURCE_EXE main.cpp help.cpp @@ -105,3 +119,10 @@ set(SOURCE_EXE add_executable(atracdenc ${SOURCE_EXE}) target_link_libraries(atracdenc pcm_io oma atracdenc_impl ${SNDFILE_LIBRARIES}) install(TARGETS atracdenc) + +#DEVTOOL +set(GHASENDTOOL_EXE + atrac/at3p/ghasend_tool.cpp +) +add_executable(ghasendtool ${GHASENDTOOL_EXE}) +target_link_libraries(ghasendtool pcm_io oma atracdenc_impl ${SNDFILE_LIBRARIES}) diff --git a/src/atrac/at3p/at3p.cpp b/src/atrac/at3p/at3p.cpp new file mode 100644 index 0000000..c48c21c --- /dev/null +++ b/src/atrac/at3p/at3p.cpp @@ -0,0 +1,257 @@ +/* + * 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 <atrac3p.h> + +#include <atrac/atrac3plus_pqf/atrac3plus_pqf.h> + +#include "at3p_bitstream.h" +#include "at3p_gha.h" +#include "at3p_mdct.h" +#include "at3p_tables.h" +#include <atrac/atrac_scale.h> + +#include <cassert> +#include <vector> +#include <unordered_map> + +using std::vector; + +namespace NAtracDEnc { + +class TAt3PEnc::TImpl { +public: + TImpl(ICompressedOutput* out, int channels, TSettings settings) + : BitStream(out, 2048) + , ChannelCtx(channels) + , GhaProcessor(MakeGhaProcessor0(channels == 2)) + , Settings(settings) + { + delay.NumToneBands = 0; + } + + TPCMEngine::EProcessResult EncodeFrame(const float* data, int channels); +private: + struct TChannelCtx { + TChannelCtx() + : PqfCtx(at3plus_pqf_create_a_ctx()) + , Specs(TAt3PEnc::NumSamples) + {} + + ~TChannelCtx() { + at3plus_pqf_free_a_ctx(PqfCtx); + } + + at3plus_pqf_a_ctx_t PqfCtx; + + float* NextBuf = Buf1; + float* CurBuf = nullptr; + float Buf1[TAt3PEnc::NumSamples]; + float Buf2[TAt3PEnc::NumSamples]; + float PrevBuf[TAt3PEnc::NumSamples]; + TAt3pMDCT::THistBuf MdctBuf; + std::vector<float> Specs; + }; + + TAt3pMDCT Mdct; + TScaler<NAt3p::TScaleTable> Scaler; + TAt3PBitStream BitStream; + vector<TChannelCtx> ChannelCtx; + std::unique_ptr<IGhaProcessor> GhaProcessor; + TAt3PGhaData delay; + const TSettings Settings; +}; + +TPCMEngine::EProcessResult TAt3PEnc::TImpl:: +EncodeFrame(const float* data, int channels) +{ + int needMore = 0; + for (int ch = 0; ch < channels; ch++) { + float src[TAt3PEnc::NumSamples]; + for (size_t i = 0; i < NumSamples; ++i) { + src[i] = data[i * channels + ch]; + } + + at3plus_pqf_do_analyse(ChannelCtx[ch].PqfCtx, src, ChannelCtx[ch].NextBuf); + if (ChannelCtx[ch].CurBuf == nullptr) { + assert(ChannelCtx[ch].NextBuf == ChannelCtx[ch].Buf1); + ChannelCtx[ch].CurBuf = ChannelCtx[ch].Buf2; + std::swap(ChannelCtx[ch].NextBuf, ChannelCtx[ch].CurBuf); + needMore++; + } + } + + if (needMore == channels) { + return TPCMEngine::EProcessResult::LOOK_AHEAD; + } + + assert(needMore == 0); + + float* b1Prev = ChannelCtx[0].PrevBuf; + const float* b1Cur = ChannelCtx[0].CurBuf; + const float* b1Next = ChannelCtx[0].NextBuf; + float* b2Prev = (channels == 2) ? ChannelCtx[1].PrevBuf : nullptr; + const float* b2Cur = (channels == 2) ? ChannelCtx[1].CurBuf : nullptr; + const float* b2Next = (channels == 2) ? ChannelCtx[1].NextBuf : nullptr; + + + const TAt3PGhaData* p = nullptr; + if (delay.NumToneBands) { + p = &delay; + } + + const TAt3PGhaData* tonalBlock = GhaProcessor->DoAnalize({b1Cur, b1Next}, {b2Cur, b2Next}, b1Prev, b2Prev); + + std::vector<std::vector<TScaledBlock>> scaledBlocks; + for (int ch = 0; ch < channels; ch++) { + float* x = (ch == 0) ? b1Prev : b2Prev; + auto& c = ChannelCtx[ch]; + TAt3pMDCT::TPcmBandsData p; + float tmp[2048]; + //TODO: scale window + if (Settings.UseGha & TSettings::GHA_WRITE_RESIUDAL) { + for (size_t i = 0; i < 2048; i++) { + //TODO: find why we need to add the 0.5db + tmp[i] = x[i] / (32768.0 / 1.122018); + } + } else { + for (size_t i = 0; i < 2048; i++) { + tmp[i] = 0.0; + } + } + for (size_t b = 0; b < 16; b++) { + p[b] = tmp + b * 128; + } + Mdct.Do(c.Specs.data(), p, c.MdctBuf); + + const auto& block = Scaler.ScaleFrame(c.Specs, NAt3p::TScaleTable::TBlockSizeMod()); + scaledBlocks.push_back(block); + } + + BitStream.WriteFrame(channels, p, scaledBlocks); + + for (int ch = 0; ch < channels; ch++) { + if (Settings.UseGha & TSettings::GHA_PASS_INPUT) { + memcpy(ChannelCtx[ch].PrevBuf, ChannelCtx[ch].CurBuf, sizeof(float) * TAt3PEnc::NumSamples); + } else { + memset(ChannelCtx[ch].PrevBuf, 0, sizeof(float) * TAt3PEnc::NumSamples); + } + std::swap(ChannelCtx[ch].NextBuf, ChannelCtx[ch].CurBuf); + } + if (tonalBlock && (Settings.UseGha & TSettings::GHA_WRITE_TONAL)) { + delay = *tonalBlock; + } else { + delay.NumToneBands = 0; + } + + return TPCMEngine::EProcessResult::PROCESSED; +} + +TAt3PEnc::TAt3PEnc(TCompressedOutputPtr&& out, int channels, TSettings settings) + : Out(std::move(out)) + , Channels(channels) + , Impl(new TImpl(Out.get(), Channels, settings)) +{ +} + +TPCMEngine::TProcessLambda TAt3PEnc::GetLambda() { + return [this](float* data, const TPCMEngine::ProcessMeta&) { + return Impl->EncodeFrame(data, Channels); + }; +} + +static void SetGha(const std::string& str, TAt3PEnc::TSettings& settings) { + int mask = std::stoi(str); + if (mask > 7 || mask < 0) { + throw std::runtime_error("invalud value of GHA processing mask"); + } + + if (mask & TAt3PEnc::TSettings::GHA_PASS_INPUT) + std::cerr << "GHA_PASS_INPUT" << std::endl; + if (mask & TAt3PEnc::TSettings::GHA_WRITE_RESIUDAL) + std::cerr << "GHA_WRITE_RESIUDAL" << std::endl; + if (mask & TAt3PEnc::TSettings::GHA_WRITE_TONAL) + std::cerr << "GHA_WRITE_TONAL" << std::endl; + + settings.UseGha = mask; +} + + + +void TAt3PEnc::ParseAdvancedOpt(const char* opt, TSettings& settings) { + typedef void (*processFn)(const std::string& str, TSettings& settings); + static std::unordered_map<std::string, processFn> keys { + {"ghadbg", &SetGha} + }; + + if (opt == nullptr) + return; + + const char* start = opt; + bool vState = false; //false - key state, true - value state + processFn handler = nullptr; + + while (opt) { + if (!vState) { + if (*opt == ',') { + throw std::runtime_error("unexpected \",\" just after key."); +// if (opt - start > 0) { +// } +// opt++; +// start = opt; + } else if (*opt == '=') { + auto it = keys.find(std::string(start, opt - start)); + if (it == keys.end()) { + throw std::runtime_error(std::string("unexpected advanced option \"") + + std::string(start, opt - start)); + } + handler = it->second; + vState = true; + opt++; + start = opt; + } else if (!*opt) { + throw std::runtime_error("unexpected end of key token"); +// if (opt - start > 0) { +// } +// opt = nullptr; + } else { + opt++; + } + } else { + if (*opt == ',') { + if (opt - start > 0) { + handler(std::string(start, opt - start), settings); + } + opt++; + start = opt; + vState = false; + } else if (*opt == '=') { + throw std::runtime_error("unexpected \"=\" inside value token."); + } else if (!*opt) { + if (opt - start > 0) { + handler(std::string(start, opt - start), settings); + } + opt = nullptr; + } else { + opt++; + } + } + } +} + +} diff --git a/src/atrac/at3p/at3p_bitstream.cpp b/src/atrac/at3p/at3p_bitstream.cpp new file mode 100644 index 0000000..f95018b --- /dev/null +++ b/src/atrac/at3p/at3p_bitstream.cpp @@ -0,0 +1,624 @@ +/* + * 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 "at3p_bitstream_impl.h" +#include "at3p_bitstream.h" +#include "at3p_gha.h" +#include "at3p_tables.h" +#include <env.h> +#include <util.h> + +#include "ff/atrac3plus_data.h" + +#include <iostream> +#include <limits> +#include <memory> + +namespace NAtracDEnc { + +using namespace NAt3p; +using std::vector; +using std::make_pair; +using std::pair; + +static THuffTables HuffTabs; + +TTonePackResult CreateFreqBitPack(const TAt3PGhaData::TWaveParam* const param, int len) +{ + const int MaxBits = 10; + + int bits[2] = {MaxBits, MaxBits}; + + std::vector<TTonePackResult::TEntry> res[2]; + res[0].reserve(len); + res[1].reserve(len); + + // try ascending order + { + auto& t = res[0]; + uint16_t prevFreqIndex = param->FreqIndex & 1023; + t.emplace_back(TTonePackResult::TEntry{prevFreqIndex, MaxBits}); + + for (int i = 1; i < len; i++) { + uint16_t curFreqIndex = param[i].FreqIndex & 1023; + if (prevFreqIndex < 512) { + t.emplace_back(TTonePackResult::TEntry{curFreqIndex, MaxBits}); + bits[0] += MaxBits; + } else { + uint16_t b = GetFirstSetBit(1023 - prevFreqIndex) + 1; + uint16_t code = curFreqIndex - (1024 - (1 << b)); + t.emplace_back(TTonePackResult::TEntry{code, b}); + bits[0] += b; + } + prevFreqIndex = curFreqIndex; + } + } + + // try descending order + if (len > 1) { + auto& t = res[1]; + uint16_t prevFreqIndex = param[len - 1].FreqIndex & 1023; + t.emplace_back(TTonePackResult::TEntry{prevFreqIndex, MaxBits}); + + for (int i = len - 2; i >= 0; i--) { + uint16_t curFreqIndex = param[i].FreqIndex & 1023; + uint16_t b = GetFirstSetBit(prevFreqIndex) + 1; + t.emplace_back(TTonePackResult::TEntry{curFreqIndex, b}); + bits[1] += b; + prevFreqIndex = curFreqIndex; + } + + } + + if (len == 1 || bits[0] < bits[1]) { + return {res[0], bits[0], ETonePackOrder::ASC}; + } else { + return {res[1], bits[1], ETonePackOrder::DESC}; + } +} + +uint32_t TDumper::GetConsumption() const noexcept +{ + return std::accumulate(Buf.begin(), Buf.end(), 0, + [](uint32_t acc, const std::pair<uint16_t, uint8_t>& x) noexcept -> uint32_t { return acc + x.second; }); +} + +IBitStreamPartEncoder::EStatus TConfigure::Encode(void* frameData, TBitAllocHandler&) +{ + TSpecFrame* frame = TSpecFrame::Cast(frameData); + + frame->WordLen.resize(frame->NumQuantUnits); + + for (size_t i = 0; i < frame->WordLen.size(); i++) { + static uint8_t allocTable[32] = { + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 5, 5, 4, 3, 2, 1 + }; + frame->WordLen[i].first = allocTable[i]; + frame->WordLen[i].second = allocTable[i]; + } + + frame->SfIdx.resize(frame->NumQuantUnits); + + for (size_t i = 0; i < frame->SfIdx.size(); i++) { + frame->SfIdx[i].first = frame->Chs[0].ScaledBlocks.at(i).ScaleFactorIndex; + if (frame->Chs.size() > 1) + frame->SfIdx[i].second = frame->Chs[1].ScaledBlocks.at(i).ScaleFactorIndex; + } + + frame->SpecTabIdx.resize(frame->NumQuantUnits); + + Insert(frame->NumQuantUnits - 1, 5); + Insert(0, 1); //mute flag + + frame->AllocatedBits = GetConsumption(); + + return EStatus::Ok; +} + +IBitStreamPartEncoder::EStatus TWordLenEncoder::Encode(void* frameData, TBitAllocHandler&) { + auto specFrame = TSpecFrame::Cast(frameData); + + ASSERT(specFrame->WordLen.size() > specFrame->NumQuantUnits); + for (size_t ch = 0; ch < specFrame->Chs.size(); ch++) { + + Insert(0, 2); // 0 - constant number of bits + + if (ch == 0) { + for (size_t i = 0; i < specFrame->NumQuantUnits; i++) { + Insert(specFrame->WordLen[i].first, 3); + } + } else { + for (size_t i = 0; i < specFrame->NumQuantUnits; i++) { + Insert(specFrame->WordLen[i].second, 3); + } + } + } + + return EStatus::Ok; +} + +IBitStreamPartEncoder::EStatus TSfIdxEncoder::Encode(void* frameData, TBitAllocHandler&) { + auto specFrame = TSpecFrame::Cast(frameData); + + if (specFrame->SfIdx.empty()) { + return EStatus::Ok; + } + + for (size_t ch = 0; ch < specFrame->Chs.size(); ch++) { + + Insert(0, 2); // 0 - constant number of bits + + if (ch == 0) { + for (size_t i = 0; i < specFrame->NumQuantUnits; i++) { + Insert(specFrame->SfIdx[i].first, 6); + } + } else { + for (size_t i = 0; i < specFrame->NumQuantUnits; i++) { + Insert(specFrame->SfIdx[i].second, 6); + } + } + } + + return EStatus::Ok; +} + +void TQuantUnitsEncoder::EncodeCodeTab(bool useFullTable, size_t channels, + size_t numQuantUnits, const std::vector<std::pair<uint8_t, uint8_t>>& specTabIdx, + std::vector<std::pair<uint16_t, uint8_t>>& data) +{ + data.emplace_back(useFullTable, 1); // use full table + + for (size_t ch = 0; ch < channels; ch++) { + + data.emplace_back(0, 1); // table type + + data.emplace_back(0, 2); // 0 - constant number of bits + + data.emplace_back(0, 1); // num_coded_vals equal to used_quant_units + + if (ch == 0) { + for (size_t i = 0; i < numQuantUnits; i++) { + data.emplace_back(specTabIdx[i].first, useFullTable + 2); + } + } else { + for (size_t i = 0; i < numQuantUnits; i++) { + data.emplace_back(specTabIdx[i].second, useFullTable + 2); + } + } + } +} + +void TQuantUnitsEncoder::EncodeQuSpectra(const int* qspec, const size_t num_spec, const size_t idx, + std::vector<std::pair<uint16_t, uint8_t>>& data) { + const Atrac3pSpecCodeTab *tab = &atrac3p_spectra_tabs[idx]; + const std::array<TVlcElement, 256>& vlcTab = HuffTabs.VlcSpecs[idx]; + + size_t groupSize = tab->group_size; + size_t numCoeffs = tab->num_coeffs; + size_t bitsCoeff = tab->bits; + bool isSigned = tab->is_signed; + + for (size_t pos = 0; pos < num_spec;) { + if (groupSize != 1) { + // TODO: Acording to FFmpeg it should be possible + // to skip group, if all rest of coeffs is zero + // but this should be checked with real AT3P decoder + data.emplace_back(1, 1); + } + + for (size_t j = 0; j < groupSize; j++) { + uint32_t val = 0; + int8_t signs[4] = {0}; + for (size_t i = 0; i < numCoeffs; i++) { + int16_t t = qspec[pos++]; +#ifndef NDEBUG + { + uint16_t x = std::abs(t); + x >>= (uint16_t)(bitsCoeff - (int)isSigned); + ASSERT(x == 0); + } +#endif + if (!isSigned && t != 0) { + signs[i] = t > 0 ? 1 : -1; + if (t < 0) + t = -t; + } else { + t = t & ((1u << (bitsCoeff)) - 1); + } + t <<= (bitsCoeff * i); + val |= t; + } + + ASSERT(val > 255); + + const TVlcElement& el = vlcTab.at(val); + + data.emplace_back(el.Code, el.Len); + for (size_t i = 0; i < 4; i++) { + if (signs[i] != 0) { + if (signs[i] > 0) { + data.emplace_back(0, 1); + } else { + data.emplace_back(1, 1); + } + } + } + } + } +} + +size_t TQuantUnitsEncoder::TUnit::MakeKey(size_t ch, size_t qu, size_t worlen) { + ASSERT(qu < 32); + ASSERT(worlen < 8); + return (ch << 8) | (qu << 3) | worlen; +} + +TQuantUnitsEncoder::TUnit::TUnit(size_t qu, size_t wordlen) + : Wordlen(wordlen) + , Multiplier(1.0f / atrac3p_mant_tab[wordlen]) +{ + Mantisas.resize(TScaleTable::SpecsPerBlock[qu]); +} + +size_t TQuantUnitsEncoder::TUnit::GetOrCompute(const float* val, std::vector<std::pair<uint16_t, uint8_t>>& res) +{ + QuantMantisas(val, 0, Mantisas.size(), Multiplier, false, Mantisas.data()); + + std::vector<std::pair<uint16_t, uint8_t>> tmp; + + size_t bestTab = 0; + size_t consumed = std::numeric_limits<size_t>::max(); + + for (size_t i = 0, tabIndex = Wordlen - 1; i < 8; i++, tabIndex += 7) { + + EncodeQuSpectra(Mantisas.data(), Mantisas.size(), tabIndex, tmp); + + size_t t = std::accumulate(tmp.begin(), tmp.end(), 0, + [](size_t acc, const std::pair<uint8_t, uint16_t>& x) noexcept -> size_t + { return acc + x.second; }); + + if (t < consumed) { + consumed = t; + ConsumedBits = t; + bestTab = i; + res.clear(); + res.swap(tmp); + tmp.clear(); + } else { + tmp.clear(); + } + } + return bestTab; +} + +IBitStreamPartEncoder::EStatus TQuantUnitsEncoder::Encode(void* frameData, TBitAllocHandler&) +{ + auto specFrame = TSpecFrame::Cast(frameData); + std::vector< + std::vector<std::pair<uint16_t, uint8_t>>> data; + for (size_t ch = 0; ch < specFrame->Chs.size(); ch++) { + auto& chData = specFrame->Chs.at(ch); + auto scaledBlocks = chData.ScaledBlocks; + + for (size_t qu = 0; qu < specFrame->NumQuantUnits; qu++) { + size_t len = (ch == 0) ? + specFrame->WordLen.at(qu).first : + specFrame->WordLen.at(qu).second; + + size_t key = TUnit::MakeKey(ch, qu, len); + + TUnit* unit; + // try_emplace + auto it = UnitBuffers.find(key); + if (it == UnitBuffers.end()) { + unit = &(UnitBuffers.emplace(key, TUnit(qu, len)).first->second); + } else { + unit = &it->second; + } + + const float* values = scaledBlocks.at(qu).Values.data(); + + data.push_back(std::vector<std::pair<uint16_t, uint8_t>>()); + auto tabIdx = unit->GetOrCompute(values, data.back()); + + if (ch == 0) { + specFrame->SpecTabIdx[qu].first = tabIdx; + } else { + specFrame->SpecTabIdx[qu].second = tabIdx; + } + } + if (true /*frame.NumUsedQuantUnits > 2*/) { + size_t numPwrSpec = atrac3p_subband_to_num_powgrps[atrac3p_qu_to_subband[specFrame->NumQuantUnits - 1]]; + data.push_back(std::vector<std::pair<uint16_t, uint8_t>>()); + auto& t = data.back(); + for (size_t i = 0; i < numPwrSpec; i++) { + t.emplace_back(15, 4); + } + } + } + + { + std::vector<std::pair<uint16_t, uint8_t>> tabIdxData; + EncodeCodeTab(true, specFrame->Chs.size(), specFrame->NumQuantUnits, specFrame->SpecTabIdx, tabIdxData); + for (size_t i = 0; i < tabIdxData.size(); i++) { + Insert(tabIdxData[i].first, tabIdxData[i].second); + } + } + + for (const auto& x : data) { + for (size_t i = 0; i < x.size(); i++) { + Insert(x[i].first, x[i].second); + } + } + + return EStatus::Ok; +} + +static std::vector<IBitStreamPartEncoder::TPtr> CreateEncParts() +{ + vector<IBitStreamPartEncoder::TPtr> parts; + parts.emplace_back(new TConfigure()); + parts.emplace_back(new TWordLenEncoder()); + parts.emplace_back(new TSfIdxEncoder()); + parts.emplace_back(new TQuantUnitsEncoder()); + parts.emplace_back(new TTonalComponentEncoder()); + + return parts; +} + +TAt3PBitStream::TAt3PBitStream(ICompressedOutput* container, uint16_t frameSz) + : Container(container) + , Encoder(CreateEncParts()) + , FrameSzToAllocBits((uint32_t)frameSz * 8 - 3) //Size of frame in bits for allocation. 3 bits is start bit and channel configuration + , FrameSz(frameSz) +{ + NEnv::SetRoundFloat(); +} + +void TTonalComponentEncoder::WriteSubbandFlags(const bool* flags, size_t numFlags) +{ + + size_t sum = 0; + for (size_t i = 0; i < numFlags; i++) { + sum += (uint32_t)flags[i]; + } + + if (sum == 0) { + Insert(0, 1); + } else if (sum == numFlags) { + Insert(1, 1); + Insert(0, 1); + } else { + Insert(1, 1); + Insert(1, 1); + for (size_t i = 0; i < numFlags; i++) { + Insert(flags[i], 1); + } + } +} + +void TTonalComponentEncoder::WriteTonalBlock(size_t channels, const TAt3PGhaData* tonalBlock) +{ + //GHA amplidude mode 1 + Insert(1, 1); + + //Num tone bands + const TVlcElement& tbHuff = HuffTabs.NumToneBands[tonalBlock->NumToneBands - 1]; + Insert(tbHuff.Code, tbHuff.Len); + + if (channels == 2) { + WriteSubbandFlags(tonalBlock->ToneSharing, tonalBlock->NumToneBands); + WriteSubbandFlags(&tonalBlock->SecondIsLeader, 1); + Insert(0, 1); + } + + for (size_t ch = 0; ch < channels; ch++) { + if (ch) { + // each channel has own envelope + Insert(0, 1); + } + // Envelope data + for (int i = 0; i < tonalBlock->NumToneBands; i++) { + if (ch && tonalBlock->ToneSharing[i]) { + continue; + } + + const auto envelope = tonalBlock->GetEnvelope(ch, i); + + if (envelope.first != TAt3PGhaData::EMPTY_POINT) { + // start point present + Insert(1, 1); + Insert(envelope.first, 5); + } else { + Insert(0, 1); + } + + if (envelope.second != TAt3PGhaData::EMPTY_POINT) { + // stop point present + Insert(1, 1); + Insert(envelope.second, 5); + } else { + Insert(0, 1); + } + } + + // Num waves + int mode = 0; //TODO: Calc mode + Insert(mode, ch + 1); + for (int i = 0; i < tonalBlock->NumToneBands; i++) { + if (ch && tonalBlock->ToneSharing[i]) { + continue; + } + Insert(tonalBlock->GetNumWaves(ch, i), 4); + } + // Tones freq + if (ch) { + // 0 - independed + // 1 - delta to leader + Insert(0, 1); + } + + for (int i = 0; i < tonalBlock->NumToneBands; i++) { + if (ch && tonalBlock->ToneSharing[i]) { + continue; + } + + auto numWaves = tonalBlock->GetNumWaves(ch, i); + if (numWaves == 0) { + continue; + } + + const auto w = tonalBlock->GetWaves(ch, i); + const auto pkt = CreateFreqBitPack(w.first, w.second); + + if (numWaves > 1) { + Insert(static_cast<bool>(pkt.Order), 1); + } + + for (const auto& d : pkt.Data) { + Insert(d.Code, d.Bits); + } + } + + // Amplitude + mode = 0; //TODO: Calc mode + Insert(mode, ch + 1); + + for (int i = 0; i < tonalBlock->NumToneBands; i++) { + if (ch && tonalBlock->ToneSharing[i]) { + continue; + } + + auto numWaves = tonalBlock->GetNumWaves(ch, i); + if (numWaves == 0) { + continue; + } + + const auto w = tonalBlock->GetWaves(ch, i); + for (size_t j = 0; j < numWaves; j++) { + Insert(w.first[j].AmpSf, 6); + } + } + + // Phase + for (int i = 0; i < tonalBlock->NumToneBands; i++) { + if (ch && tonalBlock->ToneSharing[i]) { + continue; + } + + auto numWaves = tonalBlock->GetNumWaves(ch, i); + if (numWaves == 0) { + continue; + } + + const auto w = tonalBlock->GetWaves(ch, i); + for (size_t j = 0; j < w.second; j++) { + Insert(w.first[j].PhaseIndex, 5); + } + } + } +} + +IBitStreamPartEncoder::EStatus TTonalComponentEncoder::CheckFrameDone(TSpecFrame* frame, TBitAllocHandler& ba) noexcept +{ + uint32_t totalConsumption = BitsUsed + ba.GetCurGlobalConsumption(); + + if (totalConsumption > frame->SizeBits) { + if (frame->NumQuantUnits == 32) { + frame->NumQuantUnits = 28; + } else { + frame->NumQuantUnits--; + } + return EStatus::Repeat; + } + return EStatus::Ok; +} + +IBitStreamPartEncoder::EStatus TTonalComponentEncoder::Encode(void* frameData, TBitAllocHandler& ba) +{ + auto specFrame = TSpecFrame::Cast(frameData); + auto tonalBlock = specFrame->TonalBlock; + + // Check tonal component already encoded + if (BitsUsed != 0) { + if (tonalBlock && tonalBlock->NumToneBands > specFrame->NumQuantUnits) { + std::cerr << "TODO" << std::endl; + abort(); + } + return CheckFrameDone(specFrame, ba); + } + + const size_t chNum = specFrame->Chs.size(); + + if (chNum == 2) { + Insert(0, 2); //swap_channels and negate_coeffs + } + + for (size_t ch = 0; ch < chNum; ch++) { + Insert(0, 1); // window shape + } + + for (size_t ch = 0; ch < chNum; ch++) { + Insert(0, 1); //gain comp + } + + if (tonalBlock && tonalBlock->NumToneBands) { + Insert(1, 1); + WriteTonalBlock(chNum, tonalBlock); + } else { + Insert(0, 1); + } + + Insert(0, 1); // no noise info + // Terminator + Insert(3, 2); + + BitsUsed = GetConsumption(); + + return CheckFrameDone(specFrame, ba); +} + +void TAt3PBitStream::WriteFrame(int channels, const TAt3PGhaData* tonalBlock, const std::vector<std::vector<TScaledBlock>>& scaledBlocks) +{ + NBitStream::TBitStream bitStream; + // First bit must be zero + bitStream.Write(0, 1); + // Channel block type + // 0 - MONO block + // 1 - STEREO block + // 2 - Nobody know + bitStream.Write(channels - 1, 2); + + const uint32_t initialNumQuantUnits = 32; + + TSpecFrame frame(FrameSzToAllocBits, initialNumQuantUnits, channels, tonalBlock, scaledBlocks); + + Encoder.Do(&frame, bitStream); + + std::vector<char> buf = bitStream.GetBytes(); + + ASSERT(bitStream.GetSizeInBits() > FrameSz * 8); + + buf.resize(FrameSz); + Container->WriteFrame(buf); +} + +} diff --git a/src/atrac/at3p/at3p_bitstream.h b/src/atrac/at3p/at3p_bitstream.h new file mode 100644 index 0000000..25edd1e --- /dev/null +++ b/src/atrac/at3p/at3p_bitstream.h @@ -0,0 +1,59 @@ +/* + * 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 "compressed_io.h" +#include "at3p_gha.h" +#include <lib/bs_encode/encode.h> + +namespace NAtracDEnc { + +struct TScaledBlock; + +struct TAt3PGhaData; + +enum class ETonePackOrder : bool { + ASC = false, + DESC = true +}; + +struct TTonePackResult { + struct TEntry { + uint16_t Code; + uint16_t Bits; + }; + std::vector<TEntry> Data; + int UsedBits; + ETonePackOrder Order; +}; + +TTonePackResult CreateFreqBitPack(const TAt3PGhaData::TWaveParam* param, int len); + +class TAt3PBitStream { +public: + TAt3PBitStream(ICompressedOutput* container, uint16_t frameSz); + void WriteFrame(int channels, const TAt3PGhaData* tonalData, const std::vector<std::vector<TScaledBlock>>& scaledBlocks); +private: + ICompressedOutput* Container; + TBitStreamEncoder Encoder; + const uint32_t FrameSzToAllocBits; + const uint16_t FrameSz; +}; + +} diff --git a/src/atrac/at3p/at3p_bitstream_impl.h b/src/atrac/at3p/at3p_bitstream_impl.h new file mode 100644 index 0000000..91b2df2 --- /dev/null +++ b/src/atrac/at3p/at3p_bitstream_impl.h @@ -0,0 +1,149 @@ +#pragma once + +/* + * 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 "atrac/at3p/at3p_gha.h" +#include <lib/bitstream/bitstream.h> +#include <lib/bs_encode/encode.h> +#include <atrac/atrac_scale.h> +#include <vector> + +namespace NAtracDEnc { + +struct TSpecFrame { + TSpecFrame(uint32_t sz, uint32_t numQuantUnits, size_t channels, + const TAt3PGhaData* tonalBlock, + const std::vector<std::vector<TScaledBlock>>& scaledBlocks) + : SizeBits(sz) + , NumQuantUnits(numQuantUnits) + , TonalBlock(tonalBlock) + , AllocatedBits(0) + { + Chs.reserve(channels); + for (size_t i = 0; i < channels; i++) { + Chs.emplace_back(TChannel(scaledBlocks[i])); + } + } + + const uint32_t SizeBits; + uint32_t NumQuantUnits; + const TAt3PGhaData* TonalBlock; + std::vector<std::pair<uint8_t, uint8_t>> WordLen; + std::vector<std::pair<uint8_t, uint8_t>> SfIdx; + std::vector<std::pair<uint8_t, uint8_t>> SpecTabIdx; + + struct TChannel { + TChannel(const std::vector<TScaledBlock>& scaledBlock) + : ScaledBlocks(scaledBlock) + {} + const std::vector<TScaledBlock>& ScaledBlocks; + }; + + std::vector<TChannel> Chs; + size_t AllocatedBits; + static TSpecFrame* Cast(void* p) { return reinterpret_cast<TSpecFrame*>(p); } +}; + +class TDumper : public IBitStreamPartEncoder { +public: + void Dump(NBitStream::TBitStream& bs) override { + for (const auto& pair : Buf) { + bs.Write(pair.first, pair.second); + } + Buf.clear(); + } + void Reset() noexcept override { + Buf.clear(); + } + uint32_t GetConsumption() const noexcept override; +protected: + + // value, nbits + void Insert(uint16_t value, uint8_t nbits) { Buf.emplace_back(std::make_pair(value, nbits)); } + std::vector<std::pair<uint16_t, uint8_t>> Buf; +}; + +class TConfigure : public TDumper { +public: + TConfigure() = default; + EStatus Encode(void* frameData, TBitAllocHandler& ba) override; +}; + +class TWordLenEncoder : public TDumper { +public: + TWordLenEncoder() = default; + EStatus Encode(void* frameData, TBitAllocHandler& ba) override; +private: +}; + +class TSfIdxEncoder : public TDumper { +public: + TSfIdxEncoder() = default; + EStatus Encode(void* frameData, TBitAllocHandler& ba) override; +private: +}; + +class TQuantUnitsEncoder : public TDumper { +public: + TQuantUnitsEncoder() = default; + EStatus Encode(void* frameData, TBitAllocHandler& ba) override; + static void EncodeQuSpectra(const int* qspec, const size_t num_spec, const size_t idx, + std::vector<std::pair<uint16_t, uint8_t>>& data); + static void EncodeCodeTab(bool useFullTable, size_t channels, + size_t numQuantUnits, const std::vector<std::pair<uint8_t, uint8_t>>& specTabIdx, + std::vector<std::pair<uint16_t, uint8_t>>& data); + +private: + class TUnit { + public: + static size_t MakeKey(size_t ch, size_t qu, size_t worlen); + TUnit(size_t qu, size_t wordlen); + size_t GetOrCompute(const float* val, std::vector<std::pair<uint16_t, uint8_t>>& res); + const std::vector<int>& GetMantisas() const { return Mantisas; } + + private: + uint32_t Wordlen; + float Multiplier; + uint16_t ConsumedBits; // Number of bits consumed by QuSpectr + + std::vector<int> Mantisas; + }; + // The key is <ch_id, unit_id, wordlen> + // will be used to cache unit encoding result duting bit allocation + std::map<size_t, TUnit> UnitBuffers; +}; + +class TTonalComponentEncoder : public TDumper { +public: + TTonalComponentEncoder() = default; + EStatus Encode(void* frameData, TBitAllocHandler& ba) override; + //TODO: find the common way + void Dump(NBitStream::TBitStream& bs) override { + TDumper::Dump(bs); + BitsUsed = 0; + } +private: + EStatus CheckFrameDone(TSpecFrame* frame, TBitAllocHandler& ba) noexcept; + void WriteTonalBlock(size_t channels, const TAt3PGhaData* tonalBlock); + void WriteSubbandFlags(const bool* flags, size_t numFlags); + size_t BitsUsed = 0; +}; + + +} diff --git a/src/atrac/at3p/at3p_bitstream_ut.cpp b/src/atrac/at3p/at3p_bitstream_ut.cpp new file mode 100644 index 0000000..a4d7bd7 --- /dev/null +++ b/src/atrac/at3p/at3p_bitstream_ut.cpp @@ -0,0 +1,139 @@ +#include "at3p_bitstream.h" +#include "at3p_bitstream_impl.h" +#include <gtest/gtest.h> +#include <cmath> + +using namespace NAtracDEnc; + +TEST(AT3PBitstream, ToneFreqBitPack__1) { + std::vector<TAt3PGhaData::TWaveParam> params; + + params.push_back(TAt3PGhaData::TWaveParam{1, 0, 0, 0}); + + auto r = CreateFreqBitPack(params.data(), params.size()); + EXPECT_EQ(r.UsedBits, 10); + EXPECT_EQ(r.Order, ETonePackOrder::ASC); + EXPECT_EQ(r.Data.size(), 1); + EXPECT_EQ(r.Data[0].Code, 1); + EXPECT_EQ(r.Data[0].Bits, 10); +} + +TEST(AT3PBitstream, ToneFreqBitPack__512_1020_1023) { + std::vector<TAt3PGhaData::TWaveParam> params; + + params.push_back(TAt3PGhaData::TWaveParam{512, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{1020, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{1023, 0, 0, 0}); + + auto r = CreateFreqBitPack(params.data(), params.size()); + EXPECT_EQ(r.UsedBits, 21); + EXPECT_EQ(r.Order, ETonePackOrder::ASC); + EXPECT_EQ(r.Data.size(), 3); + EXPECT_EQ(r.Data[0].Code, 512); + EXPECT_EQ(r.Data[0].Bits, 10); + EXPECT_EQ(r.Data[1].Code, 508); + EXPECT_EQ(r.Data[1].Bits, 9); + EXPECT_EQ(r.Data[2].Code, 3); + EXPECT_EQ(r.Data[2].Bits, 2); +} + +TEST(AT3PBitstream, ToneFreqBitPack__1_2_3) { + std::vector<TAt3PGhaData::TWaveParam> params; + + params.push_back(TAt3PGhaData::TWaveParam{1, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{2, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{3, 0, 0, 0}); + + auto r = CreateFreqBitPack(params.data(), params.size()); + EXPECT_EQ(r.UsedBits, 14); + EXPECT_EQ(r.Order, ETonePackOrder::DESC); + EXPECT_EQ(r.Data.size(), 3); + EXPECT_EQ(r.Data[0].Code, 3); + EXPECT_EQ(r.Data[0].Bits, 10); + EXPECT_EQ(r.Data[1].Code, 2); + EXPECT_EQ(r.Data[1].Bits, 2); + EXPECT_EQ(r.Data[2].Code, 1); + EXPECT_EQ(r.Data[2].Bits, 2); +} + +TEST(AT3PBitstream, ToneFreqBitPack__1_2_3_1020_1021_1022) { + std::vector<TAt3PGhaData::TWaveParam> params; + + params.push_back(TAt3PGhaData::TWaveParam{1, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{2, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{3, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{1020, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{1021, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{1022, 0, 0, 0}); + + auto r = CreateFreqBitPack(params.data(), params.size()); + EXPECT_EQ(r.UsedBits, 44); + EXPECT_EQ(r.Order, ETonePackOrder::DESC); + EXPECT_EQ(r.Data.size(), 6); + EXPECT_EQ(r.Data[0].Code, 1022); + EXPECT_EQ(r.Data[0].Bits, 10); + EXPECT_EQ(r.Data[1].Code, 1021); + EXPECT_EQ(r.Data[1].Bits, 10); + EXPECT_EQ(r.Data[2].Code, 1020); + EXPECT_EQ(r.Data[2].Bits, 10); + EXPECT_EQ(r.Data[3].Code, 3); + EXPECT_EQ(r.Data[3].Bits, 10); + EXPECT_EQ(r.Data[4].Code, 2); + EXPECT_EQ(r.Data[4].Bits, 2); + EXPECT_EQ(r.Data[5].Code, 1); + EXPECT_EQ(r.Data[5].Bits, 2); +} + +TEST(AT3PBitstream, ToneFreqBitPack__1_2_1020_1021_1022) { + std::vector<TAt3PGhaData::TWaveParam> params; + + params.push_back(TAt3PGhaData::TWaveParam{1, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{2, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{1020, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{1021, 0, 0, 0}); + params.push_back(TAt3PGhaData::TWaveParam{1022, 0, 0, 0}); + + auto r = CreateFreqBitPack(params.data(), params.size()); + EXPECT_EQ(r.UsedBits, 34); + EXPECT_EQ(r.Order, ETonePackOrder::ASC); + EXPECT_EQ(r.Data.size(), 5); + EXPECT_EQ(r.Data[0].Code, 1); + EXPECT_EQ(r.Data[0].Bits, 10); + EXPECT_EQ(r.Data[1].Code, 2); + EXPECT_EQ(r.Data[1].Bits, 10); + EXPECT_EQ(r.Data[2].Code, 1020); + EXPECT_EQ(r.Data[2].Bits, 10); + EXPECT_EQ(r.Data[3].Code, 1); + EXPECT_EQ(r.Data[3].Bits, 2); + EXPECT_EQ(r.Data[4].Code, 2); + EXPECT_EQ(r.Data[4].Bits, 2); +} + +void FillFrameData(TSpecFrame& frame) { + frame.NumQuantUnits = 6; + + for (size_t i = 0; i < frame.NumQuantUnits; i++) { + frame.WordLen.push_back({6, 6}); + } +} + +TEST(AT3PBitstream, Wordlen) { + std::vector<IBitStreamPartEncoder::TPtr> encoders; + + encoders.emplace_back(new TWordLenEncoder()); + + NBitStream::TBitStream bs; + TBitStreamEncoder encoder(std::move(encoders)); + + std::vector<std::vector<TScaledBlock>> scaledBlocks; + scaledBlocks.resize(2); + + TSpecFrame frame(444, 28, 2, nullptr, scaledBlocks); + + FillFrameData(frame); + + encoder.Do(&frame, bs); + + EXPECT_EQ(bs.GetSizeInBits(), 40); +} + diff --git a/src/atrac/at3p/at3p_gha.cpp b/src/atrac/at3p/at3p_gha.cpp new file mode 100644 index 0000000..70905b5 --- /dev/null +++ b/src/atrac/at3p/at3p_gha.cpp @@ -0,0 +1,859 @@ +/* + * 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 "at3p_gha.h" +#include "ff/atrac3plus.h" + +#include <assert.h> +#include <atrac/atrac_psy_common.h> +#include <libgha/include/libgha.h> + +#include <memory> + +#include <algorithm> +#include <cstring> +#include <cmath> +#include <iostream> +#include <map> +#include <vector> + +using std::map; +using std::vector; +using std::isnan; +using std::pair; +using std::max; +using std::min; + +namespace NAtracDEnc { + +namespace { + +uint32_t GhaFreqToIndex(float f, uint32_t sb) +{ + return static_cast<uint32_t>(lrintf(1024.0f * (f / M_PI)) & 1023) | (sb << 10); +} + +uint32_t GhaPhaseToIndex(float p) +{ + return static_cast<uint32_t>(lrintf(32.0 * ((p) / (2 * M_PI))) & 31); +} + +uint32_t PhaseIndexToOffset(uint32_t ind) +{ + return (ind & 0x1F) << 6; +} + +class TGhaProcessor : public IGhaProcessor { + // Number of subbands to process; + // No need to process all subbands. + static constexpr size_t SUBBANDS = 8; + static constexpr size_t SAMPLES_PER_SUBBAND = 128; + static constexpr size_t LOOK_AHEAD = 64; + static constexpr size_t GHA_SUBBAND_BUF_SZ = SAMPLES_PER_SUBBAND + LOOK_AHEAD; + static constexpr size_t CHANNEL_BUF_SZ = SUBBANDS * GHA_SUBBAND_BUF_SZ; + + using TGhaInfoMap = map<uint32_t, struct gha_info>; + using TWavesChannel = TAt3PGhaData::TWavesChannel; + using TAmpSfTab = std::array<float, 64>; + + struct TChannelData { + const float* SrcBuf; + const float* SrcBufNext; + float Buf[CHANNEL_BUF_SZ]; + pair<uint32_t, uint32_t> Envelopes[SUBBANDS] = {{TAt3PGhaData::INIT,TAt3PGhaData::INIT}}; + bool Gapless[SUBBANDS] = {false}; + uint8_t SubbandDone[SUBBANDS] = {0}; + TGhaInfoMap GhaInfos; + float MaxToneMagnitude[SUBBANDS] = {0}; // Max magnitude of sine in the band. Used to stop processing when next extracted sine become significant less then max one + float LastResuidalEnergy[SUBBANDS] = {0}; // Resuidal energy on the last round for subband. It is the second criteria to stop processing, we expect resuidal becaming less on the each round + uint16_t LastAddedFreqIdx[SUBBANDS]; + + void MarkSubbandDone(size_t sb) { + SubbandDone[sb] = 16; + } + + bool IsSubbandDone(size_t sb) const { + return SubbandDone[sb] == 16; + } + }; + + struct TChannelGhaCbCtx { + TChannelGhaCbCtx(TChannelData* data, size_t sb) + : Data(data) + , Sb(sb) + {} + TChannelData* Data; + size_t Sb; + + enum class EAdjustStatus { + Error, + Ok, + Repeat + } AdjustStatus; + size_t FrameSz = 0; + }; + +public: + TGhaProcessor(bool stereo) + : LibGhaCtx(gha_create_ctx(128)) + , Stereo(stereo) + { + gha_set_max_magnitude(LibGhaCtx, 32768); + gha_set_upsample(LibGhaCtx, 1); + + if (!StaticInited) { + ff_atrac3p_init_dsp_static(); + + FillSubbandAth(&SubbandAth[0]); + + AmpSfTab = CreateAmpSfTab(); + for (int i = 0; i < 2048; i++) { + SineTab[i] = sin(2 * M_PI * i / 2048); + } + + StaticInited = true; + } + + for (size_t ch = 0; ch < 2; ch++) { + ChUnit.channels[ch].tones_info = &ChUnit.channels[ch].tones_info_hist[0][0]; + ChUnit.channels[ch].tones_info_prev = &ChUnit.channels[ch].tones_info_hist[1][0]; + } + + ChUnit.waves_info = &ChUnit.wave_synth_hist[0]; + ChUnit.waves_info_prev = &ChUnit.wave_synth_hist[1]; + ChUnit.waves_info->tones_present = false; + ChUnit.waves_info_prev->tones_present = false; + } + + ~TGhaProcessor() + { + gha_free_ctx(LibGhaCtx); + } + + const TAt3PGhaData* DoAnalize(TBufPtr b1, TBufPtr b2, float *w1, float *w2) override; + +private: + void ApplyFilter(const TAt3PGhaData*, float *b1, float *b2); + static void FillSubbandAth(float* out); + static TAmpSfTab CreateAmpSfTab(); + static void CheckResuidalAndApply(float* resuidal, size_t size, void* self) noexcept; + static void GenWaves(const TAt3PGhaData::TWaveParam* param, size_t numWaves, size_t reg_offset, float* out, size_t outLimit); + + void AdjustEnvelope(pair<uint32_t, uint32_t>& envelope, const pair<uint32_t, uint32_t>& src, uint32_t history); + uint32_t FillFolowerRes(const TGhaInfoMap& lGha, const TChannelData* src, TGhaInfoMap::const_iterator& it, uint32_t leaderSb); + + uint32_t AmplitudeToSf(float amp) const; + bool CheckNextFrame(const float* nextSrc, const vector<gha_info>& ghaInfos) const; + + bool DoRound(TChannelData& data, size_t& totalTones) const; + bool PsyPreCheck(size_t sb, const struct gha_info& gha, const TChannelData& data) const; + void FillResultBuf(const vector<TChannelData>& data); + + gha_ctx_t LibGhaCtx; + TAt3PGhaData ResultBuf; + TAt3PGhaData ResultBufHistory; + + const bool Stereo; + + static float SubbandAth[SUBBANDS]; + static float SineTab[2048]; + static bool StaticInited; + static TAmpSfTab AmpSfTab; + + Atrac3pChanUnitCtx ChUnit; +}; + +bool TGhaProcessor::StaticInited = false; +float TGhaProcessor::SubbandAth[SUBBANDS]; +float TGhaProcessor::SineTab[2048]; +TGhaProcessor::TAmpSfTab TGhaProcessor::AmpSfTab; + +void TGhaProcessor::FillSubbandAth(float* out) +{ + const auto ath = CalcATH(16 * 1024, 44100); + #pragma GCC nounroll + for (size_t sb = 0; sb < SUBBANDS; sb++) { + float m = 999.; + for (size_t f = sb * 1024, i = 0; i < 1024; f++, i++) { + m = fmin(m, ath[f]); + } + //m += 26; //Some gap to not encode too much + out[sb] = pow(10, 0.1 * (m + 90)); //adjust to 0db level = 32768, convert to power + } +} + +TGhaProcessor::TAmpSfTab TGhaProcessor::CreateAmpSfTab() +{ + TAmpSfTab AmpSfTab; + for (int i = 0; i < (int)AmpSfTab.size(); i++) { + AmpSfTab[i] = exp2f((i - 3) / 4.0f); + } + return AmpSfTab; +} + +void TGhaProcessor::GenWaves(const TAt3PGhaData::TWaveParam* param, size_t numWaves, size_t reg_offset, float* out, size_t outLimit) +{ + for (size_t w = 0; w < numWaves; w++, param++) { + //std::cerr << "GenWaves : " << w << " FreqIndex: " << param->FreqIndex << " phaseIndex: " << param->PhaseIndex << " ampSf " << param->AmpSf << std::endl; + auto amp = AmpSfTab[param->AmpSf]; + auto inc = param->FreqIndex; + auto pos = ((int)PhaseIndexToOffset(param->PhaseIndex) + ((int)reg_offset ^ 128) * inc) & 2047; + + for (size_t i = 0; i < outLimit; i++) { + //std::cerr << "inc: " << inc << " pos: " << pos << std::endl; + out[i] += SineTab[pos] * amp; + pos = (pos + inc) & 2047; + } + } +} + +void TGhaProcessor::CheckResuidalAndApply(float* resuidal, size_t size, void* d) noexcept +{ + TChannelGhaCbCtx* ctx = (TChannelGhaCbCtx*)(d); + const float* srcBuf = ctx->Data->SrcBuf + (ctx->Sb * SAMPLES_PER_SUBBAND); + //std::cerr << "TGhaProcessor::CheckResuidal " << srcBuf[0] << " " << srcBuf[1] << " " << srcBuf[2] << " " << srcBuf[3] << std::endl; + + float resuidalEnergy = 0; + + uint32_t start = 0; + uint32_t curStart = 0; + uint32_t count = 0; + uint32_t len = 0; + bool found = false; + + if (size != SAMPLES_PER_SUBBAND) + abort(); + + for (size_t i = 0; i < SAMPLES_PER_SUBBAND; i += 4) { + float energyIn = 0.0; + float energyOut = 0.0; + for (size_t j = 0; j < 4; j++) { + energyIn += srcBuf[i + j] * srcBuf[i + j]; + energyOut += resuidal[i + j] * resuidal[i + j]; + } + + energyIn = sqrt(energyIn/4); + energyOut = sqrt(energyOut/4); + resuidalEnergy += energyOut; + + if (energyIn / energyOut < 1) { + count = 0; + found = false; + curStart = i + 4; + } else { + count++; + if (count > len) { + len = count; + if (!found) { + start = curStart; + found = true; + } + } + } + + // std::cerr << " " << i << " rms : " << energyIn << " " << energyOut << "\t\t\t" << ((energyOut < energyIn) ? "+" : "-") << std::endl; + } + + const auto sb = ctx->Sb; + // Do not encode too short frame + if (len < 4) { + ctx->AdjustStatus = TChannelGhaCbCtx::EAdjustStatus::Error; + return; + } + + const uint32_t end = start + len * 4; + + if (ctx->AdjustStatus != TChannelGhaCbCtx::EAdjustStatus::Repeat && end != SAMPLES_PER_SUBBAND) { + ctx->FrameSz = end; + ctx->AdjustStatus = TChannelGhaCbCtx::EAdjustStatus::Repeat; + return; + } + + const float threshold = 1.05; //TODO: tune it + if (static_cast<bool>(ctx->Data->LastResuidalEnergy[sb]) == false) { + ctx->Data->LastResuidalEnergy[sb] = resuidalEnergy; + } else if (ctx->Data->LastResuidalEnergy[sb] < resuidalEnergy * threshold) { + ctx->AdjustStatus = TChannelGhaCbCtx::EAdjustStatus::Error; + return; + } else { + ctx->Data->LastResuidalEnergy[sb] = resuidalEnergy; + } + + auto& envelope = ctx->Data->Envelopes[sb]; + envelope.first = start; + + if (envelope.second == TAt3PGhaData::EMPTY_POINT && end != SAMPLES_PER_SUBBAND) { + ctx->AdjustStatus = TChannelGhaCbCtx::EAdjustStatus::Error; + return; + } + + envelope.second = end; + + ctx->AdjustStatus = TChannelGhaCbCtx::EAdjustStatus::Ok; + + float* b = &ctx->Data->Buf[sb * GHA_SUBBAND_BUF_SZ]; + + memcpy(b, resuidal, sizeof(float) * SAMPLES_PER_SUBBAND); +} + +void TGhaProcessor::ApplyFilter(const TAt3PGhaData* d, float* b1, float* b2) +{ + for (size_t ch_num = 0; ch_num < 2; ch_num++) + memset(ChUnit.channels[ch_num].tones_info, 0, + sizeof(*ChUnit.channels[ch_num].tones_info) * ATRAC3P_SUBBANDS); + + if (d) { + memset(ChUnit.waves_info->waves, 0, sizeof(ChUnit.waves_info->waves)); + ChUnit.waves_info->num_tone_bands = d->NumToneBands; + ChUnit.waves_info->tones_present = true; + ChUnit.waves_info->amplitude_mode = 1; + ChUnit.waves_info->tones_index = 0; + } else { + ChUnit.waves_info->tones_present = false; + } + + if (d) { + for (size_t ch = 0; ch <= (size_t)Stereo; ch++) { + for (int i = 0; i < ChUnit.waves_info->num_tone_bands; i++) { + if (ch && d->ToneSharing[i]) { + continue; + } + + ChUnit.channels[ch].tones_info[i].num_wavs = d->GetNumWaves(ch, i); + + const auto envelope = d->GetEnvelope(ch, i); + if (envelope.first != TAt3PGhaData::EMPTY_POINT) { + // start point present + ChUnit.channels[ch].tones_info[i].pend_env.has_start_point = true; + ChUnit.channels[ch].tones_info[i].pend_env.start_pos = envelope.first; + } else { + ChUnit.channels[ch].tones_info[i].pend_env.has_start_point = false; + ChUnit.channels[ch].tones_info[i].pend_env.start_pos = -1; + } + + if (envelope.second != TAt3PGhaData::EMPTY_POINT) { + // stop point present + ChUnit.channels[ch].tones_info[i].pend_env.has_stop_point = true; + ChUnit.channels[ch].tones_info[i].pend_env.stop_pos = envelope.second; + } else { + ChUnit.channels[ch].tones_info[i].pend_env.has_stop_point = false; + ChUnit.channels[ch].tones_info[i].pend_env.stop_pos = 32; + } + } + + for (int sb = 0; sb < ChUnit.waves_info->num_tone_bands; sb++) { + if (d->GetNumWaves(ch, sb)) { + if (ChUnit.waves_info->tones_index + ChUnit.channels[ch].tones_info[sb].num_wavs > 48) { + std::cerr << "too many tones: " << ChUnit.waves_info->tones_index + ChUnit.channels[ch].tones_info[sb].num_wavs << std::endl; + abort(); + } + ChUnit.channels[ch].tones_info[sb].start_index = ChUnit.waves_info->tones_index; + ChUnit.waves_info->tones_index += ChUnit.channels[ch].tones_info[sb].num_wavs; + } + } + + Atrac3pWaveParam *iwav; + for (int sb = 0; sb < ChUnit.waves_info->num_tone_bands; sb++) { + if (d->GetNumWaves(ch, sb)) { + iwav = &ChUnit.waves_info->waves[ChUnit.channels[ch].tones_info[sb].start_index]; + auto w = d->GetWaves(ch, sb); + ChUnit.channels[ch].tones_info[sb].num_wavs = w.second; + for (size_t j = 0; j < w.second; j++) { + iwav[j].freq_index = w.first[j].FreqIndex; + iwav[j].amp_index = w.first[j].AmpIndex; + iwav[j].amp_sf = w.first[j].AmpSf; + iwav[j].phase_index = w.first[j].PhaseIndex; + } + } + } + } + + if (Stereo) { + for (int i = 0; i < ChUnit.waves_info->num_tone_bands; i++) { + if (d->ToneSharing[i]) { + ChUnit.channels[1].tones_info[i] = ChUnit.channels[0].tones_info[i]; + } + + if (d->SecondIsLeader) { + std::swap(ChUnit.channels[0].tones_info[i], + ChUnit.channels[1].tones_info[i]); + } + } + } + } + + for (size_t ch = 0; ch <= (size_t)Stereo; ch++) { + float* x = (ch == 0) ? b1 : b2; + if (ChUnit.waves_info->tones_present || + ChUnit.waves_info_prev->tones_present) { + for (size_t sb = 0; sb < SUBBANDS; sb++) { + if (ChUnit.channels[ch].tones_info[sb].num_wavs || + ChUnit.channels[ch].tones_info_prev[sb].num_wavs) { + + ff_atrac3p_generate_tones(&ChUnit, ch, sb, + (x + sb * 128)); + } + } + } + } + + for (size_t ch = 0; ch <= (size_t)Stereo; ch++) { + std::swap(ChUnit.channels[ch].tones_info, ChUnit.channels[ch].tones_info_prev); + } + + std::swap(ChUnit.waves_info, ChUnit.waves_info_prev); +} + +const TAt3PGhaData* TGhaProcessor::DoAnalize(TBufPtr b1, TBufPtr b2, float* w1, float* w2) +{ + vector<TChannelData> data((size_t)Stereo + 1); + bool progress[2] = {false}; + + for (size_t ch = 0; ch < data.size(); ch++) { + const float* bCur = (ch == 0) ? b1[0] : b2[0]; + const float* bNext = (ch == 0) ? b1[1] : b2[1]; + data[ch].SrcBuf = bCur; + data[ch].SrcBufNext = bNext; + + for (size_t sb = 0; sb < SUBBANDS; sb++, bCur += SAMPLES_PER_SUBBAND, bNext += SAMPLES_PER_SUBBAND) { + constexpr auto copyCurSz = sizeof(float) * SAMPLES_PER_SUBBAND; + constexpr auto copyNextSz = sizeof(float) * LOOK_AHEAD; + memcpy(&data[ch].Buf[0] + sb * GHA_SUBBAND_BUF_SZ , bCur, copyCurSz); + memcpy(&data[ch].Buf[0] + sb * GHA_SUBBAND_BUF_SZ + SAMPLES_PER_SUBBAND, bNext, copyNextSz); + } + //for (int i = 0; i < SAMPLES_PER_SUBBAND + LOOK_AHEAD; i++) { + //std::cerr << i << " " << data[0].Buf[i] << std::endl; + //} + } + + size_t totalTones = 0; + do { + for (size_t ch = 0; ch < data.size(); ch++) { + progress[ch] = DoRound(data[ch], totalTones); + } + } while ((progress[0] || progress[1]) && totalTones < 48); + + if (totalTones == 0) { + ApplyFilter(nullptr, w1, w2); + return nullptr; + } + + FillResultBuf(data); + + ResultBufHistory = ResultBuf; + + ApplyFilter(&ResultBuf, w1, w2); + + return &ResultBuf; +} + +bool TGhaProcessor::CheckNextFrame(const float* nextSrc, const vector<gha_info>& ghaInfos) const +{ + vector<TAt3PGhaData::TWaveParam> t; + t.reserve(ghaInfos.size()); + for (const auto& x : ghaInfos) { + t.emplace_back(TAt3PGhaData::TWaveParam + { + // TODO: do not do it twice + GhaFreqToIndex(x.frequency, 0), + AmplitudeToSf(x.magnitude), + 1, + GhaPhaseToIndex(x.phase) + } + ); + } + + float buf[LOOK_AHEAD] = {0.0}; + + GenWaves(t.data(), t.size(), 0, buf, LOOK_AHEAD); + + float energyBefore = 0.0; + float energyAfter = 0.0; + + for (size_t i = 0; i < LOOK_AHEAD; i++) { + energyBefore += nextSrc[i] * nextSrc[i]; + float t = nextSrc[i] - buf[i]; + energyAfter += t * t; + //std::cerr << buf[i] << " === " << nextSrc[i] << std::endl; + } + + // std::cerr << "ENERGY: before: " << energyBefore << " after: " << energyAfter << std::endl; + + return energyAfter < energyBefore; +} + +bool TGhaProcessor::DoRound(TChannelData& data, size_t& totalTones) const +{ + bool progress = false; + for (size_t sb = 0; sb < SUBBANDS; sb++) { + if (data.IsSubbandDone(sb)) { + continue; + } + + if (totalTones >= 48) { + return false; + } + + const float* srcB = data.SrcBuf + (sb * SAMPLES_PER_SUBBAND); + { + auto cit = data.GhaInfos.lower_bound(sb << 10); + vector<gha_info> tmp; + for(auto it = cit; it != data.GhaInfos.end() && it->first < (sb + 1) << 10; it++) { + // std::cerr << sb << " before: freq: " << it->second.frequency << " magn: " << it->second.magnitude << std::endl; + tmp.push_back(it->second); + } + if (tmp.size() > 0) { + TChannelGhaCbCtx ctx(&data, sb); + do { + int ar = gha_adjust_info(srcB, tmp.data(), tmp.size(), LibGhaCtx, CheckResuidalAndApply, &ctx, ctx.FrameSz); + if (ar < 0) { + ctx.AdjustStatus = TChannelGhaCbCtx::EAdjustStatus::Error; + }; + } while (ctx.AdjustStatus == TChannelGhaCbCtx::EAdjustStatus::Repeat); + + if (ctx.AdjustStatus == TChannelGhaCbCtx::EAdjustStatus::Ok) { + std::sort(tmp.begin(), tmp.end(), [](const gha_info& a, const gha_info& b) {return a.frequency < b.frequency;}); + + bool dupFound = false; + { + auto idx1 = GhaFreqToIndex(tmp[0].frequency, sb); + for (size_t i = 1; i < tmp.size(); i++) { + auto idx2 = GhaFreqToIndex(tmp[i].frequency, sb); + if (idx2 == idx1) { + dupFound = true; + break; + } else { + idx1 = idx2; + } + } + } + + if (!dupFound) { + // check is this tone set ok for the next one + if (data.Envelopes[sb].second == SAMPLES_PER_SUBBAND || data.Envelopes[sb].second == TAt3PGhaData::EMPTY_POINT) { + bool cont = CheckNextFrame(data.SrcBufNext + SAMPLES_PER_SUBBAND * sb, tmp); + + if (data.Gapless[sb] == true && cont == false) { + data.GhaInfos.erase(data.LastAddedFreqIdx[sb]); + totalTones--; + data.MarkSubbandDone(sb); + continue; + } else if (data.Envelopes[sb].second == SAMPLES_PER_SUBBAND && cont == true) { + data.Envelopes[sb].second = TAt3PGhaData::EMPTY_POINT; + data.Gapless[sb] = true; + } + } + + auto it = cit; + for (size_t i = 0; i < tmp.size(); i++) { + // std::cerr << sb << " after: freq: " << tmp[i].frequency << " magn: " << tmp[i].magnitude << std::endl; + it = data.GhaInfos.erase(it); + } + for (const auto& x : tmp) { + data.MaxToneMagnitude[sb] = std::max(data.MaxToneMagnitude[sb], x.magnitude); + const auto newIndex = GhaFreqToIndex(x.frequency, sb); + data.GhaInfos.insert({newIndex, x}); + } + } else { + std::cerr << "jackpot! same freq index after adjust call, sb: " << sb << " " << std::endl; + data.GhaInfos.erase(data.LastAddedFreqIdx[sb]); + totalTones--; + data.MarkSubbandDone(sb); + continue; + } + } else { + data.GhaInfos.erase(data.LastAddedFreqIdx[sb]); + totalTones--; + data.MarkSubbandDone(sb); + continue; + } + } + } + + float* b = &data.Buf[sb * GHA_SUBBAND_BUF_SZ]; + struct gha_info res; + + gha_analyze_one(b, &res, LibGhaCtx); + + auto freqIndex = GhaFreqToIndex(res.frequency, sb); + if (PsyPreCheck(sb, res, data) == false) { + data.MarkSubbandDone(sb); + } else { + if (data.SubbandDone[sb] == 0) { + bool ins = data.GhaInfos.insert({freqIndex, res}).second; + data.LastAddedFreqIdx[sb] = freqIndex; + assert(ins); + } else { + const auto it = data.GhaInfos.lower_bound(freqIndex); + const size_t minFreqDistanse = 20; // Now we unable to handle tones with close frequency + if (it != data.GhaInfos.end()) { + if (it->first == freqIndex) { + data.MarkSubbandDone(sb); + continue; + } + + if (it->first - freqIndex < minFreqDistanse) { + data.MarkSubbandDone(sb); + continue; + } + } + if (it != data.GhaInfos.begin()) { + auto prev = it; + prev--; + if (freqIndex - prev->first < minFreqDistanse) { + data.MarkSubbandDone(sb); + continue; + } + } + if (data.SubbandDone[sb] == 15) { + data.MarkSubbandDone(sb); + continue; + } + data.GhaInfos.insert(it, {freqIndex, res}); + data.LastAddedFreqIdx[sb] = freqIndex; + } + + data.SubbandDone[sb]++; + totalTones++; + progress = true; + } + + } + return progress; +} + +bool TGhaProcessor::PsyPreCheck(size_t sb, const struct gha_info& gha, const TChannelData& data) const +{ + if (isnan(gha.magnitude)) { + return false; + } + + //std::cerr << "sb: " << sb << " " << gha.magnitude << " ath: " << SubbandAth[sb] << " max: " << data.MaxToneMagnitude[sb] << std::endl; + // TODO: improve it + // Just to start. Actualy we need to consider spectral leakage during MDCT + if ((gha.magnitude * gha.magnitude) > SubbandAth[sb]) { + // Stop processing for sb if next extracted tone 23db less then maximal one + // TODO: tune + if (gha.magnitude > data.MaxToneMagnitude[sb] / 10) { + return true; + } + } + + return false; +} + +void TGhaProcessor::AdjustEnvelope(pair<uint32_t, uint32_t>& envelope, const pair<uint32_t, uint32_t>& src, uint32_t history) +{ + if (src.first == 0 && history == TAt3PGhaData::EMPTY_POINT) { + envelope.first = TAt3PGhaData::EMPTY_POINT; + } else { + if (src.first == TAt3PGhaData::EMPTY_POINT) { + abort(); //impossible right now + envelope.first = TAt3PGhaData::EMPTY_POINT; + } else { + envelope.first = src.first / 4; + } + } + if (src.second == TAt3PGhaData::EMPTY_POINT) { + envelope.second = src.second; + } else { + if (src.second == 0) + abort(); + envelope.second = (src.second - 1) / 4; + if (envelope.second >= 32) + abort(); + } +} + +void TGhaProcessor::FillResultBuf(const vector<TChannelData>& data) +{ + uint32_t usedContiguousSb[2] = {0, 0}; + uint32_t numTones[2] = {0, 0}; + + // TODO: This can be improved. Bitstream allows to set leader/folower flag for each band. + for (size_t ch = 0; ch < data.size(); ch++) { + int cur = -1; + for (const auto& info : data[ch].GhaInfos) { + int sb = info.first >> 10; + if (sb == cur + 1) { + usedContiguousSb[ch]++; + numTones[ch]++; + cur = sb; + } else if (sb == cur) { + numTones[ch]++; + continue; + } else { + break; + } + } + } + + bool leader = usedContiguousSb[1] > usedContiguousSb[0]; + + std::vector<TWavesChannel> history; + history.reserve(data.size()); + + history.push_back(ResultBuf.Waves[0]); + + ResultBuf.SecondIsLeader = leader; + ResultBuf.NumToneBands = usedContiguousSb[leader]; + + TGhaInfoMap::const_iterator leaderStartIt; + TGhaInfoMap::const_iterator folowerIt; + if (data.size() == 2) { + TWavesChannel& fWaves = ResultBuf.Waves[1]; + + history.push_back(fWaves); + + fWaves.WaveParams.clear(); + fWaves.WaveSbInfos.clear(); + // Yes, see bitstream code + fWaves.WaveSbInfos.resize(usedContiguousSb[leader]); + folowerIt = data[!leader].GhaInfos.begin(); + leaderStartIt = data[leader].GhaInfos.begin(); + } + + const auto& ghaInfos = data[leader].GhaInfos; + TWavesChannel& waves = ResultBuf.Waves[0]; + waves.WaveParams.clear(); + waves.WaveSbInfos.clear(); + waves.WaveSbInfos.resize(usedContiguousSb[leader]); + auto it = ghaInfos.begin(); + uint32_t prevSb = 0; + uint32_t index = 0; + + if (usedContiguousSb[leader] == 0) { + return; + } + + while (it != ghaInfos.end()) { + const auto sb = ((it->first) >> 10); + if (sb >= usedContiguousSb[leader]) { + break; + } + + const auto freqIndex = it->first & 1023; + const auto phaseIndex = GhaPhaseToIndex(it->second.phase); + const auto ampSf = AmplitudeToSf(it->second.magnitude); + + waves.WaveSbInfos[sb].WaveNums++; + if (sb != prevSb) { + uint32_t histStop = TAt3PGhaData::INIT; + if (ResultBufHistory.Waves[0].WaveSbInfos.size() > prevSb) { + histStop = ResultBufHistory.Waves[0].WaveSbInfos[prevSb].Envelope.second; + } + AdjustEnvelope(waves.WaveSbInfos[prevSb].Envelope, data[leader].Envelopes[prevSb], histStop); + + // update index sb -> wave position index + waves.WaveSbInfos[sb].WaveIndex = index; + + // process folower if present + if (data.size() == 2) { + FillFolowerRes(data[leader].GhaInfos, &data[!leader], folowerIt, prevSb); + leaderStartIt = it; + } + + prevSb = sb; + } + waves.WaveParams.push_back(TAt3PGhaData::TWaveParam{freqIndex, ampSf, 1, phaseIndex}); + it++; + index++; + } + + uint32_t histStop = (uint32_t)-2; + if (ResultBufHistory.Waves[0].WaveSbInfos.size() > prevSb) { + histStop = ResultBufHistory.Waves[0].WaveSbInfos[prevSb].Envelope.second; + } + + TGhaProcessor::AdjustEnvelope(waves.WaveSbInfos[prevSb].Envelope, data[leader].Envelopes[prevSb], histStop); + + if (data.size() == 2) { + FillFolowerRes(data[leader].GhaInfos, &data[!leader], folowerIt, prevSb); + } +} + +uint32_t TGhaProcessor::FillFolowerRes(const TGhaInfoMap& lGhaInfos, const TChannelData* src, TGhaInfoMap::const_iterator& it, const uint32_t curSb) +{ + uint32_t histStop = (uint32_t)-2; + if (ResultBufHistory.Waves[1].WaveSbInfos.size() > curSb) { + histStop = ResultBufHistory.Waves[1].WaveSbInfos[curSb].Envelope.second; + } + + const TGhaInfoMap& fGhaInfos = src->GhaInfos; + + TWavesChannel& waves = ResultBuf.Waves[1]; + + uint32_t folowerSbMode = 0; // 0 - no tones, 1 - sharing band, 2 - own tones set + uint32_t nextSb = 0; + uint32_t added = 0; + + while (it != fGhaInfos.end()) { + uint32_t sb = ((it->first) >> 10); + if (sb > curSb) { + nextSb = sb; + break; + } + + // search same indedx in the leader and set coresponding bit + folowerSbMode |= uint8_t(lGhaInfos.find(it->first) == lGhaInfos.end()) + 1u; + + const auto freqIndex = it->first & 1023; + const auto phaseIndex = GhaPhaseToIndex(it->second.phase); + const auto ampSf = AmplitudeToSf(it->second.magnitude); + + waves.WaveParams.push_back(TAt3PGhaData::TWaveParam{freqIndex, ampSf, 1, phaseIndex}); + + it++; + added++; + } + + switch (folowerSbMode) { + case 0: + ResultBuf.ToneSharing[curSb] = false; + waves.WaveSbInfos[curSb].WaveNums = 0; + break; + case 1: + ResultBuf.ToneSharing[curSb] = true; + waves.WaveParams.resize(waves.WaveParams.size() - added); + break; + default: + ResultBuf.ToneSharing[curSb] = false; + waves.WaveSbInfos[curSb].WaveIndex = waves.WaveParams.size() - added; + waves.WaveSbInfos[curSb].WaveNums = added; + AdjustEnvelope(waves.WaveSbInfos[curSb].Envelope, src->Envelopes[curSb], histStop); + } + return nextSb; +} + +uint32_t TGhaProcessor::AmplitudeToSf(float amp) const +{ + auto it = std::upper_bound(AmpSfTab.begin(), AmpSfTab.end(), amp); + if (it != AmpSfTab.begin()) { + it--; + } + return it - AmpSfTab.begin(); +} + +} // namespace + +std::unique_ptr<IGhaProcessor> MakeGhaProcessor0(bool stereo) +{ + return std::unique_ptr<TGhaProcessor>(new TGhaProcessor(stereo)); +} + +} // namespace NAtracDEnc diff --git a/src/atrac/at3p/at3p_gha.h b/src/atrac/at3p/at3p_gha.h new file mode 100644 index 0000000..702f593 --- /dev/null +++ b/src/atrac/at3p/at3p_gha.h @@ -0,0 +1,78 @@ +/* + * 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 <config.h> + +#include <array> +#include <memory> +#include <vector> + +namespace NAtracDEnc { + +struct TAt3PGhaData { + static constexpr uint32_t EMPTY_POINT = static_cast<uint32_t>(-1); + static constexpr uint32_t INIT = static_cast<uint32_t>(-2); + struct TWaveParam { + uint32_t FreqIndex; + uint32_t AmpSf; + uint32_t AmpIndex; + uint32_t PhaseIndex; + }; + struct TWaveSbInfo { + size_t WaveIndex; + size_t WaveNums; + std::pair<uint32_t, uint32_t> Envelope = {EMPTY_POINT, EMPTY_POINT}; + }; + struct TWavesChannel { + std::vector<TWaveSbInfo> WaveSbInfos; + std::vector<TWaveParam> WaveParams; + }; + std::array<TWavesChannel, 2> Waves; + + uint8_t NumToneBands; + + bool ToneSharing[16]; + bool SecondIsLeader; + + size_t GetNumWaves(size_t ch, size_t sb) const { + return Waves[ch].WaveSbInfos.at(sb).WaveNums; + } + + std::pair<uint32_t, uint32_t> GetEnvelope(size_t ch, size_t sb) const { + return Waves[ch].WaveSbInfos.at(sb).Envelope; + } + + std::pair<const TWaveParam*, size_t> GetWaves(size_t ch, size_t sb) const { + const auto& sbInfo = Waves[ch].WaveSbInfos.at(sb); + return { &Waves[ch].WaveParams[sbInfo.WaveIndex], sbInfo.WaveNums }; + } +}; + +class IGhaProcessor { +public: + using TBufPtr = std::array<const float*, 2>; + virtual ~IGhaProcessor() {} + virtual const TAt3PGhaData* DoAnalize(TBufPtr b1, TBufPtr b2, float* w1, float* w2) = 0; +}; + +std::unique_ptr<IGhaProcessor> MakeGhaProcessor0(bool stereo); + +} // namespace NAtracDEnc + diff --git a/src/atrac/at3p/at3p_gha_ut.cpp b/src/atrac/at3p/at3p_gha_ut.cpp new file mode 100644 index 0000000..a3cacec --- /dev/null +++ b/src/atrac/at3p/at3p_gha_ut.cpp @@ -0,0 +1,787 @@ +#include "at3p_bitstream.h" +#include "at3p_gha.h" +#include "oma.h" +#include "util.h" +#include <gtest/gtest.h> +#include <cmath> +#include <vector> +#include <cstdlib> + +using std::vector; + +using namespace NAtracDEnc; + +struct TTestParam { + float freq; + float phase; + uint16_t amplitude; + uint16_t start; + uint16_t end; +}; + +static void atde_noinline Gen(const TTestParam& p, vector<float>& out) +{ + float freq = p.freq / (44100.0 / 16.0); + float a = p.amplitude; + int end = p.end; + int j = 0; + for (int i = p.start; i < end; i++, j++) { + out[i] += sin(freq * (float)j * 2.0 * M_PI + p.phase) * a; + } +} + +static TAt3PGhaData DoAnalize(IGhaProcessor* p, IGhaProcessor::TBufPtr b1, IGhaProcessor::TBufPtr b2) { + float w1[2048] = {0}; + float w2[2048] = {0}; + return *p->DoAnalize(b1, b2, w1, w2); +} + +static const TAt3PGhaData atde_noinline GenAndRunGha(vector<TTestParam> p1, vector<TTestParam> p2) +{ + vector<float> buf1(2048 * 2); + + for (const auto& p : p1) { + Gen(p, buf1); + } + + vector<float> buf2; + + if (!p2.empty()) { + buf2.resize(2048 * 2); + + for (const auto& p : p2) { + Gen(p, buf2); + } + } + + auto processor = MakeGhaProcessor0(!p2.empty()); + const float* b1 = buf1.data(); + const float* b2 = buf2.empty() ? nullptr : buf2.data(); + + return DoAnalize(processor.get(), {b1, b1 + 2048}, {b2, b2 + 2048}); +} + +static class TDumper { +public: + TDumper() + : PathPrefix(std::getenv("GHA_UT_DUMP_DIR")) + {} + + void Dump(const TAt3PGhaData* gha, size_t channels, size_t len) { + if (!PathPrefix) { + return; + } + + std::string path = PathPrefix; + path += "/"; + path += ::testing::UnitTest::GetInstance()->current_test_info()->name(); + path += ".oma"; + + std::unique_ptr<TOma> out(new TOma(path, + "test", + channels, + 1, OMAC_ID_ATRAC3PLUS, + 2048, + false)); + + TAt3PBitStream bs(out.get(), 2048); + + for (size_t i = 0; i < len; i++) { + // bs.WriteFrame(channels, gha + i); + } + } +private: + const char* PathPrefix; +} Dumper; + + +// Single channel simple cases + +TEST(AT3PGHA, 689hz0625__full_frame_mono) { + auto res = GenAndRunGha({{689.0625f, 0, 32768, 0, 128}}, {}); + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 0).first->AmpSf, 63); + //EXPECT_EQ(res.GetWaves(0, 0).first->PhaseIndex, 0); + Dumper.Dump(&res, 1, 1); +} + +TEST(AT3PGHA, 0__full_frame_mono) { + auto res = GenAndRunGha({{0.0f, M_PI/2, 32768, 0, 128}}, {}); + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 0); + EXPECT_EQ(res.GetWaves(0, 0).first->AmpSf, 63); + //EXPECT_EQ(res.GetWaves(0, 0).first->PhaseIndex, 0); + Dumper.Dump(&res, 1, 1); +} + + +TEST(AT3PGHA, 689hz0625__partial_frame_mono) { + auto res = GenAndRunGha({{689.0625f, 0, 32768, 32, 128}}, {}); + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 512); +} + +TEST(AT3PGHA, 689hz0625_900hz__full_frame_mono) { + auto res = GenAndRunGha({{689.0625f, 0, 16384, 0, 128}, {900.0, 0, 8192, 0, 128}}, {}); + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 2); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 2); + EXPECT_EQ(res.GetWaves(0, 0).second, 2); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 0).first[1].FreqIndex, 669); + Dumper.Dump(&res, 1, 1); +} + +TEST(AT3PGHA, 400hz_800hz__full_frame_mono) { + auto res = GenAndRunGha({{400.0, 0, 16384, 0, 128}, {800.0, 0, 4096, 0, 128}}, {}); + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 2); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 2); + EXPECT_EQ(res.GetWaves(0, 0).second, 2); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 297); + EXPECT_EQ(res.GetWaves(0, 0).first[1].FreqIndex, 594); + Dumper.Dump(&res, 1, 1); +} + +TEST(AT3PGHA, 689hz0625_2067hz1875__full_frame_mono) { + auto res = GenAndRunGha({{689.0625f, 0, 16384, 0, 128}, {689.0625f, 0, 16384, 128, 256}}, {}); + EXPECT_EQ(res.NumToneBands, 2); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 2); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 2); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetNumWaves(0, 1), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 0).first->AmpSf, 59); + EXPECT_EQ(res.GetWaves(0, 1).second, 1); + EXPECT_EQ(res.GetWaves(0, 1).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 1).first->AmpSf, 59); + Dumper.Dump(&res, 1, 1); +} + +TEST(AT3PGHA, 689hz0625_4823hz4375__full_frame_mono) { + auto res = GenAndRunGha({{689.0625f, 0, 32768, 0, 128}, {689.0625f, 0, 16384, 256, 384}}, {}); + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 512); + Dumper.Dump(&res, 1, 1); +} + +// Two channels simple cases + +TEST(AT3PGHA, 689hz0625__full_frame_stereo_shared) { + auto res = GenAndRunGha({{689.0625f, 0, 32768, 0, 128}}, {{689.0625f, 0, 32768, 0, 128}}); + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 512); + + EXPECT_EQ(res.ToneSharing[0], true); + EXPECT_EQ(res.Waves[1].WaveParams.size(), 0); + Dumper.Dump(&res, 2, 1); +} + +TEST(AT3PGHA, 689hz0625__full_frame_stereo_own) { + auto res = GenAndRunGha({{689.0625f, 0, 32768, 0, 128}}, {{1000.0625f, 0, 32768, 0, 128}}); + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 512); + + EXPECT_EQ(res.ToneSharing[0], false); + + EXPECT_EQ(res.Waves[1].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[1].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(1, 0), 1); + EXPECT_EQ(res.GetWaves(1, 0).second, 1); + EXPECT_EQ(res.GetWaves(1, 0).first->FreqIndex, 743); + + Dumper.Dump(&res, 2, 1); +} + +TEST(AT3PGHA, 689hz0625__full_frame_stereo_multiple_second) { + auto res = GenAndRunGha({{689.0625f, 0, 32768, 0, 128}}, {{689.0625f, 0, 16384, 0, 128}, {900.0, 0, 8192, 0, 128}}); + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 512); + + EXPECT_EQ(res.ToneSharing[0], false); + EXPECT_EQ(res.Waves[1].WaveParams.size(), 2); + EXPECT_EQ(res.Waves[1].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(1, 0), 2); + EXPECT_EQ(res.GetWaves(1, 0).second, 2); + EXPECT_EQ(res.GetWaves(1, 0).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(1, 0).first[1].FreqIndex, 669); + Dumper.Dump(&res, 2, 1); +} + +TEST(AT3PGHA, 689hz0625_2067hz1875__full_frame_stereo_first_is_leader) { + auto res = GenAndRunGha({{689.0625f, 0, 32768, 0, 128}, {689.0625f, 0, 16384, 128, 256}}, + {{689.0625f, 0, 32768, 0, 128}}); + EXPECT_EQ(res.NumToneBands, 2); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 2); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 2); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetNumWaves(0, 1), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 1).second, 1); + EXPECT_EQ(res.GetWaves(0, 1).first[0].FreqIndex, 512); + + EXPECT_EQ(res.ToneSharing[0], true); + EXPECT_EQ(res.ToneSharing[1], false); + + EXPECT_EQ(res.Waves[1].WaveParams.size(), 0); // sb0 sharing, sb1 zerro + EXPECT_EQ(res.Waves[1].WaveSbInfos.size(), 2); + EXPECT_EQ(res.GetNumWaves(1, 1), 0); + Dumper.Dump(&res, 2, 1); +} + +TEST(AT3PGHA, 689hz0625_2067hz1875__full_frame_stereo_second_is_leader) { + auto res = GenAndRunGha({{689.0625f, 0, 32768, 0, 128}}, + {{689.0625f, 0, 32768, 0, 128}, {689.0625f, 0, 16384, 128, 256}}); + EXPECT_EQ(res.NumToneBands, 2); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 2); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 2); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetNumWaves(0, 1), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 1).second, 1); + EXPECT_EQ(res.GetWaves(0, 1).first[0].FreqIndex, 512); + + EXPECT_EQ(res.ToneSharing[0], true); + EXPECT_EQ(res.ToneSharing[1], false); + + EXPECT_EQ(res.Waves[1].WaveParams.size(), 0); + EXPECT_EQ(res.Waves[1].WaveSbInfos.size(), 2); + EXPECT_EQ(res.GetNumWaves(1, 1), 0); + + Dumper.Dump(&res, 2, 1); +} + +TEST(AT3PGHA, 689hz0625_2067hz1875_3445hz3125__full_frame_stereo_sharing_0_2) { + auto res = GenAndRunGha({{689.0625f, 0, 32768, 0, 128}, {689.0625f, 0, 32768, 128, 256}, {689.0625f, 0, 16384, 256, 384}}, + {{689.0625f, 0, 32768, 0, 128}, {689.0625f, 0, 16384, 256, 384}}); + EXPECT_EQ(res.NumToneBands, 3); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 3); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 3); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetNumWaves(0, 1), 1); + EXPECT_EQ(res.GetNumWaves(0, 2), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 1).second, 1); + EXPECT_EQ(res.GetWaves(0, 1).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 2).second, 1); + EXPECT_EQ(res.GetWaves(0, 2).first[0].FreqIndex, 512); + + EXPECT_EQ(res.ToneSharing[0], true); + EXPECT_EQ(res.ToneSharing[1], false); + EXPECT_EQ(res.ToneSharing[2], true); + EXPECT_EQ(res.Waves[1].WaveParams.size(), 0); + EXPECT_EQ(res.GetNumWaves(1, 1), 0); + Dumper.Dump(&res, 2, 1); +} + +TEST(AT3PGHA, 689hz0625_2067hz1875_3445hz3125__full_frame_stereo_folower_sharing_2) { + auto res = GenAndRunGha({{689.0625f, 0, 32768, 0, 128}, {689.0625f, 0, 32768, 128, 256}, {689.0625f, 0, 16384, 256, 384}}, + {{689.0625f, 0, 16384, 256, 384}}); + EXPECT_EQ(res.NumToneBands, 3); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 3); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 3); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetNumWaves(0, 1), 1); + EXPECT_EQ(res.GetNumWaves(0, 2), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 1).second, 1); + EXPECT_EQ(res.GetWaves(0, 1).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 2).second, 1); + EXPECT_EQ(res.GetWaves(0, 2).first[0].FreqIndex, 512); + + EXPECT_EQ(res.ToneSharing[0], false); + EXPECT_EQ(res.ToneSharing[1], false); + EXPECT_EQ(res.ToneSharing[2], true); + EXPECT_EQ(res.Waves[1].WaveParams.size(), 0); + EXPECT_EQ(res.GetNumWaves(1, 0), 0); + EXPECT_EQ(res.GetNumWaves(1, 1), 0); + Dumper.Dump(&res, 2, 1); +} + +TEST(AT3PGHA, 689hz0625_2067hz1875_3445hz3125__full_frame_stereo_folower_sharing_1) { + auto res = GenAndRunGha({{689.0625f, 0, 32768, 0, 128}, {689.0625f, 0, 32768, 128, 256}, {689.0625f, 0, 16384, 256, 384}}, + {{689.0625f, 0, 16384, 128, 256}}); + EXPECT_EQ(res.NumToneBands, 3); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 3); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 3); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetNumWaves(0, 1), 1); + EXPECT_EQ(res.GetNumWaves(0, 2), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 1).second, 1); + EXPECT_EQ(res.GetWaves(0, 1).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 2).second, 1); + EXPECT_EQ(res.GetWaves(0, 2).first[0].FreqIndex, 512); + + EXPECT_EQ(res.ToneSharing[0], false); + EXPECT_EQ(res.ToneSharing[1], true); + EXPECT_EQ(res.ToneSharing[2], false); + EXPECT_EQ(res.Waves[1].WaveParams.size(), 0); + EXPECT_EQ(res.GetNumWaves(1, 0), 0); + EXPECT_EQ(res.GetNumWaves(1, 2), 0); + Dumper.Dump(&res, 2, 1); +} + +/* +TEST(AT3PGHA, max_tones_multiple_bands_full_frame_stereo) { + auto res = GenAndRunGha({ + {60.0f, 8192, 0, 128}, {120.0f, 8192, 0, 128}, {180.0f, 4096, 0, 128}, {240.0f, 2048, 0, 128}, + {60.0f, 8192, 128, 256}, {120.0f, 8192, 128, 256}, {180.0f, 4096, 128, 256}, {240.0f, 2048, 128, 256}, + {60.0f, 8192, 256, 384}, {120.0f, 8192, 256, 384}, {180.0f, 4096, 256, 384}, {240.0f, 2048, 256, 384}, + {60.0f, 8192, 384, 512}, {120.0f, 8192, 384, 512}, {180.0f, 4096, 384, 512}, {240.0f, 2048, 384, 512}, + {60.0f, 8192, 512, 640}, {120.0f, 8192, 512, 640}, {180.0f, 4096, 512, 640}, {240.0f, 2048, 512, 640}, + {60.0f, 8192, 640, 768}, {120.0f, 8192, 640, 768}, {180.0f, 4096, 640, 768}, {240.0f, 2048, 640, 768}, + {60.0f, 8192, 768, 896}, {120.0f, 8192, 768, 896}, {180.0f, 4096, 768, 896}, {240.0f, 2048, 768, 896}, + }, { + {60.0f, 8192, 0, 128}, {120.0f, 8192, 0, 128}, {180.0f, 4096, 0, 128}, {240.0f, 2048, 0, 128}, + {60.0f, 8192, 128, 256}, {120.0f, 8192, 128, 256}, {180.0f, 4096, 128, 256}, {240.0f, 2048, 128, 256}, + {60.0f, 8192, 256, 384}, {120.0f, 8192, 256, 384}, {180.0f, 4096, 256, 384}, {240.0f, 2048, 256, 384}, + {60.0f, 8192, 384, 512}, {120.0f, 8192, 384, 512}, {180.0f, 4096, 384, 512}, {240.0f, 2048, 384, 512}, + {60.0f, 8192, 512, 640}, {120.0f, 8192, 512, 640}, {180.0f, 4096, 512, 640}, {240.0f, 2048, 512, 640}, + {60.0f, 8192, 640, 768}, {120.0f, 8192, 640, 768}, {180.0f, 4096, 640, 768}, {240.0f, 2048, 640, 768}, + {60.0f, 8192, 768, 896}, {120.0f, 8192, 768, 896}, {180.0f, 4096, 768, 896}, {240.0f, 2048, 768, 896}, + }); + EXPECT_EQ(res.NumToneBands, 7); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 28); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 7); + EXPECT_EQ(res.GetNumWaves(0, 0), 4); + EXPECT_EQ(res.GetNumWaves(0, 1), 4); + EXPECT_EQ(res.GetNumWaves(0, 2), 4); + EXPECT_EQ(res.GetNumWaves(0, 3), 4); + EXPECT_EQ(res.GetNumWaves(0, 4), 4); + EXPECT_EQ(res.GetNumWaves(0, 5), 4); + EXPECT_EQ(res.GetNumWaves(0, 6), 4); + EXPECT_EQ(res.GetWaves(0, 0).second, 4); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(0, 0).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(0, 0).first[2].FreqIndex, 134); + EXPECT_EQ(res.GetWaves(0, 0).first[3].FreqIndex, 178); + + EXPECT_EQ(res.GetWaves(0, 1).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(0, 1).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(0, 1).first[2].FreqIndex, 134); + EXPECT_EQ(res.GetWaves(0, 1).first[3].FreqIndex, 178); + + EXPECT_EQ(res.GetWaves(0, 2).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(0, 2).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(0, 2).first[2].FreqIndex, 134); + EXPECT_EQ(res.GetWaves(0, 2).first[3].FreqIndex, 178); + + EXPECT_EQ(res.GetWaves(0, 3).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(0, 3).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(0, 3).first[2].FreqIndex, 134); + EXPECT_EQ(res.GetWaves(0, 3).first[3].FreqIndex, 178); + + EXPECT_EQ(res.GetWaves(0, 4).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(0, 4).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(0, 4).first[2].FreqIndex, 134); + EXPECT_EQ(res.GetWaves(0, 4).first[3].FreqIndex, 178); + + EXPECT_EQ(res.GetWaves(0, 5).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(0, 5).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(0, 5).first[2].FreqIndex, 134); + EXPECT_EQ(res.GetWaves(0, 5).first[3].FreqIndex, 178); + + EXPECT_EQ(res.GetWaves(0, 6).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(0, 6).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(0, 6).first[2].FreqIndex, 134); + EXPECT_EQ(res.GetWaves(0, 6).first[3].FreqIndex, 178); + + EXPECT_EQ(res.Waves[1].WaveParams.size(), 21); + EXPECT_EQ(res.Waves[1].WaveSbInfos.size(), 7); + EXPECT_EQ(res.GetNumWaves(1, 0), 3); + EXPECT_EQ(res.GetNumWaves(1, 1), 3); + EXPECT_EQ(res.GetNumWaves(1, 2), 3); + EXPECT_EQ(res.GetNumWaves(1, 3), 3); + EXPECT_EQ(res.GetNumWaves(1, 4), 3); + EXPECT_EQ(res.GetNumWaves(1, 5), 3); + EXPECT_EQ(res.GetNumWaves(1, 6), 3); + EXPECT_EQ(res.GetWaves(1, 0).second, 3); + EXPECT_EQ(res.GetWaves(1, 0).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(1, 0).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(1, 0).first[2].FreqIndex, 134); + + EXPECT_EQ(res.GetWaves(1, 1).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(1, 1).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(1, 1).first[2].FreqIndex, 134); + + EXPECT_EQ(res.GetWaves(1, 2).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(1, 2).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(1, 2).first[2].FreqIndex, 134); + + EXPECT_EQ(res.GetWaves(1, 3).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(1, 3).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(1, 3).first[2].FreqIndex, 134); + + EXPECT_EQ(res.GetWaves(1, 4).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(1, 4).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(1, 4).first[2].FreqIndex, 134); + + EXPECT_EQ(res.GetWaves(1, 5).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(1, 5).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(1, 5).first[2].FreqIndex, 134); + + EXPECT_EQ(res.GetWaves(1, 6).first[0].FreqIndex, 45); + EXPECT_EQ(res.GetWaves(1, 6).first[1].FreqIndex, 89); + EXPECT_EQ(res.GetWaves(1, 6).first[2].FreqIndex, 134); + Dumper.Dump(&res, 2, 1); +} + +*/ + +TEST(AT3PGHA, 100hz__two_frames_mono) { + vector<float> buf(2048 * 2); + + Gen({100.0f, 0, 32768, 0, 256}, buf); + + memcpy(&buf[2048], &buf[128], sizeof(float) * 128); + memset(&buf[128], 0, sizeof(float) * 128); + + std::vector<TAt3PGhaData> resBuf; + auto processor = MakeGhaProcessor0(false); + + { + const auto res = DoAnalize(processor.get(), {&buf[0], &buf[2048]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 74); + EXPECT_EQ(res.GetWaves(0, 0).first->AmpSf, 62); + EXPECT_EQ(res.GetWaves(0, 0).first->PhaseIndex, 0); + + resBuf.push_back(res); + } + + { + memset(&buf[0], 0, sizeof(float) * 128); + const auto res = DoAnalize(processor.get(), {&buf[2048], &buf[0]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 74); + EXPECT_EQ(res.GetWaves(0, 0).first->AmpSf, 62); + EXPECT_EQ(res.GetWaves(0, 0).first->PhaseIndex, 21); + + resBuf.push_back(res); + } + Dumper.Dump(resBuf.data(), 1, 2); +} + +TEST(AT3PGHA, 100hz_than_500hz_than_100hz__3_frames_mono) { + vector<float> buf(2048 * 2); + + Gen({100.0f, 0, 32768, 0, 128}, buf); + Gen({500.0f, 0, 32768, 128, 256}, buf); + + memcpy(&buf[2048], &buf[128], sizeof(float) * 128); + memset(&buf[128], 0, sizeof(float) * 128); + + std::vector<TAt3PGhaData> resBuf; + auto processor = MakeGhaProcessor0(false); + + { + const auto res = DoAnalize(processor.get(), {&buf[0], &buf[2048]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 74); + EXPECT_EQ(res.GetWaves(0, 0).first->AmpSf, 62); + EXPECT_EQ(res.GetWaves(0, 0).first->PhaseIndex, 0); + + resBuf.push_back(res); + } + + { + memset(&buf[0], 0, sizeof(float) * 128); + Gen({100.0f, 0, 32768, 0, 128}, buf); + const auto res = DoAnalize(processor.get(), {&buf[2048], &buf[0]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 372); + EXPECT_EQ(res.GetWaves(0, 0).first->AmpSf, 62); + EXPECT_EQ(res.GetWaves(0, 0).first->PhaseIndex, 0); + + resBuf.push_back(res); + } + { + memset(&buf[2048], 0, sizeof(float) * 128); + const auto res = DoAnalize(processor.get(), {&buf[0], &buf[2048]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 74); + EXPECT_EQ(res.GetWaves(0, 0).first->AmpSf, 62); + EXPECT_EQ(res.GetWaves(0, 0).first->PhaseIndex, 0); + + resBuf.push_back(res); + } + + Dumper.Dump(resBuf.data(), 1, 3); +} + +TEST(AT3PGHA, 100hz__phase_two_frames_mono) { + vector<float> buf(2048 * 2); + + Gen({100.0f, M_PI * 0.25, 32768, 0, 256}, buf); + + memcpy(&buf[2048], &buf[128], sizeof(float) * 128); + memset(&buf[128], 0, sizeof(float) * 128); + + auto processor = MakeGhaProcessor0(false); + const auto res = DoAnalize(processor.get(), {&buf[0], &buf[2048]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 74); + EXPECT_EQ(res.GetWaves(0, 0).first->AmpSf, 62); + EXPECT_EQ(res.GetWaves(0, 0).first->PhaseIndex, 4); +} + +TEST(AT3PGHA, 689hz0625__two_frames_mono) { + vector<float> buf(2048 * 2); + + Gen({689.0625f, 0, 32768, 0, 256}, buf); + + memcpy(&buf[2048], &buf[128], sizeof(float) * 128); + memset(&buf[128], 0, sizeof(float) * 128); + + auto processor = MakeGhaProcessor0(false); + const auto res = DoAnalize(processor.get(), {&buf[0], &buf[2048]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 1); + EXPECT_EQ(res.GetWaves(0, 0).second, 1); + EXPECT_EQ(res.GetWaves(0, 0).first->FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 0).first->AmpSf, 63); +} + +TEST(AT3PGHA, 689hz0625_1000hz__two_frames_mono) { + vector<float> buf(2048 * 2); + + Gen({689.0625f, 0, 16384, 0, 256}, buf); + Gen({1000.0f, 0, 16384, 0, 256}, buf); + + memcpy(&buf[2048], &buf[128], sizeof(float) * 128); + memset(&buf[128], 0, sizeof(float) * 128); + + auto processor = MakeGhaProcessor0(false); + const auto res = DoAnalize(processor.get(), {&buf[0], &buf[2048]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 2); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 2); + EXPECT_EQ(res.GetWaves(0, 0).second, 2); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 512); + EXPECT_EQ(res.GetWaves(0, 0).first[0].AmpSf, 58); + EXPECT_EQ(res.GetWaves(0, 0).first[1].FreqIndex, 743); + EXPECT_EQ(res.GetWaves(0, 0).first[1].AmpSf, 58); +} + +TEST(AT3PGHA, 500hz_1000hz__two_frames_mono) { + vector<float> buf(2048 * 2); + + Gen({500.0f, 0, 16384, 0, 256}, buf); + Gen({1000.0f, 0, 2048, 0, 256}, buf); + + memcpy(&buf[2048], &buf[128], sizeof(float) * 128); + memset(&buf[128], 0, sizeof(float) * 128); + + auto processor = MakeGhaProcessor0(false); + const auto res = DoAnalize(processor.get(), {&buf[0], &buf[2048]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 2); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 2); + EXPECT_EQ(res.GetWaves(0, 0).second, 2); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 372); + EXPECT_EQ(res.GetWaves(0, 0).first[0].AmpSf, 58); + EXPECT_EQ(res.GetWaves(0, 0).first[1].FreqIndex, 743); + EXPECT_EQ(res.GetWaves(0, 0).first[1].AmpSf, 46); +} + +TEST(AT3PGHA, 500hz_1000hz__phase_two_frames_mono) { + vector<float> buf(2048 * 2); + + Gen({500.0f, M_PI * 0.5, 16384, 0, 256}, buf); + Gen({1000.0f, M_PI * 0.25, 2048, 0, 256}, buf); + + memcpy(&buf[2048], &buf[128], sizeof(float) * 128); + memset(&buf[128], 0, sizeof(float) * 128); + + auto processor = MakeGhaProcessor0(false); + const auto res = DoAnalize(processor.get(), {&buf[0], &buf[2048]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 2); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 2); + EXPECT_EQ(res.GetWaves(0, 0).second, 2); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 372); + EXPECT_EQ(res.GetWaves(0, 0).first[0].AmpSf, 59); + EXPECT_EQ(res.GetWaves(0, 0).first[1].FreqIndex, 743); + EXPECT_EQ(res.GetWaves(0, 0).first[1].AmpSf, 46); + Dumper.Dump(&res, 1, 1); +} + +TEST(AT3PGHA, 250hz_500hz_1000hz__two_frames_mono) { + vector<float> buf(2048 * 2); + + Gen({250.0f, 0, 16384, 0, 256}, buf); + Gen({500.0f, 0, 4096, 0, 256}, buf); + Gen({1000.0f, 0, 2048, 0, 256}, buf); + + memcpy(&buf[2048], &buf[128], sizeof(float) * 128); + memset(&buf[128], 0, sizeof(float) * 128); + + auto processor = MakeGhaProcessor0(false); + const auto res = DoAnalize(processor.get(), {&buf[0], &buf[2048]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 3); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 3); + EXPECT_EQ(res.GetWaves(0, 0).second, 3); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 186); + EXPECT_EQ(res.GetWaves(0, 0).first[0].AmpSf, 58); + EXPECT_EQ(res.GetWaves(0, 0).first[1].FreqIndex, 372); + EXPECT_EQ(res.GetWaves(0, 0).first[1].AmpSf, 50); + EXPECT_EQ(res.GetWaves(0, 0).first[2].FreqIndex, 743); + EXPECT_EQ(res.GetWaves(0, 0).first[2].AmpSf, 46); +} + +TEST(AT3PGHA, 250hz_500hz_1000hz_1200hz__two_frames_mono) { + vector<float> buf(2048 * 2); + + Gen({250.0f, 0, 16384, 0, 256}, buf); + Gen({500.0f, 0, 8000, 0, 256}, buf); + Gen({1000.0f, 0, 4096, 0, 256}, buf); + Gen({1200.0f, 0, 2048, 0, 256}, buf); + + memcpy(&buf[2048], &buf[128], sizeof(float) * 128); + memset(&buf[128], 0, sizeof(float) * 128); + + auto processor = MakeGhaProcessor0(false); + const auto res = DoAnalize(processor.get(), {&buf[0], &buf[2048]}, {nullptr, nullptr}); + + EXPECT_EQ(res.NumToneBands, 1); + EXPECT_EQ(res.Waves[0].WaveParams.size(), 4); + EXPECT_EQ(res.Waves[0].WaveSbInfos.size(), 1); + EXPECT_EQ(res.GetNumWaves(0, 0), 4); + EXPECT_EQ(res.GetWaves(0, 0).second, 4); + EXPECT_EQ(res.GetWaves(0, 0).first[0].FreqIndex, 186); + EXPECT_EQ(res.GetWaves(0, 0).first[0].AmpSf, 58); + EXPECT_EQ(res.GetWaves(0, 0).first[1].FreqIndex, 372); + EXPECT_EQ(res.GetWaves(0, 0).first[1].AmpSf, 54); + EXPECT_EQ(res.GetWaves(0, 0).first[2].FreqIndex, 743); + EXPECT_EQ(res.GetWaves(0, 0).first[2].AmpSf, 50); + EXPECT_EQ(res.GetWaves(0, 0).first[3].FreqIndex, 892); + EXPECT_EQ(res.GetWaves(0, 0).first[3].AmpSf, 46); +} + +void CheckReduction(float f, uint32_t expFreqIndex){ + vector<float> buf(2048 * 3); + Gen({f, 0, 16384, 0, 384}, buf); + + memcpy(&buf[2048], &buf[128], sizeof(float) * 128); + memcpy(&buf[4096], &buf[256], sizeof(float) * 128); + memset(&buf[128], 0, sizeof(float) * 256); + + auto processor = MakeGhaProcessor0(false); + float w1[2048] = {0}; + float w2[2048] = {0}; + { + const auto res = processor->DoAnalize({&buf[0], &buf[2048]}, {nullptr, nullptr}, w1, w2); + EXPECT_EQ(res->NumToneBands, 1); + EXPECT_EQ(res->Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res->GetWaves(0, 0).first[0].FreqIndex, expFreqIndex); + } + { + memcpy(&w1[0], &buf[0], sizeof(float) * 2048); + const auto res = processor->DoAnalize({&buf[2048], &buf[4096]}, {nullptr, nullptr}, w1, w2); + EXPECT_EQ(res->NumToneBands, 1); + EXPECT_EQ(res->Waves[0].WaveParams.size(), 1); + EXPECT_EQ(res->GetWaves(0, 0).first[0].FreqIndex, expFreqIndex); + double e1 = 0; + double e2 = 0; + for (size_t i = 0; i < 128; i++) { + e1 += w1[i] * w1[i]; + e2 += buf[i] * buf[i]; + } + std::cerr << 5 * log(e2/e1) << std::endl; + float reduction = 5 * log(e2/e1); + EXPECT_GE(reduction, 50); + } + +} + +TEST(AT3PGHA, 269hz166_long_frame_mono) { + CheckReduction(269.166, 200); +} + +TEST(AT3PGHA, 999hz948_long_frame_mono) { + CheckReduction(999.948, 743); +} + +TEST(AT3PGHA, 1345hz826_long_frame_mono) { + CheckReduction(1345.826, 1000); +} diff --git a/src/atrac/at3p/at3p_mdct.cpp b/src/atrac/at3p/at3p_mdct.cpp new file mode 100644 index 0000000..b92e87b --- /dev/null +++ b/src/atrac/at3p/at3p_mdct.cpp @@ -0,0 +1,104 @@ +/* + * 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 "at3p_mdct.h" +#include "util.h" + +#include <array> + +#include <iostream> +#include <vector> + +using std::vector; + +namespace NAtracDEnc { + +static std::array<float, 128> SineWin; + +static void InitSineWin() { + if (SineWin[SineWin.size() - 1] == 0.0) { + for (size_t i = 0; i < SineWin.size(); i++) { + SineWin[i] = 2.0 * sinf((i + 0.5) * (M_PI / (2.0 * SineWin.size()))); + } + } +} + +TAt3pMDCT::TAt3pMDCT() +{ + InitSineWin(); +} + +void TAt3pMDCT::Do(float specs[2048], const TPcmBandsData& bands, THistBuf& work) +{ + for (size_t b = 0; b < 16; b++) { + const float* srcBuff = bands[b]; + float* const curSpec = &specs[b*128]; + + std::array<float, 256>& tmp = work[b]; + + for (size_t i = 0; i < 128; i++) { + tmp[128 + i] = SineWin[127 - i] * srcBuff[i]; + } + + const vector<float>& sp = Mdct(tmp.data()); + + memcpy(curSpec, sp.data(), 128 * sizeof(float)); + + if (b & 1) { + SwapArray(curSpec, 128); + } + + for (size_t i = 0; i < 128; i++) { + tmp[i] = SineWin[i] * srcBuff[i]; + } + } +} + +TAt3pMIDCT::TAt3pMIDCT() +{ + InitSineWin(); +} + +void TAt3pMIDCT::Do(float specs[2048], TPcmBandsData& bands, THistBuf& work) +{ + for (size_t b = 0; b < 16; b++) { + float* dstBuff = bands[b]; + float* const curSpec = &specs[b*128]; + + std::array<float, 128>& tmp = work[b]; + + if (b & 1) { + SwapArray(curSpec, 128); + } + + vector<float> inv = Midct(curSpec); + + for (int j = 0; j < 128; ++j) { + inv[j] *= SineWin[j]; + inv[255 - j] *= SineWin[j]; + } + + for (uint32_t j = 0; j < 128; ++j) { + dstBuff[j] = inv[j] + tmp[j]; + } + + memcpy(tmp.data(), &inv[128], sizeof(float)*128); + } +} + +}; diff --git a/src/atrac/at3p/at3p_mdct.h b/src/atrac/at3p/at3p_mdct.h new file mode 100644 index 0000000..de67245 --- /dev/null +++ b/src/atrac/at3p/at3p_mdct.h @@ -0,0 +1,50 @@ +/* + * 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 <array> +#include <config.h> + +#include "lib/mdct/mdct.h" + +namespace NAtracDEnc { + +class TAt3pMDCT { +public: + TAt3pMDCT(); + using THistBuf = std::array<std::array<float, 256>, 16>; + using TPcmBandsData = std::array<const float*, 16>; + + void Do(float specs[2048], const TPcmBandsData& bands, THistBuf& work); +private: + NMDCT::TMDCT<256> Mdct; +}; + +class TAt3pMIDCT { +public: + TAt3pMIDCT(); + using THistBuf = std::array<std::array<float, 128>, 16>; + using TPcmBandsData = std::array<float*, 16>; + + void Do(float specs[2048], TPcmBandsData& bands, THistBuf& work); +private: + NMDCT::TMIDCT<256> Midct; +}; + +} diff --git a/src/atrac/at3p/at3p_mdct_ut.cpp b/src/atrac/at3p/at3p_mdct_ut.cpp new file mode 100644 index 0000000..c74eb47 --- /dev/null +++ b/src/atrac/at3p/at3p_mdct_ut.cpp @@ -0,0 +1,106 @@ +/* + * 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 "at3p_mdct.h" +#include <gtest/gtest.h> + +#include <vector> +#include <cmath> + +using namespace NAtracDEnc; + +TEST(TAtpMDCT, ZeroOneBlock) { + std::array<float, 2048> specs; + static std::array<float, 2048> zero; + + { + TAt3pMDCT mdct; + + static TAt3pMDCT::THistBuf buff; + + TAt3pMDCT::TPcmBandsData pcm; + for (size_t i = 0; i < pcm.size(); i++) { + pcm[i] = &zero[i * 128]; + } + + mdct.Do(specs.data(), pcm, buff); + + for(auto s: specs) + EXPECT_NEAR(s, 0.0, 0.0000000001); + } + + { + TAt3pMIDCT midct; + + TAt3pMIDCT::TPcmBandsData pcm; + static TAt3pMIDCT::THistBuf buff; + for (size_t i = 0; i < pcm.size(); i++) { + pcm[i] = &zero[i * 128]; + } + + midct.Do(specs.data(), pcm, buff); + + for(size_t i = 0; i < zero.size(); ++i) + EXPECT_NEAR(zero[i], 0.0, 0.0000000001); + } +} + + +TEST(TAtpMDCT, DC) { + std::array<float, 4096> specs; + std::array<float, 2048> dc; + + for (size_t i = 0; i < dc.size(); i++) { + dc[i] = 1.0; + } + + { + TAt3pMDCT mdct; + + static TAt3pMDCT::THistBuf buff; + + TAt3pMDCT::TPcmBandsData pcm; + for (size_t i = 0; i < pcm.size(); i++) { + pcm[i] = &dc[i * 128]; + } + + mdct.Do(specs.data(), pcm, buff); + mdct.Do(specs.data() + 2048, pcm, buff); + } + + { + TAt3pMIDCT midct; + + std::array<float, 2048> result; + + TAt3pMIDCT::TPcmBandsData pcm; + + static TAt3pMIDCT::THistBuf buff; + + for (size_t i = 0; i < pcm.size(); i++) { + pcm[i] = &result[i * 128]; + } + + midct.Do(specs.data(), pcm, buff); + midct.Do(specs.data() + 2048, pcm, buff); + + for(size_t i = 0; i < result.size(); ++i) { + EXPECT_NEAR(result[i], dc[i], 0.000001); + } + } +} diff --git a/src/atrac/at3p/at3p_tables.cpp b/src/atrac/at3p/at3p_tables.cpp new file mode 100644 index 0000000..fee3417 --- /dev/null +++ b/src/atrac/at3p/at3p_tables.cpp @@ -0,0 +1,125 @@ +/* + * 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 "util.h" +#include "at3p_tables.h" +#include "ff/atrac3plus_data.h" + +#include <iostream> +#include <sstream> + +namespace NAtracDEnc { +namespace NAt3p { + +static struct TInvMantTab { +public: + TInvMantTab() { + Data[0] = 0.0; //unused (for zero lenght on the decoder) + for (size_t i = 1; i < 8; i++) { + Data[i] = 1.0 / atrac3p_mant_tab[i]; + } + } + float Data[8]; +} InvMantTab_; + +float InvMantTab(size_t i) { return InvMantTab_.Data[i]; } + +static struct TScaleTableInitializer { +public: + TScaleTableInitializer() { + const std::array<float, 64> src = { + 0.027852058, 0.0350914, 0.044212341, 0.055704117, + 0.0701828, 0.088424683, 0.11140823, 0.1403656, + 0.17684937, 0.22281647, 0.2807312, 0.35369873, + 0.44563293, 0.5614624, 0.70739746, 0.89126587, + 1.1229248, 1.4147949, 1.7825317, 2.2458496, + 2.8295898, 3.5650635, 4.4916992, 5.6591797, + 7.130127, 8.9833984, 11.318359, 14.260254, + 17.966797, 22.636719, 28.520508, 35.933594, + 45.273438, 57.041016, 71.867188, 90.546875, + 114.08203, 143.73438, 181.09375, 228.16406, + 287.46875, 362.1875, 456.32812, 574.9375, + 724.375, 912.65625, 1149.875, 1448.75, + 1825.3125, 2299.75, 2897.5, 3650.625, + 4599.5, 5795.0, 7301.25, 9199.0, + 11590.0, 14602.5, 18398.0, 23180.0, + 29205.0, 36796.0, 46360.0, 58410.0 + }; + + for (size_t i = 0; i < src.size(); i++) { + TScaleTable::ScaleTable[i] = src[i] / src.back(); + } + } +} ScaleTableInitializer_; + +float TScaleTable::ScaleTable[64]; +constexpr uint32_t TScaleTable::BlocksPerBand[NumQMF + 1]; +constexpr uint32_t TScaleTable::SpecsPerBlock[MaxBfus]; +constexpr uint32_t TScaleTable::BlockSizeTab[MaxBfus + 1]; + +static uint16_t atde_noinline GenHuffmanEncTable(const uint8_t *cb, const uint8_t *xlat, uint16_t outLen, TVlcElement* const out) +{ + uint16_t index = 0; + uint16_t code = 0; + for (int b = 1; b <= 12; b++) { + for (int i = *cb++; i > 0; i--) { + uint8_t val = xlat[index]; + if (val >= outLen) { + std::stringstream ss; + ss << "encoded value out of range, invalid haffman table, got: " << (int)val << " table len: " << (int)outLen; + throw std::runtime_error(ss.str()); + } + TVlcElement* cur = out + val; + cur->Code = code; + cur->Len = b; + index++; + code++; + } + code <<= 1; + } + return index; +} + +template<typename T> +uint16_t GenHuffmanEncTable(const uint8_t *cb, const uint8_t *xlat, T& out) +{ + return GenHuffmanEncTable(cb, xlat, out.size(), out.data()); +} + +THuffTables::THuffTables() +{ + GenHuffmanEncTable(&atrac3p_tone_cbs[0][0], &atrac3p_tone_xlats[0], NumToneBands); + + for (size_t i = 0; i < VlcSpecs.size(); i++) { + for (size_t j = 0; j < VlcSpecs[i].size(); j++) { + VlcSpecs[i][j].Code = 0; + VlcSpecs[i][j].Len = 0; + } + } + + for (int i = 0, x = 0; i < 112; i++) { + if (atrac3p_spectra_cbs[i][0] >= 0) { + x += GenHuffmanEncTable((uint8_t*)&atrac3p_spectra_cbs[i][0], &atrac3p_spectra_xlats[x], VlcSpecs[i]); + } else { + VlcSpecs[i] = VlcSpecs[-atrac3p_spectra_cbs[i][0]]; + } + } +} + +} // namespace NAt3p +} // namespace NAtracDEnc diff --git a/src/atrac/at3p/at3p_tables.h b/src/atrac/at3p/at3p_tables.h new file mode 100644 index 0000000..0840273 --- /dev/null +++ b/src/atrac/at3p/at3p_tables.h @@ -0,0 +1,79 @@ +/* + * 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 <array> +#include <cstdint> +#include <cstddef> + +namespace NAtracDEnc { + +namespace NAt3p { + +float InvMantTab(size_t i); + +struct TVlcElement { + int16_t Code; + int16_t Len; +}; + +struct THuffTables { + THuffTables(); + std::array<TVlcElement, 16> NumToneBands; + std::array<std::array<TVlcElement, 256>, 112> VlcSpecs; +}; + +struct TScaleTable { + + class TBlockSizeMod { + public: + constexpr bool ShortWin(uint8_t) const noexcept { + return false; + } + }; + + static constexpr uint32_t MaxBfus = 32; + static constexpr uint32_t NumQMF = 16; + static float ScaleTable[64]; + + static constexpr uint32_t BlocksPerBand[NumQMF + 1] = + { + 0, 8, 12, 16, 18, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 + }; + static constexpr uint32_t SpecsPerBlock[MaxBfus] = { + 16, 16, 16, 16, 16, 16, 16, 16, + 32, 32, 32, 32, 32, 32, 32, 32, + 64, 64, 64, 64, 64, 64, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128 + }; + static constexpr uint32_t BlockSizeTab[MaxBfus + 1] = { + 0, 16, 32, 48, 64, 80, 96, 112, + 128, 160, 192, 224, 256, 288, 320, 352, + 384, 448, 512, 576, 640, 704, 768, 896, + 1024, 1152, 1280, 1408, 1536, 1664, 1792, 1920, + 2048 + }; + static constexpr uint32_t const * const SpecsStartShort = &BlockSizeTab[0]; + + static constexpr uint32_t const * const SpecsStartLong = &BlockSizeTab[0]; +}; + +} + +} // namespace NAtracDEnc diff --git a/src/atrac/at3p/ff/atrac3plus.h b/src/atrac/at3p/ff/atrac3plus.h new file mode 100644 index 0000000..3aeb110 --- /dev/null +++ b/src/atrac/at3p/ff/atrac3plus.h @@ -0,0 +1,165 @@ +/* + * ATRAC3+ compatible decoder + * + * Copyright (c) 2010-2013 Maxim Poliakovski + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Global structures, constants and data for ATRAC3+ decoder. + */ + +#ifndef AVCODEC_ATRAC3PLUS_H +#define AVCODEC_ATRAC3PLUS_H + +#include <stdint.h> + +/** Global unit sizes */ +#define ATRAC3P_SUBBANDS 16 ///< number of PQF subbands +#define ATRAC3P_SUBBAND_SAMPLES 128 ///< number of samples per subband +#define ATRAC3P_FRAME_SAMPLES (ATRAC3P_SUBBAND_SAMPLES * ATRAC3P_SUBBANDS) + +#define ATRAC3P_PQF_FIR_LEN 12 ///< length of the prototype FIR of the PQF + +/** Global constants */ +#define ATRAC3P_POWER_COMP_OFF 15 ///< disable power compensation + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/** ATRAC3+ channel unit types */ +enum Atrac3pChannelUnitTypes { + CH_UNIT_MONO = 0, ///< unit containing one coded channel + CH_UNIT_STEREO = 1, ///< unit containing two jointly-coded channels + CH_UNIT_EXTENSION = 2, ///< unit containing extension information + CH_UNIT_TERMINATOR = 3 ///< unit sequence terminator +}; + +/** Amplitude envelope of a group of sine waves */ +typedef struct Atrac3pWaveEnvelope { + int has_start_point; ///< indicates start point within the GHA window + int has_stop_point; ///< indicates stop point within the GHA window + int start_pos; ///< start position expressed in n*4 samples + int stop_pos; ///< stop position expressed in n*4 samples +} Atrac3pWaveEnvelope; + +/** Parameters of a group of sine waves */ +typedef struct Atrac3pWavesData { + Atrac3pWaveEnvelope pend_env; ///< pending envelope from the previous frame + Atrac3pWaveEnvelope curr_env; ///< group envelope from the current frame + int num_wavs; ///< number of sine waves in the group + int start_index; ///< start index into global tones table for that subband +} Atrac3pWavesData; + +/** Parameters of a single sine wave */ +typedef struct Atrac3pWaveParam { + int freq_index; ///< wave frequency index + int amp_sf; ///< quantized amplitude scale factor + int amp_index; ///< quantized amplitude index + int phase_index; ///< quantized phase index +} Atrac3pWaveParam; + +/** Sound channel parameters */ +typedef struct Atrac3pChanParams { + //int ch_num; + //int num_coded_vals; ///< number of transmitted quant unit values + //int fill_mode; + //int split_point; + //int table_type; ///< table type: 0 - tone?, 1- noise? + //int qu_wordlen[32]; ///< array of word lengths for each quant unit + //int qu_sf_idx[32]; ///< array of scale factor indexes for each quant unit + //int qu_tab_idx[32]; ///< array of code table indexes for each quant unit + //int16_t spectrum[2048]; ///< decoded IMDCT spectrum + //uint8_t power_levs[5]; ///< power compensation levels + + /* imdct window shape history (2 frames) for overlapping. */ + //uint8_t wnd_shape_hist[2][ATRAC3P_SUBBANDS]; ///< IMDCT window shape, 0=sine/1=steep + //uint8_t *wnd_shape; ///< IMDCT window shape for current frame + //uint8_t *wnd_shape_prev; ///< IMDCT window shape for previous frame + + //int num_gain_subbands; ///< number of subbands with gain control data + + /* tones data history (2 frames) for overlapping. */ + Atrac3pWavesData tones_info_hist[2][ATRAC3P_SUBBANDS]; + Atrac3pWavesData *tones_info; + Atrac3pWavesData *tones_info_prev; +} Atrac3pChanParams; + +/* Per-unit sine wave parameters */ +typedef struct Atrac3pWaveSynthParams { + int tones_present; ///< 1 - tones info present + int amplitude_mode; ///< 1 - low range, 0 - high range + int num_tone_bands; ///< number of PQF bands with tones + //uint8_t tone_sharing[ATRAC3P_SUBBANDS]; ///< 1 - subband-wise tone sharing flags + //uint8_t tone_master[ATRAC3P_SUBBANDS]; ///< 1 - subband-wise tone channel swapping + uint8_t invert_phase[ATRAC3P_SUBBANDS]; ///< 1 - subband-wise phase inversion + int tones_index; ///< total sum of tones in this unit + Atrac3pWaveParam waves[48]; +} Atrac3pWaveSynthParams; + +/** Channel unit parameters */ +typedef struct Atrac3pChanUnitCtx { + /* channel unit variables */ + //int unit_type; ///< unit type (mono/stereo) + //int num_quant_units; + //int num_subbands; + //int used_quant_units; ///< number of quant units with coded spectrum + //int num_coded_subbands; ///< number of subbands with coded spectrum + //int mute_flag; ///< mute flag + //int use_full_table; ///< 1 - full table list, 0 - restricted one + //int noise_present; ///< 1 - global noise info present + //int noise_level_index; ///< global noise level index + //int noise_table_index; ///< global noise RNG table index + //uint8_t swap_channels[ATRAC3P_SUBBANDS]; ///< 1 - perform subband-wise channel swapping + //uint8_t negate_coeffs[ATRAC3P_SUBBANDS]; ///< 1 - subband-wise IMDCT coefficients negation + Atrac3pChanParams channels[2]; + + /* Variables related to GHA tones */ + Atrac3pWaveSynthParams wave_synth_hist[2]; ///< waves synth history for two frames + Atrac3pWaveSynthParams *waves_info; + Atrac3pWaveSynthParams *waves_info_prev; + + //Atrac3pIPQFChannelCtx ipqf_ctx[2]; + //DECLARE_ALIGNED(32, float, prev_buf)[2][ATRAC3P_FRAME_SAMPLES]; ///< overlapping buffer +} Atrac3pChanUnitCtx; + +/** + * Initialize sine waves synthesizer and ff_sine_* tables. + */ +void ff_atrac3p_init_dsp_static(void); + +/** + * Synthesize sine waves for a particular subband. + * + * @param[in] ch_unit pointer to the channel unit context + * @param[in] fdsp pointer to float DSP context + * @param[in] ch_num which channel to process + * @param[in] sb which subband to process + * @param[out] out receives processed data + */ +void ff_atrac3p_generate_tones(Atrac3pChanUnitCtx *ch_unit, + int ch_num, int sb, float *out); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* AVCODEC_ATRAC3PLUS_H */ diff --git a/src/atrac/at3p/ff/atrac3plus_data.h b/src/atrac/at3p/ff/atrac3plus_data.h new file mode 100644 index 0000000..3e64292 --- /dev/null +++ b/src/atrac/at3p/ff/atrac3plus_data.h @@ -0,0 +1,1672 @@ +/* + * ATRAC3+ compatible decoder + * + * Copyright (c) 2010-2013 Maxim Poliakovski + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_ATRAC3PLUS_DATA_H +#define AVCODEC_ATRAC3PLUS_DATA_H + +#include <stddef.h> +#include <stdint.h> + +/* Mantissa table. */ +/* pow(10, x * log10(2) + 0.05) / 2 / ([1,2,3,5,7,15,31] + 0.5) */ +static const float atrac3p_mant_tab[8] = { + 0.0, + 0.74801636, + 0.44882202, + 0.32058716, + 0.20400238, + 0.1496048, + 0.07239151, + 0.035619736 +}; + +/** VLC tables for wordlen */ +static const uint8_t atrac3p_wl_cbs[][12] = { + { 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0 }, +}; + +/** VLC tables for code table indexes */ +static const uint8_t atrac3p_ct_cbs[][12] = { + { 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 5, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 5, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0 }, +}; +/* Symbols for wordlen interleaved with symbols for code table */ +static const uint8_t atrac3p_wl_ct_xlats[] = { + /* wordlen table 1 - 3 entries */ + 0, 1, 7, + /* code table 1 - 4 entries */ + 0, 1, 2, 3, + /* wordlen table 2 - 5 entries */ + 0, 1, 2, 6, 7, + /* code table 2 - 8 entries */ + 0, 1, 2, 3, 4, 5, 6, 7, + /* wordlen table 3 - 8 entries */ + 0, 1, 7, 2, 5, 6, 3, 4, + /* code table 3 - 8 entries */ + 0, 1, 2, 3, 6, 7, 4, 5, + /* wordlen table 4 - 8 entries */ + 0, 1, 7, 2, 3, 6, 4, 5, + /* code table 4 - 8 entries */ + 0, 1, 2, 3, 4, 5, 6, 7, +}; + +/** VLC tables for scale factor indexes */ +static const uint8_t atrac3p_sf_cbs[][12] = { + { 0, 1, 4, 2, 0, 0, 0, 7, 50, 0, 0, 0 }, + { 0, 1, 4, 2, 0, 0, 0, 7, 50, 0, 0, 0 }, + { 1, 0, 2, 0, 4, 0, 2, 1, 54, 0, 0, 0 }, + { 0, 1, 4, 0, 4, 0, 3, 0, 52, 0, 0, 0 }, + { 0, 1, 4, 2, 0, 8, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 4, 2, 0, 8, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 2, 2, 2, 0, 8, 0, 0, 0, 0, 0 }, + { 0, 1, 4, 2, 2, 2, 4, 0, 0, 0, 0, 0 }, +}; + +static const uint8_t atrac3p_sf_xlats[] = { + /* Scale factor index 1 - 64 entries */ + 0, 1, 61, 62, 63, 2, 60, 3, 4, 5, 6, 57, 58, 59, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* Scale factor index 2 - 64 entries */ + 0, 1, 2, 62, 63, 3, 61, 4, 5, 6, 57, 58, 59, 60, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + /* Scale factor index 3 - 64 entries */ + 0, 1, 63, 2, 3, 61, 62, 4, 60, 59, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + /* Scale factor index 4 - 64 entries */ + 0, 1, 2, 62, 63, 3, 4, 60, 61, 5, 58, 59, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + /* Scale factor index 5 - 15 entries */ + 0, 1, 13, 14, 15, 2, 12, 3, 4, 5, 6, 7, 9, 10, 11, + /* Scale factor index 6 - 15 entries */ + 0, 1, 2, 14, 15, 3, 13, 4, 5, 6, 7, 9, 10, 11, 12, + /* Scale factor index 7 - 15 entries */ + 0, 1, 15, 2, 14, 3, 13, 4, 5, 6, 7, 9, 10, 11, 12, + /* Scale factor index 8 - 15 entries */ + 0, 1, 2, 14, 15, 3, 13, 4, 12, 5, 11, 6, 7, 9, 10, +}; + +/* weights for quantized word lengths */ +static const int8_t atrac3p_wl_weights[6][32] = { + { 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 6, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, + { 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 6, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 2, 2, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +/* weights for quantized scale factors + * sf_weights[i] = i / (tab_idx + 1) + * where tab_idx = [1,2] */ +static const int8_t atrac3p_sf_weights[2][32] = { + { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15 }, + { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10 } +}; + +/** Ungroup table for word length segments. + * Numbers in this table tell which coeff belongs to which segment. */ +static const uint8_t atrac3p_qu_num_to_seg[32] = { + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9 +}; + +/** Map quant unit number to subband number */ +static const uint8_t atrac3p_qu_to_subband[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +/** Map subband number to number of power compensation groups */ +static const int atrac3p_subband_to_num_powgrps[16] = { + 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5 +}; + +/** 3D base shape tables. The values are grouped together as follows: + * [num_start_values = 8][num_shape_tables = 16][num_seg_coeffs = 9] + * For each of the 8 start values there are 16 different shapes each + * 9 coefficients long. */ +static const int8_t atrac3p_wl_shapes[8][16][9] = { + { { 0, 0, 0, 0, 0, 0, 0, -2, -1 }, + { 0, 0, 0, 0, 0, 0, 0, -5, -1 }, + { 0, 0, 0, -7, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, -7, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, -5, 0, 0 }, + { 0, 0, 0, 0, -5, 0, 0, 0, 0 }, + { -7, -7, 0, 0, 0, 0, 0, 0, 0 }, + { 0, -7, 0, 0, 0, 0, 0, 0, 0 }, + { -2, -2, -5, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, -2, -5, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, -2, -5, 0, 0 }, + { 0, 0, 0, -5, 0, 0, 0, 0, 0 }, + { 0, -2, -7, -2, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, -2, -5, 0, 0, 0 }, + { 0, 0, 0, -5, -5, 0, 0, 0, 0 }, + { 0, 0, 0, -5, -2, 0, 0, 0, 0 } }, + { { -1, -5, -3, -2, -1, -1, 0, 0, 0 }, + { -2, -5, -3, -3, -2, -1, -1, 0, 0 }, + { 0, -1, -1, -1, 0, 0, 0, 0, 0 }, + { -1, -3, 0, 0, 0, 0, 0, 0, 0 }, + { -1, -2, 0, 0, 0, 0, 0, 0, 0 }, + { -1, -3, -1, 0, 0, 0, 0, 1, 1 }, + { -1, -5, -3, -3, -2, -1, 0, 0, 0 }, + { -1, -1, -4, -2, -2, -1, -1, 0, 0 }, + { -1, -1, -3, -2, -3, -1, -1, -1, 0 }, + { -1, -4, -2, -3, -1, 0, 0, 0, 0 }, + { 0, -1, -2, -2, -1, -1, 0, 0, 0 }, + { 0, -2, -1, 0, 0, 0, 0, 0, 0 }, + { -1, -1, 0, 0, 0, 0, 0, 0, 0 }, + { -1, -1, -3, -2, -2, -1, -1, -1, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, -1, -3, -2, -2, -1, -1, -1, 0 }, }, + { { -1, -2, 0, 1, 1, 1, 1, 1, 1 }, + { 0, -1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, -2, 1, 1, 1, 1, 1, 1, 1 }, + { 0, -2, 0, 1, 1, 1, 1, 1, 1 }, + { -1, -1, 0, 1, 1, 1, 1, 1, 1 }, + { 0, 0, -1, 0, 1, 1, 1, 1, 1 }, + { -1, -1, 1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, -1, 1, 1, 1, 1, 1, 1 }, + { 0, -1, 0, 1, 1, 1, 1, 1, 1 }, + { -1, -1, -1, 1, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1 }, + { 0, 0, 0, 1, 1, 1, 1, 1, 1 }, + { 0, -1, -1, 1, 1, 1, 1, 1, 1 }, + { 0, 1, 0, 1, 1, 1, 1, 1, 1 }, + { 0, -3, -2, 1, 1, 1, 1, 2, 2 }, + { -3, -5, -3, 2, 2, 2, 2, 2, 2 }, }, + { { -1, -2, 0, 2, 2, 2, 2, 2, 2 }, + { -1, -2, 0, 1, 2, 2, 2, 2, 2 }, + { 0, -2, 0, 2, 2, 2, 2, 2, 2 }, + { -1, 0, 1, 2, 2, 2, 2, 2, 2 }, + { 0, 0, 1, 2, 2, 2, 2, 2, 2 }, + { 0, -2, 0, 1, 2, 2, 2, 2, 2 }, + { 0, -1, 1, 2, 2, 2, 2, 2, 2 }, + { -1, -1, 0, 2, 2, 2, 2, 2, 2 }, + { -1, -1, 0, 1, 2, 2, 2, 2, 2 }, + { -1, -2, -1, 2, 2, 2, 2, 2, 2 }, + { 0, -1, 0, 2, 2, 2, 2, 2, 2 }, + { 1, 1, 0, 1, 2, 2, 2, 2, 2 }, + { 0, 1, 2, 2, 2, 2, 2, 2, 2 }, + { 1, 0, 0, 1, 2, 2, 2, 2, 2 }, + { 0, 0, 0, 1, 2, 2, 2, 2, 2 }, + { -1, -1, -1, 1, 2, 2, 2, 2, 2 }, }, + { { 0, 1, 2, 3, 3, 3, 3, 3, 3 }, + { 1, 1, 2, 3, 3, 3, 3, 3, 3 }, + { -1, 0, 1, 2, 3, 3, 3, 3, 3 }, + { 0, 0, 2, 3, 3, 3, 3, 3, 3 }, + { -1, 0, 1, 3, 3, 3, 3, 3, 3 }, + { 0, 0, 1, 3, 3, 3, 3, 3, 3 }, + { 1, 2, 3, 3, 3, 3, 3, 3, 3 }, + { 1, 2, 2, 3, 3, 3, 3, 3, 3 }, + { 0, 1, 1, 3, 3, 3, 3, 3, 3 }, + { 0, 0, 1, 2, 3, 3, 3, 3, 3 }, + { -1, 1, 2, 3, 3, 3, 3, 3, 3 }, + { -1, 0, 2, 3, 3, 3, 3, 3, 3 }, + { 2, 2, 3, 3, 3, 3, 3, 3, 3 }, + { 1, 1, 3, 3, 3, 3, 3, 3, 3 }, + { 0, 2, 3, 3, 3, 3, 3, 3, 3 }, + { 0, 1, 1, 2, 3, 3, 3, 3, 3 }, }, + { { 0, 1, 2, 3, 4, 4, 4, 4, 4 }, + { 1, 2, 3, 4, 4, 4, 4, 4, 4 }, + { 0, 0, 2, 3, 4, 4, 4, 4, 4 }, + { 1, 1, 2, 4, 4, 4, 4, 4, 4 }, + { 0, 1, 2, 4, 4, 4, 4, 4, 4 }, + { -1, 0, 1, 3, 4, 4, 4, 4, 4 }, + { 0, 0, 1, 3, 4, 4, 4, 4, 4 }, + { 1, 1, 2, 3, 4, 4, 4, 4, 4 }, + { 0, 1, 1, 3, 4, 4, 4, 4, 4 }, + { 2, 2, 3, 4, 4, 4, 4, 4, 4 }, + { 1, 1, 3, 4, 4, 4, 4, 4, 4 }, + { 1, 2, 2, 4, 4, 4, 4, 4, 4 }, + { -1, 0, 2, 3, 4, 4, 4, 4, 4 }, + { 0, 1, 3, 4, 4, 4, 4, 4, 4 }, + { 1, 2, 2, 3, 4, 4, 4, 4, 4 }, + { 0, 2, 3, 4, 4, 4, 4, 4, 4 }, }, + { { 1, 2, 3, 4, 5, 5, 5, 5, 5 }, + { 0, 1, 2, 3, 4, 5, 5, 5, 5 }, + { 0, 1, 2, 3, 5, 5, 5, 5, 5 }, + { 1, 1, 3, 4, 5, 5, 5, 5, 5 }, + { 1, 1, 2, 4, 5, 5, 5, 5, 5 }, + { 1, 2, 2, 4, 5, 5, 5, 5, 5 }, + { 1, 1, 2, 3, 5, 5, 5, 5, 5 }, + { 2, 2, 3, 4, 5, 5, 5, 5, 5 }, + { 0, 1, 2, 4, 5, 5, 5, 5, 5 }, + { 2, 2, 3, 5, 5, 5, 5, 5, 5 }, + { 1, 2, 3, 5, 5, 5, 5, 5, 5 }, + { 0, 1, 3, 4, 5, 5, 5, 5, 5 }, + { 1, 2, 2, 3, 5, 5, 5, 5, 5 }, + { 2, 3, 4, 5, 5, 5, 5, 5, 5 }, + { 0, 2, 3, 4, 5, 5, 5, 5, 5 }, + { 1, 1, 1, 3, 4, 5, 5, 5, 5 }, }, + { { 1, 2, 3, 4, 5, 5, 5, 6, 6 }, + { 1, 2, 3, 4, 5, 6, 6, 6, 6 }, + { 2, 3, 4, 5, 6, 6, 6, 6, 6 }, + { 1, 2, 3, 4, 6, 6, 6, 6, 6 }, + { 2, 2, 3, 4, 5, 5, 5, 6, 6 }, + { 1, 2, 3, 4, 5, 5, 6, 6, 6 }, + { 2, 2, 3, 4, 6, 6, 6, 6, 6 }, + { 2, 2, 3, 4, 5, 6, 6, 6, 6 }, + { 2, 2, 4, 5, 6, 6, 6, 6, 6 }, + { 2, 2, 3, 5, 6, 6, 6, 6, 6 }, + { 1, 2, 3, 5, 6, 6, 6, 6, 6 }, + { 2, 3, 3, 5, 6, 6, 6, 6, 6 }, + { 1, 2, 4, 5, 6, 6, 6, 6, 6 }, + { 2, 2, 3, 4, 5, 5, 6, 6, 6 }, + { 2, 3, 3, 4, 6, 6, 6, 6, 6 }, + { 1, 3, 4, 5, 6, 6, 6, 6, 6 } } +}; + +/** 2D base shape tables for scale factor coding. + * The values are grouped together as follows: + * [num_shape_tables = 64][num_seg_coeffs = 9] */ +static const int8_t atrac3p_sf_shapes[64][9] = { + { -3, -2, -1, 0, 3, 5, 6, 8, 40 }, + { -3, -2, 0, 1, 7, 9, 11, 13, 20 }, + { -1, 0, 0, 1, 6, 8, 10, 13, 41 }, + { 0, 0, 0, 2, 5, 5, 6, 8, 14 }, + { 0, 0, 0, 2, 6, 7, 8, 11, 47 }, + { 0, 0, 1, 2, 5, 7, 8, 10, 32 }, + { 0, 0, 1, 3, 8, 10, 12, 14, 47 }, + { 0, 0, 2, 4, 9, 10, 12, 14, 40 }, + { 0, 0, 3, 5, 9, 10, 12, 14, 22 }, + { 0, 1, 3, 5, 10, 14, 18, 22, 31 }, + { 0, 2, 5, 6, 10, 10, 10, 12, 46 }, + { 0, 2, 5, 7, 12, 14, 15, 18, 44 }, + { 1, 1, 4, 5, 7, 7, 8, 9, 15 }, + { 1, 2, 2, 2, 4, 5, 7, 9, 26 }, + { 1, 2, 2, 3, 6, 7, 7, 8, 47 }, + { 1, 2, 2, 3, 6, 8, 10, 13, 22 }, + { 1, 3, 4, 7, 13, 17, 21, 24, 41 }, + { 1, 4, 0, 4, 10, 12, 13, 14, 17 }, + { 2, 3, 3, 3, 6, 8, 10, 13, 48 }, + { 2, 3, 3, 4, 9, 12, 14, 17, 47 }, + { 2, 3, 3, 5, 10, 12, 14, 17, 25 }, + { 2, 3, 5, 7, 8, 9, 9, 9, 13 }, + { 2, 3, 5, 9, 16, 21, 25, 28, 33 }, + { 2, 4, 5, 8, 12, 14, 17, 19, 26 }, + { 2, 4, 6, 8, 12, 13, 13, 15, 20 }, + { 2, 4, 7, 12, 20, 26, 30, 32, 35 }, + { 3, 3, 5, 6, 12, 14, 16, 19, 34 }, + { 3, 4, 4, 5, 7, 9, 10, 11, 48 }, + { 3, 4, 5, 6, 8, 9, 10, 11, 16 }, + { 3, 5, 5, 5, 7, 9, 10, 13, 35 }, + { 3, 5, 5, 7, 10, 12, 13, 15, 49 }, + { 3, 5, 7, 7, 8, 7, 9, 12, 21 }, + { 3, 5, 7, 8, 12, 14, 15, 15, 24 }, + { 3, 5, 7, 10, 16, 21, 24, 27, 44 }, + { 3, 5, 8, 14, 21, 26, 28, 29, 42 }, + { 3, 6, 10, 13, 18, 19, 20, 22, 27 }, + { 3, 6, 11, 16, 24, 27, 28, 29, 31 }, + { 4, 5, 4, 3, 4, 6, 8, 11, 18 }, + { 4, 6, 5, 6, 9, 10, 12, 14, 20 }, + { 4, 6, 7, 6, 6, 6, 7, 8, 46 }, + { 4, 6, 7, 9, 13, 16, 18, 20, 48 }, + { 4, 6, 7, 9, 14, 17, 20, 23, 31 }, + { 4, 6, 9, 11, 14, 15, 15, 17, 21 }, + { 4, 8, 13, 20, 27, 32, 35, 36, 38 }, + { 5, 6, 6, 4, 5, 6, 7, 6, 6 }, + { 5, 7, 7, 8, 9, 9, 10, 12, 49 }, + { 5, 8, 9, 9, 10, 11, 12, 13, 42 }, + { 5, 8, 10, 12, 15, 16, 17, 19, 42 }, + { 5, 8, 12, 17, 26, 31, 32, 33, 44 }, + { 5, 9, 13, 16, 20, 22, 23, 23, 35 }, + { 6, 8, 8, 7, 6, 5, 6, 8, 15 }, + { 6, 8, 8, 8, 9, 10, 12, 16, 24 }, + { 6, 8, 8, 9, 10, 10, 11, 11, 13 }, + { 6, 8, 10, 13, 19, 21, 24, 26, 32 }, + { 6, 9, 10, 11, 13, 13, 14, 16, 49 }, + { 7, 9, 9, 10, 13, 14, 16, 19, 27 }, + { 7, 10, 12, 13, 16, 16, 17, 17, 27 }, + { 7, 10, 12, 14, 17, 19, 20, 22, 48 }, + { 8, 9, 10, 9, 10, 11, 11, 11, 19 }, + { 8, 11, 12, 12, 13, 13, 13, 13, 17 }, + { 8, 11, 13, 14, 16, 17, 19, 20, 27 }, + { 8, 12, 17, 22, 26, 28, 29, 30, 33 }, + { 10, 14, 16, 19, 21, 22, 22, 24, 28 }, + { 10, 15, 17, 18, 21, 22, 23, 25, 43 } +}; + +static const uint8_t atrac3p_ct_restricted_to_full[2][7][4] = { + { { 0, 5, 4, 1 }, + { 0, 1, 2, 3 }, + { 3, 0, 4, 2 }, + { 4, 0, 1, 2 }, + { 1, 0, 4, 3 }, + { 3, 0, 2, 1 }, + { 0, 3, 1, 2 } }, + { { 4, 0, 1, 2 }, + { 0, 3, 2, 1 }, + { 0, 1, 2, 3 }, + { 0, 1, 2, 4 }, + { 0, 1, 2, 3 }, + { 1, 4, 2, 0 }, + { 0, 1, 2, 3 } } +}; + +/** Tables for spectrum coding */ + +/* If the first entry of a subtable is negative, it means + * that another VLC is to be reused. */ +static const int8_t atrac3p_spectra_cbs[][12] = { + { 1, 0, 0, 1, 7, 0, 19, 5, 13, 21, 6, 8 }, + { 0, 1, 0, 4, 11, 0, 1, 29, 6, 20, 7, 2 }, + { 0, 0, 1, 8, 0, 13, 18, 7, 2, 0, 0, 0 }, + { 0, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, 3, 5, 8, 12, 23, 72, 68, 31, 2 }, + { 0, 1, 3, 2, 6, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 2, 2, 2, 6, 14, 21, 13, 2, 0, 0 }, + { 1, 0, 0, 0, 8, 1, 18, 9, 22, 10, 12, 0 }, + { 0, 0, 0, 0, 16, 11, 32, 19, 1, 2, 0, 0 }, + { 1, 0, 0, 4, 2, 2, 9, 15, 12, 4, 0, 0 }, + { 0, 1, 0, 4, 3, 5, 16, 28, 34, 26, 4, 0 }, + { 0, 0, 0, 0, 9, 12, 16, 44, 98, 42, 4, 0 }, + { 0, 1, 1, 2, 2, 5, 7, 21, 54, 85, 62, 16 }, + { 0, 0, 3, 2, 5, 7, 17, 23, 6, 0, 0, 0 }, + { 1, 0, 0, 2, 6, 0, 7, 21, 15, 17, 8, 4 }, + { 0, 1, 4, 0, 4, 3, 8, 3, 2, 0, 0, 0 }, + { 0, 0, 5, 0, 4, 6, 10, 16, 8, 0, 0, 0 }, + { 0, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 5, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 3, 4, 12, 15, 34, 83, 75, 30, 0 }, + { 0, 0, 0, 3, 14, 10, 20, 16, 0, 0, 0, 0 }, + { 1, 0, 3, 1, 0, 4, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, 2, 2, 6, 12, 18, 19, 15, 6, 0 }, + { 0, 0, 1, 1, 13, 1, 14, 28, 33, 81, 32, 52 }, + { 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 0, 2, 3, 6, 19, 9, 75, 110, 0, 0 }, + { 0, 0, 1, 3, 5, 5, 13, 27, 69, 96, 35, 2 }, + { 0, 0, 0, 7, 6, 8, 22, 20, 0, 0, 0, 0 }, + { 1, 0, 0, 6, 2, 0, 0, 0, 19, 9, 24, 20 }, + { 0, 0, 1, 2, 13, 1, 31, 13, 16, 4, 0, 0 }, + { 0, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 5, 4, 12, 17, 47, 24, 12, 0, 0 }, + { 0, 2, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 16, 31, 36, static_cast<int8_t>(172), 0, 0, 0 }, + { 0, 0, 0, 5, 12, 9, 12, 15, 10, 0, 0, 0 }, + { 0, 1, 0, 6, 2, 6, 18, 4, 26, 6, 12, 0 }, + { 1, 0, 2, 2, 0, 4, 3, 8, 3, 2, 0, 0 }, + { 0, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 4, 7, 12, 19, 21, 58, 0, 0, 0 }, + { 1, 1, 1, 0, 3, 2, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, 8, 6, 8, 8, 0, 0, 0, 0, 0 }, + { 0, 0, 1, 5, 7, 8, 16, 22, 4, 0, 0, 0 }, + { 0, 1, 0, 8, 0, 1, 16, 10, 29, 12, 4, 0 }, + { 0, 0, 5, 2, 2, 9, 5, 2, 0, 0, 0, 0 }, + { 0, 0, 1, 1, 10, 4, 16, 29, 46, 75, 74, 0 }, + { 0, 0, 0, 1, 7, 12, 36, 63, 2, 0, 0, 0 }, + { 0, 3, 0, 1, 3, 4, 4, 0, 0, 0, 0, 0 }, + { 0, 1, 2, 2, 6, 8, 6, 3, 1, 2, 0, 0 }, + { 0, 1, 1, 3, 4, 6, 13, 25, 10, 0, 0, 0 }, + { 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 0, 4, 8, 3, 8, 24, 17, 12, 4, 0 }, + { 0, 1, 1, 5, 2, 8, 7, 13, 8, 4, 0, 0 }, + { 1, 0, 0, 4, 0, 4, 5, 9, 30, 45, 21, 2 }, + { 0, 1, 3, 3, 4, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 1, 3, 4, 8, 10, 36, 60, 78, 48, 8 }, + { -6 }, + { 1, 0, 0, 2, 6, 0, 11, 13, 12, 24, 4, 8 }, + { 1, 0, 0, 4, 0, 8, 4, 9, 19, 13, 13, 10 }, + { 1, 0, 0, 4, 0, 5, 12, 13, 14, 0, 0, 0 }, + { 0, 1, 0, 4, 4, 5, 9, 30, 45, 21, 2, 0 }, + { 0, 0, 1, 4, 4, 4, 12, 30, 73, 75, 22, 0 }, + { -5 }, + { 0, 0, 3, 2, 4, 8, 23, 13, 10, 0, 0, 0 }, + { -14 }, + { 0, 0, 1, 3, 12, 0, 30, 9, 18, 8, 0, 0 }, + { -9 }, + { 1, 0, 0, 4, 0, 3, 5, 16, 28, 34, 26, 4 }, + { -11 }, + { 0, 0, 0, 4, 4, 9, 13, 37, 76, 72, 39, 2 }, + { -6 }, + { -28 }, + { -22 }, + { -2 }, + { -31 }, + { -60 }, + { 0, 0, 2, 2, 4, 5, 11, 26, 67, 78, 51, 10 }, + { -6 }, + { -35 }, + { 0, 1, 0, 4, 6, 7, 10, 22, 11, 16, 4, 0 }, + { 0, 1, 0, 0, 4, 11, 8, 28, 92, 97, 13, 2 }, + { -59 }, + { 1, 0, 0, 0, 4, 6, 6, 14, 42, 63, 59, 30 }, + { -75 }, + { 1, 0, 2, 0, 2, 2, 6, 17, 14, 13, 6, 0 }, + { 1, 0, 0, 1, 7, 0, 20, 4, 10, 24, 2, 12 }, + { 1, 0, 1, 3, 2, 3, 7, 4, 4, 0, 0, 0 }, + { 0, 1, 1, 3, 4, 9, 15, 12, 4, 0, 0, 0 }, + { -66 }, + { -32 }, + { -12 }, + { 0, 1, 1, 3, 4, 6, 14, 22, 12, 0, 0, 0 }, + { -42 }, + { 1, 0, 1, 3, 2, 3, 7, 4, 4, 0, 0, 0 }, + { 1, 1, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0 }, + { -17 }, + { -39 }, + { 1, 0, 2, 0, 2, 4, 11, 9, 2, 0, 0, 0 }, + { -62 }, + { -28 }, + { 1, 0, 0, 2, 3, 5, 12, 14, 18, 15, 9, 2 }, + { 1, 1, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0 }, + { 1, 0, 0, 2, 2, 2, 6, 12, 34, 92, 54, 20 }, + { 1, 0, 0, 3, 0, 3, 3, 10, 40, 85, 61, 50 }, + { 0, 1, 2, 1, 4, 7, 10, 26, 12, 0, 0, 0 }, + { 1, 0, 0, 1, 7, 0, 19, 5, 13, 23, 0, 12 }, + { -78 }, + { 1, 0, 0, 0, 1, 4, 9, 4, 103, 110, 24, 0 }, + { 1, 0, 2, 2, 2, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 4, 0, 0, 0, 8, 11, 24, 53, 64, 60 }, + { -47 }, + { 0, 1, 0, 4, 6, 10, 12, 7, 15, 4, 4, 0 }, +}; + +static const uint8_t atrac3p_spectra_xlats[] = { + /* Table set A, code table 0, wordlen 1 - 81 entries */ + 0x00, 0x03, 0x40, 0xC0, 0x10, 0x30, 0x04, 0x0C, 0x01, 0x50, 0xD0, 0x70, + 0xF0, 0xC4, 0x14, 0x34, 0x4C, 0x1C, 0x3C, 0x41, 0xC1, 0x31, 0x05, 0x0D, + 0xC3, 0x13, 0x07, 0x0F, 0x44, 0xCC, 0x11, 0x43, 0x33, 0x54, 0x74, 0xDC, + 0xFC, 0x71, 0x15, 0x4D, 0xCD, 0x1D, 0xD3, 0xC7, 0x37, 0x3F, 0xD4, 0xF4, + 0x5C, 0x7C, 0x51, 0xD1, 0xF1, 0x45, 0xC5, 0x35, 0xDD, 0x3D, 0x53, 0x73, + 0xF3, 0x47, 0x17, 0x77, 0x4F, 0xCF, 0x1F, 0x55, 0xF5, 0x7D, 0xD7, 0x5F, + 0xFF, 0xD5, 0x75, 0x5D, 0xFD, 0x57, 0xF7, 0xDF, 0x7F, + /* Table set A, code table 0, wordlen 2 - 81 entries */ + 0x00, 0x40, 0x10, 0x04, 0x01, 0x50, 0x44, 0x14, 0x54, 0x41, 0x11, 0x51, + 0x05, 0x45, 0x15, 0x55, 0x90, 0x80, 0x20, 0x60, 0x84, 0x94, 0x24, 0x64, + 0x08, 0x48, 0x18, 0x58, 0x81, 0x91, 0x21, 0x85, 0x95, 0x65, 0x09, 0x49, + 0x19, 0x59, 0x02, 0x42, 0x12, 0x52, 0x06, 0x46, 0x16, 0x56, 0x88, 0x61, + 0x25, 0x29, 0x69, 0x5A, 0xA0, 0xA4, 0x98, 0x28, 0x68, 0xA1, 0xA5, 0x89, + 0x99, 0xA9, 0x82, 0x92, 0x22, 0x62, 0x96, 0x26, 0x66, 0x0A, 0x4A, 0x1A, + 0xA8, 0x86, 0xA6, 0x8A, 0x9A, 0x2A, 0x6A, 0xA2, 0xAA, + /* Table set A, code table 0, wordlen 3 - 49 entries */ + 0x00, 0x08, 0x38, 0x01, 0x09, 0x39, 0x07, 0x0F, 0x3F, 0x10, 0x30, 0x11, + 0x31, 0x02, 0x0A, 0x3A, 0x05, 0x06, 0x0E, 0x3E, 0x17, 0x37, 0x18, 0x28, + 0x19, 0x29, 0x2A, 0x32, 0x03, 0x0B, 0x33, 0x3B, 0x0D, 0x15, 0x3D, 0x16, + 0x1E, 0x36, 0x1F, 0x2F, 0x12, 0x1A, 0x13, 0x2B, 0x1D, 0x35, 0x2E, 0x1B, + 0x2D, + /* Table set A, code table 0, wordlen 4 - 6 entries */ + 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, + /* Table set A, code table 0, wordlen 5 - 225 entries */ + 0x00, 0x10, 0xF0, 0x01, 0x11, 0xF1, 0x0F, 0x1F, 0xFF, 0x20, 0xE0, 0xE1, + 0x02, 0xF2, 0x0E, 0x1E, 0x2F, 0x30, 0xD0, 0x21, 0x12, 0x22, 0xE2, 0x03, + 0x0D, 0x2E, 0xEE, 0xFE, 0xEF, 0x40, 0xC0, 0x31, 0xC1, 0xD1, 0x32, 0xD2, + 0x13, 0x23, 0xE3, 0xF3, 0x04, 0xF4, 0x0C, 0x1C, 0x1D, 0x2D, 0xED, 0xFD, + 0x3E, 0xDE, 0x3F, 0xDF, 0x50, 0x60, 0x70, 0x90, 0xA0, 0xB0, 0x41, 0x51, + 0x61, 0x71, 0x91, 0xA1, 0xB1, 0x42, 0x62, 0x92, 0xA2, 0xC2, 0x33, 0xC3, + 0xD3, 0x14, 0x24, 0x34, 0xD4, 0xE4, 0x05, 0x15, 0xF5, 0x06, 0x16, 0x26, + 0xE6, 0xF6, 0x07, 0x17, 0xE7, 0xF7, 0x09, 0x19, 0x29, 0xF9, 0x0A, 0x1A, + 0x2A, 0xEA, 0xFA, 0x0B, 0x1B, 0xFB, 0x2C, 0x3C, 0xDC, 0xEC, 0xFC, 0x3D, + 0x4D, 0xCD, 0xDD, 0x4E, 0x6E, 0x7E, 0xAE, 0xCE, 0x4F, 0x5F, 0x6F, 0x7F, + 0x9F, 0xAF, 0xBF, 0xCF, 0x52, 0x72, 0xB2, 0x43, 0x53, 0x63, 0x73, 0x93, + 0xA3, 0xB3, 0x44, 0x64, 0x74, 0x94, 0xA4, 0xB4, 0xC4, 0x25, 0x35, 0xA5, + 0xC5, 0xD5, 0xE5, 0x36, 0x46, 0xB6, 0xC6, 0xD6, 0x27, 0x37, 0x47, 0xB7, + 0xC7, 0xD7, 0x39, 0x49, 0x59, 0xC9, 0xD9, 0xE9, 0x3A, 0x4A, 0x5A, 0xCA, + 0xDA, 0x2B, 0x3B, 0x4B, 0x6B, 0x7B, 0xDB, 0xEB, 0x4C, 0x5C, 0x6C, 0x7C, + 0x9C, 0xAC, 0xCC, 0x5D, 0x6D, 0x7D, 0x9D, 0xAD, 0xBD, 0x5E, 0x9E, 0xBE, + 0x54, 0x45, 0x55, 0x65, 0x75, 0x95, 0xB5, 0x56, 0x66, 0x76, 0x96, 0xA6, + 0x57, 0x67, 0x97, 0xA7, 0x69, 0x79, 0xA9, 0xB9, 0x6A, 0x7A, 0x9A, 0xAA, + 0xBA, 0x5B, 0x9B, 0xAB, 0xBB, 0xCB, 0xBC, 0x77, 0x99, + /* Table set A, code table 0, wordlen 6 - 16 entries */ + 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0D, 0x0E, + 0x0A, 0x0B, 0x0C, 0x0F, + /* Table set A, code table 0, wordlen 7 - 63 entries */ + 0x00, 0x01, 0x3F, 0x02, 0x3E, 0x03, 0x3D, 0x04, 0x05, 0x06, 0x3A, 0x3B, + 0x3C, 0x07, 0x08, 0x09, 0x0A, 0x1A, 0x1B, 0x1C, 0x24, 0x25, 0x26, 0x36, + 0x37, 0x38, 0x39, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x19, 0x1D, 0x1E, + 0x1F, 0x21, 0x22, 0x23, 0x27, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, + 0x2E, 0x18, 0x28, + /* Table set A, code table 1, wordlen 1 - 81 entries */ + 0x00, 0x40, 0xC0, 0x10, 0x30, 0x04, 0x0C, 0x01, 0x03, 0xD0, 0x50, 0x70, + 0xF0, 0xC4, 0x34, 0x4C, 0xCC, 0x1C, 0x41, 0xC1, 0x31, 0x05, 0x0D, 0x43, + 0xC3, 0x13, 0x07, 0x0F, 0x44, 0x14, 0x74, 0xDC, 0x3C, 0x11, 0x1D, 0x33, + 0x37, 0x54, 0xD4, 0xF4, 0x5C, 0x7C, 0xFC, 0xD1, 0x71, 0xF1, 0x15, 0x35, + 0x4D, 0xCD, 0xDD, 0x3D, 0xD3, 0x73, 0x47, 0xC7, 0x17, 0x77, 0x3F, 0x51, + 0x45, 0xC5, 0x55, 0x53, 0xF3, 0x4F, 0xCF, 0x1F, 0xFF, 0xD5, 0x75, 0xF5, + 0x5D, 0x7D, 0xFD, 0x57, 0xD7, 0xF7, 0x5F, 0xDF, 0x7F, + /* Table set A, code table 1, wordlen 2 - 81 entries */ + 0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54, 0x01, 0x41, 0x11, 0x51, + 0x05, 0x45, 0x15, 0x55, 0x90, 0x94, 0x58, 0x91, 0x95, 0x19, 0x59, 0x06, + 0x46, 0x16, 0x56, 0x80, 0x60, 0x84, 0x24, 0x64, 0xA4, 0x08, 0x48, 0x18, + 0x68, 0x81, 0x21, 0x61, 0xA1, 0x85, 0x25, 0x65, 0xA5, 0x09, 0x49, 0x99, + 0x69, 0xA9, 0x02, 0x42, 0x12, 0x52, 0x96, 0x26, 0x66, 0x1A, 0x5A, 0x20, + 0xA0, 0x88, 0x98, 0x28, 0xA8, 0x89, 0x29, 0x82, 0x92, 0x22, 0x62, 0x86, + 0xA6, 0x0A, 0x4A, 0x9A, 0x6A, 0xAA, 0xA2, 0x8A, 0x2A, + /* Table set A, code table 1, wordlen 3 - 49 entries */ + 0x00, 0x08, 0x38, 0x01, 0x07, 0x39, 0x0F, 0x09, 0x3F, 0x10, 0x30, 0x31, + 0x02, 0x3A, 0x06, 0x0E, 0x3E, 0x17, 0x18, 0x28, 0x11, 0x29, 0x0A, 0x32, + 0x03, 0x0B, 0x3B, 0x05, 0x0D, 0x3D, 0x16, 0x1F, 0x37, 0x19, 0x12, 0x1A, + 0x2A, 0x13, 0x33, 0x15, 0x35, 0x1E, 0x2E, 0x36, 0x2F, 0x1B, 0x2B, 0x1D, + 0x2D, + /* Table set A, code table 1, wordlen 4 - 121 entries */ + 0x00, 0x10, 0xF0, 0x01, 0x0F, 0xF1, 0x1F, 0xFF, 0x20, 0xE0, 0x11, 0x02, + 0x0E, 0x30, 0x50, 0xB0, 0xD0, 0x21, 0xE1, 0x12, 0xF2, 0x03, 0x05, 0x0B, + 0x0D, 0x1E, 0xFE, 0x2F, 0xEF, 0x40, 0xC0, 0x31, 0x51, 0xB1, 0xC1, 0xD1, + 0x22, 0x52, 0xE2, 0x13, 0xF3, 0x04, 0x15, 0xF5, 0x1B, 0xEB, 0xFB, 0x0C, + 0x1D, 0xFD, 0x2E, 0x5E, 0xEE, 0x3F, 0x5F, 0xBF, 0xDF, 0x41, 0x32, 0x42, + 0xB2, 0xD2, 0x23, 0x53, 0xB3, 0xE3, 0x14, 0x24, 0xE4, 0xF4, 0x25, 0x35, + 0xD5, 0xE5, 0x2B, 0x3B, 0xDB, 0x1C, 0x2C, 0xBC, 0xEC, 0xFC, 0x2D, 0xBD, + 0xED, 0x3E, 0x4E, 0xBE, 0xDE, 0x4F, 0xCF, 0xC2, 0x33, 0x43, 0xC3, 0xD3, + 0x34, 0x44, 0x54, 0xB4, 0xD4, 0x45, 0x55, 0xC5, 0x4B, 0xCB, 0x3C, 0x4C, + 0x5C, 0xCC, 0xDC, 0x3D, 0x4D, 0x5D, 0xCD, 0xDD, 0xCE, 0xC4, 0xB5, 0x5B, + 0xBB, + /* Table set A, code table 1, wordlen 5 - 225 entries */ + 0x00, 0x10, 0xF0, 0x01, 0x11, 0xF1, 0x0F, 0x1F, 0xFF, 0x20, 0xE0, 0x21, + 0xE1, 0x02, 0x12, 0xF2, 0x0E, 0x1E, 0xFE, 0x2F, 0xEF, 0x30, 0xD0, 0x31, + 0xD1, 0x22, 0xE2, 0x03, 0x13, 0xF3, 0x0D, 0x1D, 0xFD, 0x2E, 0xEE, 0x3F, + 0xDF, 0x40, 0x60, 0x70, 0x90, 0xA0, 0xC0, 0x41, 0xC1, 0x32, 0x42, 0xC2, + 0xD2, 0x23, 0x33, 0xD3, 0xE3, 0x04, 0x14, 0x24, 0xE4, 0xF4, 0x06, 0x16, + 0xF6, 0x07, 0x09, 0x0A, 0x1A, 0xFA, 0x0C, 0x1C, 0x2C, 0xEC, 0xFC, 0x2D, + 0x3D, 0xDD, 0xED, 0x3E, 0x4E, 0xCE, 0xDE, 0x4F, 0xCF, 0x50, 0xB0, 0x51, + 0x61, 0x71, 0x91, 0xA1, 0xB1, 0x52, 0x62, 0x72, 0x92, 0xA2, 0xB2, 0x43, + 0x53, 0x63, 0x73, 0x93, 0xA3, 0xC3, 0x34, 0x44, 0x64, 0xA4, 0xC4, 0xD4, + 0x05, 0x15, 0x25, 0x35, 0xD5, 0xE5, 0xF5, 0x26, 0x36, 0x46, 0xC6, 0xD6, + 0xE6, 0x17, 0x27, 0x37, 0xC7, 0xD7, 0xE7, 0xF7, 0x19, 0x29, 0x39, 0xC9, + 0xD9, 0xE9, 0xF9, 0x2A, 0x3A, 0x4A, 0x5A, 0xCA, 0xDA, 0xEA, 0x0B, 0x1B, + 0x2B, 0x3B, 0xCB, 0xDB, 0xEB, 0xFB, 0x3C, 0x4C, 0x6C, 0x7C, 0x9C, 0xAC, + 0xBC, 0xCC, 0xDC, 0x4D, 0x5D, 0x6D, 0x7D, 0x9D, 0xAD, 0xBD, 0xCD, 0x5E, + 0x6E, 0x7E, 0x9E, 0xAE, 0xBE, 0x5F, 0x6F, 0x7F, 0x9F, 0xAF, 0xBF, 0xB3, + 0x54, 0x74, 0x94, 0xB4, 0x45, 0x55, 0x65, 0x75, 0x95, 0xA5, 0xB5, 0xC5, + 0x56, 0x66, 0x76, 0x96, 0xA6, 0xB6, 0x47, 0x57, 0x67, 0xA7, 0xB7, 0x49, + 0x59, 0x69, 0xA9, 0xB9, 0x6A, 0x7A, 0x9A, 0xAA, 0xBA, 0x4B, 0x5B, 0x6B, + 0x7B, 0x9B, 0xAB, 0xBB, 0x5C, 0x77, 0x97, 0x79, 0x99, + /* Table set A, code table 1, wordlen 6 - 256 entries */ + 0x00, 0x01, 0x10, 0x11, 0x21, 0x12, 0x20, 0x31, 0x02, 0x22, 0x13, 0x30, + 0x41, 0x32, 0x03, 0x23, 0x14, 0x24, 0x40, 0x51, 0x61, 0xD1, 0xE1, 0x42, + 0x52, 0xD2, 0x33, 0x43, 0xD3, 0x04, 0x34, 0x05, 0x15, 0x25, 0x16, 0x1D, + 0x2D, 0x1E, 0x2E, 0x50, 0x60, 0xD0, 0xE0, 0xF0, 0x71, 0x81, 0xF1, 0x62, + 0x72, 0xE2, 0xF2, 0x53, 0x63, 0xE3, 0xF3, 0x44, 0x54, 0xD4, 0xE4, 0xF4, + 0x35, 0x45, 0x55, 0xD5, 0xE5, 0xF5, 0x06, 0x26, 0x36, 0xD6, 0x07, 0x17, + 0x27, 0x37, 0xD7, 0x18, 0x28, 0x1C, 0x0D, 0x3D, 0x4D, 0x5D, 0x6D, 0x8D, + 0x0E, 0x3E, 0x4E, 0x5E, 0x0F, 0x1F, 0x2F, 0x3F, 0x5F, 0x70, 0x80, 0x90, + 0xC0, 0x91, 0xA1, 0xB1, 0xC1, 0x82, 0x92, 0xA2, 0xC2, 0x73, 0x83, 0x93, + 0xA3, 0xC3, 0x64, 0x74, 0x84, 0x94, 0xA4, 0xC4, 0x65, 0x75, 0x85, 0x46, + 0x56, 0x66, 0xC6, 0xE6, 0xF6, 0x47, 0x57, 0xE7, 0xF7, 0x08, 0x38, 0x48, + 0x58, 0x68, 0xD8, 0xE8, 0xF8, 0x09, 0x19, 0x29, 0x39, 0x59, 0xD9, 0xE9, + 0xF9, 0x1A, 0x2A, 0x3A, 0xDA, 0xEA, 0xFA, 0x1B, 0x2B, 0xDB, 0xEB, 0xFB, + 0x0C, 0x2C, 0x3C, 0xDC, 0xEC, 0x7D, 0x9D, 0xAD, 0xBD, 0xCD, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0x4F, 0x6F, 0x7F, 0x8F, 0xAF, 0xA0, 0xB2, + 0xB3, 0xB4, 0x95, 0xA5, 0xB5, 0xC5, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0x67, + 0x77, 0x87, 0x97, 0xC7, 0x78, 0x88, 0x98, 0xC8, 0x49, 0x69, 0x79, 0x89, + 0x99, 0xC9, 0x0A, 0x4A, 0x5A, 0x6A, 0x7A, 0xCA, 0x0B, 0x3B, 0x4B, 0x5B, + 0x6B, 0xCB, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xFC, + 0xDD, 0xED, 0xFD, 0xDE, 0xEE, 0xFE, 0x9F, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF, + 0xB0, 0xA7, 0xB7, 0xA8, 0xB8, 0xA9, 0xB9, 0x8A, 0x9A, 0xAA, 0xBA, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, + /* Table set A, code table 1, wordlen 7 - 63 entries */ + 0x00, 0x01, 0x3F, 0x02, 0x3E, 0x03, 0x04, 0x3B, 0x3C, 0x3D, 0x05, 0x06, + 0x07, 0x08, 0x38, 0x39, 0x3A, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x1A, + 0x1B, 0x24, 0x25, 0x26, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x19, 0x1C, 0x1D, 0x1E, 0x1F, 0x21, 0x22, + 0x23, 0x27, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x16, 0x17, 0x18, + 0x28, 0x29, 0x2A, + /* Table set A, code table 2, wordlen 1 - 81 entries */ + 0x00, 0x40, 0xC0, 0x10, 0x30, 0x04, 0x0C, 0x01, 0x03, 0xD0, 0x70, 0x34, + 0x1C, 0x0D, 0x13, 0x07, 0x50, 0xF0, 0x44, 0xC4, 0x14, 0x74, 0x4C, 0xCC, + 0xDC, 0x3C, 0x41, 0xC1, 0x11, 0x31, 0x05, 0x1D, 0x43, 0xC3, 0x33, 0x37, + 0x0F, 0x54, 0xF4, 0xFC, 0xD1, 0x71, 0x15, 0x4D, 0xCD, 0xDD, 0xD3, 0x73, + 0x47, 0xC7, 0x77, 0x3F, 0xD4, 0x5C, 0x7C, 0x51, 0xF1, 0x45, 0xC5, 0x55, + 0x35, 0x3D, 0x53, 0xF3, 0x17, 0x4F, 0xCF, 0x1F, 0xFF, 0x75, 0xF5, 0x5D, + 0x7D, 0xD7, 0xF7, 0x5F, 0xDF, 0xD5, 0xFD, 0x57, 0x7F, + /* Table set A, code table 2, wordlen 2 - 25 entries */ + 0x00, 0x08, 0x38, 0x01, 0x07, 0x09, 0x39, 0x0F, 0x3F, 0x10, 0x02, 0x06, + 0x30, 0x11, 0x31, 0x0A, 0x3A, 0x0E, 0x17, 0x37, 0x32, 0x16, 0x3E, 0x12, + 0x36, + /* Table set A, code table 2, wordlen 3 - 49 entries */ + 0x00, 0x08, 0x38, 0x01, 0x07, 0x09, 0x39, 0x0F, 0x3F, 0x10, 0x30, 0x02, + 0x3A, 0x06, 0x0E, 0x18, 0x28, 0x11, 0x31, 0x0A, 0x03, 0x05, 0x3E, 0x17, + 0x37, 0x19, 0x29, 0x12, 0x2A, 0x32, 0x0B, 0x33, 0x3B, 0x0D, 0x15, 0x3D, + 0x16, 0x1E, 0x36, 0x1F, 0x2F, 0x1A, 0x13, 0x1B, 0x2B, 0x1D, 0x2D, 0x35, + 0x2E, + /* Table set A, code table 2, wordlen 4 - 6 entries */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + /* Table set A, code table 2, wordlen 5 - 8 entries */ + 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + /* Table set A, code table 2, wordlen 6 - 256 entries */ + 0x00, 0x01, 0x11, 0x10, 0x21, 0x12, 0x22, 0x20, 0x30, 0x31, 0x41, 0x02, + 0x32, 0x03, 0x13, 0x23, 0x33, 0x14, 0x24, 0x40, 0x51, 0x61, 0x42, 0x52, + 0x43, 0x53, 0x04, 0x34, 0x44, 0x15, 0x25, 0x35, 0x16, 0x26, 0x50, 0x60, + 0x71, 0x81, 0xD1, 0x62, 0x72, 0x82, 0xD2, 0x63, 0x73, 0xD3, 0x54, 0x64, + 0x05, 0x45, 0x55, 0x65, 0x06, 0x36, 0x46, 0x56, 0x17, 0x27, 0x37, 0x47, + 0x18, 0x28, 0x38, 0x19, 0x1D, 0x2D, 0x3D, 0x1E, 0x70, 0x80, 0x90, 0xD0, + 0xE0, 0x91, 0xA1, 0xB1, 0xC1, 0xE1, 0xF1, 0x92, 0xA2, 0xC2, 0xE2, 0xF2, + 0x83, 0x93, 0xA3, 0xC3, 0xE3, 0xF3, 0x74, 0x84, 0x94, 0xA4, 0xC4, 0xD4, + 0xE4, 0xF4, 0x75, 0x85, 0x95, 0xD5, 0xE5, 0x66, 0x76, 0x86, 0xD6, 0xE6, + 0x07, 0x57, 0x67, 0x77, 0xD7, 0x08, 0x48, 0x58, 0x68, 0xD8, 0x09, 0x29, + 0x39, 0x49, 0x59, 0x69, 0x1A, 0x2A, 0x3A, 0x4A, 0x1B, 0x2B, 0x1C, 0x2C, + 0x3C, 0x4C, 0x0D, 0x4D, 0x5D, 0x6D, 0x7D, 0x8D, 0x0E, 0x2E, 0x3E, 0x4E, + 0x5E, 0x6E, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0xA0, 0xB0, 0xC0, 0xF0, 0xB2, + 0xB3, 0xB4, 0xA5, 0xB5, 0xC5, 0xF5, 0x96, 0xA6, 0xB6, 0xC6, 0xF6, 0x87, + 0x97, 0xA7, 0xB7, 0xC7, 0xE7, 0xF7, 0x78, 0x88, 0x98, 0xA8, 0xC8, 0xE8, + 0xF8, 0x79, 0x89, 0x99, 0xC9, 0xD9, 0xE9, 0xF9, 0x0A, 0x5A, 0x6A, 0x7A, + 0x8A, 0xDA, 0xEA, 0xFA, 0x0B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, 0x8B, 0xDB, + 0x0C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xDC, 0x9D, 0xAD, 0xBD, 0xCD, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0x0F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xB8, 0xA9, + 0xB9, 0x9A, 0xAA, 0xBA, 0xCA, 0x9B, 0xAB, 0xBB, 0xCB, 0xEB, 0xFB, 0xAC, + 0xBC, 0xCC, 0xEC, 0xFC, 0xDD, 0xED, 0xFD, 0xCE, 0xDE, 0xEE, 0xFE, 0xBF, + 0xCF, 0xDF, 0xEF, 0xFF, + /* Table set A, code table 2, wordlen 7 - 63 entries */ + 0x00, 0x02, 0x03, 0x01, 0x05, 0x06, 0x07, 0x09, 0x36, 0x37, 0x38, 0x3A, + 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x04, 0x08, 0x0A, 0x0B, 0x0C, 0x0E, 0x31, + 0x34, 0x35, 0x39, 0x0D, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x16, 0x17, 0x19, + 0x1A, 0x1E, 0x27, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x32, 0x33, 0x14, + 0x15, 0x18, 0x1B, 0x1C, 0x1D, 0x1F, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, + 0x28, 0x29, 0x2A, + /* Table set A, code table 3, wordlen 1 - 9 entries */ + 0x00, 0x04, 0x0C, 0x01, 0x03, 0x05, 0x0D, 0x07, 0x0F, + /* Table set A, code table 3, wordlen 2 - 81 entries */ + 0x00, 0x40, 0x01, 0x10, 0x04, 0x80, 0x50, 0x20, 0x14, 0x05, 0x02, 0x90, + 0x60, 0x44, 0x54, 0x24, 0x08, 0x18, 0x41, 0x11, 0x15, 0x09, 0x06, 0xA0, + 0x84, 0x94, 0x64, 0xA4, 0x48, 0x58, 0x28, 0x51, 0x21, 0x45, 0x55, 0x25, + 0x19, 0x12, 0x16, 0x0A, 0x1A, 0x68, 0xA8, 0x81, 0x91, 0x61, 0xA1, 0x85, + 0x95, 0x65, 0xA5, 0x49, 0x59, 0x29, 0x69, 0x42, 0x52, 0x46, 0x56, 0x2A, + 0x88, 0x98, 0x89, 0x99, 0xA9, 0x82, 0x92, 0x22, 0x62, 0x86, 0x26, 0x66, + 0x4A, 0x5A, 0x6A, 0xA2, 0x96, 0xA6, 0x8A, 0x9A, 0xAA, + /* Table set A, code table 3, wordlen 3 - 256 entries */ + 0x00, 0x10, 0x40, 0x50, 0x04, 0x44, 0x14, 0x54, 0x01, 0x41, 0x11, 0x51, + 0x05, 0x45, 0x15, 0x55, 0x90, 0x20, 0x94, 0x64, 0x18, 0x21, 0x95, 0x19, + 0x69, 0x02, 0x52, 0x06, 0x46, 0x16, 0x80, 0x60, 0x84, 0xD4, 0x24, 0x08, + 0x48, 0x58, 0x68, 0x81, 0x91, 0x61, 0x85, 0x25, 0x65, 0xA5, 0x09, 0x49, + 0x59, 0x29, 0x42, 0x12, 0x56, 0x96, 0xA6, 0x0A, 0x17, 0x1B, 0xD0, 0xC4, + 0x74, 0xF4, 0x88, 0xC8, 0x28, 0xA1, 0x71, 0xC5, 0xD5, 0x75, 0x99, 0xB9, + 0x4D, 0x1D, 0x2D, 0x6D, 0x22, 0x62, 0x66, 0x4A, 0x1A, 0x9A, 0x6A, 0x8E, + 0x5E, 0x43, 0x23, 0x07, 0x47, 0x57, 0x6B, 0xC0, 0xA0, 0xE0, 0x70, 0xB0, + 0xA4, 0xE4, 0x34, 0xB4, 0x98, 0xD8, 0xA8, 0x38, 0x78, 0x0C, 0x4C, 0x1C, + 0x5C, 0x9C, 0x6C, 0x7C, 0xC1, 0xD1, 0xE1, 0x31, 0xE5, 0x35, 0xB5, 0xF5, + 0x89, 0xA9, 0x79, 0xF9, 0x0D, 0xCD, 0x9D, 0xDD, 0xAD, 0x3D, 0x7D, 0x82, + 0xC2, 0x92, 0xD2, 0xE2, 0x72, 0xF2, 0x86, 0xD6, 0xE6, 0x76, 0xB6, 0x8A, + 0x5A, 0xDA, 0xEA, 0xFA, 0x4E, 0x1E, 0x9E, 0xEE, 0x03, 0x13, 0x53, 0x97, + 0xB7, 0x0B, 0x4B, 0x8B, 0x5B, 0x9B, 0xEB, 0x7B, 0x0F, 0x4F, 0x1F, 0x5F, + 0x9F, 0x2F, 0x3F, 0xBF, 0xE8, 0xB8, 0xF8, 0x8C, 0x2C, 0x3C, 0xFC, 0xB1, + 0xC9, 0xD9, 0xE9, 0x39, 0x5D, 0xED, 0xBD, 0xA2, 0x32, 0x26, 0x36, 0x2A, + 0xAA, 0xBA, 0x0E, 0x2E, 0x6E, 0x83, 0xC3, 0x93, 0x63, 0xB3, 0xA7, 0x37, + 0x30, 0xF0, 0xCC, 0xDC, 0xAC, 0xEC, 0xBC, 0xF1, 0x8D, 0xFD, 0xB2, 0xC6, + 0xF6, 0xCA, 0x3A, 0x7A, 0xCE, 0xDE, 0xAE, 0x3E, 0x7E, 0xBE, 0xFE, 0xD3, + 0xA3, 0xE3, 0x33, 0x73, 0xF3, 0x87, 0xC7, 0xD7, 0x27, 0x67, 0xE7, 0x77, + 0xF7, 0xCB, 0xDB, 0x2B, 0xAB, 0x3B, 0xBB, 0xFB, 0x8F, 0xCF, 0xDF, 0x6F, + 0xAF, 0xEF, 0x7F, 0xFF, + /* Table set A, code table 3, wordlen 4 - 6 entries */ + 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, + /* Table set A, code table 3, wordlen 5 - 225 entries */ + 0x00, 0xF0, 0x0F, 0x10, 0x01, 0xFF, 0x20, 0xE0, 0x11, 0xF1, 0x0E, 0x1F, + 0x30, 0x40, 0xD0, 0x21, 0xE1, 0x02, 0x12, 0x22, 0xE2, 0xF2, 0x03, 0x13, + 0x1E, 0x2E, 0x3E, 0xEE, 0xFE, 0x2F, 0xEF, 0xD2, 0x43, 0xF3, 0x04, 0x0D, + 0x2D, 0x3D, 0x3F, 0xDF, 0x50, 0x60, 0x70, 0x90, 0xB0, 0x31, 0x41, 0x91, + 0xA1, 0xC1, 0xD1, 0x42, 0xA2, 0xC2, 0x23, 0x33, 0xE3, 0x24, 0x34, 0xB4, + 0xD4, 0xF4, 0x05, 0x15, 0x45, 0xE5, 0x16, 0x36, 0x56, 0xA6, 0xC6, 0xD6, + 0xF6, 0x57, 0xC7, 0xF7, 0x09, 0x29, 0x49, 0x59, 0x69, 0xF9, 0x0A, 0x2A, + 0x3A, 0x4A, 0xDA, 0xEA, 0xFA, 0x0B, 0x2B, 0xAB, 0xEB, 0xFB, 0x0C, 0x1C, + 0x2C, 0x3C, 0x4C, 0x5C, 0xCC, 0xDC, 0xFC, 0x1D, 0x4D, 0x6D, 0xBD, 0xCD, + 0xED, 0xFD, 0x4E, 0x6E, 0xCE, 0xDE, 0x7F, 0xA0, 0xC0, 0x51, 0x61, 0x71, + 0xB1, 0x32, 0x52, 0x62, 0x72, 0x92, 0xB2, 0x53, 0x63, 0x73, 0x93, 0xA3, + 0xB3, 0xC3, 0xD3, 0x14, 0x44, 0x54, 0x64, 0x74, 0x94, 0xA4, 0xC4, 0xE4, + 0x25, 0x35, 0x55, 0x65, 0x75, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xF5, 0x06, + 0x26, 0x46, 0x66, 0x76, 0x96, 0xB6, 0xE6, 0x07, 0x17, 0x27, 0x37, 0x47, + 0x67, 0x77, 0x97, 0xA7, 0xB7, 0xD7, 0xE7, 0x19, 0x39, 0x79, 0x99, 0xA9, + 0xB9, 0xC9, 0xD9, 0xE9, 0x1A, 0x5A, 0x6A, 0x7A, 0x9A, 0xAA, 0xBA, 0xCA, + 0x1B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, 0x9B, 0xBB, 0xCB, 0xDB, 0x6C, 0x7C, + 0x9C, 0xAC, 0xBC, 0xEC, 0x5D, 0x7D, 0x9D, 0xAD, 0xDD, 0x5E, 0x7E, 0x9E, + 0xAE, 0xBE, 0x4F, 0x5F, 0x6F, 0x9F, 0xAF, 0xBF, 0xCF, + /* Table set A, code table 3, wordlen 6 - 256 entries */ + 0x00, 0x10, 0x01, 0x11, 0x20, 0x21, 0x02, 0x12, 0x22, 0x31, 0x41, 0x32, + 0x13, 0x23, 0x30, 0x40, 0x51, 0x42, 0x03, 0x33, 0x43, 0x04, 0x14, 0x24, + 0x34, 0x15, 0x25, 0x50, 0x61, 0x71, 0xD1, 0x52, 0x62, 0x72, 0xD2, 0x53, + 0x63, 0xD3, 0x44, 0x54, 0x64, 0x05, 0x35, 0x45, 0x55, 0x16, 0x26, 0x36, + 0x46, 0x17, 0x27, 0x1D, 0x2D, 0x3D, 0x60, 0x70, 0xD0, 0x81, 0x91, 0xA1, + 0xC1, 0xE1, 0xF1, 0x82, 0x92, 0xC2, 0xE2, 0xF2, 0x73, 0x83, 0xE3, 0xF3, + 0x74, 0x84, 0xC4, 0xD4, 0xE4, 0xF4, 0x65, 0x75, 0x85, 0xD5, 0xE5, 0x06, + 0x56, 0x66, 0xD6, 0xE6, 0x07, 0x37, 0x47, 0x57, 0x67, 0xD7, 0xE7, 0x18, + 0x28, 0x38, 0x48, 0x58, 0xD8, 0x19, 0x29, 0x2A, 0x1C, 0x2C, 0x0D, 0x4D, + 0x5D, 0x6D, 0x7D, 0x8D, 0x9D, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x1F, 0x2F, 0x3F, 0x80, 0x90, 0xA0, 0xC0, 0xE0, 0xF0, 0xB1, 0xA2, 0xB2, + 0x93, 0xA3, 0xB3, 0xC3, 0x94, 0xA4, 0xB4, 0x95, 0xA5, 0xB5, 0xC5, 0xF5, + 0x76, 0x86, 0x96, 0xA6, 0xC6, 0xF6, 0x77, 0x87, 0x97, 0xA7, 0xC7, 0xF7, + 0x08, 0x68, 0x78, 0x88, 0x98, 0xC8, 0xE8, 0xF8, 0x09, 0x39, 0x49, 0x59, + 0x69, 0x79, 0x89, 0xD9, 0xE9, 0xF9, 0x0A, 0x1A, 0x3A, 0x4A, 0x5A, 0x6A, + 0xDA, 0xEA, 0xFA, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0xDB, 0xEB, 0xFB, 0x0C, + 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xDC, 0xEC, 0xAD, 0xBD, 0xCD, + 0xDD, 0xED, 0x0E, 0x8E, 0x9E, 0xAE, 0xBE, 0x0F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xB0, 0xB6, 0xB7, 0xA8, 0xB8, 0x99, 0xA9, 0xB9, 0xC9, + 0x7A, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0x0B, 0x6B, 0x7B, 0x8B, 0x9B, 0xCB, + 0xAC, 0xBC, 0xCC, 0xFC, 0xFD, 0xCE, 0xDE, 0xEE, 0xFE, 0xBF, 0xCF, 0xDF, + 0xEF, 0xFF, 0xAB, 0xBB, + /* Table set A, code table 3, wordlen 7 - 63 entries */ + 0x00, 0x01, 0x02, 0x03, 0x3D, 0x3E, 0x3F, 0x04, 0x05, 0x06, 0x3A, 0x3B, + 0x3C, 0x07, 0x08, 0x09, 0x0A, 0x36, 0x37, 0x38, 0x39, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x23, 0x24, 0x25, + 0x26, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x1F, 0x21, 0x22, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, + 0x2D, 0x2E, 0x2F, + /* Table set A, code table 4, wordlen 1 - 81 entries */ + 0x00, 0x40, 0xC0, 0x10, 0x30, 0x04, 0x0C, 0x01, 0x03, 0x50, 0xD0, 0x70, + 0xF0, 0xC4, 0x34, 0x4C, 0xCC, 0x1C, 0x41, 0xC1, 0x31, 0x05, 0x0D, 0x43, + 0xC3, 0x13, 0x07, 0x0F, 0x44, 0x14, 0x74, 0xDC, 0x3C, 0x11, 0x1D, 0x33, + 0x37, 0x54, 0xD4, 0xF4, 0x5C, 0x7C, 0xFC, 0xD1, 0x71, 0xF1, 0xC5, 0x15, + 0x35, 0x4D, 0xCD, 0xDD, 0x3D, 0xD3, 0x73, 0x47, 0xC7, 0x17, 0x77, 0x1F, + 0x3F, 0x51, 0x45, 0x55, 0xD5, 0x75, 0xF5, 0x5D, 0x7D, 0xFD, 0x53, 0xF3, + 0x57, 0xD7, 0xF7, 0x4F, 0xCF, 0x5F, 0xDF, 0x7F, 0xFF, + /* Table set A, code table 4, wordlen 2 - 81 entries */ + 0x00, 0x40, 0x01, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54, 0x41, 0x11, 0x51, + 0x05, 0x45, 0x15, 0x55, 0x59, 0x80, 0x90, 0x20, 0x60, 0x84, 0x94, 0x24, + 0x64, 0x08, 0x48, 0x18, 0x58, 0x81, 0x91, 0x21, 0x61, 0x85, 0x95, 0x25, + 0x65, 0x09, 0x49, 0x19, 0x02, 0x42, 0x12, 0x52, 0x06, 0x46, 0x16, 0x56, + 0xA0, 0xA4, 0x68, 0xA1, 0xA5, 0x99, 0x29, 0x69, 0x96, 0x66, 0x4A, 0x1A, + 0x5A, 0x88, 0x98, 0x28, 0x89, 0xA9, 0x82, 0x92, 0x22, 0x62, 0x86, 0x26, + 0xA6, 0x0A, 0x9A, 0x2A, 0x6A, 0xA8, 0xA2, 0x8A, 0xAA, + /* Table set A, code table 4, wordlen 3 - 7 entries */ + 0x00, 0x07, 0x01, 0x02, 0x06, 0x03, 0x05, + /* Table set A, code table 4, wordlen 4 - 121 entries */ + 0x00, 0x10, 0xF0, 0x01, 0x0F, 0x11, 0xF1, 0x1F, 0xFF, 0x20, 0xE0, 0x21, + 0xE1, 0x02, 0x12, 0xF2, 0x0E, 0x1E, 0xFE, 0x2F, 0xEF, 0x30, 0x50, 0xD0, + 0xD1, 0x22, 0xE2, 0x03, 0x13, 0xF3, 0x0D, 0x1D, 0x2D, 0xFD, 0x2E, 0xEE, + 0x3F, 0xDF, 0x40, 0xB0, 0xC0, 0x31, 0x41, 0x51, 0xB1, 0xC1, 0x32, 0xB2, + 0xC2, 0xD2, 0x23, 0xB3, 0xD3, 0xE3, 0x04, 0x14, 0xE4, 0xF4, 0x05, 0x15, + 0xD5, 0xE5, 0xF5, 0x0B, 0x1B, 0x2B, 0x3B, 0xEB, 0xFB, 0x0C, 0x1C, 0x2C, + 0xFC, 0x3D, 0x5D, 0xED, 0x3E, 0x4E, 0x5E, 0xBE, 0xDE, 0x4F, 0x5F, 0xBF, + 0xCF, 0x42, 0x52, 0x33, 0x53, 0xC3, 0x24, 0xB4, 0xD4, 0x25, 0x35, 0xC5, + 0x4B, 0xCB, 0xDB, 0x3C, 0x4C, 0x5C, 0xDC, 0xEC, 0x4D, 0xBD, 0xCD, 0xDD, + 0xCE, 0x43, 0x34, 0x44, 0x54, 0xC4, 0x45, 0x55, 0xB5, 0x5B, 0xBB, 0xBC, + 0xCC, + /* Table set A, code table 4, wordlen 5 - 8 entries */ + 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x06, 0x07, + /* Table set A, code table 4, wordlen 6 - 256 entries */ + 0x02, 0x00, 0x30, 0x21, 0x31, 0x41, 0x61, 0x12, 0x22, 0x42, 0x62, 0x43, + 0x53, 0x24, 0x45, 0x26, 0x27, 0x10, 0x40, 0xB0, 0x01, 0x11, 0x81, 0x32, + 0x52, 0x72, 0x92, 0x03, 0x13, 0x33, 0x63, 0x14, 0x34, 0x54, 0x64, 0x74, + 0x05, 0x15, 0x25, 0x35, 0x55, 0x65, 0x06, 0x46, 0x56, 0x57, 0x67, 0x88, + 0x20, 0x51, 0x91, 0xD1, 0xF2, 0x23, 0x83, 0x93, 0x04, 0x44, 0x84, 0x94, + 0x75, 0x85, 0xC5, 0x36, 0x66, 0x96, 0xB6, 0x07, 0x37, 0x97, 0x08, 0x28, + 0x38, 0x48, 0x68, 0x09, 0x69, 0x79, 0x0A, 0x2A, 0x1B, 0x9B, 0x2C, 0x4D, + 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xC0, 0xD0, 0xE0, 0xF0, 0x71, 0xA1, + 0xB1, 0xC1, 0xE1, 0xF1, 0x82, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0x73, 0xA3, + 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, 0x95, + 0xA5, 0xB5, 0xD5, 0xE5, 0xF5, 0x16, 0x76, 0x86, 0xA6, 0xC6, 0xD6, 0xE6, + 0xF6, 0x17, 0x47, 0x77, 0x87, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x18, + 0x58, 0x78, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, 0x19, 0x29, 0x39, + 0x49, 0x59, 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, 0x1A, 0x3A, + 0x4A, 0x5A, 0x6A, 0x7A, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, 0x8B, 0xAB, 0xBB, 0xCB, 0xDB, + 0xEB, 0xFB, 0x0C, 0x1C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xAC, + 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, 0x0D, 0x1D, 0x2D, 0x3D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, 0x0E, 0x1E, 0x2E, 0x3E, + 0x4E, 0x5E, 0x6E, 0x7E, 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF, + 0xCF, 0xDF, 0xEF, 0xFF, + /* Table set A, code table 4, wordlen 7 - 63 entries */ + 0x00, 0x01, 0x02, 0x3E, 0x3F, 0x03, 0x04, 0x05, 0x06, 0x08, 0x36, 0x38, + 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x07, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x35, 0x37, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x24, 0x25, 0x27, + 0x2A, 0x34, 0x16, 0x19, 0x1C, 0x23, 0x26, 0x28, 0x29, 0x2B, 0x2D, 0x2E, + 0x2F, 0x30, 0x31, 0x32, 0x33, 0x17, 0x18, 0x1A, 0x1B, 0x1D, 0x1E, 0x1F, + 0x21, 0x22, 0x2C, + /* Table set A, code table 5, wordlen 1 - 81 entries */ + 0x00, 0x40, 0xC0, 0x30, 0x04, 0x01, 0x03, 0x10, 0x0C, 0xD0, 0x70, 0x34, + 0x1C, 0x0D, 0x07, 0x50, 0xF0, 0x44, 0xC4, 0x14, 0x4C, 0xCC, 0x3C, 0x41, + 0xC1, 0x11, 0x31, 0x05, 0x43, 0xC3, 0x13, 0x33, 0x0F, 0x74, 0xDC, 0x1D, + 0x37, 0x54, 0xD4, 0xF4, 0x5C, 0x7C, 0xFC, 0xD1, 0x71, 0xF1, 0xC5, 0x15, + 0x35, 0x4D, 0xCD, 0xDD, 0x3D, 0x53, 0xD3, 0x73, 0x47, 0xC7, 0x17, 0x77, + 0x4F, 0x1F, 0x3F, 0x51, 0x45, 0x55, 0xF3, 0xCF, 0xFF, 0xD5, 0x75, 0xF5, + 0x5D, 0x7D, 0xFD, 0x57, 0xD7, 0xF7, 0x5F, 0xDF, 0x7F, + /* Table set A, code table 5, wordlen 2 - 25 entries */ + 0x00, 0x08, 0x38, 0x01, 0x07, 0x09, 0x39, 0x0F, 0x3F, 0x10, 0x02, 0x06, + 0x30, 0x11, 0x31, 0x0A, 0x3A, 0x0E, 0x17, 0x37, 0x32, 0x16, 0x3E, 0x12, + 0x36, + /* Table set A, code table 5, wordlen 3 - 7 entries */ + 0x00, 0x01, 0x02, 0x06, 0x07, 0x03, 0x05, + /* Table set A, code table 5, wordlen 4 - 121 entries */ + 0x00, 0x01, 0x0F, 0x1F, 0x10, 0xE0, 0xF0, 0x11, 0xF1, 0x2F, 0xFF, 0x20, + 0x21, 0xE1, 0x02, 0x12, 0xF2, 0x03, 0xF3, 0x0E, 0x2E, 0xFE, 0x3F, 0x30, + 0x40, 0xD0, 0xC1, 0xD1, 0x22, 0xC2, 0x33, 0xE3, 0x0C, 0xCC, 0x0D, 0x1D, + 0x2D, 0xFD, 0x1E, 0x3E, 0x5E, 0xEF, 0xC0, 0x52, 0xB2, 0xD2, 0x43, 0xC3, + 0xD3, 0x24, 0x45, 0xF5, 0x4B, 0x5B, 0xFB, 0x1C, 0x3D, 0xBD, 0xDD, 0xEE, + 0xBF, 0xCF, 0xDF, 0x50, 0xB0, 0x31, 0x41, 0x51, 0xB1, 0x32, 0x42, 0xE2, + 0x13, 0x23, 0x53, 0xB3, 0x04, 0x14, 0x34, 0x44, 0x54, 0xB4, 0xC4, 0xD4, + 0xE4, 0xF4, 0x05, 0x15, 0x25, 0x35, 0x55, 0xB5, 0xC5, 0xD5, 0xE5, 0x0B, + 0x1B, 0x2B, 0x3B, 0xBB, 0xCB, 0xDB, 0xEB, 0x2C, 0x3C, 0x4C, 0x5C, 0xBC, + 0xDC, 0xEC, 0xFC, 0x4D, 0x5D, 0xCD, 0xED, 0x4E, 0xBE, 0xCE, 0xDE, 0x4F, + 0x5F, + /* Table set A, code table 5, wordlen 5 - 8 entries */ + 0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05, + /* Table set A, code table 5, wordlen 6 - 31 entries */ + 0x04, 0x00, 0x01, 0x02, 0x03, 0x1C, 0x1D, 0x1E, 0x1F, 0x05, 0x06, 0x07, + 0x18, 0x19, 0x1B, 0x08, 0x09, 0x0E, 0x13, 0x15, 0x16, 0x17, 0x1A, 0x0A, + 0x0B, 0x0C, 0x0D, 0x0F, 0x11, 0x12, 0x14, + /* Table set A, code table 5, wordlen 7 - 63 entries */ + 0x00, 0x01, 0x02, 0x3D, 0x3E, 0x3F, 0x03, 0x04, 0x05, 0x06, 0x3A, 0x3B, + 0x3C, 0x07, 0x08, 0x09, 0x0A, 0x36, 0x37, 0x38, 0x39, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x1A, 0x1B, 0x1C, 0x24, 0x25, 0x26, 0x31, 0x32, 0x33, 0x34, + 0x35, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x17, 0x18, 0x19, 0x1D, 0x1E, + 0x1F, 0x21, 0x22, 0x23, 0x27, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x16, + 0x28, 0x29, 0x2A, + /* Table set A, code table 6, wordlen 1 - 81 entries */ + 0x00, 0x40, 0xC0, 0x10, 0x30, 0x04, 0x0C, 0x01, 0x03, 0x70, 0x50, 0xD0, + 0xF0, 0x44, 0xC4, 0x14, 0x34, 0x4C, 0x1C, 0x3C, 0x31, 0x05, 0x0D, 0x13, + 0x07, 0x0F, 0x74, 0xCC, 0xDC, 0xFC, 0x41, 0xC1, 0x11, 0x43, 0xC3, 0x33, + 0x54, 0xD4, 0xF4, 0x5C, 0x7C, 0x51, 0xD1, 0x71, 0xF1, 0x45, 0xC5, 0x15, + 0x35, 0x4D, 0xCD, 0x1D, 0x3D, 0x53, 0xD3, 0x73, 0xF3, 0x47, 0xC7, 0x17, + 0x37, 0x4F, 0xCF, 0x1F, 0x3F, 0x55, 0xD5, 0x75, 0xF5, 0x5D, 0xDD, 0xFD, + 0x57, 0xD7, 0x77, 0xF7, 0xFF, 0x7D, 0x5F, 0xDF, 0x7F, + /* Table set A, code table 6, wordlen 2 - 25 entries */ + 0x00, 0x08, 0x38, 0x01, 0x07, 0x39, 0x0F, 0x09, 0x3F, 0x10, 0x30, 0x31, + 0x02, 0x3A, 0x06, 0x0E, 0x17, 0x37, 0x11, 0x0A, 0x32, 0x16, 0x3E, 0x12, + 0x36, + /* Table set A, code table 6, wordlen 3 - 256 entries */ + 0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x01, 0x41, 0x05, 0x45, 0x55, + 0x54, 0x11, 0x51, 0x15, 0x80, 0x90, 0x60, 0x24, 0x64, 0xA4, 0x48, 0x61, + 0x95, 0x25, 0xA5, 0x02, 0x42, 0x52, 0x16, 0x56, 0x20, 0x84, 0x94, 0x18, + 0x58, 0x81, 0x91, 0x85, 0x65, 0x09, 0x49, 0x19, 0x59, 0x99, 0x29, 0x69, + 0x79, 0x5D, 0x12, 0x62, 0x06, 0x46, 0x86, 0x66, 0x1A, 0x5A, 0x6A, 0x47, + 0x17, 0xC0, 0xA0, 0xE0, 0xC4, 0xD4, 0x74, 0x08, 0x78, 0x0C, 0x4C, 0x1C, + 0x5C, 0xD1, 0x21, 0xE1, 0x71, 0xC5, 0xE5, 0x75, 0xB5, 0x89, 0xBD, 0x92, + 0x22, 0x96, 0xA6, 0x36, 0x0A, 0x4A, 0x8A, 0x9A, 0x2A, 0x7A, 0xDE, 0x6E, + 0x43, 0x13, 0x53, 0x23, 0x07, 0x77, 0x4B, 0x1B, 0x9B, 0x6B, 0x2F, 0xD0, + 0x30, 0x70, 0xE4, 0x34, 0xF4, 0xC8, 0x98, 0x28, 0x68, 0xA8, 0xE8, 0x38, + 0xB8, 0xF8, 0x9C, 0x2C, 0x6C, 0x7C, 0xA1, 0xB1, 0xD5, 0x35, 0xC9, 0xD9, + 0xA9, 0xE9, 0x39, 0xB9, 0xF9, 0xCD, 0x1D, 0x2D, 0xAD, 0x7D, 0xC2, 0xD2, + 0xA2, 0xB2, 0xF2, 0xC6, 0x26, 0x76, 0xB6, 0xDA, 0xAA, 0xEA, 0x3A, 0xFA, + 0x0E, 0x4E, 0x2E, 0x7E, 0xBE, 0xFE, 0x03, 0x83, 0x63, 0xA3, 0xB3, 0x87, + 0x57, 0x97, 0xD7, 0x27, 0x0B, 0x8B, 0x5B, 0x2B, 0xAB, 0xCF, 0x1F, 0x9F, + 0x7F, 0xBF, 0xB0, 0xF0, 0xB4, 0x88, 0xD8, 0x8C, 0xCC, 0xDC, 0xAC, 0xEC, + 0x3C, 0xBC, 0xFC, 0xC1, 0x31, 0xF1, 0xF5, 0x0D, 0x4D, 0x8D, 0x9D, 0xDD, + 0x6D, 0xED, 0x3D, 0xFD, 0x82, 0xE2, 0x32, 0x72, 0xD6, 0xE6, 0xF6, 0xCA, + 0xBA, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xAE, 0xEE, 0x3E, 0xC3, 0x93, 0xD3, + 0xE3, 0x33, 0x73, 0xF3, 0xC7, 0x67, 0xA7, 0xE7, 0x37, 0xB7, 0xF7, 0xCB, + 0xDB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, 0x0F, 0x4F, 0x8F, 0x5F, 0xDF, 0x6F, + 0xAF, 0xEF, 0x3F, 0xFF, + /* Table set A, code table 6, wordlen 4 - 121 entries */ + 0x00, 0x10, 0x20, 0xE0, 0xF0, 0x02, 0x0E, 0xEF, 0x30, 0x01, 0x11, 0x21, + 0x31, 0xF1, 0x12, 0xF2, 0x1E, 0xEE, 0xDF, 0xFF, 0x40, 0xC0, 0xD0, 0xD1, + 0xE1, 0x22, 0x32, 0x42, 0xD2, 0xE2, 0x03, 0x13, 0x23, 0xB3, 0xC3, 0xE3, + 0xF3, 0xE4, 0x05, 0xF5, 0x2B, 0x0C, 0xFC, 0x1D, 0x2D, 0xBD, 0xDD, 0xFD, + 0x2E, 0x4E, 0xDE, 0xFE, 0x0F, 0x1F, 0x2F, 0x3F, 0x50, 0xB0, 0x41, 0x51, + 0xB1, 0xC1, 0x52, 0xB2, 0xC2, 0x33, 0x43, 0x53, 0xD3, 0x04, 0x14, 0x24, + 0x34, 0x44, 0x54, 0xB4, 0xC4, 0xD4, 0xF4, 0x15, 0x25, 0x35, 0x45, 0x55, + 0xB5, 0xC5, 0xD5, 0xE5, 0x0B, 0x1B, 0x3B, 0x4B, 0x5B, 0xBB, 0xCB, 0xDB, + 0xEB, 0xFB, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0xBC, 0xCC, 0xDC, 0xEC, 0x0D, + 0x3D, 0x4D, 0x5D, 0xCD, 0xED, 0x3E, 0x5E, 0xBE, 0xCE, 0x4F, 0xCF, 0x5F, + 0xBF, + /* Table set A, code table 6, wordlen 5 - 15 entries */ + 0x00, 0x01, 0x0F, 0x0E, 0x02, 0x03, 0x0D, 0x04, 0x06, 0x0A, 0x0C, 0x05, + 0x07, 0x09, 0x0B, + /* Table set A, code table 6, wordlen 6 - 31 entries */ + 0x00, 0x01, 0x1F, 0x02, 0x1E, 0x03, 0x04, 0x0F, 0x11, 0x1C, 0x1D, 0x05, + 0x06, 0x07, 0x08, 0x18, 0x19, 0x1A, 0x1B, 0x09, 0x0A, 0x0B, 0x15, 0x16, + 0x17, 0x0C, 0x13, 0x14, 0x0D, 0x0E, 0x12, + /* Table set A, code table 6, wordlen 7 - 63 entries */ + 0x00, 0x01, 0x02, 0x3E, 0x3F, 0x03, 0x04, 0x3C, 0x3D, 0x05, 0x06, 0x07, + 0x39, 0x3A, 0x3B, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x1A, 0x26, 0x34, + 0x35, 0x36, 0x37, 0x38, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x19, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x21, 0x22, 0x23, 0x24, 0x25, 0x27, 0x2D, 0x2E, + 0x2F, 0x30, 0x31, 0x32, 0x33, 0x14, 0x15, 0x16, 0x17, 0x18, 0x28, 0x29, + 0x2A, 0x2B, 0x2C, + /* Table set A, code table 7, wordlen 1 - 4 entries */ + 0x00, 0x01, 0x02, 0x03, + /* Table set A, code table 7, wordlen 2 - 81 entries */ + 0x00, 0x40, 0x10, 0x04, 0x01, 0x50, 0x44, 0x14, 0x54, 0x41, 0x11, 0x05, + 0x15, 0x51, 0x45, 0x55, 0x80, 0x90, 0x20, 0x64, 0x08, 0x19, 0x02, 0x06, + 0x60, 0x84, 0x94, 0x24, 0x48, 0x18, 0x58, 0x81, 0x91, 0x21, 0x61, 0x85, + 0x95, 0x25, 0x65, 0x09, 0x49, 0x59, 0x42, 0x12, 0x52, 0x46, 0x16, 0x56, + 0xA0, 0xA4, 0x98, 0x28, 0x68, 0xA1, 0xA5, 0x99, 0x29, 0x69, 0x96, 0x26, + 0x66, 0x0A, 0x4A, 0x1A, 0x5A, 0x88, 0xA8, 0x89, 0xA9, 0x82, 0x92, 0x22, + 0x62, 0x86, 0xA6, 0x2A, 0x6A, 0xA2, 0x8A, 0x9A, 0xAA, + /* Table set A, code table 7, wordlen 3 - 49 entries */ + 0x00, 0x08, 0x38, 0x01, 0x39, 0x07, 0x0F, 0x09, 0x3F, 0x10, 0x30, 0x31, + 0x02, 0x3A, 0x06, 0x0E, 0x17, 0x11, 0x0A, 0x32, 0x0D, 0x16, 0x3E, 0x37, + 0x18, 0x28, 0x19, 0x29, 0x12, 0x2A, 0x03, 0x3B, 0x05, 0x15, 0x1E, 0x1F, + 0x2F, 0x1A, 0x0B, 0x2B, 0x33, 0x35, 0x3D, 0x2E, 0x36, 0x13, 0x1B, 0x1D, + 0x2D, + /* Table set A, code table 7, wordlen 4 - 121 entries */ + 0x00, 0x10, 0xF0, 0x01, 0x0F, 0x11, 0xF1, 0x1F, 0xFF, 0x20, 0xE0, 0x02, + 0xF2, 0x0E, 0x21, 0xE1, 0x12, 0xE2, 0x1E, 0x2E, 0xFE, 0x2F, 0xEF, 0x30, + 0x50, 0xB0, 0xC0, 0xD0, 0x31, 0xB1, 0xD1, 0x22, 0xD2, 0x03, 0x13, 0xE3, + 0xF3, 0xF4, 0x05, 0xE5, 0xF5, 0x0B, 0x1B, 0x0C, 0x0D, 0x1D, 0x2D, 0xFD, + 0x3E, 0xEE, 0x3F, 0x5F, 0xDF, 0x40, 0x41, 0x51, 0xC1, 0x32, 0x42, 0x52, + 0xB2, 0xC2, 0x23, 0x33, 0xB3, 0xC3, 0xD3, 0x04, 0x14, 0x24, 0xD4, 0xE4, + 0x15, 0x25, 0xC5, 0xD5, 0x2B, 0x3B, 0xEB, 0xFB, 0x1C, 0x2C, 0x3C, 0x5C, + 0xEC, 0xFC, 0x3D, 0x5D, 0xDD, 0xED, 0x4E, 0x5E, 0xBE, 0xCE, 0xDE, 0x4F, + 0xBF, 0xCF, 0x43, 0x53, 0x34, 0x54, 0xB4, 0xC4, 0x35, 0x45, 0x55, 0xB5, + 0x4B, 0x5B, 0xCB, 0xDB, 0x4C, 0xBC, 0xCC, 0xDC, 0x4D, 0xBD, 0xCD, 0x44, + 0xBB, + /* Table set A, code table 7, wordlen 5 - 15 entries */ + 0x00, 0x01, 0x0E, 0x0F, 0x02, 0x03, 0x0D, 0x04, 0x06, 0x0A, 0x0C, 0x05, + 0x07, 0x09, 0x0B, + /* Table set A, code table 7, wordlen 6 - 256 entries */ + 0x00, 0x10, 0x01, 0x11, 0x20, 0x21, 0x02, 0x12, 0x30, 0x31, 0x41, 0x22, + 0x03, 0x13, 0x23, 0x14, 0x40, 0x51, 0x61, 0x32, 0x42, 0x33, 0x04, 0x24, + 0x15, 0x16, 0x50, 0x60, 0xD0, 0x71, 0x81, 0xD1, 0xE1, 0xF1, 0x52, 0x62, + 0x72, 0xD2, 0x43, 0x53, 0x63, 0xD3, 0x34, 0x44, 0x54, 0x05, 0x25, 0x35, + 0x45, 0x06, 0x26, 0x36, 0x17, 0x27, 0x18, 0x0D, 0x1D, 0x2D, 0x3D, 0x1E, + 0x2E, 0x1F, 0x70, 0x80, 0xE0, 0xF0, 0x91, 0xA1, 0xC1, 0x82, 0x92, 0xC2, + 0xE2, 0xF2, 0x73, 0x83, 0x93, 0xE3, 0xF3, 0x64, 0x74, 0x84, 0xD4, 0xE4, + 0xF4, 0x55, 0x65, 0xD5, 0xE5, 0xF5, 0x46, 0x56, 0x66, 0xD6, 0x07, 0x37, + 0x47, 0x57, 0x08, 0x28, 0x38, 0x48, 0x19, 0x29, 0x39, 0x1A, 0x2A, 0x1B, + 0x1C, 0x2C, 0x3C, 0x4D, 0x5D, 0x6D, 0x0E, 0x3E, 0x4E, 0x5E, 0x0F, 0x2F, + 0x3F, 0x4F, 0x90, 0xA0, 0xB0, 0xC0, 0xB1, 0xA2, 0xB2, 0xA3, 0xB3, 0xC3, + 0x94, 0xA4, 0xB4, 0xC4, 0x75, 0x85, 0x95, 0xA5, 0xC5, 0x76, 0x86, 0x96, + 0xE6, 0xF6, 0x67, 0x77, 0x87, 0xD7, 0xE7, 0xF7, 0x58, 0x68, 0x78, 0x88, + 0xD8, 0xE8, 0xF8, 0x09, 0x49, 0x59, 0x69, 0xD9, 0xE9, 0xF9, 0x0A, 0x3A, + 0x4A, 0x5A, 0xDA, 0xEA, 0x0B, 0x2B, 0x3B, 0x4B, 0xDB, 0x0C, 0x4C, 0x5C, + 0x6C, 0xDC, 0x7D, 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0x6E, 0x7E, 0x8E, 0x9E, + 0xAE, 0xBE, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xB5, 0xA6, 0xB6, 0xC6, + 0x97, 0xA7, 0xC7, 0x98, 0xA8, 0xB8, 0xC8, 0x79, 0x89, 0x99, 0xA9, 0xB9, + 0xC9, 0x6A, 0x7A, 0x8A, 0x9A, 0xAA, 0xCA, 0xFA, 0x5B, 0x6B, 0x7B, 0x8B, + 0xCB, 0xEB, 0xFB, 0x7C, 0x8C, 0x9C, 0xAC, 0xBC, 0xEC, 0xFC, 0xDD, 0xED, + 0xFD, 0xCE, 0xDE, 0xEE, 0xFE, 0xBF, 0xCF, 0xDF, 0xB7, 0xBA, 0x9B, 0xAB, + 0xBB, 0xCC, 0xEF, 0xFF, + /* Table set B, code table 0, wordlen 1 - 81 entries */ + 0x00, 0x01, 0x03, 0x40, 0xC0, 0x10, 0x30, 0x04, 0x0C, 0x50, 0xD0, 0x70, + 0xF0, 0x34, 0x1C, 0x05, 0x0D, 0x13, 0x07, 0x0F, 0x44, 0xC4, 0x14, 0x4C, + 0xCC, 0x3C, 0x41, 0xC1, 0x11, 0x31, 0x43, 0xC3, 0x33, 0x54, 0x74, 0xDC, + 0xFC, 0x71, 0x15, 0x4D, 0x1D, 0xD3, 0xC7, 0x37, 0x3F, 0xD4, 0xF4, 0x5C, + 0x7C, 0x51, 0xD1, 0xF1, 0x45, 0xC5, 0x55, 0x35, 0xCD, 0xDD, 0x3D, 0x53, + 0x73, 0xF3, 0x47, 0x17, 0x77, 0x4F, 0xCF, 0x1F, 0xFF, 0xF5, 0x7D, 0xD7, + 0x5F, 0xD5, 0x75, 0x5D, 0xFD, 0x57, 0xF7, 0xDF, 0x7F, + /* Table set B, code table 0, wordlen 2 - 81 entries */ + 0x00, 0x40, 0x10, 0x04, 0x01, 0x50, 0x44, 0x14, 0x54, 0x41, 0x11, 0x05, + 0x15, 0x80, 0x51, 0x45, 0x55, 0x90, 0x20, 0x60, 0x24, 0x08, 0x18, 0x09, + 0x02, 0x06, 0x84, 0x94, 0x64, 0x48, 0x58, 0x81, 0x91, 0x21, 0x61, 0x95, + 0x25, 0x65, 0x19, 0x59, 0x42, 0x12, 0x46, 0x16, 0x56, 0xA0, 0xA4, 0x28, + 0x68, 0x85, 0xA5, 0x49, 0x29, 0x69, 0x52, 0x0A, 0x1A, 0x5A, 0x88, 0x98, + 0xA1, 0x89, 0x99, 0xA9, 0x22, 0x62, 0x96, 0x26, 0x66, 0x4A, 0x6A, 0xA8, + 0x82, 0x92, 0xA2, 0x86, 0xA6, 0x8A, 0x9A, 0x2A, 0xAA, + /* Table set B, code table 0, wordlen 3 - 49 entries */ + 0x00, 0x08, 0x38, 0x01, 0x07, 0x30, 0x09, 0x39, 0x0F, 0x3F, 0x10, 0x18, + 0x28, 0x31, 0x02, 0x3A, 0x03, 0x05, 0x06, 0x0E, 0x17, 0x37, 0x11, 0x19, + 0x29, 0x0A, 0x32, 0x0B, 0x3B, 0x0D, 0x15, 0x3D, 0x3E, 0x1F, 0x2F, 0x12, + 0x1A, 0x2A, 0x13, 0x1B, 0x2B, 0x33, 0x1D, 0x2D, 0x35, 0x16, 0x1E, 0x2E, + 0x36, + /* Table set B, code table 0, wordlen 4 - 121 entries */ + 0x00, 0x10, 0xF0, 0x01, 0x0F, 0x11, 0xF1, 0x1F, 0xFF, 0x20, 0xE0, 0x02, + 0xF2, 0x0E, 0x21, 0xE1, 0x12, 0xE2, 0x1E, 0x2E, 0xFE, 0x2F, 0xEF, 0x30, + 0x50, 0xB0, 0xC0, 0xD0, 0x31, 0xB1, 0xD1, 0x22, 0xD2, 0x03, 0x13, 0xE3, + 0xF3, 0xF4, 0x05, 0xE5, 0xF5, 0x0B, 0x1B, 0x0C, 0x0D, 0x1D, 0x2D, 0xFD, + 0x3E, 0xEE, 0x3F, 0x5F, 0xDF, 0x40, 0x41, 0x51, 0xC1, 0x32, 0x42, 0x52, + 0xB2, 0xC2, 0x23, 0x33, 0xB3, 0xC3, 0xD3, 0x04, 0x14, 0x24, 0xD4, 0xE4, + 0x15, 0x25, 0xC5, 0xD5, 0x2B, 0x3B, 0xEB, 0xFB, 0x1C, 0x2C, 0x3C, 0x5C, + 0xEC, 0xFC, 0x3D, 0x5D, 0xDD, 0xED, 0x4E, 0x5E, 0xBE, 0xCE, 0xDE, 0x4F, + 0xBF, 0xCF, 0x43, 0x53, 0x34, 0x54, 0xB4, 0xC4, 0x35, 0x45, 0x55, 0xB5, + 0x4B, 0x5B, 0xCB, 0xDB, 0x4C, 0xBC, 0xCC, 0xDC, 0x4D, 0xBD, 0xCD, 0x44, + 0xBB, + /* Table set B, code table 0, wordlen 5 - 225 entries */ + 0x00, 0x10, 0xF0, 0x01, 0x0F, 0x11, 0xF1, 0x1F, 0xFF, 0x20, 0xE0, 0x02, + 0x0E, 0x30, 0xD0, 0x21, 0xE1, 0x12, 0xF2, 0x03, 0x0D, 0x1E, 0xFE, 0x2F, + 0xEF, 0x40, 0x60, 0x70, 0x90, 0xA0, 0xC0, 0x31, 0xD1, 0x22, 0x32, 0xD2, + 0xE2, 0x13, 0x23, 0xE3, 0xF3, 0x04, 0x06, 0x07, 0x09, 0x0A, 0x0C, 0x1D, + 0x2D, 0xFD, 0x2E, 0x3E, 0xEE, 0x3F, 0xDF, 0x50, 0xB0, 0x41, 0x51, 0x61, + 0x71, 0x91, 0xA1, 0xB1, 0xC1, 0x42, 0x62, 0x72, 0x92, 0xA2, 0xC2, 0x33, + 0x93, 0xA3, 0xD3, 0x14, 0x24, 0xE4, 0xF4, 0x05, 0x15, 0xF5, 0x16, 0x26, + 0xD6, 0xE6, 0xF6, 0x17, 0x27, 0xD7, 0xE7, 0xF7, 0x19, 0x29, 0x39, 0xE9, + 0xF9, 0x1A, 0x2A, 0xEA, 0xFA, 0x0B, 0x1B, 0xFB, 0x1C, 0x2C, 0xEC, 0xFC, + 0x3D, 0x7D, 0x9D, 0xDD, 0xED, 0x4E, 0x6E, 0x7E, 0x9E, 0xAE, 0xCE, 0xDE, + 0x4F, 0x5F, 0x6F, 0x7F, 0x9F, 0xAF, 0xBF, 0xCF, 0x52, 0xB2, 0x43, 0x53, + 0x63, 0x73, 0xB3, 0xC3, 0x34, 0x44, 0x64, 0x74, 0x94, 0xA4, 0xB4, 0xC4, + 0xD4, 0x25, 0x35, 0x65, 0x75, 0x95, 0xA5, 0xD5, 0xE5, 0x36, 0x46, 0x56, + 0x66, 0xA6, 0xB6, 0xC6, 0x37, 0x47, 0x57, 0xB7, 0xC7, 0x49, 0x59, 0x69, + 0xB9, 0xC9, 0xD9, 0x3A, 0x4A, 0x5A, 0x6A, 0xAA, 0xBA, 0xCA, 0xDA, 0x2B, + 0x3B, 0x6B, 0x7B, 0x9B, 0xAB, 0xDB, 0xEB, 0x3C, 0x4C, 0x6C, 0x7C, 0x9C, + 0xAC, 0xCC, 0xDC, 0x4D, 0x5D, 0x6D, 0xAD, 0xBD, 0xCD, 0x5E, 0xBE, 0x54, + 0x45, 0x55, 0xB5, 0xC5, 0x76, 0x96, 0x67, 0x77, 0x97, 0xA7, 0x79, 0x99, + 0xA9, 0x7A, 0x9A, 0x4B, 0x5B, 0xBB, 0xCB, 0x5C, 0xBC, + /* Table set B, code table 0, wordlen 7 - 63 entries */ + 0x00, 0x01, 0x3F, 0x02, 0x3E, 0x03, 0x04, 0x3C, 0x3D, 0x05, 0x06, 0x07, + 0x08, 0x38, 0x39, 0x3A, 0x3B, 0x09, 0x0A, 0x0B, 0x0C, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x33, + 0x34, 0x35, 0x36, 0x37, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x2D, + 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x14, 0x15, 0x16, 0x17, 0x18, 0x28, 0x29, + 0x2A, 0x2B, 0x2C, + /* Table set B, code table 1, wordlen 2 - 81 entries */ + 0x00, 0x40, 0x04, 0x01, 0x10, 0x50, 0x44, 0x14, 0x54, 0x41, 0x11, 0x51, + 0x05, 0x45, 0x15, 0x55, 0x80, 0x90, 0x20, 0x60, 0x84, 0x94, 0x24, 0x64, + 0x08, 0x48, 0x18, 0x81, 0x91, 0x61, 0x85, 0x95, 0x25, 0x65, 0x09, 0x49, + 0x19, 0x59, 0x02, 0x42, 0x12, 0x52, 0x06, 0x46, 0x16, 0x56, 0xA4, 0x58, + 0x68, 0x21, 0xA5, 0x29, 0x69, 0x1A, 0x5A, 0xA0, 0x88, 0x98, 0x28, 0xA1, + 0x89, 0x99, 0xA9, 0x92, 0x22, 0x62, 0x86, 0x96, 0x26, 0x66, 0x0A, 0x4A, + 0x6A, 0xA8, 0x82, 0xA2, 0xA6, 0x8A, 0x9A, 0x2A, 0xAA, + /* Table set B, code table 1, wordlen 4 - 121 entries */ + 0x00, 0x10, 0xF0, 0x01, 0x0F, 0xF1, 0x1F, 0xFF, 0x20, 0xE0, 0x11, 0x02, + 0x0E, 0x30, 0x50, 0xB0, 0xD0, 0x21, 0xE1, 0x12, 0xF2, 0x03, 0x05, 0x0B, + 0x0D, 0x1E, 0xFE, 0x2F, 0xEF, 0x40, 0xC0, 0x31, 0x51, 0xB1, 0xC1, 0xD1, + 0x22, 0x52, 0xE2, 0x13, 0xF3, 0x04, 0x15, 0xF5, 0x1B, 0xEB, 0xFB, 0x0C, + 0x1D, 0xFD, 0x2E, 0x5E, 0xEE, 0x3F, 0x5F, 0xBF, 0xDF, 0x41, 0x32, 0x42, + 0xB2, 0xD2, 0x23, 0x53, 0xB3, 0xE3, 0x14, 0x24, 0xE4, 0xF4, 0x25, 0x35, + 0xD5, 0xE5, 0x2B, 0x3B, 0xDB, 0x1C, 0x2C, 0xBC, 0xEC, 0xFC, 0x2D, 0xBD, + 0xED, 0x3E, 0x4E, 0xBE, 0xDE, 0x4F, 0xCF, 0xC2, 0x33, 0x43, 0xC3, 0xD3, + 0x34, 0x44, 0x54, 0xB4, 0xD4, 0x45, 0x55, 0xC5, 0x4B, 0xCB, 0x3C, 0x4C, + 0x5C, 0xCC, 0xDC, 0x3D, 0x4D, 0x5D, 0xCD, 0xDD, 0xCE, 0xC4, 0xB5, 0x5B, + 0xBB, + /* Table set B, code table 1, wordlen 6 - 256 entries */ + 0x00, 0x10, 0x01, 0x11, 0x20, 0x21, 0x02, 0x12, 0x30, 0x31, 0x41, 0x22, + 0x32, 0x03, 0x13, 0x23, 0x14, 0x40, 0x51, 0x61, 0x42, 0x52, 0x33, 0x43, + 0x04, 0x24, 0x34, 0x15, 0x25, 0x16, 0x50, 0x60, 0x70, 0x71, 0x81, 0xD1, + 0xE1, 0x62, 0x72, 0x82, 0xD2, 0x53, 0x63, 0x73, 0xD3, 0x44, 0x54, 0x05, + 0x35, 0x45, 0x55, 0x06, 0x26, 0x36, 0x07, 0x17, 0x27, 0x37, 0x18, 0x28, + 0x19, 0x1D, 0x2D, 0x3D, 0x1E, 0x2E, 0x1F, 0x80, 0x90, 0xD0, 0xE0, 0xF0, + 0x91, 0xA1, 0xB1, 0xC1, 0xF1, 0x92, 0xA2, 0xB2, 0xC2, 0xE2, 0xF2, 0x83, + 0x93, 0xA3, 0xC3, 0xE3, 0xF3, 0x64, 0x74, 0x84, 0x94, 0xD4, 0xE4, 0xF4, + 0x65, 0x75, 0x85, 0xD5, 0xE5, 0x46, 0x56, 0x66, 0x76, 0xD6, 0xE6, 0x47, + 0x57, 0x67, 0xD7, 0x08, 0x38, 0x48, 0x58, 0x09, 0x29, 0x39, 0x49, 0x0A, + 0x1A, 0x2A, 0x3A, 0x1B, 0x2B, 0x0C, 0x1C, 0x2C, 0x3C, 0x0D, 0x4D, 0x5D, + 0x6D, 0x7D, 0x0E, 0x3E, 0x4E, 0x5E, 0x6E, 0x0F, 0x2F, 0x3F, 0x4F, 0xA0, + 0xB0, 0xC0, 0xB3, 0xA4, 0xB4, 0xC4, 0x95, 0xA5, 0xB5, 0xC5, 0xF5, 0x86, + 0x96, 0xA6, 0xB6, 0xC6, 0xF6, 0x77, 0x87, 0x97, 0xA7, 0xC7, 0xE7, 0xF7, + 0x68, 0x78, 0x88, 0x98, 0xD8, 0xE8, 0xF8, 0x59, 0x69, 0x79, 0x89, 0xD9, + 0xE9, 0xF9, 0x4A, 0x5A, 0x6A, 0x7A, 0xDA, 0xEA, 0x0B, 0x3B, 0x4B, 0x5B, + 0xDB, 0xEB, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0xDC, 0x8D, 0x9D, 0xAD, 0xBD, + 0xCD, 0x7E, 0x8E, 0x9E, 0xAE, 0xBE, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xB7, + 0xA8, 0xB8, 0xC8, 0x99, 0xA9, 0xB9, 0xC9, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, + 0xFA, 0x6B, 0x7B, 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xFB, 0x9C, 0xAC, 0xBC, + 0xCC, 0xEC, 0xFC, 0xDD, 0xED, 0xFD, 0xCE, 0xDE, 0xEE, 0xFE, 0xAF, 0xBF, + 0xCF, 0xDF, 0xEF, 0xFF, + /* Table set B, code table 2, wordlen 6 - 256 entries */ + 0x00, 0x01, 0x10, 0x11, 0x20, 0x21, 0x02, 0x12, 0x30, 0x31, 0x22, 0x03, + 0x13, 0x40, 0x41, 0x51, 0x32, 0x42, 0x23, 0x33, 0x04, 0x14, 0x24, 0x15, + 0x50, 0x61, 0x71, 0xD1, 0xE1, 0x52, 0x62, 0xD2, 0x43, 0x53, 0xD3, 0x34, + 0x44, 0x05, 0x25, 0x35, 0x06, 0x16, 0x26, 0x17, 0x18, 0x1D, 0x2D, 0x3D, + 0x1E, 0x2E, 0x60, 0x70, 0x80, 0xD0, 0xE0, 0xF0, 0x81, 0x91, 0xA1, 0xC1, + 0xF1, 0x72, 0x82, 0x92, 0xC2, 0xE2, 0xF2, 0x63, 0x73, 0xE3, 0xF3, 0x54, + 0x64, 0x74, 0xD4, 0xE4, 0xF4, 0x45, 0x55, 0x65, 0xD5, 0xE5, 0xF5, 0x36, + 0x46, 0x56, 0xD6, 0xE6, 0x07, 0x27, 0x37, 0x47, 0xD7, 0x08, 0x28, 0x38, + 0x19, 0x29, 0x1A, 0x1B, 0x1C, 0x2C, 0x0D, 0x4D, 0x5D, 0x6D, 0x7D, 0x0E, + 0x3E, 0x4E, 0x5E, 0x6E, 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x90, 0xA0, 0xC0, + 0xB1, 0xA2, 0xB2, 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0x84, 0x94, 0xA4, 0xC4, + 0x75, 0x85, 0x95, 0xC5, 0x66, 0x76, 0x86, 0x96, 0xC6, 0xF6, 0x57, 0x67, + 0x77, 0xE7, 0xF7, 0x48, 0x58, 0x68, 0x78, 0xD8, 0xE8, 0xF8, 0x09, 0x39, + 0x49, 0x59, 0xD9, 0xE9, 0xF9, 0x0A, 0x2A, 0x3A, 0x4A, 0xDA, 0xEA, 0x0B, + 0x2B, 0x3B, 0xDB, 0xEB, 0x0C, 0x3C, 0x4C, 0x5C, 0x6C, 0xDC, 0x8D, 0x9D, + 0xAD, 0xBD, 0xCD, 0x7E, 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xB0, 0xB4, 0xA5, 0xB5, 0xA6, 0xB6, 0x87, 0x97, 0xA7, + 0xB7, 0xC7, 0x88, 0x98, 0xA8, 0xC8, 0x69, 0x79, 0x89, 0x99, 0xA9, 0xC9, + 0x5A, 0x6A, 0x7A, 0x9A, 0xCA, 0xFA, 0x4B, 0x5B, 0x6B, 0x7B, 0xCB, 0xFB, + 0x7C, 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xEC, 0xFC, 0xDD, 0xED, 0xFD, 0xDE, + 0xEE, 0xFE, 0xBF, 0xCF, 0xDF, 0xEF, 0xB8, 0xB9, 0x8A, 0xAA, 0xBA, 0x8B, + 0x9B, 0xAB, 0xBB, 0xFF, + /* Table set B, code table 3, wordlen 2 - 81 entries */ + 0x00, 0x40, 0x10, 0x04, 0x01, 0x50, 0x44, 0x14, 0x41, 0x11, 0x05, 0x80, + 0x54, 0x51, 0x45, 0x15, 0x55, 0x02, 0x90, 0x20, 0x60, 0x84, 0x24, 0x08, + 0x18, 0x09, 0x12, 0x06, 0xA0, 0x94, 0x64, 0x48, 0x58, 0x81, 0x91, 0x21, + 0x61, 0x85, 0x95, 0x25, 0x65, 0x49, 0x19, 0x59, 0x42, 0x52, 0x46, 0x16, + 0x56, 0x0A, 0xA4, 0x28, 0x68, 0xA1, 0xA5, 0x29, 0x69, 0x26, 0x4A, 0x1A, + 0x5A, 0x88, 0x98, 0xA8, 0x89, 0x99, 0xA9, 0x82, 0x92, 0x22, 0x62, 0x86, + 0x96, 0x66, 0x9A, 0x2A, 0x6A, 0xA2, 0xA6, 0x8A, 0xAA, + /* Table set B, code table 3, wordlen 3 - 256 entries */ + 0x00, 0x40, 0x10, 0x04, 0x01, 0x50, 0x44, 0x14, 0x54, 0x41, 0x11, 0x51, + 0x05, 0x45, 0x15, 0x55, 0x20, 0x95, 0x65, 0x49, 0x59, 0x52, 0x46, 0x16, + 0x80, 0x90, 0x60, 0x84, 0x94, 0x24, 0x64, 0xA4, 0x08, 0x48, 0x18, 0x58, + 0x81, 0x91, 0x21, 0x61, 0x85, 0x25, 0x09, 0x19, 0x69, 0x02, 0x42, 0x12, + 0x06, 0x56, 0x5A, 0x57, 0xD0, 0x74, 0x68, 0x5C, 0xC1, 0xD5, 0xA5, 0xE5, + 0x75, 0xB5, 0xF5, 0x99, 0xD9, 0xA9, 0xE9, 0x79, 0xB9, 0xF9, 0x1D, 0x5D, + 0x9D, 0xDD, 0x6D, 0xAD, 0xED, 0x7D, 0xBD, 0xFD, 0x82, 0x92, 0xD2, 0x62, + 0x96, 0xD6, 0x26, 0x66, 0xA6, 0xE6, 0x76, 0xB6, 0xF6, 0x0A, 0x4A, 0x1A, + 0x9A, 0xDA, 0x2A, 0x6A, 0xAA, 0xEA, 0x7A, 0xBA, 0xFA, 0x5E, 0x9E, 0xDE, + 0x6E, 0xAE, 0xEE, 0x7E, 0xBE, 0xFE, 0x03, 0x13, 0x53, 0x17, 0x97, 0xD7, + 0x67, 0xA7, 0xE7, 0x77, 0xB7, 0xF7, 0x5B, 0x9B, 0xDB, 0x6B, 0xAB, 0xEB, + 0x7B, 0xBB, 0xFB, 0x5F, 0x9F, 0xDF, 0x6F, 0xAF, 0xEF, 0x7F, 0xBF, 0xFF, + 0xC0, 0xA0, 0xE0, 0x30, 0xC4, 0xD4, 0xE4, 0x34, 0xB4, 0xF4, 0x88, 0xC8, + 0x98, 0xD8, 0x28, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, 0x0C, 0x4C, 0x1C, + 0x9C, 0xDC, 0x6C, 0xAC, 0xEC, 0x7C, 0xBC, 0xFC, 0xD1, 0xA1, 0xE1, 0x31, + 0x71, 0xB1, 0xF1, 0xC5, 0x35, 0x89, 0xC9, 0x29, 0x39, 0x0D, 0x4D, 0x8D, + 0xCD, 0x2D, 0x3D, 0x22, 0xA2, 0xE2, 0x72, 0xB2, 0xF2, 0x86, 0xC6, 0x36, + 0x8A, 0xCA, 0x3A, 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x2E, 0x3E, 0x43, 0x83, + 0x93, 0xD3, 0x23, 0x63, 0xA3, 0xE3, 0x73, 0xB3, 0xF3, 0x07, 0x47, 0x87, + 0xC7, 0x27, 0x37, 0x4B, 0x8B, 0xCB, 0x1B, 0x2B, 0x3B, 0x4F, 0x8F, 0xCF, + 0x1F, 0x70, 0xB0, 0xF0, 0x8C, 0xCC, 0x2C, 0x3C, 0xC2, 0x32, 0xC3, 0x0F, + 0x2F, 0x3F, 0x33, 0x0B, + /* Table set B, code table 3, wordlen 5 - 225 entries */ + 0x00, 0x10, 0xF0, 0x01, 0x0F, 0x11, 0xF1, 0x02, 0x0E, 0x1F, 0xFF, 0x20, + 0xE0, 0x21, 0xF2, 0xFE, 0xEF, 0x30, 0xD0, 0xE1, 0x12, 0x22, 0xE2, 0x03, + 0x0D, 0x1D, 0x1E, 0x2E, 0xEE, 0x2F, 0xDF, 0x40, 0x60, 0x70, 0x90, 0xA0, + 0xB0, 0xC0, 0x31, 0x71, 0x91, 0xC1, 0xD1, 0x32, 0xD2, 0x13, 0xE3, 0xF3, + 0x04, 0x05, 0x06, 0x07, 0x17, 0xF7, 0x09, 0x19, 0x0A, 0x1A, 0xFA, 0x0C, + 0x1C, 0x2D, 0xED, 0xFD, 0x3E, 0x7E, 0xDE, 0x3F, 0x6F, 0x7F, 0x9F, 0xAF, + 0xCF, 0x50, 0x41, 0x51, 0x61, 0xA1, 0xB1, 0x62, 0x72, 0x92, 0xA2, 0xC2, + 0x23, 0x33, 0x63, 0x73, 0x93, 0xA3, 0xD3, 0x14, 0x24, 0x34, 0xD4, 0xE4, + 0xF4, 0x15, 0xF5, 0x16, 0x26, 0xD6, 0xE6, 0xF6, 0x27, 0x37, 0x47, 0xE7, + 0x29, 0x39, 0xC9, 0xD9, 0xE9, 0xF9, 0x2A, 0xEA, 0x0B, 0x1B, 0xFB, 0x2C, + 0x7C, 0xEC, 0xFC, 0x3D, 0x4D, 0x6D, 0x7D, 0xDD, 0x4E, 0x5E, 0x6E, 0x9E, + 0xAE, 0xCE, 0x4F, 0x5F, 0x42, 0x52, 0xB2, 0x43, 0xB3, 0xC3, 0x44, 0x64, + 0x74, 0x94, 0xA4, 0x25, 0x35, 0x65, 0x75, 0x95, 0xA5, 0xE5, 0x36, 0x46, + 0x66, 0x76, 0x96, 0xA6, 0xB6, 0xC6, 0x57, 0xA7, 0xB7, 0xC7, 0xD7, 0x59, + 0xA9, 0xB9, 0x3A, 0x4A, 0x6A, 0xCA, 0xDA, 0x2B, 0x3B, 0x6B, 0x9B, 0xAB, + 0xDB, 0xEB, 0x3C, 0x6C, 0x9C, 0xAC, 0xCC, 0xDC, 0x5D, 0x9D, 0xAD, 0xBD, + 0xCD, 0xBE, 0xBF, 0x53, 0x54, 0xB4, 0xC4, 0x45, 0x55, 0xB5, 0xC5, 0xD5, + 0x56, 0x67, 0x77, 0x97, 0x49, 0x69, 0x79, 0x99, 0x5A, 0x7A, 0x9A, 0xAA, + 0xBA, 0x4B, 0x5B, 0x7B, 0xBB, 0xCB, 0x4C, 0x5C, 0xBC, + /* Table set B, code table 3, wordlen 7 - 63 entries */ + 0x00, 0x01, 0x3F, 0x02, 0x3E, 0x03, 0x3D, 0x04, 0x05, 0x06, 0x3A, 0x3B, + 0x3C, 0x07, 0x08, 0x09, 0x0A, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x22, 0x23, + 0x25, 0x26, 0x27, 0x37, 0x38, 0x39, 0x0B, 0x0D, 0x0E, 0x0F, 0x1E, 0x1F, + 0x21, 0x24, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x0C, 0x10, 0x11, 0x12, + 0x13, 0x15, 0x29, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x14, 0x16, 0x17, + 0x18, 0x28, 0x2A, + /* Table set B, code table 4, wordlen 1 - 81 entries */ + 0x00, 0x01, 0x40, 0xC0, 0x10, 0x30, 0x04, 0x0C, 0x03, 0x50, 0xD0, 0x70, + 0xF0, 0xC4, 0x14, 0x34, 0x4C, 0x1C, 0x3C, 0xC1, 0x11, 0x31, 0x05, 0x0D, + 0xC3, 0x13, 0x33, 0x07, 0x0F, 0x44, 0xCC, 0x41, 0x43, 0x54, 0x74, 0xDC, + 0xFC, 0x71, 0x15, 0x4D, 0x1D, 0x37, 0x3F, 0xD4, 0xF4, 0x5C, 0x7C, 0x51, + 0xD1, 0xF1, 0x45, 0xC5, 0x35, 0xCD, 0xDD, 0x3D, 0x53, 0xD3, 0x73, 0xF3, + 0x47, 0xC7, 0x17, 0x77, 0x4F, 0xCF, 0x1F, 0x55, 0xFF, 0xD5, 0x75, 0xF5, + 0x5D, 0x7D, 0xFD, 0x57, 0xD7, 0xF7, 0x5F, 0xDF, 0x7F, + /* Table set B, code table 4, wordlen 2 - 25 entries */ + 0x00, 0x07, 0x08, 0x38, 0x01, 0x39, 0x0F, 0x10, 0x09, 0x3F, 0x30, 0x31, + 0x02, 0x3A, 0x06, 0x0E, 0x17, 0x11, 0x0A, 0x3E, 0x37, 0x12, 0x32, 0x16, + 0x36, + /* Table set B, code table 4, wordlen 3 - 49 entries */ + 0x00, 0x07, 0x08, 0x38, 0x01, 0x09, 0x39, 0x0F, 0x3F, 0x10, 0x30, 0x31, + 0x02, 0x3A, 0x06, 0x0E, 0x17, 0x37, 0x18, 0x28, 0x11, 0x19, 0x29, 0x0A, + 0x03, 0x0B, 0x3B, 0x05, 0x0D, 0x3D, 0x3E, 0x1F, 0x2F, 0x12, 0x1A, 0x2A, + 0x32, 0x13, 0x33, 0x15, 0x35, 0x16, 0x1E, 0x2E, 0x36, 0x1B, 0x2B, 0x1D, + 0x2D, + /* Table set B, code table 4, wordlen 7 - 63 entries */ + 0x00, 0x01, 0x02, 0x3E, 0x3F, 0x03, 0x04, 0x3C, 0x3D, 0x05, 0x06, 0x07, + 0x39, 0x3A, 0x3B, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x1A, 0x1B, 0x25, 0x26, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x19, + 0x1C, 0x1D, 0x1E, 0x1F, 0x21, 0x22, 0x23, 0x24, 0x27, 0x2E, 0x2F, 0x30, + 0x31, 0x32, 0x33, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x28, 0x29, 0x2A, + 0x2B, 0x2C, 0x2D, + /* Table set B, code table 5, wordlen 2 - 25 entries */ + 0x00, 0x01, 0x08, 0x38, 0x07, 0x39, 0x0F, 0x30, 0x09, 0x3F, 0x10, 0x31, + 0x02, 0x3A, 0x06, 0x0E, 0x17, 0x11, 0x0A, 0x3E, 0x37, 0x12, 0x32, 0x16, + 0x36, + /* Table set B, code table 5, wordlen 3 - 7 entries */ + 0x00, 0x07, 0x01, 0x02, 0x03, 0x05, 0x06, + /* Table set B, code table 5, wordlen 6 - 31 entries */ + 0x00, 0x01, 0x1F, 0x02, 0x1E, 0x03, 0x04, 0x0D, 0x1D, 0x05, 0x06, 0x07, + 0x0E, 0x0F, 0x11, 0x12, 0x13, 0x1A, 0x1B, 0x1C, 0x08, 0x09, 0x0C, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x0A, 0x0B, + /* Table set B, code table 6, wordlen 2 - 81 entries */ + 0x00, 0x40, 0x01, 0x10, 0x04, 0x02, 0x80, 0x50, 0x90, 0x05, 0x06, 0x20, + 0x60, 0x44, 0x14, 0x54, 0x24, 0x08, 0x18, 0x41, 0x11, 0x15, 0x09, 0xA0, + 0x84, 0x94, 0x64, 0xA4, 0x28, 0x51, 0x45, 0x55, 0x19, 0x12, 0x16, 0x0A, + 0x1A, 0x48, 0x58, 0x68, 0x81, 0x91, 0x21, 0x61, 0x85, 0x95, 0x25, 0x65, + 0x49, 0x59, 0x29, 0x69, 0x42, 0x46, 0x56, 0x88, 0x98, 0xA8, 0xA1, 0xA5, + 0x99, 0xA9, 0x52, 0x22, 0x26, 0x66, 0x4A, 0x5A, 0x2A, 0x6A, 0x89, 0x82, + 0x92, 0x62, 0x86, 0x96, 0xA6, 0x8A, 0xAA, 0xA2, 0x9A, + /* Table set B, code table 6, wordlen 3 - 7 entries */ + 0x00, 0x01, 0x07, 0x02, 0x03, 0x05, 0x06, + /* Table set B, code table 6, wordlen 4 - 6 entries */ + 0x01, 0x00, 0x02, 0x05, 0x03, 0x04, + /* Table set B, code table 6, wordlen 5 - 225 entries */ + 0x00, 0xF0, 0x01, 0x10, 0x0F, 0x11, 0xF1, 0x20, 0xE0, 0x02, 0x0E, 0x1F, + 0xFF, 0xD0, 0x21, 0xE1, 0x12, 0xF2, 0x07, 0x0A, 0x0D, 0x1E, 0xFE, 0x2F, + 0xEF, 0x30, 0x70, 0x90, 0xA0, 0xC0, 0x71, 0x91, 0xC1, 0xD1, 0x32, 0x92, + 0xE2, 0x03, 0x13, 0x63, 0x04, 0x06, 0xE6, 0xE7, 0xF7, 0x09, 0x19, 0x39, + 0xFA, 0x0C, 0x1C, 0xDD, 0xED, 0xFD, 0x2E, 0x7E, 0x9E, 0x3F, 0x9F, 0x40, + 0x50, 0x60, 0xB0, 0x31, 0x41, 0x61, 0xA1, 0xB1, 0x22, 0x42, 0x72, 0xA2, + 0xB2, 0xC2, 0xD2, 0x23, 0x33, 0x73, 0xA3, 0xC3, 0xD3, 0xE3, 0xF3, 0x14, + 0x24, 0x34, 0x44, 0x74, 0xD4, 0xE4, 0x05, 0x25, 0x45, 0x65, 0x95, 0xA5, + 0x16, 0x26, 0x46, 0x76, 0xA6, 0xB6, 0xC6, 0xD6, 0xF6, 0x17, 0x27, 0x37, + 0x47, 0x67, 0xA7, 0xD7, 0x29, 0x69, 0xB9, 0xD9, 0xE9, 0xF9, 0x1A, 0x2A, + 0x3A, 0x9A, 0xCA, 0xDA, 0xEA, 0x0B, 0x1B, 0x3B, 0x6B, 0xEB, 0xFB, 0x2C, + 0x6C, 0xEC, 0xFC, 0x1D, 0x2D, 0x4D, 0x6D, 0x9D, 0xAD, 0x3E, 0x4E, 0x6E, + 0xAE, 0xCE, 0xEE, 0x4F, 0x5F, 0x6F, 0xDF, 0x51, 0x52, 0x62, 0x43, 0x93, + 0xB3, 0x54, 0x94, 0xA4, 0xF4, 0x15, 0x75, 0xB5, 0xE5, 0xF5, 0x36, 0x56, + 0x66, 0x96, 0x57, 0x77, 0x49, 0x59, 0xA9, 0xC9, 0x4A, 0x5A, 0x6A, 0x7A, + 0xAA, 0xBA, 0x2B, 0x4B, 0x7B, 0x9B, 0xAB, 0xDB, 0x3C, 0x4C, 0x7C, 0x9C, + 0xAC, 0xBC, 0xCC, 0x3D, 0x5D, 0x7D, 0xBD, 0xCD, 0x5E, 0xBE, 0xDE, 0xBF, + 0xCF, 0x53, 0x64, 0xB4, 0xC4, 0x35, 0x55, 0xC5, 0xD5, 0x97, 0xB7, 0xC7, + 0x79, 0x99, 0x5B, 0xBB, 0xCB, 0x5C, 0xDC, 0x7F, 0xAF, + /* Table set B, code table 6, wordlen 6 - 256 entries */ + 0x00, 0x10, 0x01, 0x11, 0x21, 0x02, 0x12, 0x20, 0x22, 0x13, 0x30, 0x31, + 0x41, 0xD1, 0xE1, 0x32, 0x52, 0x03, 0x23, 0x2D, 0x40, 0x50, 0x60, 0x80, + 0xD0, 0xE0, 0x51, 0x61, 0xF1, 0x42, 0x62, 0xD2, 0xE2, 0xF2, 0x33, 0x43, + 0xC3, 0xD3, 0xE3, 0x04, 0x14, 0xD4, 0xF4, 0x25, 0x35, 0x16, 0x17, 0xF7, + 0xD8, 0x1C, 0x3C, 0x0D, 0x1D, 0x3D, 0x5D, 0x0E, 0x1E, 0x2E, 0x7E, 0x2F, + 0xC0, 0xF0, 0x71, 0x81, 0x91, 0xC1, 0x72, 0x82, 0x92, 0xB2, 0xC2, 0x53, + 0x63, 0x73, 0x93, 0xA3, 0xF3, 0x24, 0x44, 0x64, 0x84, 0xA4, 0xB4, 0x05, + 0x15, 0x95, 0xD5, 0x06, 0x26, 0x36, 0x46, 0x96, 0xD6, 0xE6, 0xF6, 0x07, + 0x27, 0x37, 0xD7, 0xE7, 0x08, 0x18, 0x28, 0x38, 0xE8, 0xF8, 0x09, 0x19, + 0x29, 0xE9, 0xF9, 0x0A, 0x1A, 0xCA, 0xDA, 0xEA, 0x0B, 0x1B, 0xDB, 0xEB, + 0xFB, 0x2C, 0x4C, 0x5C, 0x7C, 0x8C, 0x4D, 0x6D, 0x8D, 0x9D, 0xFD, 0x3E, + 0x5E, 0x6E, 0x8E, 0x9E, 0xEE, 0x0F, 0x1F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0xCF, 0x70, 0xA1, 0xA2, 0x83, 0xB3, 0x34, 0x74, 0xC4, 0xE4, 0x55, 0x65, + 0x85, 0xA5, 0xC5, 0xE5, 0xF5, 0x56, 0x66, 0x76, 0x86, 0xA6, 0xC6, 0x57, + 0x67, 0x77, 0x97, 0xA7, 0x48, 0x88, 0x98, 0x49, 0x59, 0x79, 0x99, 0x3A, + 0x4A, 0x8A, 0xBA, 0xFA, 0x2B, 0x7B, 0x0C, 0xAC, 0xBC, 0xCC, 0xEC, 0x7D, + 0xAD, 0xBD, 0xDD, 0x4E, 0xBE, 0xCE, 0xFE, 0x8F, 0x9F, 0xAF, 0xBF, 0xDF, + 0xEF, 0xFF, 0x90, 0xA0, 0xB0, 0xB1, 0x54, 0x94, 0x45, 0x75, 0xB5, 0xB6, + 0x47, 0x87, 0xB7, 0xC7, 0x58, 0x68, 0x78, 0xA8, 0xB8, 0xC8, 0x39, 0x69, + 0x89, 0xA9, 0xB9, 0xC9, 0xD9, 0x2A, 0x5A, 0x6A, 0x7A, 0x9A, 0xAA, 0x3B, + 0x4B, 0x5B, 0x6B, 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0x6C, 0x9C, 0xDC, 0xFC, + 0xCD, 0xED, 0xAE, 0xDE, + /* Table set B, code table 6, wordlen 7 - 63 entries */ + 0x00, 0x01, 0x3F, 0x3E, 0x02, 0x03, 0x3C, 0x3D, 0x04, 0x05, 0x06, 0x07, + 0x39, 0x3A, 0x3B, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, + 0x1E, 0x1F, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x2E, 0x2F, 0x30, + 0x31, 0x32, 0x33, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x28, 0x29, 0x2A, + 0x2B, 0x2C, 0x2D, + /* Table set B, code table 7, wordlen 1 - 81 entries */ + 0x00, 0x03, 0x40, 0xC0, 0x10, 0x30, 0x04, 0x0C, 0x01, 0x50, 0xD0, 0x70, + 0xF0, 0xC4, 0x14, 0x34, 0x4C, 0x1C, 0x3C, 0xC1, 0x11, 0x31, 0x05, 0x0D, + 0x13, 0x33, 0x07, 0x0F, 0x44, 0xCC, 0x41, 0x43, 0xC3, 0x54, 0x74, 0xDC, + 0xFC, 0xF1, 0xC5, 0x15, 0x1D, 0x53, 0xC7, 0x37, 0x4F, 0x3F, 0xD4, 0xF4, + 0x5C, 0x7C, 0x51, 0xD1, 0x71, 0x45, 0x55, 0x35, 0x4D, 0xCD, 0xDD, 0x3D, + 0xD3, 0x73, 0xF3, 0x47, 0x17, 0x77, 0xCF, 0x1F, 0xFF, 0xD5, 0x75, 0xF5, + 0x5D, 0x7D, 0xFD, 0x57, 0xD7, 0xF7, 0x5F, 0xDF, 0x7F, + /* Table set B, code table 7, wordlen 3 - 256 entries */ + 0x00, 0x40, 0x10, 0x04, 0x01, 0x05, 0x50, 0x14, 0x54, 0x41, 0x11, 0x51, + 0x45, 0x15, 0x55, 0x44, 0x95, 0x6A, 0x03, 0x80, 0xC0, 0x90, 0xD0, 0x94, + 0xD4, 0x24, 0x64, 0x58, 0x91, 0xA1, 0x85, 0xD5, 0x25, 0x65, 0xA5, 0xE5, + 0x75, 0xB5, 0xF5, 0x19, 0x59, 0x99, 0xD9, 0x69, 0xA9, 0xE9, 0x79, 0xB9, + 0xF9, 0x4D, 0x5D, 0x9D, 0xDD, 0x6D, 0xAD, 0xED, 0x7D, 0xBD, 0xFD, 0x02, + 0x42, 0x52, 0x06, 0x46, 0x16, 0x56, 0x96, 0xD6, 0x26, 0x66, 0xA6, 0xE6, + 0x76, 0xB6, 0xF6, 0x1A, 0x5A, 0x9A, 0xDA, 0xAA, 0xEA, 0x7A, 0xBA, 0xFA, + 0x5E, 0x9E, 0xDE, 0x6E, 0xAE, 0xEE, 0x7E, 0xBE, 0xFE, 0x07, 0x47, 0x57, + 0x97, 0xD7, 0x67, 0xA7, 0xE7, 0x77, 0xB7, 0xF7, 0x5B, 0x9B, 0xDB, 0x6B, + 0xAB, 0xEB, 0x7B, 0xBB, 0xFB, 0x5F, 0x9F, 0xDF, 0x6F, 0xAF, 0xEF, 0x7F, + 0xBF, 0xFF, 0x20, 0x60, 0x70, 0xB0, 0xF0, 0x84, 0xC4, 0xA4, 0xE4, 0x74, + 0xB4, 0xF4, 0x08, 0x88, 0x18, 0x98, 0xD8, 0x68, 0xA8, 0xE8, 0x78, 0xB8, + 0xF8, 0x1C, 0x5C, 0x9C, 0xDC, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x81, 0xD1, 0x21, 0x61, 0xE1, 0x71, 0xB1, 0xF1, 0xC5, 0x35, 0x09, 0x49, + 0x89, 0xC9, 0x29, 0x39, 0x0D, 0x8D, 0xCD, 0x1D, 0x2D, 0x3D, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x72, 0xB2, 0xF2, 0x86, 0xC6, 0x36, 0x0A, 0x4A, + 0x8A, 0xCA, 0x2A, 0x3A, 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x2E, 0x3E, 0x13, + 0x53, 0x93, 0xD3, 0x63, 0xA3, 0xE3, 0x73, 0xB3, 0xF3, 0x87, 0xC7, 0x17, + 0x27, 0x37, 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x2B, 0x3B, 0x0F, 0x4F, 0x8F, + 0xCF, 0x1F, 0x2F, 0x3F, 0xA0, 0xE0, 0x30, 0x34, 0x48, 0xC8, 0x28, 0x38, + 0x0C, 0x4C, 0x8C, 0xCC, 0x2C, 0xC1, 0x31, 0x82, 0xC2, 0x12, 0x32, 0x43, + 0x83, 0xC3, 0x23, 0x33, + /* Table set B, code table 7, wordlen 4 - 11 entries */ + 0x00, 0x01, 0x0F, 0x02, 0x0E, 0x05, 0x0D, 0x03, 0x04, 0x0B, 0x0C, + /* Table set B, code table 7, wordlen 5 - 225 entries */ + 0x00, 0x10, 0xF0, 0x01, 0x0F, 0x20, 0xE0, 0x11, 0xF1, 0x02, 0x0E, 0x1F, + 0xFF, 0xD0, 0x21, 0xE1, 0x12, 0xF2, 0x03, 0x0D, 0x1E, 0xFE, 0x2F, 0xEF, + 0x30, 0x40, 0x60, 0x70, 0x90, 0xA0, 0xC0, 0x31, 0xD1, 0x22, 0xE2, 0x13, + 0xF3, 0x04, 0x06, 0x07, 0x09, 0x0C, 0x1D, 0xFD, 0x2E, 0xEE, 0x3F, 0xDF, + 0x50, 0xB0, 0x41, 0x61, 0x71, 0x91, 0xA1, 0xC1, 0x32, 0x62, 0x72, 0x92, + 0xA2, 0xD2, 0x23, 0xD3, 0xE3, 0x14, 0xF4, 0x05, 0x16, 0x26, 0xE6, 0xF6, + 0x17, 0x27, 0xE7, 0xF7, 0x19, 0x29, 0xF9, 0x0A, 0x1A, 0x2A, 0xFA, 0x0B, + 0x1C, 0x2C, 0xFC, 0x2D, 0x3D, 0xED, 0x3E, 0x4E, 0x7E, 0x9E, 0xDE, 0x4F, + 0x6F, 0x7F, 0x9F, 0xAF, 0xCF, 0x51, 0xB1, 0x42, 0x52, 0xB2, 0xC2, 0x33, + 0x63, 0x73, 0x93, 0xA3, 0xB3, 0xC3, 0x24, 0x34, 0x74, 0xA4, 0xD4, 0xE4, + 0x15, 0x25, 0x65, 0x95, 0xE5, 0xF5, 0x36, 0xD6, 0x37, 0x47, 0xC7, 0xD7, + 0x39, 0x59, 0xB9, 0xC9, 0xD9, 0xE9, 0x3A, 0x6A, 0xDA, 0xEA, 0x1B, 0x2B, + 0x9B, 0xAB, 0xEB, 0xFB, 0x6C, 0x7C, 0x9C, 0xAC, 0xEC, 0x4D, 0x6D, 0x7D, + 0x9D, 0xAD, 0xBD, 0xDD, 0x5E, 0x6E, 0xAE, 0xCE, 0x5F, 0x43, 0x53, 0x44, + 0x54, 0x64, 0x94, 0xB4, 0xC4, 0x35, 0x45, 0x55, 0x75, 0xA5, 0xB5, 0xC5, + 0xD5, 0x46, 0x56, 0x66, 0x76, 0x96, 0xA6, 0xB6, 0xC6, 0x57, 0x67, 0x77, + 0x97, 0xA7, 0xB7, 0x49, 0x69, 0x79, 0x99, 0xA9, 0x4A, 0x5A, 0x7A, 0x9A, + 0xAA, 0xBA, 0xCA, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, 0xBB, 0xCB, 0xDB, 0x3C, + 0x4C, 0x5C, 0xBC, 0xCC, 0xDC, 0x5D, 0xCD, 0xBE, 0xBF, + /* Table set B, code table 7, wordlen 7 - 63 entries */ + 0x00, 0x01, 0x02, 0x3E, 0x3F, 0x03, 0x04, 0x05, 0x3B, 0x3C, 0x3D, 0x06, + 0x07, 0x08, 0x09, 0x0A, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x2F, 0x31, 0x32, 0x33, 0x34, 0x35, 0x11, 0x12, 0x13, + 0x14, 0x2D, 0x2E, 0x30, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x1C, 0x1D, 0x1E, 0x23, 0x1F, + 0x21, 0x22, 0x24, + /* Table set B, code table 7, wordlen 7 - 8 entries */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + /* Table set B, code table 7, wordlen 7 - 8 entries */ + 0x00, 0x01, 0x07, 0x02, 0x06, 0x03, 0x04, 0x05, + /* Table set B, code table 7, wordlen 7 - 16 entries */ + 0x07, 0x05, 0x08, 0x06, 0x09, 0x04, 0x0A, 0x0B, 0x00, 0x01, 0x02, 0x03, + 0x0C, 0x0D, 0x0E, 0x0F, + /* Table set B, code table 7, wordlen 7 - 15 entries */ + 0x0F, 0x0E, 0x01, 0x0D, 0x02, 0x03, 0x0C, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, + /* Table set B, code table 7, wordlen 7 - 16 entries */ + 0x00, 0x01, 0x0E, 0x0F, 0x02, 0x0D, 0x03, 0x0C, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, + /* Table set B, code table 7, wordlen 7 - 16 entries */ + 0x00, 0x01, 0x0F, 0x0E, 0x02, 0x0D, 0x03, 0x0C, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, + /* Table set B, code table 7, wordlen 7 - 32 entries */ + 0x00, 0x01, 0x1F, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x1A, 0x1B, 0x1C, + 0x1D, 0x1E, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + /* Table set B, code table 7, wordlen 7 - 31 entries */ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + /* Table set B, code table 7, wordlen 7 - 32 entries */ + 0x00, 0x1C, 0x1D, 0x01, 0x02, 0x03, 0x04, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1E, 0x1F, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, + /* Table set B, code table 7, wordlen 7 - 31 entries */ + 0x02, 0x03, 0x04, 0x05, 0x06, 0x01, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + /* Table set B, code table 7, wordlen 7 - 32 entries */ + 0x00, 0x01, 0x02, 0x1F, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x1D, 0x1E, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, + /* Table set B, code table 7, wordlen 7 - 16 entries */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, + /* Table set B, code table 7, wordlen 7 - 8 entries */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + /* Table set B, code table 7, wordlen 7 - 8 entries */ + 0x00, 0x01, 0x07, 0x02, 0x03, 0x04, 0x05, 0x06, + /* Table set B, code table 7, wordlen 7 - 32 entries */ + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x05, 0x06, + 0x07, 0x12, 0x13, 0x14, 0x15, 0x16, 0x04, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x00, 0x01, 0x02, 0x03, 0x1C, 0x1D, 0x1E, 0x1F, + /* Table set B, code table 7, wordlen 7 - 32 entries */ + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x0E, + 0x0F, 0x10, 0x11, 0x1D, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x1E, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x1F, 0x00, 0x01, 0x02, 0x03, + /* Table set B, code table 7, wordlen 7 - 32 entries */ + 0x00, 0x01, 0x02, 0x1F, 0x03, 0x1D, 0x1E, 0x04, 0x05, 0x06, 0x1B, 0x1C, + 0x07, 0x18, 0x19, 0x1A, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + /* Table set B, code table 7, wordlen 7 - 256 entries */ + 0x00, 0x01, 0xFF, 0x02, 0xFE, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xFB, + 0xFC, 0xFD, 0x09, 0x0A, 0x0B, 0x0C, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0xF3, 0xF4, 0xF5, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, + 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, + 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0xC2, + 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, + 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, + 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, + 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, + 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, + 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, + 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, + 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, + 0xBE, 0xBF, 0xC0, 0xC1, +}; + +/** Tables for spectrum coding. */ +typedef struct Atrac3pSpecCodeTab { + uint8_t group_size; ///< number of coefficients grouped together + uint8_t num_coeffs; ///< 1 - map index to a single value, > 1 - map index to a vector of values + uint8_t bits; ///< number of bits a single coefficient occupy + uint8_t is_signed; ///< 1 - values in that table are signed ones, otherwise - absolute ones +} Atrac3pSpecCodeTab; + +static const Atrac3pSpecCodeTab atrac3p_spectra_tabs[112] = { + /* table set = A */ + /* code table = 0 */ + { 1, 4, 2, 1 }, // wordlen = 1 + { 1, 4, 2, 0 }, // wordlen = 2 + { 1, 2, 3, 1 }, // wordlen = 3 + { 1, 1, 3, 0 }, // wordlen = 4 + { 1, 2, 4, 1 }, // wordlen = 5 + { 1, 1, 4, 0 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 1 */ + { 4, 4, 2, 1 }, // wordlen = 1 + { 1, 4, 2, 0 }, // wordlen = 2 + { 1, 2, 3, 1 }, // wordlen = 3 + { 1, 2, 4, 1 }, // wordlen = 4 + { 1, 2, 4, 1 }, // wordlen = 5 + { 1, 2, 4, 0 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 2 */ + { 1, 4, 2, 1 }, // wordlen = 1 + { 1, 2, 3, 1 }, // wordlen = 2 + { 1, 2, 3, 1 }, // wordlen = 3 + { 1, 1, 3, 0 }, // wordlen = 4 + { 1, 1, 3, 0 }, // wordlen = 5 + { 1, 2, 4, 0 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 3 */ + { 1, 2, 2, 1 }, // wordlen = 1 + { 1, 4, 2, 0 }, // wordlen = 2 + { 1, 4, 2, 0 }, // wordlen = 3 + { 1, 1, 3, 0 }, // wordlen = 4 + { 1, 2, 4, 1 }, // wordlen = 5 + { 1, 2, 4, 0 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 4 */ + { 1, 4, 2, 1 }, // wordlen = 1 + { 1, 4, 2, 0 }, // wordlen = 2 + { 1, 1, 3, 1 }, // wordlen = 3 + { 1, 2, 4, 1 }, // wordlen = 4 + { 1, 1, 3, 0 }, // wordlen = 5 + { 1, 2, 4, 0 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 5 */ + { 1, 4, 2, 1 }, // wordlen = 1 + { 1, 2, 3, 1 }, // wordlen = 2 + { 1, 1, 3, 1 }, // wordlen = 3 + { 1, 2, 4, 1 }, // wordlen = 4 + { 1, 1, 3, 0 }, // wordlen = 5 + { 1, 1, 5, 1 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 6 */ + { 2, 4, 2, 1 }, // wordlen = 1 + { 1, 2, 3, 1 }, // wordlen = 2 + { 1, 4, 2, 0 }, // wordlen = 3 + { 1, 2, 4, 1 }, // wordlen = 4 + { 1, 1, 4, 1 }, // wordlen = 5 + { 1, 1, 5, 1 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 7 */ + { 1, 2, 1, 0 }, // wordlen = 1 + { 2, 4, 2, 0 }, // wordlen = 2 + { 1, 2, 3, 1 }, // wordlen = 3 + { 1, 2, 4, 1 }, // wordlen = 4 + { 1, 1, 4, 1 }, // wordlen = 5 + { 2, 2, 4, 0 }, // wordlen = 6 + { 4, 1, 6, 1 }, // wordlen = 7 + + /* table set = B */ + /* code table = 0 */ + { 4, 4, 2, 1 }, // wordlen = 1 + { 1, 4, 2, 0 }, // wordlen = 2 + { 4, 2, 3, 1 }, // wordlen = 3 + { 1, 2, 4, 1 }, // wordlen = 4 + { 1, 2, 4, 1 }, // wordlen = 5 + { 1, 1, 4, 0 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 1 */ + { 1, 4, 2, 1 }, // wordlen = 1 + { 1, 4, 2, 0 }, // wordlen = 2 + { 1, 2, 3, 1 }, // wordlen = 3 + { 1, 2, 4, 1 }, // wordlen = 4 + { 1, 2, 4, 1 }, // wordlen = 5 + { 1, 2, 4, 0 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 2 */ + { 4, 4, 2, 1 }, // wordlen = 1 + { 4, 4, 2, 0 }, // wordlen = 2 + { 1, 2, 3, 1 }, // wordlen = 3 + { 1, 2, 4, 1 }, // wordlen = 4 + { 2, 2, 4, 1 }, // wordlen = 5 + { 2, 2, 4, 0 }, // wordlen = 6 + { 4, 1, 6, 1 }, // wordlen = 7 + + /* code table = 3 */ + { 1, 4, 2, 1 }, // wordlen = 1 + { 1, 4, 2, 0 }, // wordlen = 2 + { 1, 4, 2, 0 }, // wordlen = 3 + { 2, 2, 4, 1 }, // wordlen = 4 + { 1, 2, 4, 1 }, // wordlen = 5 + { 1, 2, 4, 0 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 4 */ + { 1, 4, 2, 1 }, // wordlen = 1 + { 4, 2, 3, 1 }, // wordlen = 2 + { 1, 2, 3, 1 }, // wordlen = 3 + { 4, 2, 4, 1 }, // wordlen = 4 + { 1, 1, 3, 0 }, // wordlen = 5 + { 1, 2, 4, 0 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 5 */ + { 2, 4, 2, 1 }, // wordlen = 1 + { 1, 2, 3, 1 }, // wordlen = 2 + { 4, 1, 3, 1 }, // wordlen = 3 + { 1, 1, 3, 0 }, // wordlen = 4 + { 1, 1, 3, 0 }, // wordlen = 5 + { 1, 1, 5, 1 }, // wordlen = 6 + { 2, 1, 6, 1 }, // wordlen = 7 + + /* code table = 6 */ + { 1, 4, 2, 1 }, // wordlen = 1 + { 1, 4, 2, 0 }, // wordlen = 2 + { 1, 1, 3, 1 }, // wordlen = 3 + { 1, 1, 3, 0 }, // wordlen = 4 + { 4, 2, 4, 1 }, // wordlen = 5 + { 1, 2, 4, 0 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 + + /* code table = 7 */ + { 1, 4, 2, 1 }, // wordlen = 1 + { 4, 4, 2, 0 }, // wordlen = 2 + { 4, 4, 2, 0 }, // wordlen = 3 + { 1, 1, 4, 1 }, // wordlen = 4 + { 1, 2, 4, 1 }, // wordlen = 5 + { 1, 1, 5, 1 }, // wordlen = 6 + { 1, 1, 6, 1 }, // wordlen = 7 +}; + +/* Huffman tables for gain control data. */ +static const uint8_t atrac3p_gain_cbs[][12] = { + { 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0 }, + { 1, 0, 2, 2, 1, 2, 8, 0, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 1, 0, 2, 0, 8, 0, 0, 0 }, + { 1, 0, 3, 1, 1, 0, 2, 0, 8, 0, 0, 0 }, + { 1, 1, 1, 1, 1, 0, 1, 2, 8, 0, 0, 0 }, + { 0, 1, 0, 2, 11, 18, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 2, 4, 4, 4, 0, 16, 0, 0, 0, 0 }, + { 0, 0, 0, 3, 23, 6, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 5, 3, 2, 3, 2, 16, 0, 0, 0, 0 }, + { 1, 0, 0, 3, 2, 6, 20, 0, 0, 0, 0, 0 }, +}; + +static const uint8_t atrac3p_gain_xlats[] = { + /* Number of gain control points 1 - 8 entries */ + 0, 1, 2, 3, 4, 5, 6, 7, + /* Number of gain control points 2 - 8 entries */ + 0, 1, 7, 2, 6, 3, 4, 5, + /* Gain compensation level 1 - 16 entries */ + 7, 5, 8, 6, 9, 4, 10, 11, 0, 1, 2, 3, 12, 13, 14, 15, + /* Gain compensation level 2 - 15 entries */ + 15, 14, 1, 13, 2, 3, 12, 4, 5, 6, 7, 8, 9, 10, 11, + /* Gain compensation level 3 - 16 entries */ + 0, 1, 14, 15, 2, 13, 3, 12, 4, 5, 6, 7, 8, 9, 10, 11, + /* Gain compensation level 4 - 16 entries */ + 0, 1, 15, 14, 2, 13, 3, 12, 4, 5, 6, 7, 8, 9, 10, 11, + /* Gain compensation location 3 - 32 entries */ + 0, 1, 31, 2, 3, 4, 5, 6, 7, 26, 27, 28, 29, 30, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + /* Gain compensation location 1 - 31 entries */ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + /* Gain compensation location 4 - 32 entries */ + 0, 28, 29, 1, 2, 3, 4, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 30, 31, 5, 6, 7, 8, 9, 10, + /* Gain compensation location 2 - 31 entries */ + 2, 3, 4, 5, 6, 1, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + /* Gain compensation location 5 - 32 entries */ + 0, 1, 2, 31, 3, 4, 5, 6, 7, 8, 29, 30, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, +}; + +/* Huffman tables for GHA waves data. */ +static const uint8_t atrac3p_tone_cbs[][12] = { + { 1, 0, 1, 2, 4, 8, 0, 0, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0 }, + { 1, 1, 1, 1, 0, 4, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 10, 8, 6, 0, 8, 0, 0, 0, 0 }, + { 0, 0, 0, 11, 5, 6, 6, 4, 0, 0, 0, 0 }, + { 0, 1, 3, 3, 1, 4, 4, 16, 0, 0, 0, 0 }, + { 1, 0, 0, 2, 2, 0, 9, 9, 29,104, 100, 0 }, +}; + +static const uint8_t atrac3p_tone_xlats[] = { + /* Number of tone bands - 16 entries */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, + /* Number of tones 1 - 8 entries */ + 0, 1, 2, 3, 4, 5, 6, 7, + /* Number of tones 2 - 8 entries */ + 0, 1, 7, 2, 3, 4, 5, 6, + /* Amplitude information 1 - 32 entries */ + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 5, 6, 7, 18, 19, + 20, 21, 22, 4, 23, 24, 25, 26, 27, 0, 1, 2, 3, 28, 29, + 30, 31, + /* Amplitude information 2 - 32 entries */ + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 14, 15, 16, 17, + 29, 9, 10, 11, 12, 13, 30, 4, 5, 6, 7, 8, 31, 0, 1, + 2, 3, + /* Amplitude information 3 - 32 entries */ + 0, 1, 2, 31, 3, 29, 30, 4, 5, 6, 27, 28, 7, 24, 25, + 26, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, + /* Frequencies - 256 entries */ + 0, 1, 255, 2, 254, 3, 4, 5, 6, 7, 8, 251, 252, 253, 9, + 10, 11, 12, 246, 247, 248, 249, 250, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 243, 244, 245, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 94, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, +}; + +#endif /* AVCODEC_ATRAC3PLUS_DATA_H */ diff --git a/src/atrac/at3p/ff/atrac3plusdsp.c b/src/atrac/at3p/ff/atrac3plusdsp.c new file mode 100644 index 0000000..5859876 --- /dev/null +++ b/src/atrac/at3p/ff/atrac3plusdsp.c @@ -0,0 +1,204 @@ +/* + * ATRAC3+ compatible decoder + * + * Copyright (c) 2010-2013 Maxim Poliakovski + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * DSP functions for ATRAC3+ decoder. + */ + +#define _USE_MATH_DEFINES +#include <math.h> +#include <string.h> + +#include "atrac3plus.h" + +#define TWOPI (2 * M_PI) +#define DEQUANT_PHASE(ph) (((ph) & 0x1F) << 6) + +static float sine_table[2048]; ///< wave table +static float hann_window[256]; ///< Hann windowing function +static float amp_sf_tab[64]; ///< scalefactors for quantized amplitudes + +static void vector_fmul(float *dst, const float *src0, const float *src1, + int len) +{ + int i; + for (i = 0; i < len; i++) + dst[i] = src0[i] * src1[i]; +} + +void ff_atrac3p_init_dsp_static(void) +{ + int i; + + /* generate sine wave table */ + for (i = 0; i < 2048; i++) + sine_table[i] = sin(TWOPI * i / 2048); + + /* generate Hann window */ + for (i = 0; i < 256; i++) + hann_window[i] = (1.0f - cos(TWOPI * i / 256.0f)) * 0.5f; + + /* generate amplitude scalefactors table */ + for (i = 0; i < 64; i++) + amp_sf_tab[i] = exp2f((i - 3) / 4.0f); +} + +/** + * Synthesize sine waves according to given parameters. + * + * @param[in] synth_param ptr to common synthesis parameters + * @param[in] waves_info parameters for each sine wave + * @param[in] envelope envelope data for all waves in a group + * @param[in] invert_phase flag indicating 180° phase shift + * @param[in] reg_offset region offset for trimming envelope data + * @param[out] out receives sythesized data + */ + +static void waves_synth(Atrac3pWaveSynthParams *synth_param, + Atrac3pWavesData *waves_info, + Atrac3pWaveEnvelope *envelope, + int invert_phase, int reg_offset, float *out) +{ + int i, wn, inc, pos; + double amp; + Atrac3pWaveParam *wave_param = &synth_param->waves[waves_info->start_index]; + + for (wn = 0; wn < waves_info->num_wavs; wn++, wave_param++) { + /* amplitude dequantization */ + amp = amp_sf_tab[wave_param->amp_sf] * + (!synth_param->amplitude_mode + ? (wave_param->amp_index + 1) / 15.13f + : 1.0f); + + inc = wave_param->freq_index; + pos = DEQUANT_PHASE(wave_param->phase_index) - (reg_offset ^ 128) * inc & 2047; + + /* waveform generation */ + for (i = 0; i < 128; i++) { + out[i] += sine_table[pos] * amp; + pos = (pos + inc) & 2047; + } + } + + /* invert phase if requested */ + if (invert_phase) + for (i = 0; i < 128; i++) + out[i] *= -1.0f; + + /* fade in with steep Hann window if requested */ + if (envelope->has_start_point) { + pos = (envelope->start_pos << 2) - reg_offset; + if (pos > 0 && pos <= 128) { + memset(out, 0, pos * sizeof(*out)); + if (!envelope->has_stop_point || + envelope->start_pos != envelope->stop_pos) { + out[pos + 0] *= hann_window[0]; + out[pos + 1] *= hann_window[32]; + out[pos + 2] *= hann_window[64]; + out[pos + 3] *= hann_window[96]; + } + } + } + + /* fade out with steep Hann window if requested */ + if (envelope->has_stop_point) { + pos = (envelope->stop_pos + 1 << 2) - reg_offset; + if (pos > 0 && pos <= 128) { + out[pos - 4] *= hann_window[96]; + out[pos - 3] *= hann_window[64]; + out[pos - 2] *= hann_window[32]; + out[pos - 1] *= hann_window[0]; + memset(&out[pos], 0, (128 - pos) * sizeof(out[pos])); + } + } +} + +void ff_atrac3p_generate_tones(Atrac3pChanUnitCtx *ch_unit, + int ch_num, int sb, float *out) +{ + float wavreg1[128] = { 0 }; + float wavreg2[128] = { 0 }; + int i, reg1_env_nonzero, reg2_env_nonzero; + Atrac3pWavesData *tones_now = &ch_unit->channels[ch_num].tones_info_prev[sb]; + Atrac3pWavesData *tones_next = &ch_unit->channels[ch_num].tones_info[sb]; + + /* reconstruct full envelopes for both overlapping regions + * from truncated bitstream data */ + if (tones_next->pend_env.has_start_point && + tones_next->pend_env.start_pos < tones_next->pend_env.stop_pos) { + tones_next->curr_env.has_start_point = 1; + tones_next->curr_env.start_pos = tones_next->pend_env.start_pos + 32; + } else if (tones_now->pend_env.has_start_point) { + tones_next->curr_env.has_start_point = 1; + tones_next->curr_env.start_pos = tones_now->pend_env.start_pos; + } else { + tones_next->curr_env.has_start_point = 0; + tones_next->curr_env.start_pos = 0; + } + + if (tones_now->pend_env.has_stop_point && + tones_now->pend_env.stop_pos >= tones_next->curr_env.start_pos) { + tones_next->curr_env.has_stop_point = 1; + tones_next->curr_env.stop_pos = tones_now->pend_env.stop_pos; + } else if (tones_next->pend_env.has_stop_point) { + tones_next->curr_env.has_stop_point = 1; + tones_next->curr_env.stop_pos = tones_next->pend_env.stop_pos + 32; + } else { + tones_next->curr_env.has_stop_point = 0; + tones_next->curr_env.stop_pos = 64; + } + + /* is the visible part of the envelope non-zero? */ + reg1_env_nonzero = (tones_now->curr_env.stop_pos < 32) ? 0 : 1; + reg2_env_nonzero = (tones_next->curr_env.start_pos >= 32) ? 0 : 1; + + /* synthesize waves for both overlapping regions */ + if (tones_now->num_wavs && reg1_env_nonzero) { + waves_synth(ch_unit->waves_info_prev, tones_now, &tones_now->curr_env, + ch_unit->waves_info_prev->invert_phase[sb] & ch_num, + 128, wavreg1); + } + + if (tones_next->num_wavs && reg2_env_nonzero) { + waves_synth(ch_unit->waves_info, tones_next, &tones_next->curr_env, + ch_unit->waves_info->invert_phase[sb] & ch_num, 0, wavreg2); + } + + /* Hann windowing for non-faded wave signals */ + if (tones_now->num_wavs && tones_next->num_wavs && + reg1_env_nonzero && reg2_env_nonzero) { + vector_fmul(wavreg1, wavreg1, &hann_window[128], 128); + vector_fmul(wavreg2, wavreg2, hann_window, 128); + } else { + if (tones_now->num_wavs && !tones_now->curr_env.has_stop_point) + vector_fmul(wavreg1, wavreg1, &hann_window[128], 128); + + if (tones_next->num_wavs && !tones_next->curr_env.has_start_point) + vector_fmul(wavreg2, wavreg2, hann_window, 128); + } + + /* Overlap and subtract to get residual */ + for (i = 0; i < 128; i++) { + out[i] -= wavreg1[i] + wavreg2[i]; + } +} diff --git a/src/atrac/at3p/ghasend_tool.cpp b/src/atrac/at3p/ghasend_tool.cpp new file mode 100644 index 0000000..c6be7c5 --- /dev/null +++ b/src/atrac/at3p/ghasend_tool.cpp @@ -0,0 +1,90 @@ +#include "at3p_bitstream.h" +#include "oma.h" + +#include <string> +#include <iostream> + +using namespace std; + +void process(const string& in, NAtracDEnc::TAt3PBitStream* bs) { + size_t pos = 0; + int cur_num_pos = 0; + enum { + TAB, + NUM, + ERR, + } state = NUM; + + std::vector<int> nums; + + while (pos < in.size()) { + switch (state) { + case TAB: + if (in[pos] == '\t') { + break; + } else if (('0' <= in[pos] && in[pos] <= '9') || in[pos] == '-') { + state = NUM; + cur_num_pos = pos; + break; + } else { + fprintf(stderr, "TAB state: %s\n", &in[pos]); + abort(); + } + case NUM: + if (in[pos] == '\t') { + nums.push_back(stoi(in.substr(cur_num_pos, pos - cur_num_pos))); + state = TAB; + break; + } else if (pos == in.size() - 1) { + nums.push_back(stoi(in.substr(cur_num_pos))); + break; + } else if (('0' <= in[pos] && in[pos] <= '9') || in[pos] == '-') { + break; + } else { + fprintf(stderr, "NUM state: %s\n", &in[pos]); + abort(); + } + case ERR: + abort(); + } + pos++; + } + + if (nums.size() != 3) + return; + + std::cerr << "gen: " << nums[0] << '\t' << nums[1] << '\t' << nums[2] << std::endl; + + NAtracDEnc::TAt3PGhaData frame; + frame.NumToneBands = 1; + frame.Waves[0].WaveParams.push_back(NAtracDEnc::TAt3PGhaData::TWaveParam{(uint32_t)nums[1], 53, 0, 0}); + frame.Waves[0].WaveSbInfos.resize(1); + frame.Waves[0].WaveSbInfos[0].WaveIndex = 0; + frame.Waves[0].WaveSbInfos[0].WaveNums = 1; + frame.Waves[0].WaveSbInfos[0].Envelope = {(uint32_t)nums[0], (uint32_t)nums[2]}; + + //bs->WriteFrame(1, &frame); +} + +int main(int argc, char** argv) { + if (argc != 2) + return -1; + + string path(argv[1]); + + std::unique_ptr<TOma> out(new TOma(path, + "test", + 1, + 1, OMAC_ID_ATRAC3PLUS, + 2048, + false)); + + NAtracDEnc::TAt3PBitStream bs(out.get(), 2048); + + cout << "output: " << path << endl; + string textline; + while (getline(cin, textline)) { + process(textline, &bs); + } + return 0; +} diff --git a/src/atrac/atrac3plus_pqf/ut/ipqf_ut.cpp b/src/atrac/atrac3plus_pqf/ut/ipqf_ut.cpp index 4492cb9..3344a00 100644 --- a/src/atrac/atrac3plus_pqf/ut/ipqf_ut.cpp +++ b/src/atrac/atrac3plus_pqf/ut/ipqf_ut.cpp @@ -133,6 +133,8 @@ TEST(pqf, DC_Short) { for (int i = 368; i < 2048; i++) { EXPECT_NEAR(tmp[i], x[i], err); } + + at3plus_pqf_free_a_ctx(actx); } TEST(pqf, DC_Long) { @@ -160,6 +162,8 @@ TEST(pqf, DC_Long) { for (int i = 368; i < 4096; i++) { EXPECT_NEAR(tmp[i], x[i-368], err); } + + at3plus_pqf_free_a_ctx(actx); } TEST(pqf, Seq_Short) { @@ -187,6 +191,8 @@ TEST(pqf, Seq_Short) { for (int i = 368; i < 2048; i++) { EXPECT_NEAR(tmp[i], x[i - 368], err); } + + at3plus_pqf_free_a_ctx(actx); } TEST(pqf, Seq_Long) { @@ -213,6 +219,8 @@ TEST(pqf, Seq_Long) { for (int i = 368; i < 4096; i++) { EXPECT_NEAR(tmp[i], x[i-368], err); } + + at3plus_pqf_free_a_ctx(actx); } TEST(pqf, Chirp_Short) { @@ -238,6 +246,8 @@ TEST(pqf, Chirp_Short) { for (int i = 368; i < 2048; i++) { EXPECT_NEAR(tmp[i], x[i - 368], err); } + + at3plus_pqf_free_a_ctx(actx); } TEST(pqf, Chirp_Long) { @@ -264,6 +274,8 @@ TEST(pqf, Chirp_Long) { for (int i = 368; i < 4096; i++) { EXPECT_NEAR(tmp[i], x[i-368], err); } + + at3plus_pqf_free_a_ctx(actx); } TEST(pqf, Noise_Long) { @@ -290,6 +302,8 @@ TEST(pqf, Noise_Long) { for (int i = 368; i < 4096; i++) { EXPECT_NEAR(tmp[i], x[i-368], err); } + + at3plus_pqf_free_a_ctx(actx); } diff --git a/src/atrac/atrac_scale.cpp b/src/atrac/atrac_scale.cpp index 5a75bdd..b3cb324 100644 --- a/src/atrac/atrac_scale.cpp +++ b/src/atrac/atrac_scale.cpp @@ -19,6 +19,7 @@ #include "atrac_scale.h" #include "atrac1.h" #include "atrac3.h" +#include "atrac/at3p/at3p_tables.h" #include "util.h" #include <cmath> #include <iostream> @@ -193,4 +194,7 @@ class TScaler<NAtrac1::TAtrac1Data>; template class TScaler<NAtrac3::TAtrac3Data>; +template +class TScaler<NAt3p::TScaleTable>; + } //namespace NAtracDEnc diff --git a/src/atrac1denc.cpp b/src/atrac1denc.cpp index 4ac10c1..a6e2d45 100644 --- a/src/atrac1denc.cpp +++ b/src/atrac1denc.cpp @@ -159,7 +159,7 @@ TPCMEngine::TProcessLambda TAtrac1Decoder::GetLambda() { data[i * srcChannels + channel] = sum[i]; } } - + return TPCMEngine::EProcessResult::PROCESSED; }; } @@ -236,6 +236,8 @@ TPCMEngine::TProcessLambda TAtrac1Encoder::GetLambda() { for (uint32_t channel = 0; channel < srcChannels; channel++) { bitAlloc[channel]->Write(Scaler.ScaleFrame((*buf)[channel].Specs, blockSz[channel]), blockSz[channel], Loudness / LoudFactor); } + + return TPCMEngine::EProcessResult::PROCESSED; }; } diff --git a/src/atrac1denc.h b/src/atrac1denc.h index 9756ee2..525596e 100644 --- a/src/atrac1denc.h +++ b/src/atrac1denc.h @@ -30,11 +30,6 @@ namespace NAtracDEnc { -enum EMode { - E_ENCODE = 1, - E_DECODE = 2, - E_ATRAC3 = 4 -}; class TAtrac1MDCT { NMDCT::TMDCT<512> Mdct512; diff --git a/src/atrac3denc.cpp b/src/atrac3denc.cpp index 3dc9027..3a1ad18 100644 --- a/src/atrac3denc.cpp +++ b/src/atrac3denc.cpp @@ -371,6 +371,7 @@ TPCMEngine::TProcessLambda TAtrac3Encoder::GetLambda() } bitStreamWriter->WriteSoundUnit(SingleChannelElements, Loudness); + return TPCMEngine::EProcessResult::PROCESSED; }; } diff --git a/src/atrac3p.h b/src/atrac3p.h new file mode 100644 index 0000000..779929d --- /dev/null +++ b/src/atrac3p.h @@ -0,0 +1,55 @@ +/* + * 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 "config.h" +#include "pcmengin.h" +#include "compressed_io.h" + +namespace NAtracDEnc { + +class TAt3PEnc : public IProcessor { +public: + struct TSettings { + enum GhaProcessingFlags : uint8_t { + GHA_PASS_INPUT = 1, + GHA_WRITE_TONAL = 1 << 1, + GHA_WRITE_RESIUDAL = 1 << 2, + + GHA_ENABLED = GHA_PASS_INPUT | GHA_WRITE_TONAL | GHA_WRITE_RESIUDAL + }; + uint8_t UseGha; + + TSettings() + : UseGha(GHA_ENABLED) + {} + }; + TAt3PEnc(TCompressedOutputPtr&& out, int channels, TSettings settings); + TPCMEngine::TProcessLambda GetLambda() override; + static constexpr int NumSamples = 2048; + static void ParseAdvancedOpt(const char* opt, TSettings& settings); + +private: + TCompressedOutputPtr Out; + int Channels; + class TImpl; + std::unique_ptr<TImpl> Impl; +}; + +} diff --git a/src/lib/bs_encode/encode.cpp b/src/lib/bs_encode/encode.cpp new file mode 100644 index 0000000..230ae0f --- /dev/null +++ b/src/lib/bs_encode/encode.cpp @@ -0,0 +1,184 @@ +/* + * 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 "encode.h" + +#include <iostream> + +namespace NAtracDEnc { + +using NBitStream::TBitStream; + +class TBitStreamEncoder::TImpl : public TBitAllocHandler { +public: + explicit TImpl(std::vector<IBitStreamPartEncoder::TPtr>&& encoders); + void DoStart(size_t targetBits, float minLambda, float maxLambda) noexcept; + float DoContinue() noexcept; + void DoSubmit(size_t gotBits) noexcept; + bool DoCheck(size_t gotBits) const noexcept; + void DoRun(void* frameData, TBitStream& bs); + uint32_t DoGetCurGlobalConsumption() const noexcept; +private: + std::vector<IBitStreamPartEncoder::TPtr> Encoders; + size_t CurEncPos; + size_t RepeatEncPos; + + size_t TargetBits; + float MinLambda; + float MaxLambda; + float CurLambda; + float LastLambda; + + bool NeedRepeat = false; +}; + +TBitStreamEncoder::TImpl::TImpl(std::vector<IBitStreamPartEncoder::TPtr>&& encoders) + : Encoders(std::move(encoders)) + , CurEncPos(0) + , RepeatEncPos(0) +{ +} + +void TBitStreamEncoder::TImpl::DoStart(size_t targetBits, float minLambda, float maxLambda) noexcept +{ + TargetBits = targetBits; + MinLambda = minLambda; + MaxLambda = maxLambda; +} + +float TBitStreamEncoder::TImpl::DoContinue() noexcept +{ + if (MaxLambda <= MinLambda) { + return LastLambda; + } + + CurLambda = (MaxLambda + MinLambda) / 2.0; + RepeatEncPos = CurEncPos; + return CurLambda; +} + +void TBitStreamEncoder::TImpl::DoSubmit(size_t gotBits) noexcept +{ + if (MaxLambda <= MinLambda) { + NeedRepeat = false; + } else { + if (gotBits < TargetBits) { + LastLambda = CurLambda; + MaxLambda = CurLambda - 0.01f; + NeedRepeat = true; + } else if (gotBits > TargetBits) { + MinLambda = CurLambda + 0.01f; + NeedRepeat = true; + } else { + NeedRepeat = false; + } + } +} + +bool TBitStreamEncoder::TImpl::DoCheck(size_t gotBits) const noexcept +{ + return gotBits < TargetBits; +} + +void TBitStreamEncoder::TImpl::DoRun(void* frameData, TBitStream& bs) +{ + bool cont = false; + do { + for (CurEncPos = RepeatEncPos; CurEncPos < Encoders.size(); CurEncPos++) { + auto status = Encoders[CurEncPos]->Encode(frameData, *this); + if (NeedRepeat) { + NeedRepeat = false; + cont = true; + break; + } else { + cont = false; + } + if (status == IBitStreamPartEncoder::EStatus::Repeat) { + cont = true; + for (size_t i = 0; i < CurEncPos; i++) { + Encoders[i]->Reset(); + } + RepeatEncPos = 0; + break; + } + } + } while (cont); + + for (size_t i = 0; i < Encoders.size(); i++) { + Encoders[i]->Dump(bs); + } +} + +uint32_t TBitStreamEncoder::TImpl::DoGetCurGlobalConsumption() const noexcept +{ + uint32_t consumption = 0; + for (size_t i = 0; i < CurEncPos; i++) { + consumption += Encoders[i]->GetConsumption(); + } + return consumption; +} + +///// + +TBitStreamEncoder::TBitStreamEncoder(std::vector<IBitStreamPartEncoder::TPtr>&& encoders) + : Impl(new TBitStreamEncoder::TImpl(std::move(encoders))) +{} + +TBitStreamEncoder::~TBitStreamEncoder() +{ + delete Impl; +} + +void TBitStreamEncoder::Do(void* frameData, TBitStream& bs) +{ + Impl->DoRun(frameData, bs); +} + +///// + +void TBitAllocHandler::Start(size_t targetBits, float minLambda, float maxLambda) noexcept +{ + TBitStreamEncoder::TImpl* self = static_cast<TBitStreamEncoder::TImpl*>(this); + self->DoStart(targetBits, minLambda, maxLambda); +} + +float TBitAllocHandler::Continue() noexcept +{ + TBitStreamEncoder::TImpl* self = static_cast<TBitStreamEncoder::TImpl*>(this); + return self->DoContinue(); +} + +void TBitAllocHandler::Submit(size_t gotBits) noexcept +{ + TBitStreamEncoder::TImpl* self = static_cast<TBitStreamEncoder::TImpl*>(this); + self->DoSubmit(gotBits); +} + +bool TBitAllocHandler::Check(size_t gotBits) const noexcept +{ + const TBitStreamEncoder::TImpl* self = static_cast<const TBitStreamEncoder::TImpl*>(this); + return self->DoCheck(gotBits); +} + +uint32_t TBitAllocHandler::GetCurGlobalConsumption() const noexcept +{ + const TBitStreamEncoder::TImpl* self = static_cast<const TBitStreamEncoder::TImpl*>(this); + return self->DoGetCurGlobalConsumption(); +} + +} diff --git a/src/lib/bs_encode/encode.h b/src/lib/bs_encode/encode.h new file mode 100644 index 0000000..3302d10 --- /dev/null +++ b/src/lib/bs_encode/encode.h @@ -0,0 +1,71 @@ +/* + * 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 <memory> +#include <vector> +#include <functional> + +namespace NBitStream { +class TBitStream; +} + +namespace NAtracDEnc { + +class TBitAllocHandler { +public: + void Start(size_t targetBits, float minLambda, float maxLambda) noexcept; + float Continue() noexcept; + bool Check(size_t gitBits) const noexcept; + void Submit(size_t gotBits) noexcept; + + // Returns consumption of all previous encoded parts (except part from this method called) + uint32_t GetCurGlobalConsumption() const noexcept; +}; + +class IBitStreamPartEncoder { +public: + using TPtr = std::unique_ptr<IBitStreamPartEncoder>; + enum class EStatus { + Ok, // Ok, go to the next stage + Repeat, // Repeat from first stage + }; + + virtual ~IBitStreamPartEncoder() = default; + virtual EStatus Encode(void* frameData, TBitAllocHandler& ba) = 0; + virtual void Dump(NBitStream::TBitStream& bs) = 0; + virtual void Reset() noexcept {}; + virtual uint32_t GetConsumption() const noexcept = 0; +}; + +class TBitStreamEncoder { +public: + class TImpl; + explicit TBitStreamEncoder(std::vector<IBitStreamPartEncoder::TPtr>&& encoders); + ~TBitStreamEncoder(); + + void Do(void* frameData, NBitStream::TBitStream& bs); + TBitStreamEncoder(const TBitStreamEncoder&) = delete; + TBitStreamEncoder& operator=(const TBitStreamEncoder&) = delete; +private: + std::vector<IBitStreamPartEncoder::TPtr> Encoders; + TImpl* Impl; +}; + +} diff --git a/src/lib/bs_encode/encode_ut.cpp b/src/lib/bs_encode/encode_ut.cpp new file mode 100644 index 0000000..39f0ff1 --- /dev/null +++ b/src/lib/bs_encode/encode_ut.cpp @@ -0,0 +1,178 @@ +/* + * 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 "encode.h" +#include <gtest/gtest.h> +#include <cmath> +#include <memory> +#include <bitstream/bitstream.h> + +using namespace NAtracDEnc; + +static size_t SomeBitFn1(float lambda) { + return sqrt(lambda * (-1.0f)) * 300; +} + +static size_t SomeBitFn2(float lambda) { + return 1 + (SomeBitFn1(lambda) & (~(size_t)7)); +} + +class TPartEncoder1 : public IBitStreamPartEncoder { +public: + explicit TPartEncoder1(size_t expCalls) + : ExpCalls(expCalls) + {} + + EStatus Encode(void* frameData, TBitAllocHandler& ba) override { + EncCalls++; + ba.Start(1000, -15, -1); + return EStatus::Ok; + } + + void Dump(NBitStream::TBitStream& bs) override { + EXPECT_EQ(EncCalls, ExpCalls); + } + + uint32_t GetConsumption() const noexcept override { + return 0; + } +private: + const size_t ExpCalls; + size_t EncCalls = 0; +}; + +template<size_t (*F)(float)> +class TPartEncoder2 : public IBitStreamPartEncoder { +public: + explicit TPartEncoder2(size_t expCalls) + : ExpCalls(expCalls) + {} + + EStatus Encode(void* frameData, TBitAllocHandler& ba) override { + EncCalls++; + auto lambda = ba.Continue(); + auto bits = F(lambda); + ba.Submit(bits); + Bits = bits; + return EStatus::Ok; + } + + void Dump(NBitStream::TBitStream& bs) override { + EXPECT_EQ(EncCalls, ExpCalls); + for (size_t i = 0; i < Bits; i++) { + bs.Write(1, 1); + } + } + + uint32_t GetConsumption() const noexcept override { + return 1 * Bits; + } +private: + const size_t ExpCalls; + size_t EncCalls = 0; + size_t Bits = 0; +}; + +class TPartEncoder3 : public IBitStreamPartEncoder { +public: + explicit TPartEncoder3(size_t expCalls) + : ExpCalls(expCalls) + {} + + EStatus Encode(void* frameData, TBitAllocHandler& ba) override { + EncCalls++; + return EStatus::Ok; + } + + void Dump(NBitStream::TBitStream& bs) override { + EXPECT_EQ(EncCalls, ExpCalls); + } + + uint32_t GetConsumption() const noexcept override { + return 0; + } +private: + const size_t ExpCalls; + size_t EncCalls = 0; +}; + +class TPartEncoder4 : public IBitStreamPartEncoder { +public: + TPartEncoder4() = default; + + EStatus Encode(void* frameData, TBitAllocHandler& ba) override { + if (EncCalls == 0) { + EncCalls++; + return EStatus::Repeat; + } + + return EStatus::Ok; + } + + void Dump(NBitStream::TBitStream& bs) override { + EXPECT_EQ(EncCalls, 1); + } + + uint32_t GetConsumption() const noexcept override { + return 0; + } +private: + size_t EncCalls = 0; +}; + +TEST(BsEncode, SimpleAlloc) { + std::vector<IBitStreamPartEncoder::TPtr> encoders; + encoders.emplace_back(std::make_unique<TPartEncoder1>(1)); + encoders.emplace_back(std::make_unique<TPartEncoder2<SomeBitFn1>>(8)); + encoders.emplace_back(std::make_unique<TPartEncoder3>(1)); + + NBitStream::TBitStream bs; + TBitStreamEncoder encoder(std::move(encoders)); + encoder.Do(nullptr, bs); + + EXPECT_EQ(bs.GetSizeInBits(), 1000); +} + +TEST(BsEncode, AllocWithRepeat) { + std::vector<IBitStreamPartEncoder::TPtr> encoders; + encoders.emplace_back(std::make_unique<TPartEncoder1>(2)); + encoders.emplace_back(std::make_unique<TPartEncoder2<SomeBitFn1>>(16)); + encoders.emplace_back(std::make_unique<TPartEncoder3>(2)); + encoders.emplace_back(std::make_unique<TPartEncoder4>()); + + NBitStream::TBitStream bs; + TBitStreamEncoder encoder(std::move(encoders)); + encoder.Do(nullptr, bs); + + EXPECT_EQ(bs.GetSizeInBits(), 1000); +} + +TEST(BsEncode, NotExactAlloc) { + std::vector<IBitStreamPartEncoder::TPtr> encoders; + encoders.emplace_back(std::make_unique<TPartEncoder1>(1)); + encoders.emplace_back(std::make_unique<TPartEncoder2<SomeBitFn2>>(11)); + encoders.emplace_back(std::make_unique<TPartEncoder3>(1)); + + NBitStream::TBitStream bs; + TBitStreamEncoder encoder(std::move(encoders)); + encoder.Do(nullptr, bs); + + EXPECT_EQ(bs.GetSizeInBits(), 993); +} + + diff --git a/src/lib/fft/kissfft_impl/kiss_fft.h b/src/lib/fft/kissfft_impl/kiss_fft.h index 7786eb6..64c50f4 100644 --- a/src/lib/fft/kissfft_impl/kiss_fft.h +++ b/src/lib/fft/kissfft_impl/kiss_fft.h @@ -44,7 +44,6 @@ extern "C" { #else # ifndef kiss_fft_scalar /* default is float */ -# error "wrong type" # define kiss_fft_scalar float # endif #endif diff --git a/src/lib/fft/kissfft_impl/tools/kiss_fftr.c b/src/lib/fft/kissfft_impl/tools/kiss_fftr.c new file mode 100644 index 0000000..8102132 --- /dev/null +++ b/src/lib/fft/kissfft_impl/tools/kiss_fftr.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ + +#include "kiss_fftr.h" +#include "_kiss_fft_guts.h" + +struct kiss_fftr_state{ + kiss_fft_cfg substate; + kiss_fft_cpx * tmpbuf; + kiss_fft_cpx * super_twiddles; +#ifdef USE_SIMD + void * pad; +#endif +}; + +kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) +{ + int i; + kiss_fftr_cfg st = NULL; + size_t subsize = 0, memneeded; + + if (nfft & 1) { + fprintf(stderr,"Real FFT optimization must be even.\n"); + return NULL; + } + nfft >>= 1; + + kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize); + memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2); + + if (lenmem == NULL) { + st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded); + } else { + if (*lenmem >= memneeded) + st = (kiss_fftr_cfg) mem; + *lenmem = memneeded; + } + if (!st) + return NULL; + + st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */ + st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize); + st->super_twiddles = st->tmpbuf + nfft; + kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize); + + for (i = 0; i < nfft/2; ++i) { + double phase = + -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5); + if (inverse_fft) + phase *= -1; + kf_cexp (st->super_twiddles+i,phase); + } + return st; +} + +void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata) +{ + /* input buffer timedata is stored row-wise */ + int k,ncfft; + kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc; + + if ( st->substate->inverse) { + fprintf(stderr,"kiss fft usage error: improper alloc\n"); + exit(1); + } + + ncfft = st->substate->nfft; + + /*perform the parallel fft of two real signals packed in real,imag*/ + kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf ); + /* The real part of the DC element of the frequency spectrum in st->tmpbuf + * contains the sum of the even-numbered elements of the input time sequence + * The imag part is the sum of the odd-numbered elements + * + * The sum of tdc.r and tdc.i is the sum of the input time sequence. + * yielding DC of input time sequence + * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... + * yielding Nyquist bin of input time sequence + */ + + tdc.r = st->tmpbuf[0].r; + tdc.i = st->tmpbuf[0].i; + C_FIXDIV(tdc,2); + CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i); + CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i); + freqdata[0].r = tdc.r + tdc.i; + freqdata[ncfft].r = tdc.r - tdc.i; +#ifdef USE_SIMD + freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0); +#else + freqdata[ncfft].i = freqdata[0].i = 0; +#endif + + for ( k=1;k <= ncfft/2 ; ++k ) { + fpk = st->tmpbuf[k]; + fpnk.r = st->tmpbuf[ncfft-k].r; + fpnk.i = - st->tmpbuf[ncfft-k].i; + C_FIXDIV(fpk,2); + C_FIXDIV(fpnk,2); + + C_ADD( f1k, fpk , fpnk ); + C_SUB( f2k, fpk , fpnk ); + C_MUL( tw , f2k , st->super_twiddles[k-1]); + + freqdata[k].r = HALF_OF(f1k.r + tw.r); + freqdata[k].i = HALF_OF(f1k.i + tw.i); + freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r); + freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i); + } +} + +void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata) +{ + /* input buffer timedata is stored row-wise */ + int k, ncfft; + + if (st->substate->inverse == 0) { + fprintf (stderr, "kiss fft usage error: improper alloc\n"); + exit (1); + } + + ncfft = st->substate->nfft; + + st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r; + st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r; + C_FIXDIV(st->tmpbuf[0],2); + + for (k = 1; k <= ncfft / 2; ++k) { + kiss_fft_cpx fk, fnkc, fek, fok, tmp; + fk = freqdata[k]; + fnkc.r = freqdata[ncfft - k].r; + fnkc.i = -freqdata[ncfft - k].i; + C_FIXDIV( fk , 2 ); + C_FIXDIV( fnkc , 2 ); + + C_ADD (fek, fk, fnkc); + C_SUB (tmp, fk, fnkc); + C_MUL (fok, tmp, st->super_twiddles[k-1]); + C_ADD (st->tmpbuf[k], fek, fok); + C_SUB (st->tmpbuf[ncfft - k], fek, fok); +#ifdef USE_SIMD + st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0); +#else + st->tmpbuf[ncfft - k].i *= -1; +#endif + } + kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata); +} diff --git a/src/lib/fft/kissfft_impl/tools/kiss_fftr.h b/src/lib/fft/kissfft_impl/tools/kiss_fftr.h new file mode 100644 index 0000000..588948d --- /dev/null +++ b/src/lib/fft/kissfft_impl/tools/kiss_fftr.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ + +#ifndef KISS_FTR_H +#define KISS_FTR_H + +#include "kiss_fft.h" +#ifdef __cplusplus +extern "C" { +#endif + + +/* + + Real optimized version can save about 45% cpu time vs. complex fft of a real seq. + + + + */ + +typedef struct kiss_fftr_state *kiss_fftr_cfg; + + +kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); +/* + nfft must be even + + If you don't care to allocate space, use mem = lenmem = NULL +*/ + + +void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); +/* + input timedata has nfft scalar points + output freqdata has nfft/2+1 complex points +*/ + +void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); +/* + input freqdata has nfft/2+1 complex points + output timedata has nfft scalar points +*/ + +#define kiss_fftr_free KISS_FFT_FREE + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/lib/libgha b/src/lib/libgha new file mode 160000 +Subproject 0c3e65863f31459c4be8a12051a8d6260cb1d30 diff --git a/src/main.cpp b/src/main.cpp index faf88e9..861495f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,6 +32,7 @@ #include "config.h" #include "atrac1denc.h" #include "atrac3denc.h" +#include "atrac3p.h" #ifdef PLATFORM_WINDOWS #include <windows.h> @@ -89,6 +90,13 @@ static int checkedStoi(const char* data, int min, int max, int def) } } +enum EMode { + E_ENCODE = 1, + E_DECODE = 2, + E_ATRAC3 = 4, + E_ATRAC3PLUS = 8 +}; + enum EOptions { O_ENCODE = 'e', @@ -102,6 +110,7 @@ enum EOptions O_NOSTDOUT = '4', O_NOTONAL = 5, O_NOGAINCONTROL = 6, + O_ADVANCED_OPT = 7, }; static void CheckInputFormat(const TWav* p) @@ -243,9 +252,68 @@ static void PrepareAtrac3Encoder(const string& inFile, atracProcessor->reset(new TAtrac3Encoder(std::move(omaIO), std::move(encoderSettings))); } +static void PrepareAtrac3PEncoder(const string& inFile, + const string& outFile, + const bool noStdOut, + int numChannels, + uint64_t* totalSamples, + const TWavPtr& wavIO, + TPcmEnginePtr* pcmEngine, + TAtracProcessorPtr* atracProcessor, + const char* advancedOpt) +{ + *totalSamples = wavIO->GetTotalSamples(); + const uint64_t numFrames = (*totalSamples) / 2048; + if (numFrames >= UINT32_MAX) { + std::cerr << "Number of input samples exceeds output format limitation," + "the result will be incorrect" << std::endl; + } + + const string ext = GetFileExt(outFile); + + TCompressedOutputPtr omaIO; + + string contName; + if (ext == "wav" || ext == "at3") { + throw std::runtime_error("Not implemented"); + } else if (ext == "rm") { + throw std::runtime_error("RealMedia container is not supported for ATRAC3PLUS"); + } else { + contName = "OMA"; + omaIO.reset(new TOma(outFile, + "test", + numChannels, + (int32_t)numFrames, OMAC_ID_ATRAC3PLUS, + 2048, + false)); + } + + if (!noStdOut) + cout << "Input:\n Filename: " << inFile + << "\n Channels: " << (int)numChannels + << "\n SampleRate: " << wavIO->GetSampleRate() + << "\n Duration (sec): " << *totalSamples / wavIO->GetSampleRate() + << "\nOutput:\n Filename: " << outFile + << "\n Codec: ATRAC3Plus" + << "\n Container: " << contName + //<< "\n Bitrate: " << encoderSettings.ConteinerParams->Bitrate + << endl; + + pcmEngine->reset(new TPCMEngine(4096, + numChannels, + TPCMEngine::TReaderPtr(wavIO->GetPCMReader()))); + TAt3PEnc::TSettings settings; + if (advancedOpt) { + TAt3PEnc::ParseAdvancedOpt(advancedOpt, settings); + } + atracProcessor->reset(new TAt3PEnc(std::move(omaIO), numChannels, settings)); +} + + int main_(int argc, char* const* argv) { const char* myName = argv[0]; + const char* advancedOpt = nullptr; static struct option longopts[] = { { "encode", optional_argument, NULL, O_ENCODE }, { "decode", no_argument, NULL, O_DECODE }, @@ -256,6 +324,7 @@ int main_(int argc, char* const* argv) { "notransient", optional_argument, NULL, O_NOTRANSIENT}, { "nostdout", no_argument, NULL, O_NOSTDOUT}, { "nogaincontrol", no_argument, NULL, O_NOGAINCONTROL}, + { "advanced", required_argument, NULL, O_ADVANCED_OPT}, { NULL, 0, NULL, 0} }; @@ -282,6 +351,8 @@ int main_(int argc, char* const* argv) } else if (strcmp(optarg, "atrac3_lp4") == 0) { mode |= E_ATRAC3; bitrate = 64; + } else if (strcmp(optarg, "atrac3plus") == 0) { + mode |= E_ATRAC3PLUS; } else if (strcmp(optarg, "atrac1") == 0) { // this is the default } else { @@ -336,6 +407,9 @@ int main_(int argc, char* const* argv) case O_NOGAINCONTROL: noGainControl = true; break; + case O_ADVANCED_OPT: + advancedOpt = optarg; + break; default: printUsage(myName); return 1; @@ -399,6 +473,14 @@ int main_(int argc, char* const* argv) pcmFrameSz = TAtrac3Data::NumSamples;; } break; + case (E_ENCODE | E_ATRAC3PLUS): + { + wavIO = OpenWavFile(inFile); + PrepareAtrac3PEncoder(inFile, outFile, noStdOut, wavIO->GetChannelNum(), + &totalSamples, wavIO, &pcmEngine, &atracProcessor, advancedOpt); + pcmFrameSz = 2048; + } + break; default: { throw std::runtime_error("Processing mode was not specified"); diff --git a/src/oma.cpp b/src/oma.cpp index bef90dd..7ef06c1 100644 --- a/src/oma.cpp +++ b/src/oma.cpp @@ -24,12 +24,14 @@ using std::string; using std::vector; using std::unique_ptr; -TOma::TOma(const string& filename, const string&, size_t /*numChannel*/, +TOma::TOma(const string& filename, const string&, size_t numChannel, uint32_t /*numFrames*/, int cid, uint32_t framesize, bool jointStereo) { oma_info_t info; info.codec = cid; info.samplerate = 44100; - info.channel_format = jointStereo ? OMA_STEREO_JS : OMA_STEREO; + info.channel_format = (cid == OMAC_ID_ATRAC3) + ? (jointStereo ? OMA_STEREO_JS : OMA_STEREO) + : (numChannel == 1 ? OMA_MONO : OMA_STEREO); info.framesize = framesize; File = oma_open(filename.c_str(), OMAM_W, &info); if (!File) diff --git a/src/pcmengin.h b/src/pcmengin.h index d5d9644..8d1db3f 100644 --- a/src/pcmengin.h +++ b/src/pcmengin.h @@ -28,6 +28,9 @@ #include <assert.h> #include <string.h> +class TNoDataToRead : public std::exception { +}; + class TPCMBufferTooSmall : public std::exception { virtual const char* what() const throw() { return "PCM buffer too small"; @@ -99,7 +102,7 @@ class IPCMWriter { class IPCMReader { public: - virtual void Read(TPCMBuffer& data , const uint32_t size) const = 0; + virtual bool Read(TPCMBuffer& data , const uint32_t size) const = 0; IPCMReader() {}; virtual ~IPCMReader() {}; }; @@ -116,6 +119,7 @@ private: TWriterPtr Writer; TReaderPtr Reader; uint64_t Processed = 0; + uint64_t ToDrain = 0; public: TPCMEngine(uint16_t bufSize, size_t numChannels) : Buffer(bufSize, numChannels) { @@ -137,23 +141,45 @@ public: , Reader(std::move(reader)) { } - typedef std::function<void(float* data, const ProcessMeta& meta)> TProcessLambda; + enum class EProcessResult { + LOOK_AHEAD, // need more data to process + PROCESSED, + }; + + typedef std::function<EProcessResult(float* data, const ProcessMeta& meta)> TProcessLambda; uint64_t ApplyProcess(size_t step, TProcessLambda lambda) { if (step > Buffer.Size()) { throw TPCMBufferTooSmall(); } + bool drain = false; if (Reader) { const uint32_t sizeToRead = Buffer.Size(); - Reader->Read(Buffer, sizeToRead); + const bool ok = Reader->Read(Buffer, sizeToRead); + if (!ok) { + if (ToDrain) { + drain = true; + } else { + throw TNoDataToRead(); + } + } } size_t lastPos = 0; ProcessMeta meta = {Buffer.Channels()}; + for (size_t i = 0; i + step <= Buffer.Size(); i+=step) { - lambda(Buffer[i], meta); - lastPos = i + step; + auto res = lambda(Buffer[i], meta); + if (res == EProcessResult::PROCESSED) { + lastPos += step; + if (drain && ToDrain--) { + break; + } + } else { + assert(!drain); + ToDrain++; + } } assert(lastPos == Buffer.Size()); @@ -33,6 +33,11 @@ #define ASSERT(x) assert(x) #endif +#if defined(_MSC_VER) +# define atde_noinline __declspec(noinline) +#else +# define atde_noinline __attribute__((noinline)) +#endif template<class T> inline void SwapArray(T* p, const size_t len) { diff --git a/src/wav.cpp b/src/wav.cpp index f62bbb3..08bff51 100644 --- a/src/wav.cpp +++ b/src/wav.cpp @@ -51,10 +51,12 @@ IPCMReader* TWav::GetPCMReader() const { size_t read; if ((read = Impl->Read(data, size)) != size) { if (!read) - throw TNoDataToRead(); + return false; data.Zero(read, size - read); } + + return true; }); } @@ -28,18 +28,15 @@ class TFileAlreadyExists : public std::exception { }; -class TNoDataToRead : public std::exception { -}; - class TWavPcmReader : public IPCMReader { public: - typedef std::function<void(TPCMBuffer& data, const uint32_t size)> TLambda; + typedef std::function<bool(TPCMBuffer& data, const uint32_t size)> TLambda; TLambda Lambda; TWavPcmReader(TLambda lambda) : Lambda(lambda) {} - void Read(TPCMBuffer& data , const uint32_t size) const override { - Lambda(data, size); + bool Read(TPCMBuffer& data , const uint32_t size) const override { + return Lambda(data, size); } }; |