diff options
author | Daniil Cherednik <dan.cherednik@gmail.com> | 2019-05-15 01:14:42 +0300 |
---|---|---|
committer | Daniil Cherednik <dan.cherednik@gmail.com> | 2019-05-15 01:14:42 +0300 |
commit | ed9629395ce9696164c1d617d573a47982e82169 (patch) | |
tree | 5dbe92165b9fa3750740331b7af165d0aaab88c3 | |
parent | 218e2163453deac6f7a74765a21258b5c566a8cc (diff) | |
download | atracdenc-ed9629395ce9696164c1d617d573a47982e82169.tar.gz |
Support for MS Windows platform
- VS build (2017 tested)
- Media Foundation Framework support to read/write pcm data (instead of
libsndfile)
61 files changed, 1491 insertions, 142 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index a1f680e..fe996e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) -add_subdirectory(3rd/gtest-1.7.0) +if (UNIX) + add_subdirectory(3rd/gtest-1.7.0) + add_subdirectory(test) +endif() add_subdirectory(src) -add_subdirectory(test) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d0395ab..146b670 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +CMAKE_MINIMUM_REQUIRED(VERSION 3.0) macro(use_11) if (CMAKE_VERSION VERSION_LESS "3.1") @@ -17,14 +17,34 @@ use_11() #add_definitions( "-Wall -O2 -g -Rpass-analysis=loop-vectorize" ) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address -fno-omit-frame-pointer") -add_definitions( "-Wall -O2 -g" ) +if (UNIX) + add_definitions( "-Wall -O2 -g" ) +endif() project(atracdenc) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") -INCLUDE(FindLibSndFile) -include_directories(${LIBSNDFILE_INCLUDE_DIR}) +if (WIN32) + add_compile_definitions(PLATFORM_WINDOWS) + include_directories("platform/win/getopt") + set(SOURCE_PCM_IO_LIB + platform/win/pcm_io_mf/pcm_io_mf.cpp + ) +else() + INCLUDE(FindLibSndFile) + include_directories(${LIBSNDFILE_INCLUDE_DIR}) + set(SOURCE_PCM_IO_LIB + pcm_io_sndfile.cpp + ) +endif() + +include (TestBigEndian) +TEST_BIG_ENDIAN(BIGENDIAN_ORDER) +if (${BIGENDIAN}) + add_compile_definitions(BIGENDIAN_ORDER) +endif() + include_directories("oma/liboma/include") set(SOURCE_FFT_LIB fft/kissfft_impl/kiss_fft.c) @@ -34,6 +54,7 @@ set(SOURCE_EXE main.cpp wav.cpp aea.cpp + env.cpp transient_detector.cpp atrac1denc.cpp bitstream/bitstream.cpp @@ -50,7 +71,8 @@ set(SOURCE_EXE ) add_library(fft_impl STATIC ${SOURCE_FFT_LIB}) +add_library(pcm_io STATIC ${SOURCE_PCM_IO_LIB}) add_library(oma STATIC ${SOURCE_OMA_LIB}) add_executable(atracdenc ${SOURCE_EXE}) -target_link_libraries(atracdenc fft_impl oma ${SNDFILE_LIBRARIES}) +target_link_libraries(atracdenc fft_impl pcm_io oma ${SNDFILE_LIBRARIES}) diff --git a/src/aea.cpp b/src/aea.cpp index 4603aea..4387825 100644 --- a/src/aea.cpp +++ b/src/aea.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,9 +24,8 @@ using std::string; - TAea::TMeta TAea::ReadMeta(const string& filename) { - FILE* fp = fopen(filename.c_str(), "r"); + FILE* fp = fopen(filename.c_str(), "rb"); if (!fp) throw TAeaIOError("Can't open file to read", errno); std::array<char, AeaMetaSize> buf; @@ -44,7 +43,7 @@ TAea::TMeta TAea::ReadMeta(const string& filename) { } TAea::TMeta TAea::CreateMeta(const string& filename, const string& title, int channelNum, uint32_t numFrames) { - FILE* fp = fopen(filename.c_str(), "w"); + FILE* fp = fopen(filename.c_str(), "wb"); if (!fp) throw TAeaIOError("Can't open file to write", errno); std::array<char, AeaMetaSize> buf; @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac/atrac1.cpp b/src/atrac/atrac1.cpp index b96fa30..f7e6c61 100644 --- a/src/atrac/atrac1.cpp +++ b/src/atrac/atrac1.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac/atrac1.h b/src/atrac/atrac1.h index 460c86d..45d1bdf 100644 --- a/src/atrac/atrac1.h +++ b/src/atrac/atrac1.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -22,6 +22,7 @@ #include <map> #include <math.h> #include "../bitstream/bitstream.h" +#include "../config.h" namespace NAtracDEnc { namespace NAtrac1 { diff --git a/src/atrac/atrac1_bitalloc.cpp b/src/atrac/atrac1_bitalloc.cpp index 1a7b155..ae6ad2f 100644 --- a/src/atrac/atrac1_bitalloc.cpp +++ b/src/atrac/atrac1_bitalloc.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,7 @@ #include <math.h> #include <cassert> #include "../bitstream/bitstream.h" +#include "../env.h" namespace NAtracDEnc { namespace NAtrac1 { @@ -217,6 +218,12 @@ uint32_t TAtrac1SimpleBitAlloc::Write(const std::vector<TScaledBlock>& scaledBlo return BfuAmountTab[bfuIdx]; } +TAtrac1BitStreamWriter::TAtrac1BitStreamWriter(TAea* container) + : Container(container) +{ + NEnv::SetRoundFloat(); +}; + void TAtrac1BitStreamWriter::WriteBitStream(const vector<uint32_t>& bitsPerEachBlock, const std::vector<TScaledBlock>& scaledBlocks, uint32_t bfuAmountIdx, diff --git a/src/atrac/atrac1_bitalloc.h b/src/atrac/atrac1_bitalloc.h index 695defc..2478ddb 100644 --- a/src/atrac/atrac1_bitalloc.h +++ b/src/atrac/atrac1_bitalloc.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -24,7 +24,6 @@ #include <vector> #include <map> #include <cstdint> -#include <cfenv> namespace NAtracDEnc { namespace NAtrac1 { @@ -50,11 +49,8 @@ public: class TAtrac1BitStreamWriter : public virtual TAtrac1Data { TAea* Container; public: - explicit TAtrac1BitStreamWriter(TAea* container) - : Container(container) - { - fesetround(FE_TONEAREST); - }; + explicit TAtrac1BitStreamWriter(TAea* container); + void WriteBitStream(const std::vector<uint32_t>& bitsPerEachBlock, const std::vector<TScaledBlock>& scaledBlocks, uint32_t bfuAmountIdx, const TBlockSize& blockSize); }; diff --git a/src/atrac/atrac1_dequantiser.cpp b/src/atrac/atrac1_dequantiser.cpp index 9cc0666..a259f42 100644 --- a/src/atrac/atrac1_dequantiser.cpp +++ b/src/atrac/atrac1_dequantiser.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac/atrac1_dequantiser.h b/src/atrac/atrac1_dequantiser.h index 80e1e9c..d3c25bd 100644 --- a/src/atrac/atrac1_dequantiser.h +++ b/src/atrac/atrac1_dequantiser.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac/atrac1_qmf.h b/src/atrac/atrac1_qmf.h index efcf2c1..8b3e5e4 100644 --- a/src/atrac/atrac1_qmf.h +++ b/src/atrac/atrac1_qmf.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac/atrac3.cpp b/src/atrac/atrac3.cpp index 8236a92..82fb34b 100644 --- a/src/atrac/atrac3.cpp +++ b/src/atrac/atrac3.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac/atrac3.h b/src/atrac/atrac3.h index b6594e5..eb97808 100644 --- a/src/atrac/atrac3.h +++ b/src/atrac/atrac3.h @@ -12,11 +12,12 @@ * 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 + * 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 <math.h> #include <cstdint> #include <vector> diff --git a/src/atrac/atrac3_bitstream.cpp b/src/atrac/atrac3_bitstream.cpp index 8a391db..d39faa2 100644 --- a/src/atrac/atrac3_bitstream.cpp +++ b/src/atrac/atrac3_bitstream.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -196,7 +196,7 @@ void TAtrac3BitStreamWriter::EncodeSpecs(const TSingleChannelElement& sce, NBitS int mt[MaxSpecs]; const vector<TScaledBlock>& scaledBlocks = sce.ScaledBlocks; - auto allocation = CreateAllocation(sce, bitsUsed, mt); + const auto& allocation = CreateAllocation(sce, bitsUsed, mt); const vector<uint32_t>& precisionPerEachBlocks = allocation.second; EncodeTonalComponents(sce, precisionPerEachBlocks, bitStream); const uint32_t numBlocks = precisionPerEachBlocks.size(); //number of blocks to save @@ -471,7 +471,7 @@ void TAtrac3BitStreamWriter::WriteSoundUnit(const vector<TSingleChannelElement>& assert(s < 8); } } - const uint16_t bitsUsedByGainInfoAndHeader = bitStream->GetSizeInBits(); + const uint16_t bitsUsedByGainInfoAndHeader = (uint16_t)bitStream->GetSizeInBits(); usedBits[channel] = bitsUsedByGainInfoAndHeader; assert(bitStream->GetSizeInBits() == usedBits[channel]); } diff --git a/src/atrac/atrac3_bitstream.h b/src/atrac/atrac3_bitstream.h index b65e212..24c2ca7 100644 --- a/src/atrac/atrac3_bitstream.h +++ b/src/atrac/atrac3_bitstream.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,7 +25,7 @@ #include "atrac_scale.h" #include <vector> #include <utility> -#include <cfenv> +#include "../env.h" namespace NAtracDEnc { namespace NAtrac3 { @@ -89,7 +89,7 @@ public: , Params(params) , BfuIdxConst(bfuIdxConst) { - fesetround(FE_TONEAREST); + NEnv::SetRoundFloat(); } void WriteSoundUnit(const std::vector<TSingleChannelElement>& singleChannelElements); diff --git a/src/atrac/atrac3_qmf.h b/src/atrac/atrac3_qmf.h index 49f9dcd..6f26d54 100644 --- a/src/atrac/atrac3_qmf.h +++ b/src/atrac/atrac3_qmf.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac/atrac_psy_common.cpp b/src/atrac/atrac_psy_common.cpp index 8ff86a1..257b8f3 100644 --- a/src/atrac/atrac_psy_common.cpp +++ b/src/atrac/atrac_psy_common.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac/atrac_psy_common.h b/src/atrac/atrac_psy_common.h index 45a0706..dc1e65e 100644 --- a/src/atrac/atrac_psy_common.h +++ b/src/atrac/atrac_psy_common.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac/atrac_scale.cpp b/src/atrac/atrac_scale.cpp index a8a6fda..f24f7b0 100644 --- a/src/atrac/atrac_scale.cpp +++ b/src/atrac/atrac_scale.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac/atrac_scale.h b/src/atrac/atrac_scale.h index c992c03..eadb6bd 100644 --- a/src/atrac/atrac_scale.h +++ b/src/atrac/atrac_scale.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac1denc.cpp b/src/atrac1denc.cpp index 49ee644..e15680b 100644 --- a/src/atrac1denc.cpp +++ b/src/atrac1denc.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac1denc.h b/src/atrac1denc.h index 821591a..33bbc76 100644 --- a/src/atrac1denc.h +++ b/src/atrac1denc.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -20,7 +20,6 @@ #include "pcmengin.h" #include "aea.h" #include "oma.h" -#include "atrac_encode_settings.h" #include "transient_detector.h" #include "atrac/atrac1.h" #include "atrac/atrac1_qmf.h" diff --git a/src/atrac3denc.cpp b/src/atrac3denc.cpp index 519cd9b..9bb7eb2 100644 --- a/src/atrac3denc.cpp +++ b/src/atrac3denc.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atrac3denc.h b/src/atrac3denc.h index a07f717..adf6df6 100644 --- a/src/atrac3denc.h +++ b/src/atrac3denc.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -32,6 +32,7 @@ #include "mdct/mdct.h" #include "gain_processor.h" +#include <algorithm> #include <functional> #include <array> #include <cmath> @@ -41,10 +42,10 @@ namespace NAtracDEnc { inline uint16_t RelationToIdx(TFloat x) { if (x <= 0.5) { - x = 1.0 / std::max(x, 0.00048828125); + x = 1.0 / std::max(x, (TFloat)0.00048828125); return 4 + GetFirstSetBit(std::trunc(x)); } else { - x = std::min(x, 16.0); + x = std::min(x, (TFloat)16.0); return 4 - GetFirstSetBit(std::trunc(x)); } } diff --git a/src/atrac3denc_ut.cpp b/src/atrac3denc_ut.cpp index 7813210..f52af02 100644 --- a/src/atrac3denc_ut.cpp +++ b/src/atrac3denc_ut.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/atracdenc_ut.cpp b/src/atracdenc_ut.cpp index 7e81716..9b7b331 100644 --- a/src/atracdenc_ut.cpp +++ b/src/atracdenc_ut.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/bitstream/bitstream.cpp b/src/bitstream/bitstream.cpp index 733fb15..ddcd288 100644 --- a/src/bitstream/bitstream.cpp +++ b/src/bitstream/bitstream.cpp @@ -12,13 +12,13 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "bitstream.h" -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#ifndef BIGENDIAN_ORDER #define NBITSTREAM__LITTLE_ENDIAN_CPU #endif diff --git a/src/bitstream/bitstream.h b/src/bitstream/bitstream.h index 08eb74f..cfba790 100644 --- a/src/bitstream/bitstream.h +++ b/src/bitstream/bitstream.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/bitstream/bitstream_ut.cpp b/src/bitstream/bitstream_ut.cpp index d339b06..853b969 100644 --- a/src/bitstream/bitstream_ut.cpp +++ b/src/bitstream/bitstream_ut.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/compressed_io.h b/src/compressed_io.h index c325ce9..7fca4f1 100644 --- a/src/compressed_io.h +++ b/src/compressed_io.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,12 +25,12 @@ class ICompressedIO { public: class TFrame { - uint64_t Sz; + size_t Sz; char* Data; TFrame(const TFrame& src) = delete; TFrame() = delete; public: - TFrame(uint64_t sz) + TFrame(size_t sz) : Sz(sz) { Data = new char[Sz]; @@ -38,7 +38,7 @@ public: ~TFrame() { delete[] Data; } - uint64_t Size() const { return Sz; } + size_t Size() const { return Sz; } char* Get() { return Data; } }; virtual void WriteFrame(std::vector<char> data) = 0; diff --git a/src/config.h b/src/config.h index 8d9461d..0e2fd8d 100644 --- a/src/config.h +++ b/src/config.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -20,6 +20,8 @@ #define CONFIG_DOUBLE +#define NOMINMAX + #ifdef CONFIG_DOUBLE # define kiss_fft_scalar double typedef double TFloat; diff --git a/src/delay_buffer.h b/src/delay_buffer.h index 9e0cfb1..14555fa 100644 --- a/src/delay_buffer.h +++ b/src/delay_buffer.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/env.cpp b/src/env.cpp new file mode 100644 index 0000000..b5d2a5b --- /dev/null +++ b/src/env.cpp @@ -0,0 +1,31 @@ +/* + * 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 "env.h" + +#include <fenv.h> + +#pragma STDC FENV_ACCESS on + +namespace NEnv { + +void SetRoundFloat() { + fesetround(FE_TONEAREST); +} + +} // namespace NEnv
\ No newline at end of file diff --git a/src/atrac_encode_settings.h b/src/env.h index 897b6ae..66a21dc 100644 --- a/src/atrac_encode_settings.h +++ b/src/env.h @@ -12,12 +12,14 @@ * 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 + * 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 -namespace NAtracDEnc { +namespace NEnv { -} +void SetRoundFloat(); + +} // namespace NEnv
\ No newline at end of file diff --git a/src/gain_processor.h b/src/gain_processor.h index 66d6b1c..f43531f 100644 --- a/src/gain_processor.h +++ b/src/gain_processor.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/main.cpp b/src/main.cpp index a1f2867..4fb1ee9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -29,6 +29,11 @@ #include "atrac1denc.h" #include "atrac3denc.h" +#ifdef PLATFORM_WINDOWS +#include <windows.h> +#include <shellapi.h> +#endif + using std::cout; using std::cerr; using std::endl; @@ -104,8 +109,8 @@ enum EOptions static void CheckInputFormat(const TWav* p) { - if (p->IsFormatSupported() == false) - throw std::runtime_error("unsupported format of input file"); +// if (p->IsFormatSupported() == false) +// throw std::runtime_error("unsupported format of input file"); if (p->GetSampleRate() != 44100) throw std::runtime_error("unsupported sample rate"); @@ -199,7 +204,7 @@ static void PrepareAtrac3Encoder(const string& inFile, atracProcessor->reset(new TAtrac3Processor(std::move(omaIO), std::move(encoderSettings))); } -int main(int argc, char* const* argv) +int main_(int argc, char* const* argv) { const char* myName = argv[0]; static struct option longopts[] = { @@ -364,17 +369,57 @@ int main(int argc, char* const* argv) while (totalSamples > (processed = pcmEngine->ApplyProcess(pcmFrameSz, atracLambda))) { if (!noStdOut) - printProgress(processed*100/totalSamples); + printProgress(static_cast<int>(processed*100/totalSamples)); } if (!noStdOut) cout << "\nDone" << endl; } catch (TAeaIOError err) { cerr << "Aea IO fatal error: " << err.what() << endl; - exit(1); + return 1; } catch (TWavIOError err) { cerr << "Wav IO fatal error: " << err.what() << endl; - exit(1); + return 1; } + catch (const std::exception& ex) { + cerr << "Encode/Decode error: " << ex.what() << endl; + return 1; + } + return 0; +} + +int main(int argc, char* const* argv) { +#ifndef PLATFORM_WINDOWS + return main_(argc, argv); +# else + LPWSTR* argvW = CommandLineToArgvW(GetCommandLineW(), &argc); + + std::vector<char*> newArgv(argc); + + for (int i = 0; i < argc; i++) { + const size_t sz = wcslen(argvW[i]); + const size_t maxUtf8Len = sz * 4; + // 0 termination in any case + std::vector<char> buf(maxUtf8Len + 1); + + if (!WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, argvW[i], -1, buf.data(), maxUtf8Len, NULL, NULL)) { + DWORD lastError = GetLastError(); + std::cerr << "Unable to convert argument to uft8, " << std::hex << lastError << std::endl; + } + const size_t utf8Len = strlen(buf.data()); + newArgv[i] = new char[utf8Len + 1]; + strcpy(newArgv[i], buf.data()); + } + + + int rv = main_(argc, newArgv.data()); + + for (auto& a : newArgv) { + delete[] a; + } + + LocalFree(argvW); + return rv; +#endif } diff --git a/src/mdct/common.h b/src/mdct/common.h index 1dd2afd..9b3c893 100644 --- a/src/mdct/common.h +++ b/src/mdct/common.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/mdct/mdct.cpp b/src/mdct/mdct.cpp index eb77fde..6acd8d3 100644 --- a/src/mdct/mdct.cpp +++ b/src/mdct/mdct.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/mdct/mdct.h b/src/mdct/mdct.h index 6af27b0..97d5437 100644 --- a/src/mdct/mdct.h +++ b/src/mdct/mdct.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/mdct/mdct_ut.cpp b/src/mdct/mdct_ut.cpp index fcfd4ff..f9fb688 100644 --- a/src/mdct/mdct_ut.cpp +++ b/src/mdct/mdct_ut.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/oma.cpp b/src/oma.cpp index 3de7f8b..ceeaf69 100644 --- a/src/oma.cpp +++ b/src/oma.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/oma/liboma/include/oma.h b/src/oma/liboma/include/oma.h index 232f8e5..89fbbaa 100644 --- a/src/oma/liboma/include/oma.h +++ b/src/oma/liboma/include/oma.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/oma/liboma/src/liboma.c b/src/oma/liboma/src/liboma.c index d8a4358..d44b490 100644 --- a/src/oma/liboma/src/liboma.c +++ b/src/oma/liboma/src/liboma.c @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -25,11 +25,9 @@ #include <string.h> #include <assert.h> -//to use htonl -//TODO: rewrite -#include <arpa/inet.h> -static const int OMA_HEADER_SIZE = 96; +#define 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'}; @@ -44,7 +42,20 @@ enum { OMAERR_EOF = -6 }; +static uint32_t swapbyte32(uint32_t in) { +#ifdef BIGENDIAN_ORDER + return in; +#else + return ((in & 0xff) << 24 ) | ((in & 0xff00) << 8) | ((in & 0xff0000) >> 8) | ((in & 0xff000000) >> 24); +#endif +} + +#ifdef _MSC_VER +__declspec(thread) int err; +#else static __thread int err; +#endif + int oma_get_last_err() { return err; } @@ -116,7 +127,7 @@ static int oma_write_atrac3_header(uint32_t *params, oma_info_t *info) { 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); + *params = swapbyte32((OMAC_ID_ATRAC3 << 24) | (js << 17) | ((uint32_t)samplerate_idx << 13) | framesz); return 0; } @@ -152,7 +163,7 @@ static int oma_write_atrac3p_header(uint32_t *params, oma_info_t *info) { if (ch_id < 0) return -1; - *params = htonl((OMAC_ID_ATRAC3PLUS << 24) | ((int32_t)samplerate_idx << 13) | ((ch_id + 1) << 10) | framesz); + *params = swapbyte32((OMAC_ID_ATRAC3PLUS << 24) | ((int32_t)samplerate_idx << 13) | ((ch_id + 1) << 10) | framesz); return 0; } diff --git a/src/oma/liboma/src/oma_internal.h b/src/oma/liboma/src/oma_internal.h index f6e2e2e..1334080 100644 --- a/src/oma/liboma/src/oma_internal.h +++ b/src/oma/liboma/src/oma_internal.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/oma/liboma/src/tools/omacp.c b/src/oma/liboma/src/tools/omacp.c index 77e1325..b938f04 100644 --- a/src/oma/liboma/src/tools/omacp.c +++ b/src/oma/liboma/src/tools/omacp.c @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/oma/liboma/src/tools/omainfo.c b/src/oma/liboma/src/tools/omainfo.c index d9f31aa..1edee98 100644 --- a/src/oma/liboma/src/tools/omainfo.c +++ b/src/oma/liboma/src/tools/omainfo.c @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/pcm_io_sndfile.cpp b/src/pcm_io_sndfile.cpp new file mode 100644 index 0000000..52737c0 --- /dev/null +++ b/src/pcm_io_sndfile.cpp @@ -0,0 +1,81 @@ +/* + * 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 "wav.h" + +#include <sndfile.hh> +#include <algorithm> + +static int fileext_to_libsndfmt(const std::string& filename) { + int fmt = SF_FORMAT_WAV; //default fmt + if (filename == "-") + return SF_FORMAT_AU; + size_t pos = filename.find_last_of("."); + if (pos == std::string::npos || pos == filename.size() - 1) //no dot or filename. + return fmt; + std::string ext = filename.substr(pos+1); + std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper); + if (ext == "AU") { + fmt = SF_FORMAT_AU; + } else if (ext == "AIFF") { + fmt = SF_FORMAT_AIFF; + } else if (ext == "PCM" || ext == "RAW") { + fmt = SF_FORMAT_RAW; + } + return fmt; +} + +class TPCMIOSndFile : public IPCMProviderImpl { +public: + TPCMIOSndFile(const std::string& filename) + : File(SndfileHandle(filename)) + { + File.command(SFC_SET_NORM_DOUBLE /*| SFC_SET_NORM_FLOAT*/, nullptr, SF_TRUE); + } + TPCMIOSndFile(const std::string& filename, int channels, int sampleRate) + : File(SndfileHandle(filename, SFM_WRITE, fileext_to_libsndfmt(filename) | SF_FORMAT_PCM_16, channels, sampleRate)) + { + File.command(SFC_SET_NORM_DOUBLE /*| SFC_SET_NORM_FLOAT*/, nullptr, SF_TRUE); + } +public: + size_t GetChannelsNum() const override { + return File.channels(); + } + size_t GetSampleRate() const override { + return File.samplerate(); + } + size_t GetTotalSamples() const override { + return File.frames(); + } + size_t Read(TPCMBuffer<TFloat>& buf, size_t sz) override { + return File.readf(buf[0], sz); + } + size_t Write(const TPCMBuffer<TFloat>& buf, size_t sz) override { + return File.writef(buf[0], sz); + } +private: + mutable SndfileHandle File; +}; + +IPCMProviderImpl* CreatePCMIOReadImpl(const std::string& path) { + return new TPCMIOSndFile(path); +} + +IPCMProviderImpl* CreatePCMIOWriteImpl(const std::string& path, int channels, int sampleRate) { + return new TPCMIOSndFile(path, channels, sampleRate); +} diff --git a/src/pcmengin.h b/src/pcmengin.h index 629f98b..915fc7c 100644 --- a/src/pcmengin.h +++ b/src/pcmengin.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,7 @@ #include <exception> #include <functional> #include <cstdlib> +#include <iostream> #include <assert.h> #include <string.h> @@ -61,14 +62,18 @@ public: } T* operator[](size_t pos) { size_t rpos = pos * NumChannels; - if (rpos >= Buf_.size()) + if (rpos >= Buf_.size()) { + std::cerr << "attempt to access out of buffer pos: " << pos << std::endl; std::abort(); + } return &Buf_[rpos]; } const T* operator[](size_t pos) const { size_t rpos = pos * NumChannels; - if (rpos >= Buf_.size()) + if (rpos >= Buf_.size()) { + std::cerr << "attempt to access out of buffer pos: " << pos << std::endl; std::abort(); + } return &Buf_[rpos]; } uint16_t Channels() const { diff --git a/src/platform/win/getopt/getopt.h b/src/platform/win/getopt/getopt.h new file mode 100644 index 0000000..bfa86b3 --- /dev/null +++ b/src/platform/win/getopt/getopt.h @@ -0,0 +1,655 @@ +#ifndef __GETOPT_H__ +/** + * DISCLAIMER + * This file is part of the mingw-w64 runtime package. + * + * The mingw-w64 runtime package and its code is distributed in the hope that it + * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR + * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to + * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + /* + * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma warning(disable:4996); + +#define __GETOPT_H__ + +#define NOMINMAX + +/* All the headers include this file. */ +#include <crtdefs.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <windows.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +#undef optreset /* see getopt.h */ +#define optreset __mingw_optreset +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +//extern int optind; /* index of first non-option in argv */ +//extern int optopt; /* single option character, as parsed */ +//extern int opterr; /* flag to enable built-in diagnostics... */ +// /* (user may set to zero, to suppress) */ +// +//extern char *optarg; /* pointer to argument of current option */ + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#ifndef __CYGWIN__ +#define __progname __argv[0] +#else +extern char __declspec(dllimport) *__progname; +#endif + +#ifdef __CYGWIN__ +static char EMSG[] = ""; +#else +#define EMSG "" +#endif + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +static void +_vwarnx(const char *fmt,va_list ap) +{ + (void)fprintf(stderr,"%s: ",__progname); + if (fmt != NULL) + (void)vfprintf(stderr,fmt,ap); + (void)fprintf(stderr,"\n"); +} + +static void +warnx(const char *fmt,...) +{ + va_list ap; + va_start(ap,fmt); + _vwarnx(fmt,ap); + va_end(ap); +} + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +//extern int getopt(int nargc, char * const *nargv, const char *options); + +#ifdef _BSD_SOURCE +/* + * BSD adds the non-standard `optreset' feature, for reinitialisation + * of `getopt' parsing. We support this feature, for applications which + * proclaim their BSD heritage, before including this header; however, + * to maintain portability, developers are advised to avoid it. + */ +# define optreset __mingw_optreset +extern int optreset; +#endif +#ifdef __cplusplus +} +#endif +/* + * POSIX requires the `getopt' API to be specified in `unistd.h'; + * thus, `unistd.h' includes this header. However, we do not want + * to expose the `getopt_long' or `getopt_long_only' APIs, when + * included in this manner. Thus, close the standard __GETOPT_H__ + * declarations block, and open an additional __GETOPT_LONG_H__ + * specific block, only when *not* __UNISTD_H_SOURCED__, in which + * to declare the extended API. + */ +#endif /* !defined(__GETOPT_H__) */ + +#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) +#define __GETOPT_LONG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct option /* specification for a long form option... */ +{ + const char *name; /* option name, without leading hyphens */ + int has_arg; /* does it take an argument? */ + int *flag; /* where to save its status, or NULL */ + int val; /* its associated status value */ +}; + +enum /* permitted values for its `has_arg' field... */ +{ + no_argument = 0, /* option never takes an argument */ + required_argument, /* option always requires an argument */ + optional_argument /* option may take an argument */ +}; + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, ambiguous, match; + +#define IDENTICAL_INTERPRETATION(_x, _y) \ + (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ + long_options[(_x)].flag == long_options[(_y)].flag && \ + long_options[(_x)].val == long_options[(_y)].val) + + current_argv = place; + match = -1; + ambiguous = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + ambiguous = 0; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else if (!IDENTICAL_INTERPRETATION(i, match)) + ambiguous = 1; + } + if (ambiguous) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +#undef IDENTICAL_INTERPRETATION +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + * + * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or + * optreset != 0 for GNU compatibility. + */ + if (posixly_correct == -1 || optreset != 0) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = (char*)strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} + +//extern int getopt_long(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +//extern int getopt_long_only(int nargc, char * const *nargv, const char *options, +// const struct option *long_options, int *idx); +/* + * Previous MinGW implementation had... + */ +#ifndef HAVE_DECL_GETOPT +/* + * ...for the long form API only; keep this for compatibility. + */ +# define HAVE_DECL_GETOPT 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ diff --git a/src/platform/win/pcm_io_mf/pcm_io_mf.cpp b/src/platform/win/pcm_io_mf/pcm_io_mf.cpp new file mode 100644 index 0000000..b8ddd98 --- /dev/null +++ b/src/platform/win/pcm_io_mf/pcm_io_mf.cpp @@ -0,0 +1,493 @@ +/* + * 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 "../../../wav.h" +#include "../../../env.h" + +#include <comdef.h> + +#include <initguid.h> +#include <cguid.h> +#include <atlbase.h> +#include <windows.h> +#include <mfapi.h> +#include <mfidl.h> +#include <mfreadwrite.h> +#include <stdio.h> +#include <mferror.h> + +#include <propvarutil.h> + +#include <algorithm> +#include <iostream> +#include <sstream> + +#include <string> + +#pragma comment(lib, "Mfplat.lib") +#pragma comment(lib, "Mfreadwrite.lib") +#pragma comment(lib, "Propsys.lib") + +class THException : public std::exception { + static std::string hrToText(HRESULT hr) { + _com_error err(hr); + return err.ErrorMessage(); + } +public: + THException(HRESULT hr, const std::string& msg) + : std::exception((msg + ", " + hrToText(hr)).c_str()) + {} +}; + +static std::wstring Utf8ToMultiByte(const std::string& in) { + std::vector<wchar_t> buf; + int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, in.data(), in.size(), buf.data(), 0); + if (!len) { + throw std::exception("unable to convert utf8 to multiByte"); + } + buf.resize((size_t)len); + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, in.data(), in.size(), buf.data(), buf.size()); + return std::wstring(buf.data(), buf.size()); +} + +// TODO: add dither, noise shape? +static inline int16_t FloatToInt16(TFloat in) { + return std::min((int)INT16_MAX, std::max((int)INT16_MIN, (int)lrint(in * (TFloat)INT16_MAX))); +} + +static HRESULT WriteToFile(HANDLE hFile, void* p, DWORD cb) { + DWORD cbWritten = 0; + HRESULT hr = S_OK; + + BOOL bResult = WriteFile(hFile, p, cb, &cbWritten, NULL); + if (!bResult) { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + return hr; +} + +static HRESULT ConfigureAudioStream(IMFSourceReader *pReader, IMFMediaType **ppPCMAudio) { + CComPtr<IMFMediaType> pUncompressedAudioType; + CComPtr<IMFMediaType> pPartialType; + + // Select the first audio stream, and deselect all other streams. + HRESULT hr = pReader->SetStreamSelection( + (DWORD)MF_SOURCE_READER_ALL_STREAMS, FALSE); + + if (SUCCEEDED(hr)) { + hr = pReader->SetStreamSelection( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, TRUE); + } + + // Create a partial media type that specifies uncompressed PCM audio. + hr = MFCreateMediaType(&pPartialType); + + if (SUCCEEDED(hr)) { + hr = pPartialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); + } + + if (SUCCEEDED(hr)) { + hr = pPartialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM); + } + + // Set this type on the source reader. The source reader will + // load the necessary decoder. + if (SUCCEEDED(hr)) { + hr = pReader->SetCurrentMediaType( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + NULL, pPartialType); + } + + // Get the complete uncompressed format. + if (SUCCEEDED(hr)) { + hr = pReader->GetCurrentMediaType( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + &pUncompressedAudioType); + } + + // Ensure the stream is selected. + if (SUCCEEDED(hr)) { + hr = pReader->SetStreamSelection( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + TRUE); + } + + // Return the PCM format to the caller. + if (SUCCEEDED(hr)) { + *ppPCMAudio = pUncompressedAudioType; + (*ppPCMAudio)->AddRef(); + } + + return hr; +} + +static HRESULT CreatePCMAudioType(UINT32 sampleRate, UINT32 bitsPerSample, UINT32 cChannels, IMFMediaType **ppType) { + HRESULT hr = S_OK; + + CComPtr<IMFMediaType> pType; + + // Calculate derived values. + UINT32 blockAlign = cChannels * (bitsPerSample / 8); + UINT32 bytesPerSecond = blockAlign * sampleRate; + + // Create the empty media type. + hr = MFCreateMediaType(&pType); + + // Set attributes on the type. + if (SUCCEEDED(hr)) { + hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); + } + + if (SUCCEEDED(hr)) { + hr = pType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM); + } + + if (SUCCEEDED(hr)) { + hr = pType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, cChannels); + } + + if (SUCCEEDED(hr)) { + hr = pType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sampleRate); + } + + if (SUCCEEDED(hr)) { + hr = pType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, blockAlign); + } + + if (SUCCEEDED(hr)) { + hr = pType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bytesPerSecond); + } + + if (SUCCEEDED(hr)) { + hr = pType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample); + } + + if (SUCCEEDED(hr)) { + hr = pType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + } + + if (SUCCEEDED(hr)) { + // Return the type to the caller. + *ppType = pType; + (*ppType)->AddRef(); + } + + return hr; +} + +class TPCMIOMediaFoundationFile : public IPCMProviderImpl { +public: + TPCMIOMediaFoundationFile(const std::string& path) { + + HRESULT hr = S_OK; + + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + + if (!SUCCEEDED(hr)) { + throw THException(hr, "unable to initialize COM library"); + } + + hr = MFStartup(MF_VERSION); + + if (!SUCCEEDED(hr)) { + throw THException(hr, "unable to initialize Media Foundation platform"); + } + + std::wstring wpath = Utf8ToMultiByte(path); + + hr = MFCreateSourceReaderFromURL(wpath.c_str(), NULL, &Reader_); + + if (FAILED(hr)) { + throw THException(hr, "unable to open input file"); + } + + hr = ConfigureAudioStream(Reader_, &MediaType_); + + if (FAILED(hr)) { + throw THException(hr, "unable to get media type"); + } + + if (FAILED(MediaType_->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &ChannelsNum_))) { + throw THException(hr, "unable to get channels number"); + } + + if (FAILED(MediaType_->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &SampleRate_))) { + throw THException(hr, "unable to get sample rate"); + } + + if (FAILED(MediaType_->GetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, &BytesPerSample_))) { + throw THException(hr, "unable to get sample size"); + } + + if (!(ChannelsNum_ == 1 && BytesPerSample_ == 2 || ChannelsNum_ == 2 && BytesPerSample_ == 4)) { + throw THException(hr, "unsupported samaple format"); + } + + NEnv::SetRoundFloat(); + } + + TPCMIOMediaFoundationFile(const std::string& path, int channels, int sampleRate) { + + HRESULT hr = S_OK; + ChannelsNum_ = channels; + SampleRate_ = sampleRate; + + hr = CreatePCMAudioType(sampleRate, 16, channels, &MediaType_); + + if (FAILED(hr)) { + throw THException(hr, "unable to create PCM audio type"); + } + + UINT32 format = 0; + + WAVEFORMATEX *wav = NULL; + + hr = MFCreateWaveFormatExFromMFMediaType(MediaType_, &wav, &format); + + if (FAILED(hr)) { + throw THException(hr, "unable to create wave format"); + } + + DWORD header[] = { + // RIFF header + FCC('RIFF'), + 0, + FCC('WAVE'), + // Start of 'fmt ' chunk + FCC('fmt '), + format + }; + + DWORD dataHeader[] = { FCC('data'), 0 }; + + std::wstring wpath = Utf8ToMultiByte(path); + + OutFile = CreateFileW(wpath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, + CREATE_ALWAYS, 0, NULL); + + if (OutFile == INVALID_HANDLE_VALUE) { + hr = HRESULT_FROM_WIN32(GetLastError()); + throw THException(hr, "unable to open output file"); + } + + hr = WriteToFile(OutFile, header, sizeof(header)); + + // Write the WAVEFORMATEX structure. + if (SUCCEEDED(hr)) { + hr = WriteToFile(OutFile, wav, format); + } + + // Write the start of the 'data' chunk + + if (SUCCEEDED(hr)) { + hr = WriteToFile(OutFile, dataHeader, sizeof(dataHeader)); + } + + if (FAILED(hr)) { + throw THException(hr, "unable to write headers"); + } + + NEnv::SetRoundFloat(); + } + + ~TPCMIOMediaFoundationFile() { + if (OutFile) { + if (!CloseHandle(OutFile)) { + std::cerr << "unable to close handle" << std::endl; + } + } + } + +public: + size_t GetChannelsNum() const override { + return ChannelsNum_; + } + + size_t GetSampleRate() const override { + return SampleRate_; + } + + size_t GetTotalSamples() const override { + PROPVARIANT var; + LONGLONG duration; // duration in 100 nanosecond units + HRESULT hr = Reader_->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &var); + if (SUCCEEDED(hr)) { + hr = PropVariantToInt64(var, &duration); + PropVariantClear(&var); + } + if (!SUCCEEDED(hr)) { + throw THException(hr, "unable to extract duration from source"); + } + + const UINT32 bytesPerSecond = MFGetAttributeUINT32(MediaType_, MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 0); + const UINT32 blockSize = MFGetAttributeUINT32(MediaType_, MF_MT_AUDIO_BLOCK_ALIGNMENT, 0); + + if (!blockSize) { + throw THException(hr, "got zero block size"); + } + + const LONGLONG samplesPerSecond = bytesPerSecond / blockSize; + const LONGLONG totalSamples = samplesPerSecond * duration / 10000000; + + return static_cast<size_t>(totalSamples); + } + + void ConvertToPcmBuffer(const BYTE* audioData, TPCMBuffer<TFloat>& buf, size_t sz, size_t shift) { + if (ChannelsNum_ == 1) { + for (size_t i = 0; i < sz; i++) { + *(buf[i + shift] + 0) = (*(int16_t*)(audioData + i * 2 + 0)) / (TFloat)32768.0; + } + } else { + for (size_t i = 0; i < sz; i++) { + *(buf[i + shift] + 0) = (*(int16_t*)(audioData + i * 4 + 0)) / (TFloat)32768.0; + *(buf[i + shift] + 1) = (*(int16_t*)(audioData + i * 4 + 2)) / (TFloat)32768.0; + } + } + } + + size_t Read(TPCMBuffer<TFloat>& buf, size_t sz) override { + HRESULT hr = S_OK; + + const size_t sizeBytes = sz * BytesPerSample_; + + size_t curPos = 0; // position in outpuf buffer (TPCMBuffer) + + if (!Buf_.empty()) { + if (sizeBytes > (Buf_.size() - ConsummerPos_)) { + if (Buf_.size() & 0x3) { + std::cerr << "buffer misconfiguration" << std::endl; + abort(); + } + curPos = (Buf_.size() - ConsummerPos_) / BytesPerSample_; + ConvertToPcmBuffer(Buf_.data() + ConsummerPos_, buf, curPos, 0); + ConsummerPos_ = 0; + } else { + // We have all data in our buffer, just convert it and shift consumer position + ConvertToPcmBuffer(Buf_.data() + ConsummerPos_, buf, sz, 0); + ConsummerPos_ += sizeBytes; + return sz; + } + } + + bool cont = true; + while (cont) { + DWORD flags = 0; + DWORD readyToRead = 0; + CComPtr<IMFSample> sample; + + hr = Reader_->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + 0, NULL, &flags, NULL, &sample); + + if (FAILED(hr)) { + break; + } + + if (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED) { + std::cerr << "Type change - not supported by WAVE file format.\n" << std::endl; + break; + } + + if (flags & MF_SOURCE_READERF_ENDOFSTREAM) { + std::cerr << "End of input file.\n" << std::endl; + break; + } + + if (sample == NULL) { + continue; + } + LONGLONG duration; + sample->GetSampleDuration(&duration); + + CComPtr<IMFMediaBuffer> buffer; + BYTE *audioData = NULL; + + hr = sample->ConvertToContiguousBuffer(&buffer); + + if (FAILED(hr)) { + break; + } + + hr = buffer->Lock(&audioData, NULL, &readyToRead); + + if (FAILED(hr)) { + break; + } + + if (sizeBytes > (readyToRead + (curPos * BytesPerSample_))) { + const size_t ready = readyToRead / BytesPerSample_; + ConvertToPcmBuffer(audioData, buf, ready, curPos); + curPos += ready; + } else { + ConvertToPcmBuffer(audioData, buf, sz - curPos, curPos); + size_t leftBytes = readyToRead - (sz - curPos) * BytesPerSample_; + Buf_.resize(leftBytes); + std::memcpy(Buf_.data(), audioData + (sz - curPos) * BytesPerSample_, leftBytes); + // out buffer writen completely + curPos = sz; + cont = false; + } + + hr = buffer->Unlock(); + audioData = NULL; + + if (FAILED(hr)) { + break; + } + } + + if (FAILED(hr)) { + throw THException(hr, "unable to read from PCM stream"); + } + + return curPos; + } + + size_t Write(const TPCMBuffer<TFloat>& buf, size_t sz) override { + const size_t samples = ChannelsNum_ * sz; + Buf_.resize(samples * 2); + for (size_t i = 0; i < samples; i++) { + *(int16_t*)(Buf_.data() + i * 2) = FloatToInt16(*(buf[0] + i)); + } + if (FAILED(WriteToFile(OutFile, Buf_.data(), Buf_.size()))) { + throw std::exception("unable to write PCM buffer to file"); + } + return sz; + } + +private: + CComPtr<IMFSourceReader> Reader_; + CComPtr<IMFMediaType> MediaType_; + + uint32_t ChannelsNum_; + uint32_t SampleRate_; + uint32_t BytesPerSample_; + + // Temporial buffer and consumer position + std::vector<BYTE> Buf_; + size_t ConsummerPos_ = 0; + + HANDLE OutFile = NULL; +}; + +IPCMProviderImpl* CreatePCMIOReadImpl(const std::string& path) { + return new TPCMIOMediaFoundationFile(path); +} + +IPCMProviderImpl* CreatePCMIOWriteImpl(const std::string& path, int channels, int sampleRate) { + return new TPCMIOMediaFoundationFile(path, channels, sampleRate); +}
\ No newline at end of file diff --git a/src/qmf/qmf.cpp b/src/qmf/qmf.cpp index 09e6f16..24d2400 100644 --- a/src/qmf/qmf.cpp +++ b/src/qmf/qmf.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/qmf/qmf.h b/src/qmf/qmf.h index c00c1d3..67bbef2 100644 --- a/src/qmf/qmf.h +++ b/src/qmf/qmf.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/transient_detector.cpp b/src/transient_detector.cpp index 660b776..692c3a7 100644 --- a/src/transient_detector.cpp +++ b/src/transient_detector.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/transient_detector.h b/src/transient_detector.h index 392bc8e..0bd0ca0 100644 --- a/src/transient_detector.h +++ b/src/transient_detector.h @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/transient_detector_ut.cpp b/src/transient_detector_ut.cpp index 65055fd..e41d4ac 100644 --- a/src/transient_detector_ut.cpp +++ b/src/transient_detector_ut.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/util_ut.cpp b/src/util_ut.cpp index adef6ba..d4626a8 100644 --- a/src/util_ut.cpp +++ b/src/util_ut.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/wav.cpp b/src/wav.cpp index b5827b3..649189b 100644 --- a/src/wav.cpp +++ b/src/wav.cpp @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -28,56 +28,39 @@ #include "wav.h" #include "pcmengin.h" -static int fileext_to_libsndfmt(const std::string& filename) { - int fmt = SF_FORMAT_WAV; //default fmt - if (filename == "-") - return SF_FORMAT_AU; - size_t pos = filename.find_last_of("."); - if (pos == std::string::npos || pos == filename.size() - 1) //no dot or filename. - return fmt; - std::string ext = filename.substr(pos+1); - std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper); - if (ext == "AU") { - fmt = SF_FORMAT_AU; - } else if (ext == "AIFF") { - fmt = SF_FORMAT_AIFF; - } else if (ext == "PCM" || ext == "RAW") { - fmt = SF_FORMAT_RAW; - } - return fmt; -} +IPCMProviderImpl* CreatePCMIOReadImpl(const std::string& path); -TWav::TWav(const std::string& filename) - : File(SndfileHandle(filename)) { - File.command(SFC_SET_NORM_DOUBLE /*| SFC_SET_NORM_FLOAT*/, nullptr, SF_TRUE); -} +IPCMProviderImpl* CreatePCMIOWriteImpl(const std::string& path, int channels, int sampleRate); -TWav::TWav(const std::string& filename, int channels, int sampleRate) - : File(SndfileHandle(filename, SFM_WRITE, fileext_to_libsndfmt(filename) | SF_FORMAT_PCM_16, channels, sampleRate)) { - File.command(SFC_SET_NORM_DOUBLE /*| SFC_SET_NORM_FLOAT*/, nullptr, SF_TRUE); -} +TWav::TWav(const std::string& path) + : Impl(CreatePCMIOReadImpl(path)) +{ } + +TWav::TWav(const std::string& path, int channels, int sampleRate) + : Impl(CreatePCMIOWriteImpl(path, channels, sampleRate)) +{ } TWav::~TWav() { } uint64_t TWav::GetTotalSamples() const { - return File.frames(); + return Impl->GetTotalSamples(); } uint32_t TWav::GetChannelNum() const { - return File.channels(); + return Impl->GetChannelsNum(); } uint32_t TWav::GetSampleRate() const { - return File.samplerate(); + return Impl->GetSampleRate(); } -bool TWav::IsFormatSupported() const { - switch (File.format() & SF_FORMAT_TYPEMASK) { - case SF_FORMAT_WAV: - case SF_FORMAT_AIFF: - return true; - default: - return false; - } -} +//bool TWav::IsFormatSupported() const { +// switch (File.format() & SF_FORMAT_TYPEMASK) { +// case SF_FORMAT_WAV: +// case SF_FORMAT_AIFF: +// return true; +// default: +// return false; +// } +//} @@ -12,7 +12,7 @@ * 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 + * License along with AtracDEnc; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -21,7 +21,7 @@ #include <memory> #include <string> -#include <sndfile.hh> +#include "config.h" #include "pcmengin.h" @@ -70,8 +70,19 @@ public: } }; +class IPCMProviderImpl { +public: + virtual ~IPCMProviderImpl() = default; + virtual size_t GetChannelsNum() const = 0; + virtual size_t GetSampleRate() const = 0; + virtual size_t GetTotalSamples() const = 0; + virtual size_t Read(TPCMBuffer<TFloat>& buf, size_t sz) = 0; + virtual size_t Write(const TPCMBuffer<TFloat>& buf, size_t sz) = 0; +}; + +//TODO: split for reader/writer class TWav { - mutable SndfileHandle File; + mutable std::unique_ptr<IPCMProviderImpl> Impl; public: enum Mode { E_READ, @@ -83,7 +94,7 @@ public: uint32_t GetChannelNum() const; uint32_t GetSampleRate() const; uint64_t GetTotalSamples() const; - bool IsFormatSupported() const; + template<class T> IPCMReader<T>* GetPCMReader() const; @@ -96,9 +107,9 @@ typedef std::unique_ptr<TWav> TWavPtr; template<class T> IPCMReader<T>* TWav::GetPCMReader() const { return new TWavPcmReader<T>([this](TPCMBuffer<T>& data, const uint32_t size) { - if (data.Channels() != (size_t)File.channels()) + if (data.Channels() != Impl->GetChannelsNum()) throw TWrongReadBuffer(); - if (size_t read = File.readf(data[0], size) != size) { + if (size_t read = Impl->Read(data, size) != size) { assert(read < size); //fprintf(stderr, "to zero: %d\n", size-read); data.Zero(read, size - read); @@ -109,9 +120,9 @@ IPCMReader<T>* TWav::GetPCMReader() const { template<class T> IPCMWriter<T>* TWav::GetPCMWriter() { return new TWavPcmWriter<T>([this](const TPCMBuffer<T>& data, const uint32_t size) { - if (data.Channels() != (size_t)File.channels()) + if (data.Channels() != Impl->GetChannelsNum()) throw TWrongReadBuffer(); - if (File.writef(data[0], size) != size) { + if (Impl->Write(data, size) != size) { fprintf(stderr, "can't write block\n"); } }); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fdd974f..0425f7a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,6 +39,7 @@ add_executable(bitstream_test ${bitstream_test_sources}) target_link_libraries(bitstream_test gtest_main) set(atrac1mdct_test_sources + ../src/env.cpp ../src/atrac1denc.cpp ../src/transient_detector.cpp ../src/bitstream/bitstream.cpp @@ -56,6 +57,7 @@ add_executable(atrac1mdct_test ${atrac1mdct_test_sources}) target_link_libraries(atrac1mdct_test fft_impl gtest_main) set(atrac3mdct_test_sources + ../src/env.cpp ../src/atrac3denc.cpp ../src/transient_detector.cpp ../src/bitstream/bitstream.cpp |