aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniil Cherednik <dan.cherednik@gmail.com>2016-03-13 09:49:33 +0300
committerDaniil Cherednik <dan.cherednik@gmail.com>2016-03-13 09:49:33 +0300
commitae7ffde2740b9f7108e521be050d690360099f7e (patch)
tree2beef13ae40d8641b42d3abaf69de418a45d9b4d
parentc0a41cd391cb29e41a6d00e419830fd235837e80 (diff)
downloadatracdenc-ae7ffde2740b9f7108e521be050d690360099f7e.tar.gz
Initial implementation of atrac3 stream (CLC and VLC)
- no psy, gc, tonal component encoding, js
-rw-r--r--src/CMakeLists.txt30
-rw-r--r--src/atrac/atrac3.cpp23
-rw-r--r--src/atrac/atrac3.h189
-rw-r--r--src/atrac/atrac3_bitstream.cpp143
-rw-r--r--src/atrac/atrac3_bitstream.h27
-rw-r--r--src/atrac/atrac3_qmf.h23
-rw-r--r--src/atrac/atrac_scale.cpp4
-rw-r--r--src/atrac3denc.cpp73
-rw-r--r--src/atrac3denc.h31
-rw-r--r--src/atracdenc.cpp4
-rw-r--r--src/atracdenc.h5
-rw-r--r--src/main.cpp55
-rw-r--r--src/oma.cpp42
-rw-r--r--src/oma.h17
-rw-r--r--src/oma/liboma/include/oma.h60
-rw-r--r--src/oma/liboma/src/liboma.c265
-rw-r--r--src/oma/liboma/src/oma_internal.h18
-rw-r--r--src/oma/liboma/src/tools/omacp.c38
-rw-r--r--src/oma/liboma/src/tools/omainfo.c25
-rw-r--r--src/pcmengin.h14
20 files changed, 1062 insertions, 24 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3fc1d11..7a30576 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -11,11 +11,29 @@ INCLUDE(FindLibSndFile)
find_package(libsndfile REQUIRED)
include_directories(${LIBSNDFILE_INCLUDE_DIR})
-
-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)
-
-add_library(mdct_impl STATIC ${SOURCE_LIB})
+include_directories("oma/liboma/include")
+
+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_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 6d66cf3..94cf394 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 51794ea..5a9547e 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},
@@ -68,11 +83,17 @@ int main(int argc, char* const* argv) {
bool mono = 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;
@@ -92,13 +113,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;
@@ -141,6 +160,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();
@@ -151,6 +171,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();
@@ -160,6 +181,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;
@@ -170,7 +207,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 802d2d8..8070e26 100644
--- a/src/pcmengin.h
+++ b/src/pcmengin.h
@@ -29,9 +29,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);
@@ -51,7 +51,7 @@ public:
abort();
return &Buf_[rpos];
}
- size_t Channels() const {
+ uint16_t Channels() const {
return NumChannels;
}
void Zero(size_t pos, size_t len) {
@@ -81,6 +81,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;
@@ -103,7 +106,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(int step, TProcessLambda lambda) {
if (step > Buffer.Size()) {
@@ -114,8 +117,9 @@ public:
Reader->Read(Buffer, sizeToRead);
}
int32_t lastPos = 0;
+ ProcessMeta meta = {Buffer.Channels()};
for (int i = 0; i + step <= Buffer.Size(); i+=step) {
- lambda(Buffer[i]);
+ lambda(Buffer[i], meta);
lastPos = i + step;
}
assert(lastPos == Buffer.Size());