diff options
author | Daniil Cherednik <dan.cherednik@gmail.com> | 2016-03-13 09:49:33 +0300 |
---|---|---|
committer | Daniil Cherednik <dan.cherednik@gmail.com> | 2016-07-17 20:26:30 +0300 |
commit | 59818683fe2930ea4a7f898f43f47db94f437cc9 (patch) | |
tree | 03b6522e3a2e9926d2de4faabad6a700b35b90fd | |
parent | b4df8a7c2dd12eea27c8cc52bd52a1bb8c00943f (diff) | |
download | atracdenc-59818683fe2930ea4a7f898f43f47db94f437cc9.tar.gz |
Initial implementation of atrac3 stream (CLC and VLC)
- no psy, gc, tonal component encoding, js
-rw-r--r-- | src/CMakeLists.txt | 26 | ||||
-rw-r--r-- | src/atrac/atrac3.cpp | 23 | ||||
-rw-r--r-- | src/atrac/atrac3.h | 189 | ||||
-rw-r--r-- | src/atrac/atrac3_bitstream.cpp | 143 | ||||
-rw-r--r-- | src/atrac/atrac3_bitstream.h | 27 | ||||
-rw-r--r-- | src/atrac/atrac3_qmf.h | 23 | ||||
-rw-r--r-- | src/atrac/atrac_scale.cpp | 4 | ||||
-rw-r--r-- | src/atrac3denc.cpp | 73 | ||||
-rw-r--r-- | src/atrac3denc.h | 31 | ||||
-rw-r--r-- | src/atracdenc.cpp | 4 | ||||
-rw-r--r-- | src/atracdenc.h | 5 | ||||
-rw-r--r-- | src/main.cpp | 55 | ||||
-rw-r--r-- | src/oma.cpp | 42 | ||||
-rw-r--r-- | src/oma.h | 17 | ||||
-rw-r--r-- | src/oma/liboma/include/oma.h | 60 | ||||
-rw-r--r-- | src/oma/liboma/src/liboma.c | 265 | ||||
-rw-r--r-- | src/oma/liboma/src/oma_internal.h | 18 | ||||
-rw-r--r-- | src/oma/liboma/src/tools/omacp.c | 38 | ||||
-rw-r--r-- | src/oma/liboma/src/tools/omainfo.c | 25 | ||||
-rw-r--r-- | src/pcmengin.h | 14 |
20 files changed, 1060 insertions, 22 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fd7fe24..f7e0aac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,11 +8,29 @@ set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") INCLUDE(FindLibSndFile) include_directories(${LIBSNDFILE_INCLUDE_DIR}) +include_directories("oma/liboma/include") -set(SOURCE_LIB mdct/vorbis_impl/mdct.c) -set(SOURCE_EXE main.cpp wav.cpp aea.cpp transient_detector.cpp atracdenc.cpp bitstream/bitstream.cpp atrac/atrac1.cpp atrac/atrac1_dequantiser.cpp atrac/atrac_scale.cpp atrac/atrac1_bitalloc.cpp) +set(SOURCE_MDCT_LIB mdct/vorbis_impl/mdct.c) +set(SOURCE_OMA_LIB oma/liboma/src/liboma.c) +set(SOURCE_EXE + main.cpp + wav.cpp + aea.cpp + transient_detector.cpp + atracdenc.cpp + bitstream/bitstream.cpp + atrac/atrac1.cpp + atrac/atrac1_dequantiser.cpp + atrac/atrac_scale.cpp + atrac/atrac1_bitalloc.cpp + oma.cpp + atrac3denc.cpp + atrac/atrac3.cpp + atrac/atrac3_bitstream.cpp + ) -add_library(mdct_impl STATIC ${SOURCE_LIB}) +add_library(mdct_impl STATIC ${SOURCE_MDCT_LIB}) +add_library(oma STATIC ${SOURCE_OMA_LIB}) add_executable(atracdenc ${SOURCE_EXE}) -target_link_libraries(atracdenc mdct_impl ${SNDFILE_LIBRARIES}) +target_link_libraries(atracdenc mdct_impl oma ${SNDFILE_LIBRARIES}) diff --git a/src/atrac/atrac3.cpp b/src/atrac/atrac3.cpp new file mode 100644 index 0000000..54bf497 --- /dev/null +++ b/src/atrac/atrac3.cpp @@ -0,0 +1,23 @@ +#include "atrac3.h" +#include <algorithm> +constexpr uint32_t TAtrac3Data::BlockSizeTab[33]; +constexpr uint32_t TAtrac3Data::ClcLengthTab[8]; +constexpr double TAtrac3Data::MaxQuant[8]; +constexpr uint32_t TAtrac3Data::BlocksPerBand[4 + 1]; +constexpr uint32_t TAtrac3Data::SpecsPerBlock[33]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable1[HuffTable1Sz]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable2[HuffTable2Sz]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable3[HuffTable3Sz]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable5[HuffTable5Sz]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable6[HuffTable6Sz]; +constexpr TAtrac3Data::THuffEntry TAtrac3Data::HuffTable7[HuffTable7Sz]; +constexpr TAtrac3Data::THuffTablePair TAtrac3Data::HuffTables[7]; + +constexpr TContainerParams TAtrac3Data::ContainerParams[8]; +double TAtrac3Data::EncodeWindow[256] = {0}; +double TAtrac3Data::ScaleTable[64] = {0}; + +const TContainerParams* TAtrac3Data::GetContainerParamsForBitrate(uint32_t bitrate) { + std::cout << bitrate << std::endl; + return std::lower_bound(ContainerParams, ContainerParams+8, bitrate); +} diff --git a/src/atrac/atrac3.h b/src/atrac/atrac3.h new file mode 100644 index 0000000..01c8ae0 --- /dev/null +++ b/src/atrac/atrac3.h @@ -0,0 +1,189 @@ +#pragma once +#include <math.h> +#include <cstdint> +#include <vector> +#include <cassert> +#include <iostream> + + +struct TContainerParams { + const uint32_t Bitrate; + const uint16_t FrameSz; + const bool Js; +}; + +inline bool operator< (const TContainerParams& x, const TContainerParams& y) +{ + return x.Bitrate < y.Bitrate; +} +inline bool operator> (const TContainerParams& x, const TContainerParams& y) +{ + return x.Bitrate > y.Bitrate; +} +inline bool operator< (const TContainerParams& x, const unsigned int y) +{ + return x.Bitrate < y; +} +inline bool operator> (const TContainerParams& x, const unsigned int y) +{ + return x.Bitrate > y; +} + +class TAtrac3Data { +protected: + static double ScaleTable[64]; + static double EncodeWindow[256]; + + static const uint32_t NumSamples=1024; + static const uint32_t frameSz = 152; + static constexpr double MaxQuant[8] = { + 0.0, 1.5, 2.5, 3.5, + 4.5, 7.5, 15.5, 31.5 + }; + static constexpr uint32_t BlockSizeTab[33] = { + 0, 8, 16, 24, 32, 40, 48, 56, + 64, 80, 96, 112, 128, 144, 160, 176, + 192, 224, 256, 288, 320, 352, 384, 416, + 448, 480, 512, 576, 640, 704, 768, 896, + 1024 + }; + static constexpr uint32_t const * const SpecsStartShort = &BlockSizeTab[0]; + + static constexpr uint32_t const * const SpecsStartLong = &BlockSizeTab[0]; + static constexpr uint32_t ClcLengthTab[8] = { 0, 4, 3, 3, 4, 4, 5, 6 }; + static const int NumQMF = 4; + + static constexpr uint32_t BlocksPerBand[NumQMF + 1] = {0, 18, 26, 30, 32}; + static constexpr uint32_t SpecsPerBlock[33] = { + 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 64, 64, 64, 64, 128, 128, + 128 + }; + struct THuffEntry { + const uint8_t Code; + const uint8_t Bits; + }; + static constexpr uint8_t HuffTable1Sz = 9; + static constexpr THuffEntry HuffTable1[HuffTable1Sz] = { + { 0x0, 1 }, + { 0x4, 3 }, { 0x5, 3 }, + { 0xC, 4 }, { 0xD, 4 }, + { 0x1C, 5 }, { 0x1D, 5 }, { 0x1E, 5 }, { 0x1F, 5 } + }; + static constexpr uint8_t HuffTable2Sz = 5; + static constexpr THuffEntry HuffTable2[HuffTable2Sz] = { + { 0x0, 1 }, + { 0x4, 3 }, { 0x5, 3 }, { 0x6, 3 }, { 0x7, 3 } + }; + static constexpr uint8_t HuffTable3Sz = 7; + static constexpr THuffEntry HuffTable3[HuffTable3Sz] = { + { 0x0, 1 }, + { 0x4, 3}, { 0x5, 3 }, + { 0xC, 4 }, { 0xD, 4 }, { 0xE, 4 }, { 0xF, 4 } + }; + static constexpr uint8_t HuffTable5Sz = 15; + static constexpr THuffEntry HuffTable5[HuffTable5Sz] = { + { 0x0, 2 }, + { 0x2, 3 }, { 0x3, 3 }, + { 0x8, 4 }, { 0x9, 4 }, { 0xA, 4 }, { 0xB, 4 }, //{ 0xC, 4 }, { 0xD, 4 }, + { 0x1C, 5 }, { 0x1D, 5 }, + { 0x3C, 6 }, { 0x3D, 6 }, { 0x3E, 6 }, { 0x3F, 6}, + { 0xC, 4 }, { 0xD, 4 } //TODO: is it right table??? + }; + static constexpr uint8_t HuffTable6Sz = 31; + static constexpr THuffEntry HuffTable6[HuffTable6Sz] = { + { 0x0, 3 }, + { 0x2, 4 }, { 0x3, 4 }, { 0x4, 4 }, { 0x5, 4 }, { 0x6, 4 }, { 0x7, 4 }, //{ 0x8, 4 }, { 0x9, 4 }, + { 0x14, 5 }, { 0x15, 5 }, { 0x16, 5 }, { 0x17, 5 }, { 0x18, 5 }, { 0x19, 5 }, + { 0x34, 6 }, { 0x35, 6 }, { 0x36, 6 }, { 0x37, 6 }, { 0x38, 6 }, { 0x39, 6 }, { 0x3A, 6 }, { 0x3B, 6 }, + { 0x78, 7 }, { 0x79, 7 }, { 0x7A, 7 }, { 0x7B, 7 }, { 0x7C, 7 }, { 0x7D, 7 }, { 0x7E, 7 }, { 0x7F, 7 }, + { 0x8, 4 }, { 0x9, 4 } //TODO: is it right table??? + }; + static constexpr uint8_t HuffTable7Sz = 63; + static constexpr THuffEntry HuffTable7[HuffTable7Sz] = { + { 0x0, 3 }, + //{ 0x2, 4 }, { 0x3, 4 }, + { 0x8, 5 }, { 0x9, 5 }, { 0xA, 5}, { 0xB, 5 }, { 0xC, 5 }, { 0xD, 5 }, { 0xE, 5}, { 0xF, 5 }, { 0x10, 5 }, { 0x11, 5 }, + { 0x24, 6 }, { 0x25, 6 }, { 0x26, 6 }, { 0x27, 6 }, { 0x28, 6 }, { 0x29, 6 }, { 0x2A, 6 }, { 0x2B, 6 }, + { 0x2C, 6 }, { 0x2D, 6 }, { 0x2E, 6 }, { 0x2F, 6 }, { 0x30, 6 }, { 0x31, 6 }, { 0x32, 6 }, { 0x33, 6 }, + { 0x68, 7 }, { 0x69, 7 }, { 0x6A, 7 }, { 0x6B, 7 }, { 0x6C, 7 }, { 0x6D, 7 }, { 0x6E, 7 }, + { 0x6F, 7 }, { 0x70, 7 }, { 0x71, 7 }, { 0x72, 7 }, { 0x73, 7 }, { 0x74, 7 }, { 0x75, 7 }, + { 0xEC, 8 }, { 0xED, 8 }, { 0xEE, 8 }, { 0xEF, 8 }, { 0xF0, 8 }, { 0xF1, 8 }, { 0xF2, 8 }, { 0xF3, 8 }, { 0xF4, 8 }, { 0xF5, 8 }, + { 0xF6, 8 }, { 0xF7, 8 }, { 0xF8, 8 }, { 0xF9, 8 }, { 0xFA, 8 }, { 0xFB, 8 }, { 0xFC, 8 }, { 0xFD, 8 }, { 0xFE, 8 }, { 0xFF, 8 }, + { 0x2, 4 }, { 0x3, 4 } //TODO: is it right table??? + }; + + struct THuffTablePair { + const THuffEntry* Table; + const uint32_t Sz; + }; + + static constexpr THuffTablePair HuffTables[7] { + { HuffTable1, HuffTable1Sz }, + { HuffTable2, HuffTable2Sz }, + { HuffTable3, HuffTable3Sz }, + { HuffTable1, HuffTable1Sz }, + { HuffTable5, HuffTable5Sz }, + { HuffTable6, HuffTable6Sz }, + { HuffTable7, HuffTable7Sz } + }; +public: + TAtrac3Data() { + if (ScaleTable[0] == 0) { + for (uint32_t i = 0; i < 64; i++) { + ScaleTable[i] = pow(2.0, (double)(i - 15.0) / 3.0); + } + } + for (int i = 0; i < 256; i++) { + EncodeWindow[i] = (sin(((i + 0.5) / 256.0 - 0.5) * M_PI) + 1.0) * 0.5; + } + } + static uint32_t MantissaToCLcIdx(int32_t mantissa) { + assert(mantissa > -3 && mantissa < 2); + const uint32_t mantissa_clc_rtab[4] = { 2, 3, 0, 1}; + return mantissa_clc_rtab[mantissa + 2]; + } + static uint32_t MantissasToVlcIndex(int32_t a, int32_t b) { + assert(a > -2 && a < 2); + assert(b > -2 && b < 2); + const uint32_t mantissas_vlc_rtab[9] = { 8, 4, 7, 2, 0, 1, 6, 3, 5 }; + const uint8_t idx = 3 * (a + 1) + (b + 1); + return mantissas_vlc_rtab[idx]; + } + static constexpr TContainerParams ContainerParams[8] = { + { 66150, 192, true }, + { 93713, 272, true }, + { 104738, 304, false }, + { 132300, 384, false }, + { 146081, 424, false }, + { 176400, 512, false }, + { 264600, 768, false }, + { 352800, 1024, false } + }; + static const TContainerParams* GetContainerParamsForBitrate(uint32_t bitrate); +}; + +class TAtrac3SubbandInfo { +public: + struct TGainPoint { + const uint32_t Level; + const uint32_t Location; + }; +private: + std::vector<std::vector<TGainPoint>> Info; +public: + TAtrac3SubbandInfo() + { + Info.resize(4); + } + uint32_t GetQmfNum() const { + return Info.size(); + } + const std::vector<TGainPoint>& GetGainPoints(uint32_t i) const { + assert(Info[i].size() == 0); + return Info[i]; + } +}; + diff --git a/src/atrac/atrac3_bitstream.cpp b/src/atrac/atrac3_bitstream.cpp new file mode 100644 index 0000000..3612afb --- /dev/null +++ b/src/atrac/atrac3_bitstream.cpp @@ -0,0 +1,143 @@ +#include "atrac3_bitstream.h" +#include "../bitstream/bitstream.h" +#include <cassert> +#include <algorithm> +#include <iostream> +#include <vector> +#include <cstdlib> + +using NAtracDEnc::TScaledBlock; +using std::vector; + +void TAtrac3BitStreamWriter::CLCEnc(const uint32_t selector, const int mantissas[MAXSPECPERBLOCK], const uint32_t blockSize, NBitStream::TBitStream* bitStream) { + uint32_t numBits = ClcLengthTab[selector]; + if (selector > 1) { + for (uint32_t i = 0; i < blockSize; ++i) { + bitStream->Write(NBitStream::MakeSign(mantissas[i], numBits), numBits); + } + } else { + for (uint32_t i = 0; i < blockSize / 2; ++i) { + uint32_t code = MantissaToCLcIdx(mantissas[i * 2]) << 2; + code |= MantissaToCLcIdx(mantissas[i * 2 + 1]); + assert(numBits == 4); + bitStream->Write(code, numBits); + } + } +} + +void TAtrac3BitStreamWriter::VLCEnc(const uint32_t selector, const int mantissas[MAXSPECPERBLOCK], const uint32_t blockSize, NBitStream::TBitStream* bitStream) { + const THuffEntry* huffTable = HuffTables[selector - 1].Table; + const uint8_t tableSz = HuffTables[selector - 1].Sz; + if (selector > 1) { + for (uint32_t i = 0; i < blockSize; ++i) { + int m = mantissas[i]; + uint32_t huffS = (m < 0) ? (((uint32_t)(-m)) << 1) | 1 : ((uint32_t)m) << 1; + if (huffS) + huffS -= 1; + assert(huffS < 256); + assert(huffS < tableSz); + std::cout << "m: " << m << "huff: " << huffS << std::endl; + bitStream->Write(huffTable[huffS].Code, huffTable[huffS].Bits); + } + } else { + assert(tableSz == 9); + for (uint32_t i = 0; i < blockSize / 2; ++i) { + const int ma = mantissas[i * 2]; + const int mb = mantissas[i * 2 + 1]; + const uint32_t huffS = MantissasToVlcIndex(ma, mb); + bitStream->Write(huffTable[huffS].Code, huffTable[huffS].Bits); + } + } +} +void TAtrac3BitStreamWriter::EncodeSpecs(const vector<TScaledBlock>& scaledBlocks, const vector<uint32_t>& precisionPerEachBlocks, NBitStream::TBitStream* bitStream) { + const uint32_t numBlocks = precisionPerEachBlocks.size(); //number of blocks to save + const uint32_t codingMode = 0;//1; //0 - VLC, 1 - CLC + int mantisas[MAXSPECPERBLOCK]; + assert(numBlocks <= 32); + bitStream->Write(numBlocks-1, 5); + bitStream->Write(codingMode, 1); + + for (uint32_t i = 0; i < numBlocks; ++i) { + uint32_t val = precisionPerEachBlocks[i]; //coding table used (VLC) or number of bits used (CLC) + bitStream->Write(val, 3); + } + for (uint32_t i = 0; i < numBlocks; ++i) { + if (precisionPerEachBlocks[i] == 0) + continue; + bitStream->Write(scaledBlocks[i].ScaleFactorIndex, 6); + } + for (uint32_t i = 0; i < numBlocks; ++i) { + if (precisionPerEachBlocks[i] == 0) + continue; + + uint32_t first = BlockSizeTab[i]; + const uint32_t last = BlockSizeTab[i+1]; + const uint32_t blockSize = last - first; + const double mul = MaxQuant[std::min(precisionPerEachBlocks[i], (uint32_t)7)]; + + for (uint32_t j = 0; first < last; first++, j++) { + mantisas[j] = round(scaledBlocks[i].Values[j] * mul); + } + + if (codingMode == 1) { + CLCEnc(precisionPerEachBlocks[i], mantisas, blockSize, bitStream); + } else { + VLCEnc(precisionPerEachBlocks[i], mantisas, blockSize, bitStream); + } + } + + +} + +void TAtrac3BitStreamWriter::WriteSoundUnit(const TAtrac3SubbandInfo& subbandInfo, const vector<TScaledBlock>& scaledBlocks) { + NBitStream::TBitStream bitStream; + if (Params.Js) { + //TODO + } else { + bitStream.Write(0x28, 6); //0x28 - id + } + const uint32_t numQmfBand = subbandInfo.GetQmfNum(); + bitStream.Write(numQmfBand - 1, 2); + + //write gain info + for (uint32_t band = 0; band < numQmfBand; ++band) { + const vector<TAtrac3SubbandInfo::TGainPoint>& GainPoints = subbandInfo.GetGainPoints(band); + bitStream.Write(GainPoints.size(), 3); + assert(GainPoints.size() == 0); + for (const TAtrac3SubbandInfo::TGainPoint& point : GainPoints) { + abort(); + bitStream.Write(point.Level, 4); + bitStream.Write(point.Location, 5); + } + } + + //tonal components + bitStream.Write(0, 5); //disabled + + vector<uint32_t> precisionPerEachBlocks(27,1); + for (int i = 0; i < 2; i++) { + //precisionPerEachBlocks[i] = 4; + } + + //spec + EncodeSpecs(scaledBlocks, precisionPerEachBlocks, &bitStream); + + if (!Container) + abort(); + if (OutBuffer.empty()) { + std::vector<char> channel = bitStream.GetBytes(); + //std::cout << channel.size() << std::endl; + assert(channel.size() <= Params.FrameSz/2); + channel.resize(Params.FrameSz/2); + OutBuffer.insert(OutBuffer.end(), channel.begin(), channel.end()); + } else { + std::vector<char> channel = bitStream.GetBytes(); + channel.resize(Params.FrameSz/2); + //std::cout << channel.size() << std::endl; + assert(channel.size() <= Params.FrameSz/2); + OutBuffer.insert(OutBuffer.end(), channel.begin(), channel.end()); + Container->WriteFrame(OutBuffer); + OutBuffer.clear(); + } + +} diff --git a/src/atrac/atrac3_bitstream.h b/src/atrac/atrac3_bitstream.h new file mode 100644 index 0000000..8a63d01 --- /dev/null +++ b/src/atrac/atrac3_bitstream.h @@ -0,0 +1,27 @@ +#pragma once +#include "atrac3.h" +#include "atrac1.h" +#include "../aea.h" +#include "../oma.h" +#include "../atrac/atrac1.h" +#include "atrac_scale.h" +#include <vector> + +static const uint32_t MAXSPECPERBLOCK = 128; + +class TAtrac3BitStreamWriter : public virtual TAtrac3Data { + TOma* Container; + const TContainerParams Params; + std::vector<char> OutBuffer; + void CLCEnc(const uint32_t selector, const int mantissas[MAXSPECPERBLOCK], const uint32_t blockSize, NBitStream::TBitStream* bitStream); + void VLCEnc(const uint32_t selector, const int mantissas[MAXSPECPERBLOCK], const uint32_t blockSize, NBitStream::TBitStream* bitStream); + void EncodeSpecs(const std::vector<NAtracDEnc::TScaledBlock>& scaledBlocks, const std::vector<uint32_t>& bitsPerEachBlock, NBitStream::TBitStream* bitStream); +public: + TAtrac3BitStreamWriter(TOma* container, const TContainerParams& params) //no mono mode for atrac3 + : Container(container) + , Params(params) + { + + } + void WriteSoundUnit(const TAtrac3SubbandInfo& subbandInfo, const std::vector<NAtracDEnc::TScaledBlock>& scaledBlocks); +}; diff --git a/src/atrac/atrac3_qmf.h b/src/atrac/atrac3_qmf.h new file mode 100644 index 0000000..bcd0475 --- /dev/null +++ b/src/atrac/atrac3_qmf.h @@ -0,0 +1,23 @@ +#pragma once +#include <vector> +#include "../qmf/qmf.h" + +template<class TIn> +class Atrac3SplitFilterBank { + const static int nInSamples = 1024; + TQmf<TIn, nInSamples> Qmf1; + TQmf<TIn, nInSamples / 2> Qmf2; + TQmf<TIn, nInSamples / 2> Qmf3; + std::vector<double> Buf1; + std::vector<double> Buf2; +public: + Atrac3SplitFilterBank() { + Buf1.resize(nInSamples); + Buf2.resize(nInSamples); + } + void Split(TIn* pcm, double* subs[4]) { + Qmf1.Split(pcm, Buf1.data(), Buf2.data()); + Qmf2.Split(Buf1.data(), subs[0], subs[1]); + Qmf3.Split(Buf2.data(), subs[3], subs[2]); + } +}; diff --git a/src/atrac/atrac_scale.cpp b/src/atrac/atrac_scale.cpp index 1e24cb5..92d2645 100644 --- a/src/atrac/atrac_scale.cpp +++ b/src/atrac/atrac_scale.cpp @@ -1,5 +1,6 @@ #include "atrac_scale.h" #include "atrac1.h" +#include "atrac3.h" #include <cmath> #include <iostream> #include <algorithm> @@ -16,7 +17,7 @@ vector<TScaledBlock> TScaler<TBaseData>::Scale(const vector<double>& specs, cons for (uint8_t bandNum = 0; bandNum < this->NumQMF; ++bandNum) { const bool shortWinMode = !!blockSize.LogCount[bandNum]; for (uint8_t blockNum = this->BlocksPerBand[bandNum]; blockNum < this->BlocksPerBand[bandNum + 1]; ++blockNum) { - const uint16_t specNumStart = shortWinMode ? this->SpecsStartShort[blockNum] : this->SpecsStartLong[blockNum]; + const uint16_t specNumStart = shortWinMode ? TBaseData::SpecsStartShort[blockNum] : TBaseData::SpecsStartLong[blockNum]; const uint16_t specNumEnd = specNumStart + this->SpecsPerBlock[blockNum]; double maxAbsSpec = 0; for (uint16_t curSpec = specNumStart; curSpec < specNumEnd; ++curSpec) { @@ -45,4 +46,5 @@ vector<TScaledBlock> TScaler<TBaseData>::Scale(const vector<double>& specs, cons return scaledBlocks; } template class TScaler<TAtrac1Data>; +template class TScaler<TAtrac3Data>; } diff --git a/src/atrac3denc.cpp b/src/atrac3denc.cpp new file mode 100644 index 0000000..9b5b9e6 --- /dev/null +++ b/src/atrac3denc.cpp @@ -0,0 +1,73 @@ +#include "atrac3denc.h" +#include "atrac/atrac3_bitstream.h" +#include <assert.h> + +#include <iostream> + +namespace NAtracDEnc { + +using namespace NMDCT; +using std::vector; + +void TAtrac3MDCT::Mdct(double specs[1024], double* bands[4]) { + for (int band = 0; band < 4; ++band) { + double* srcBuff = bands[band]; + vector<double> tmp(512); + memcpy(&tmp[0], &srcBuff[256], 256 * sizeof(double)); + for (int i = 0; i < 256; i++) { + srcBuff[256+i] = TAtrac3Data::EncodeWindow[i] * srcBuff[i]; + srcBuff[i] = TAtrac3Data::EncodeWindow[255-i] * srcBuff[i]; + } + memcpy(&tmp[256], &srcBuff[0], 256 * sizeof(double)); + const vector<double>& sp = Mdct512(&tmp[0]); + assert(sp.size() == 256); + memcpy(&specs[band*256], sp.data(), 256*sizeof(double)); + if (band & 1) { + for (uint32_t j = 0; j < sp.size() / 2; j++) { + double tmp = specs[band*256 +j]; + specs[band*256 + j] = specs[band*256 + sp.size() - 1 -j]; + specs[band*256 + sp.size() - 1 -j] = tmp; + } + } + } +} + +TAtrac3Processor::TAtrac3Processor(TAeaPtr&& oma, const TContainerParams& params) + : Oma(std::move(oma)) + , Params(params) +{} + +TAtrac3Processor::~TAtrac3Processor() +{} + +TPCMEngine<double>::TProcessLambda TAtrac3Processor::GetEncodeLambda() { + TOma* omaptr = dynamic_cast<TOma*>(Oma.get()); + if (!omaptr) { + std::cerr << "Wrong container" << std::endl; + abort(); + } + + TAtrac3BitStreamWriter* bitStreamWriter = new TAtrac3BitStreamWriter(omaptr, Params); + return [this, bitStreamWriter](double* data, const TPCMEngine<double>::ProcessMeta& meta) { + for (uint32_t channel=0; channel < 2; channel++) { + vector<double> specs(1024); + double src[NumSamples]; + for (int i = 0; i < NumSamples; ++i) { + src[i] = data[meta.Channels == 1 ? i : (i * 2 + channel)]; //no mono mode in atrac1. //TODO we can double frame after encoding + } + + double* p[4] = {&PcmBuffer[channel][0][0], &PcmBuffer[channel][1][0], &PcmBuffer[channel][2][0], &PcmBuffer[channel][3][0]}; + SplitFilterBank[channel].Split(&src[0], p); + Mdct(specs.data(), p); + const TBlockSize blockSize(false, false, false); + bitStreamWriter->WriteSoundUnit(TAtrac3SubbandInfo(), Scaler.Scale(specs, blockSize)); + } + }; +} + +TPCMEngine<double>::TProcessLambda TAtrac3Processor::GetDecodeLambda() { + abort(); + return {}; +} + +}//namespace NAtracDEnc diff --git a/src/atrac3denc.h b/src/atrac3denc.h new file mode 100644 index 0000000..0b20061 --- /dev/null +++ b/src/atrac3denc.h @@ -0,0 +1,31 @@ +#pragma once +#include "pcmengin.h" +#include "oma.h" +#include "aea.h" +#include "atrac/atrac3.h" +#include "atrac/atrac3_qmf.h" + +#include "atrac/atrac_scale.h" +#include "mdct/mdct.h" + +namespace NAtracDEnc { + +class TAtrac3MDCT : public virtual TAtrac3Data { + NMDCT::TMDCT<512> Mdct512; +public: + void Mdct(double specs[1024], double* bands[4]); +}; + +class TAtrac3Processor : public IProcessor<double>, public TAtrac3MDCT, public virtual TAtrac3Data { + TAeaPtr Oma; + const TContainerParams Params; + double PcmBuffer[2][4][256 + 256]; //2 channel, 4 band, 256 sample + 256 for overlap buffer + Atrac3SplitFilterBank<double> SplitFilterBank[2]; + TScaler<TAtrac3Data> Scaler; +public: + TAtrac3Processor(TAeaPtr&& oma, const TContainerParams& params); + ~TAtrac3Processor(); + TPCMEngine<double>::TProcessLambda GetDecodeLambda() override; + TPCMEngine<double>::TProcessLambda GetEncodeLambda() override; +}; +} diff --git a/src/atracdenc.cpp b/src/atracdenc.cpp index 2de1fda..d13ef18 100644 --- a/src/atracdenc.cpp +++ b/src/atracdenc.cpp @@ -140,7 +140,7 @@ void TAtrac1MDCT::IMdct(double Specs[512], const TBlockSize& mode, double* low, } TPCMEngine<double>::TProcessLambda TAtrac1Processor::GetDecodeLambda() { - return [this](double* data) { + return [this](double* data, const TPCMEngine<double>::ProcessMeta& meta) { double sum[512]; const uint32_t srcChannels = Aea->GetChannelNum(); for (uint32_t channel = 0; channel < srcChannels; channel++) { @@ -180,7 +180,7 @@ TPCMEngine<double>::TProcessLambda TAtrac1Processor::GetEncodeLambda() { bitAlloc.push_back(new TAtrac1SimpleBitAlloc(atrac1container, Settings.GetBfuIdxConst(), Settings.GetFastBfuNumSearch())); } - return [this, srcChannels, bitAlloc](double* data) { + return [this, srcChannels, bitAlloc](double* data, const TPCMEngine<double>::ProcessMeta& meta) { for (uint32_t channel = 0; channel < srcChannels; channel++) { double src[NumSamples]; vector<double> specs(512); diff --git a/src/atracdenc.h b/src/atracdenc.h index 30dbd20..8a9d59f 100644 --- a/src/atracdenc.h +++ b/src/atracdenc.h @@ -1,6 +1,8 @@ #pragma once #include "pcmengin.h" +#include "atrac3denc.h" #include "aea.h" +#include "oma.h" #include "atrac_encode_settings.h" #include "transient_detector.h" #include "atrac/atrac1.h" @@ -15,7 +17,8 @@ namespace NAtracDEnc { enum EMode { E_ENCODE = 1, - E_DECODE = 2 + E_DECODE = 2, + E_ATRAC3 = 4 }; class TAtrac1MDCT : public virtual TAtrac1Data { diff --git a/src/main.cpp b/src/main.cpp index f74b253..e7f063c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,17 +40,32 @@ static string GetHelp() { "\n--decode -d \t - decode mode" "\n -i input file" "\n -o output file" + "\n --bitrate (only if supported by codec)" "\nAdvanced options:\n --bfuidxconst\t Set constant amount of used BFU. WARNING: It is not a lowpass filter! Do not use it to cut off hi frequency." "\n --bfuidxfast\t enable fast search of BFU amount" "\n --notransient[=mask] disable transient detection and use optional mask to set bands with short MDCT window"; } +static int checkedStoi(const char* data, int min, int max, int def) { + int tmp = 0; + try { + tmp = stoi(data); + if (tmp < min || tmp > max) + throw std::invalid_argument(data); + return tmp; + } catch (std::invalid_argument&) { + cerr << "Wrong arg: " << data << " " << def << " will be used" << endl; + return def; + } +} + int main(int argc, char* const* argv) { const char* myName = argv[0]; static struct option longopts[] = { - { "encode", no_argument, NULL, 'e' }, + { "encode", optional_argument, NULL, 'e' }, { "decode", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, + { "bitrate", required_argument, NULL, 'b'}, { "bfuidxconst", required_argument, NULL, 1}, { "bfuidxfast", no_argument, NULL, 2}, { "notransient", optional_argument, NULL, 3}, @@ -66,11 +81,17 @@ int main(int argc, char* const* argv) { bool fastBfuNumSearch = false; bool nostdout = false; TAtrac1EncodeSettings::EWindowMode windowMode = TAtrac1EncodeSettings::EWindowMode::EWM_AUTO; - uint32_t winMask = 0; //all is long + uint32_t winMask = 0; //0 - all is long + uint32_t bitrate = 0; //0 - use default for codec while ((ch = getopt_long(argc, argv, "edhi:o:m", longopts, NULL)) != -1) { switch (ch) { case 'e': mode |= E_ENCODE; + if (optarg) { + if (strcmp(optarg, "atrac3") == 0) { + mode |= E_ATRAC3; + } + } break; case 'd': mode |= E_DECODE; @@ -87,13 +108,11 @@ int main(int argc, char* const* argv) { cout << GetHelp() << endl; return 0; break; + case 'b': + bitrate = checkedStoi(optarg, 32, 384, 0); + break; case 1: - try { - bfuIdxConst = stoi(optarg); - } catch (std::invalid_argument&) { - cerr << "Wrong arg: " << optarg << " should be (0, 8]" << endl; - return -1; - } + bfuIdxConst = checkedStoi(optarg, 1, 8, 0); break; case 2: fastBfuNumSearch = true; @@ -136,6 +155,7 @@ int main(int argc, char* const* argv) { IProcessor<double>* atracProcessor; uint64_t totalSamples = 0; TWavPtr wavIO; + uint32_t pcmFrameSz = 0; //size of one pcm frame to process if (mode == E_ENCODE) { wavIO = TWavPtr(new TWav(inFile)); const int numChannels = wavIO->GetChannelNum(); @@ -146,6 +166,7 @@ int main(int argc, char* const* argv) { if (!nostdout) cout << "Input file: " << inFile << "\n Channels: " << numChannels << "\n SampleRate: " << wavIO->GetSampleRate() << "\n TotalSamples: " << totalSamples << endl; atracProcessor = new TAtrac1Processor(move(aeaIO), TAtrac1EncodeSettings(bfuIdxConst, fastBfuNumSearch, windowMode, winMask)); + pcmFrameSz = 512; } else if (mode == E_DECODE) { TAeaPtr aeaIO = TAeaPtr(new TAea(inFile)); totalSamples = aeaIO->GetLengthInSamples(); @@ -155,6 +176,22 @@ int main(int argc, char* const* argv) { wavIO = TWavPtr(new TWav(outFile, aeaIO->GetChannelNum(), 44100)); pcmEngine = new TPCMEngine<double>(4096, aeaIO->GetChannelNum(), TPCMEngine<double>::TWriterPtr(wavIO->GetPCMWriter<double>())); atracProcessor = new TAtrac1Processor(move(aeaIO), TAtrac1EncodeSettings(bfuIdxConst, fastBfuNumSearch, windowMode, winMask)); + pcmFrameSz = 512; + } else if (mode == (E_ENCODE | E_ATRAC3)) { + std::cout << "WARNING: ATRAC3 is uncompleted mode (no psy, tonal encoding, gc), result will be not good )))" << std::endl; + const TContainerParams* atrac3params = TAtrac3Data::GetContainerParamsForBitrate(bitrate*1024); + if (atrac3params == nullptr) { + std::cerr << "wrong atrac3 params, exiting" << std::endl; + return 1; + } + std::cout << "bitrate " << atrac3params->Bitrate << std::endl; + wavIO = TWavPtr(new TWav(inFile)); + const int numChannels = wavIO->GetChannelNum(); + totalSamples = wavIO->GetTotalSamples(); + TAeaPtr omaIO = TAeaPtr(new TOma(outFile, "test", numChannels, numChannels * totalSamples / 512, OMAC_ID_ATRAC3, atrac3params->FrameSz)); + pcmEngine = new TPCMEngine<double>(4096, numChannels, TPCMEngine<double>::TReaderPtr(wavIO->GetPCMReader<double>())); + atracProcessor = new TAtrac3Processor(move(omaIO), *atrac3params); + pcmFrameSz = 1024; } else { cerr << "Processing mode was not specified" << endl; return 1; @@ -165,7 +202,7 @@ int main(int argc, char* const* argv) { uint64_t processed = 0; try { - while (totalSamples > (processed = pcmEngine->ApplyProcess(512, atracLambda))) + while (totalSamples > (processed = pcmEngine->ApplyProcess(pcmFrameSz, atracLambda))) { if (!nostdout) printProgress(processed*100/totalSamples); diff --git a/src/oma.cpp b/src/oma.cpp new file mode 100644 index 0000000..5fd18b7 --- /dev/null +++ b/src/oma.cpp @@ -0,0 +1,42 @@ +#include "oma.h" +#include <stdlib.h> + +TOma::TOma(const std::string& filename, const std::string& title, int numChannel, uint32_t numFrames, int cid, uint32_t framesize) { + oma_info_t info; + info.codec = cid; + info.samplerate = 44100; + info.channel_format = OMA_STEREO; + info.framesize = framesize; + File = oma_open(filename.c_str(), OMAM_W, &info); + if (!File) + abort(); +} + +TOma::~TOma() { + oma_close(File); +} + +std::unique_ptr<ICompressedIO::TFrame> TOma::ReadFrame() { + abort(); + return nullptr; +} + +void TOma::WriteFrame(std::vector<char> data) { + if (oma_write(File, &data[0], 1) == -1) { + fprintf(stderr, "write error\n"); + abort(); + } +} + +std::string TOma::GetName() const { + abort(); + return {}; +} + +int TOma::GetChannelNum() const { + return 2; //for ATRAC3 +} +long long TOma::GetLengthInSamples() const { + abort(); + return 0; +} diff --git a/src/oma.h b/src/oma.h new file mode 100644 index 0000000..838322d --- /dev/null +++ b/src/oma.h @@ -0,0 +1,17 @@ +#pragma once + +#include "compressed_io.h" +#include "oma/liboma/include/oma.h" + + +class TOma : public ICompressedIO { + OMAFILE* File; +public: + TOma(const std::string& filename, const std::string& title, int numChannel, uint32_t numFrames, int cid, uint32_t framesize); + ~TOma(); + std::unique_ptr<TFrame> ReadFrame() override; + void WriteFrame(std::vector<char> data) override; + std::string GetName() const override; + int GetChannelNum() const override; + long long GetLengthInSamples() const override; +}; diff --git a/src/oma/liboma/include/oma.h b/src/oma/liboma/include/oma.h new file mode 100644 index 0000000..29d1b72 --- /dev/null +++ b/src/oma/liboma/include/oma.h @@ -0,0 +1,60 @@ + +#ifndef OMA_H +#define OMA_H + +typedef struct omafile_ctx OMAFILE; + +struct oma_info { + int codec; + int framesize; + int samplerate; + int channel_format; +}; + +enum { + OMAM_R = 0x1, + OMAM_W = 0x2, +}; + +enum { + OMAC_ID_ATRAC3 = 0, + OMAC_ID_ATRAC3PLUS = 1, + OMAC_ID_MP3 = 2, + OMAC_ID_LPCM = 3, + OMAC_ID_WMA = 5 +}; + +enum { + OMA_MONO = 0, + OMA_STEREO = 1, + OMA_STEREO_JS = 2, + OMA_3 = 3, + OMA_4 = 4, + OMA_6 = 5, + OMA_7 = 6, + OMA_8 = 7 + +}; + +typedef struct oma_info oma_info_t; +typedef long long block_count_t; + +#ifdef __cplusplus +extern "C" { +#endif +int oma_get_last_err(); + +OMAFILE* oma_open(const char *path, int mode, oma_info_t *info); +int oma_close(OMAFILE* oma_file); + +block_count_t oma_read(OMAFILE *oma_file, void *ptr, block_count_t blocks); +block_count_t oma_write(OMAFILE *oma_file, const void *ptr, block_count_t blocks); + +oma_info_t* oma_get_info(OMAFILE *oma_file); +int oma_get_bitrate(oma_info_t *info); +const char *oma_get_codecname(oma_info_t *info); +#ifdef __cplusplus +} +#endif + +#endif /* OMA_H */ diff --git a/src/oma/liboma/src/liboma.c b/src/oma/liboma/src/liboma.c new file mode 100644 index 0000000..42a5601 --- /dev/null +++ b/src/oma/liboma/src/liboma.c @@ -0,0 +1,265 @@ +#include "../include/oma.h" +#include "oma_internal.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +static const int OMA_HEADER_SIZE = 96; +static const int liboma_samplerates[8] = { 32000, 44100, 48000, 88200, 96000, 0 }; +static const char* codec_name[6] = { "ATRAC3", "ATRAC3PLUS", "MPEG1LAYER3", "LPCM", "", "OMAC_ID_WMA" }; +static char ea3_str[] = {'E', 'A', '3'}; +static int channel_id_to_format_tab[7] = { OMA_MONO, OMA_STEREO, OMA_3, OMA_4, OMA_6, OMA_7, OMA_8 }; +enum { + OMAERR_OK = 0, + OMAERR_IO = -1, + OMAERR_PERM = -2, + OMAERR_FMT = -3, + OMAERR_ENCRYPT = -4, + OMAERR_VAL = -5, + OMAERR_EOF = -6 +}; + +static __thread int err; +int oma_get_last_err() { + return err; +} + +static void save_err(int e) { + err = e; +} + +static int oma_check_header(const char* buf) { + if (memcmp(buf, &ea3_str[0], 3) || buf[4] != 0 || buf[5] != OMA_HEADER_SIZE) { + return OMAERR_FMT; + } + return OMAERR_OK; +} + +static int oma_check_encryption(const char* buf) { + if (buf[6] == -1 && buf[7] == -1) + return OMAERR_OK; + return OMAERR_ENCRYPT; +} + +static int oma_get_samplerate_idx(int samplerate) { + if (samplerate <= 0) { + fprintf(stderr, "wrong samplerate\n"); + return -1; + } + for (int i = 0; ; i++) { + if (liboma_samplerates[i] == samplerate) + return i; + if (liboma_samplerates[i] == 0) + return -1; + } + return -1; +} + +static int oma_read_atrac3_header(uint32_t params, oma_info_t* info) { + const int js = (params >> 17) & 0x1; + const int samplerate = liboma_samplerates[(params >> 13) & 0x7]; + if (samplerate == 0) { + fprintf(stderr, "liboma: wrong samplerate params, can't read header\n"); + return -1; + } + info->codec = OMAC_ID_ATRAC3; + info->framesize = (params & 0x3FF) * 8; + info->samplerate = samplerate; + info->channel_format = js ? OMA_STEREO_JS : OMA_STEREO; + return 0; +} + +static int oma_write_atrac3_header(uint32_t *params, oma_info_t *info) { + const int channel_format = info->channel_format; + if (channel_format != OMA_STEREO_JS && channel_format != OMA_STEREO) { + fprintf(stderr, "wrong channel format\n"); + return -1; + } + const uint32_t js = channel_format == OMA_STEREO_JS; + const int samplerate_idx = oma_get_samplerate_idx(info->samplerate); + if (samplerate_idx == -1) + return -1; + const uint32_t framesz = info->framesize / 8; + fprintf(stderr, "framesize: %d\n", framesz); + if (framesz > 0x3FF) + return -1; + *params = htonl((OMAC_ID_ATRAC3 << 24) | (js << 17) | ((uint32_t)samplerate_idx << 13) | framesz); + return 0; +} + +static int oma_read_atrac3p_header(uint32_t params, oma_info_t* info) { + const int channel_id = (params >> 10) & 7; + if (channel_id == 0) { + fprintf(stderr, ""); + return -1; + } + const int samplerate = liboma_samplerates[(params >> 13) & 0x7]; + if (samplerate == 0) { + fprintf(stderr, "liboma: wrong samplerate params, can't read header\n"); + return -1; + } + info->codec = OMAC_ID_ATRAC3PLUS; + info->framesize = ((params & 0x3FF) * 8) + 8; + info->samplerate = samplerate; + uint32_t ch_id = (params >> 10) & 7; + info->channel_format = channel_id_to_format_tab[ch_id - 1]; + return 0; +} + +static int oma_write_header(OMAFILE* ctx, oma_info_t *omainfo) { + if (ctx == NULL || omainfo == NULL) + return -1; + char *headerbuf = (char*)calloc(OMA_HEADER_SIZE, 1); + memcpy(headerbuf, &ea3_str[0], 3); + headerbuf[3] = 1; //??? + headerbuf[5] = OMA_HEADER_SIZE; + headerbuf[6] = 0xFF; + headerbuf[7] = 0xFF; + uint32_t *params = (uint32_t*)(headerbuf+32); + switch (omainfo->codec) { + case OMAC_ID_ATRAC3: + oma_write_atrac3_header(params, omainfo); + break; + case OMAC_ID_ATRAC3PLUS: + assert(0); + break; + default: + assert(0); + break; + } + int rv = fwrite(headerbuf, sizeof(char), OMA_HEADER_SIZE, ctx->file); + if (rv != OMA_HEADER_SIZE) { + fprintf(stderr, "can't write header\n"); + rv = -1; + } + free(headerbuf); + return rv; +} + +static int oma_parse_header(OMAFILE* file) { + char buf[OMA_HEADER_SIZE]; + int read = fread(&buf[0], sizeof(char), OMA_HEADER_SIZE, file->file); + int err = 0; + uint32_t params = 0; + if (OMA_HEADER_SIZE != read) + return feof(file->file) ? OMAERR_FMT : OMAERR_IO; + + err = oma_check_header(&buf[0]); + if (OMAERR_OK != err) + return err; + + err = oma_check_encryption(&buf[0]); + if (OMAERR_OK != err) + return err; + + //detect codecs + params = ((uint8_t)buf[33]) << 16 | ((uint8_t)buf[34]) << 8 | ((uint8_t)buf[35]); + switch (buf[32]) { + case OMAC_ID_ATRAC3: + oma_read_atrac3_header(params, &file->info); + break; + case OMAC_ID_ATRAC3PLUS: + oma_read_atrac3p_header(params, &file->info); + break; + + default: + fprintf(stderr, "got unsupported format: %d\n", buf[32]); + return OMAERR_FMT; + } + + return OMAERR_OK; +} + +OMAFILE* oma_open(const char *path, int mode, oma_info_t *info) { + const static char* modes[3] = {"", "rb", "wb"}; + FILE* file = fopen(path, modes[mode]); + int err = 0; + if (NULL == file) { + return NULL; + } + + struct omafile_ctx *ctx = (struct omafile_ctx*)malloc(sizeof(struct omafile_ctx)); + if (NULL == ctx) { + goto close_ret; + } + + ctx->file = file; + if (mode == OMAM_R) { + err = oma_parse_header(ctx); + if (OMAERR_OK != err) { + goto free_close_ret; + } + } else { + if (!info) { + err = OMAERR_VAL; + goto free_close_ret; + } + memcpy(&ctx->info, info, sizeof(oma_info_t)); + err = oma_write_header(ctx, info); + } + + return ctx; + +free_close_ret: + free(ctx); + +close_ret: + save_err(err); + fclose(file); + return NULL; +} + +int oma_close(OMAFILE *ctx) { + FILE* file = ctx->file; + free(ctx); + fclose(file); + return 0; +} + +block_count_t oma_read(OMAFILE *oma_file, void *ptr, block_count_t blocks) { + size_t read = fread(ptr, oma_file->info.framesize, blocks, oma_file->file); + if (read == blocks) + return read; + if (feof(oma_file->file)) { + save_err(OMAERR_EOF); + return 0; + } + return -1; +} + +block_count_t oma_write(OMAFILE *oma_file, const void *ptr, block_count_t blocks) { + size_t writen = fwrite(ptr, oma_file->info.framesize, blocks, oma_file->file); + if (writen == blocks) + return writen; + return -1; +} + +oma_info_t* oma_get_info(OMAFILE *oma_file) { + if (oma_file == NULL) + return NULL; + return &oma_file->info; +} +int oma_get_bitrate(oma_info_t *info) { + switch (info->codec) { + case OMAC_ID_ATRAC3: + return info->samplerate * info->framesize * 8 / 1024; + break; + case OMAC_ID_ATRAC3PLUS: + return info->samplerate * info->framesize * 8 / 2048; + break; + default: + return -1; + } + return -1; +} + +const char *oma_get_codecname(oma_info_t *info) { + if (info == NULL) + return ""; + int id = info->codec; + if (id < 0 || id > 5) + return ""; + return codec_name[id]; +} diff --git a/src/oma/liboma/src/oma_internal.h b/src/oma/liboma/src/oma_internal.h new file mode 100644 index 0000000..881e89b --- /dev/null +++ b/src/oma/liboma/src/oma_internal.h @@ -0,0 +1,18 @@ +#ifndef OMA_INTERNAL_H +#define OMA_INTERNAL_H + +#include <stdio.h> +#include "oma.h" + +struct omafile_ctx { + FILE* file; + oma_info_t info; +}; + + + +//static inline uint16_t read_big16(void *x) { +// return (((const uint8_t*)x)[0] << 8) | ((const uint8_t)x); +//} + +#endif /* OMA_INTERNAL_H */ diff --git a/src/oma/liboma/src/tools/omacp.c b/src/oma/liboma/src/tools/omacp.c new file mode 100644 index 0000000..3d9190e --- /dev/null +++ b/src/oma/liboma/src/tools/omacp.c @@ -0,0 +1,38 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "oma.h" + +int main(int argc, char* const* argv) { + if (3 != argc) + fprintf(stdout, "usage: \n\t omainfo [in] [out]\n"); + + OMAFILE* infile = oma_open(argv[1], OMAM_R, NULL); + if (NULL == infile) + fprintf(stderr, "Can't open %s to read, err: %d\n", argv[1], oma_get_last_err()); + + oma_info_t *info = oma_get_info(infile); + const char *codecname = oma_get_codecname(info); + const int bitrate = oma_get_bitrate(info); + + fprintf(stdout, "codec: %s, bitrate: %d, channel format: %d\n", codecname, bitrate, info->chanel_format); + + OMAFILE* outfile = oma_open(argv[2], OMAM_W, info); + if (NULL == outfile) + fprintf(stderr, "Can't open %s to write, err: %d\n", argv[2], oma_get_last_err()); + + char* buf = (char*)malloc(info->framesize); + for (;;) { + block_count_t rcount = oma_read(infile, buf, 1); + if (rcount == 0) + break; + if (rcount == -1) { + fprintf(stderr, "read error\n"); + break; + } + if (oma_write(outfile, buf, 1) == -1) { + fprintf(stderr, "write error\n"); + break; + } + } +} diff --git a/src/oma/liboma/src/tools/omainfo.c b/src/oma/liboma/src/tools/omainfo.c new file mode 100644 index 0000000..a487010 --- /dev/null +++ b/src/oma/liboma/src/tools/omainfo.c @@ -0,0 +1,25 @@ +#include <stdio.h> + +#include "oma.h" + +int main(int argc, char* const* argv) { + fprintf(stderr, "%d\n", argc); + if (2 > argc) { + fprintf(stdout, "usage: \n\t omainfo [filename]\n"); + return 1; + } + + for (int i = 1; i < argc; i++) { + OMAFILE* file = oma_open(argv[i], OMAM_R, NULL); + if (NULL == file) + fprintf(stderr, "Can't open %s\n", argv[i]); + + oma_info_t *info = oma_get_info(file); + const char *codecname = oma_get_codecname(info); + const int bitrate = oma_get_bitrate(info); + + fprintf(stdout, "%s codec: %s, bitrate: %d, channelformat: %d framesz: %d\n", argv[i], codecname, bitrate, info->channel_format, info->framesize); + oma_close(file); + } + return 0; +} diff --git a/src/pcmengin.h b/src/pcmengin.h index a0e0127..8023fed 100644 --- a/src/pcmengin.h +++ b/src/pcmengin.h @@ -30,9 +30,9 @@ class TEndOfRead : public std::exception { template <class T> class TPCMBuffer { std::vector<T> Buf_; - int32_t NumChannels; + uint16_t NumChannels; public: - TPCMBuffer(const int32_t bufSize, const int32_t numChannels) + TPCMBuffer(const int32_t bufSize, const uint32_t numChannels) : NumChannels(numChannels) { Buf_.resize(bufSize*numChannels); @@ -52,7 +52,7 @@ public: abort(); return &Buf_[rpos]; } - size_t Channels() const { + uint16_t Channels() const { return NumChannels; } void Zero(size_t pos, size_t len) { @@ -82,6 +82,9 @@ class TPCMEngine { public: typedef std::unique_ptr<IPCMWriter<T>> TWriterPtr; typedef std::unique_ptr<IPCMReader<T>> TReaderPtr; + struct ProcessMeta { + const uint16_t Channels; + }; private: TPCMBuffer<T> Buffer; TWriterPtr Writer; @@ -104,7 +107,7 @@ public: , Writer(std::move(writer)) , Reader(std::move(reader)) { } - typedef std::function<void(T* data)> TProcessLambda; + typedef std::function<void(T* data, const ProcessMeta& meta)> TProcessLambda; uint64_t ApplyProcess(size_t step, TProcessLambda lambda) { if (step > Buffer.Size()) { @@ -115,8 +118,9 @@ public: Reader->Read(Buffer, sizeToRead); } size_t lastPos = 0; + ProcessMeta meta = {Buffer.Channels()}; for (size_t i = 0; i + step <= Buffer.Size(); i+=step) { - lambda(Buffer[i]); + lambda(Buffer[i], meta); lastPos = i + step; } assert(lastPos == Buffer.Size()); |