diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ea78f75 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.yml] +indent_size = 2 +trim_trailing_whitespace = false + +[*.rb] +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e464b40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +build +tmp +data +*.o +*.swp +*.swo +*~ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c7aaeef --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.15) + +# コーデックライブラリ +project(SRLACodecLibrary C) +set(CODEC_LIB_NAME srlacodec) +add_library(${CODEC_LIB_NAME} + STATIC + $ + $ + $ + $ + $ + $ + ) + +# デコーダライブラリ +project(SRLADecoderLibrary C) +set(DECODER_LIB_NAME srladec) +add_library(${DECODER_LIB_NAME} + STATIC + $ + $ + $ + $ + ) + +# 依存するプロジェクト +add_subdirectory(libs) + +# テスト +if(NOT without-test) + enable_testing() + # C++環境でないとgtestがビルドできないので、CXXプロジェクトを作る + # -> Cとの挙動で差異が生じるかもしれない... + project(SRLATest CXX) + if(MSVC) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + else() + set(CMAKE_CXX_FLAGS "-std=gnu++11") # gtestがGNU独自拡張を使用しているため + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -DDEBUG") + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") + endif() + include(cmake/gtest.cmake) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) + add_subdirectory(test) +endif() diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..4b06cc4 --- /dev/null +++ b/COPYING @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 aikiriao + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e85bfd1 --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# SRLA + +aka Soleil Rising Lossless Audio codec + +# How to build + +## Requirement + +* [CMake](https://cmake.org) >= 3.15 + +## Build SRLA Codec + +```bash +git clone https://github.com/ShounoLab/SRLA.git +cd SRLA/tools/srla_codec +cmake -B build +cmake --build build +``` + +# Usage + +## SRLA Codec + +### Encode + +```bash +./srla -e INPUT.wav OUTPUT.srl +``` + +you can change compression mode by `-m` option. +Following example encoding in maximum compression (but slow) option. + +```bash +./srla -e -m 5 INPUT.wav OUTPUT.srl +``` + +### Decode + +```bash +./srla -d INPUT.srl OUTPUT.wav +``` + +## License + +MIT diff --git a/cmake/gtest.cmake b/cmake/gtest.cmake new file mode 100644 index 0000000..f4cd178 --- /dev/null +++ b/cmake/gtest.cmake @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.15) + +# Google Test settings +include(ExternalProject) + +# gtest追加 +ExternalProject_Add(GoogleTest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.12.0 + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + ) + +# インクルードパス追加 +ExternalProject_Get_Property(GoogleTest source_dir) +include_directories(${source_dir}/googletest/include) + +# 成果物のディレクトリ取得 +ExternalProject_Get_Property(GoogleTest binary_dir) + +# ライブラリ追加 +add_library(gtest STATIC IMPORTED) +if(MSVC) + set_target_properties(gtest + PROPERTIES + IMPORTED_LOCATION "${binary_dir}/lib/Debug/gtest.lib" + IMPORTED_LOCATION_DEBUG "${binary_dir}/lib/Debug/gtest.lib" + IMPORTED_LOCATION_RELEASE "${binary_dir}/lib/Release/gtest.lib" + ) +else() + set_target_properties(gtest + PROPERTIES + IMPORTED_LOCATION ${binary_dir}/lib/libgtest.a + ) +endif() + +# メインエントリ追加 +add_library(gtest_main STATIC IMPORTED) +if(MSVC) + set_target_properties(gtest_main + PROPERTIES + IMPORTED_LOCATION "${binary_dir}/lib/Debug/gtest_main.lib" + IMPORTED_LOCATION_DEBUG "${binary_dir}/lib/Debug/gtest_main.lib" + IMPORTED_LOCATION_RELEASE "${binary_dir}/lib/Release/gtest_main.lib" + ) +else() + set_target_properties(gtest_main + PROPERTIES + IMPORTED_LOCATION ${binary_dir}/lib/libgtest_main.a + ) +endif() diff --git a/evaluation/codec_comparison_summery.csv b/evaluation/codec_comparison_summery.csv new file mode 100644 index 0000000..cc315d9 --- /dev/null +++ b/evaluation/codec_comparison_summery.csv @@ -0,0 +1,19 @@ +,SFLA -m 0,SFLA -m 1,SFLA -m 2,SFLA -m 3,SFLA -m 4,SFLA -m 5 +classic mean encode time,2.1463422375623296,10.07418304959943,1.9410030808785546,8.168703015165022,4.3587425386216365,9.224339659192657 +genre mean encode time,2.12240976190562,9.805849101835305,2.0300083861255915,8.048654987900365,4.000971228283839,8.903252905958409 +jazz mean encode time,2.0901955034801376,10.233647398591351,2.0885893799250352,7.59833719809332,4.047462738277384,9.084297794282588 +popular mean encode time,2.1062993339775824,9.244210549952072,2.073766501603843,8.228736590277194,4.097600123214178,11.380466953318077 +right mean encode time,2.151912552638434,8.792490786101663,2.072541237903354,7.527640951489073,4.104422125150039,9.849197809848953 +total mean encode time,2.115843248752311,9.708067239300949,2.045844317304964,7.978871838339197,4.106302094538328,9.816523363852014 +classic mean decode time,0.23532411995210242,0.24146054144651874,0.18582631588974155,0.18499188976473074,0.1870066425538413,0.19813029993712092 +genre mean decode time,0.23754737758750252,0.2290321044232394,0.1965400700104293,0.18355494589056645,0.18623609007395378,0.18703174084555974 +jazz mean decode time,0.2310707603696749,0.22479188782559598,0.20146217343942385,0.19309912088540826,0.19099264815075956,0.1848804252351176 +popular mean decode time,0.2389101940810997,0.21725211364323724,0.20352851668634508,0.1972963818041787,0.20378588269988956,0.1858582672682349 +right mean decode time,0.24353698084874226,0.21764401628896637,0.2045213652897606,0.19755539783229734,0.21591995601500277,0.19682529757174275 +total mean decode time,0.23649355144538797,0.22564734279752893,0.19866716739334367,0.1912114554595751,0.1949207886774234,0.18865063312297645 +classic mean compression rate,43.94922828916623,43.94465295981446,43.85064224764169,43.84544940647651,43.846626459795914,43.84130142245651 +genre mean compression rate,58.406170090094484,58.35404773195658,58.318265983580794,58.26165970999139,58.30614357904465,58.25151775676188 +jazz mean compression rate,47.93229431056631,47.87342167392149,47.827005209736136,47.76487209475472,47.81897521735741,47.759679420383236 +popular mean compression rate,67.83985786295364,67.77027270365582,67.76775650884096,67.69427274648717,67.75300388322596,67.68276755961523 +right mean compression rate,60.85819586164452,60.806215550493555,60.768906383799134,60.714754945215724,60.75667699000537,60.70506580834179 +total mean compression rate,56.63037195657916,56.57902493722776,56.541321689295316,56.48674564261108,56.53066993209297,56.478361055202626 diff --git a/evaluation/codec_comparison_summery_sse41.csv b/evaluation/codec_comparison_summery_sse41.csv new file mode 100644 index 0000000..c8b05eb --- /dev/null +++ b/evaluation/codec_comparison_summery_sse41.csv @@ -0,0 +1,19 @@ +,SFLA -m 0,SFLA -m 1,SFLA -m 2,SFLA -m 3,SFLA -m 4,SFLA -m 5 +classic mean encode time,1.853644518349601,9.033031663315231,1.9307104294040605,6.774597894822078,3.771602566031967,8.52801475610579 +genre mean encode time,1.8938096272300136,9.380883281063456,1.9619670765323722,6.804070612250814,3.759008462857397,8.591708777157196 +jazz mean encode time,1.8868362771708331,9.543943597586129,1.9369440208780122,6.789060641169558,3.7530082732126586,8.611576517030251 +popular mean encode time,1.9083161670041597,9.28966216323793,1.9433015347541789,7.178663163399555,3.803710867246452,8.738973062562122 +right mean encode time,1.911434825229543,8.939300392415419,1.9494931226247207,6.885123601205838,3.8300126380047117,8.747879459326832 +total mean encode time,1.891338135811271,9.304410133917198,1.9444524662598615,6.915147139081168,3.7782227118327882,8.641923302163663 +classic mean decode time,0.16280584641137985,0.17383497498923337,0.161880264897766,0.16271098867523728,0.16140331657731016,0.16097628480396278 +genre mean decode time,0.16874013178574807,0.17292584458932456,0.16505980603675505,0.1630915347196641,0.1644649594814271,0.16233090020728527 +jazz mean decode time,0.16927131369020393,0.17662810854192657,0.16299880741136044,0.1616782013793746,0.1610726422144156,0.16376791265650223 +popular mean decode time,0.17225964085808726,0.1761409472513793,0.16542970807898155,0.17004469355453866,0.1643255223900824,0.16423267325594415 +right mean decode time,0.1741159126920204,0.17134420686599539,0.17055075810779607,0.1689573280641753,0.17340951382687358,0.17215079964422333 +total mean decode time,0.16935946205455407,0.1748124115833936,0.1645616244337823,0.16522131591907002,0.1637613226058114,0.1637280834557029 +classic mean compression rate,43.94922828916623,43.94465295981446,43.85064224764169,43.84544940647651,43.846626459795914,43.84130142245651 +genre mean compression rate,58.406170090094484,58.35404773195658,58.318265983580794,58.26165970999139,58.30614357904465,58.25151775676188 +jazz mean compression rate,47.93229431056631,47.87342167392149,47.827005209736136,47.76487209475472,47.81897521735741,47.759679420383236 +popular mean compression rate,67.83985786295364,67.77027270365582,67.76775650884096,67.69427274648717,67.75300388322596,67.68276755961523 +right mean compression rate,60.85819586164452,60.806215550493555,60.768906383799134,60.714754945215724,60.75667699000537,60.70506580834179 +total mean compression rate,56.63037195657916,56.57902493722776,56.541321689295316,56.48674564261108,56.53066993209297,56.478361055202626 diff --git a/include/srla.h b/include/srla.h new file mode 100644 index 0000000..6885e14 --- /dev/null +++ b/include/srla.h @@ -0,0 +1,49 @@ +#ifndef SRLA_H_INCLUDED +#define SRLA_H_INCLUDED + +#include "srla_stdint.h" + +/* フォーマットバージョン */ +#define SRLA_FORMAT_VERSION 1 + +/* コーデックバージョン */ +#define SRLA_CODEC_VERSION 1 + +/* ヘッダサイズ */ +#define SRLA_HEADER_SIZE 29 + +/* 処理可能な最大チャンネル数 */ +#define SRLA_MAX_NUM_CHANNELS 8 + +/* 最大係数サイズ */ +#define SRLA_MAX_COEFFICIENT_ORDER 32 + +/* パラメータプリセット数 */ +#define SRLA_NUM_PARAMETER_PRESETS 6 + + +/* API結果型 */ +typedef enum SRLAApiResultTag { + SRLA_APIRESULT_OK = 0, /* 成功 */ + SRLA_APIRESULT_INVALID_ARGUMENT, /* 無効な引数 */ + SRLA_APIRESULT_INVALID_FORMAT, /* 不正なフォーマット */ + SRLA_APIRESULT_INSUFFICIENT_BUFFER, /* バッファサイズが足りない */ + SRLA_APIRESULT_INSUFFICIENT_DATA, /* データが足りない */ + SRLA_APIRESULT_PARAMETER_NOT_SET, /* パラメータがセットされてない */ + SRLA_APIRESULT_DETECT_DATA_CORRUPTION, /* データ破損を検知した */ + SRLA_APIRESULT_NG /* 分類不能な失敗 */ +} SRLAApiResult; + +/* ヘッダ情報 */ +struct SRLAHeader { + uint32_t format_version; /* フォーマットバージョン */ + uint32_t codec_version; /* エンコーダバージョン */ + uint16_t num_channels; /* チャンネル数 */ + uint32_t num_samples; /* 1チャンネルあたり総サンプル数 */ + uint32_t sampling_rate; /* サンプリングレート */ + uint16_t bits_per_sample; /* サンプルあたりビット数 */ + uint32_t num_samples_per_block; /* ブロックあたりサンプル数 */ + uint8_t preset; /* パラメータプリセット */ +}; + +#endif /* SRLA_H_INCLUDED */ diff --git a/include/srla_decoder.h b/include/srla_decoder.h new file mode 100644 index 0000000..fcf59fb --- /dev/null +++ b/include/srla_decoder.h @@ -0,0 +1,55 @@ +#ifndef SRLA_DECODER_H_INCLUDED +#define SRLA_DECODER_H_INCLUDED + +#include "srla.h" +#include "srla_stdint.h" + +/* デコーダコンフィグ */ +struct SRLADecoderConfig { + uint32_t max_num_channels; /* 最大チャンネル数 */ + uint32_t max_num_parameters; /* 最大パラメータ数 */ + uint8_t check_checksum; /* チェックサムによるデータ破損検査を行うか? 1:ON それ以外:OFF */ +}; + +/* デコーダハンドル */ +struct SRLADecoder; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* ヘッダデコード */ +SRLAApiResult SRLADecoder_DecodeHeader( + const uint8_t *data, uint32_t data_size, struct SRLAHeader *header); + +/* デコーダハンドルの作成に必要なワークサイズの計算 */ +int32_t SRLADecoder_CalculateWorkSize(const struct SRLADecoderConfig *condig); + +/* デコーダハンドルの作成 */ +struct SRLADecoder* SRLADecoder_Create(const struct SRLADecoderConfig *condig, void *work, int32_t work_size); + +/* デコーダハンドルの破棄 */ +void SRLADecoder_Destroy(struct SRLADecoder *decoder); + +/* デコーダにヘッダをセット */ +SRLAApiResult SRLADecoder_SetHeader( + struct SRLADecoder *decoder, const struct SRLAHeader *header); + +/* 単一データブロックデコード */ +SRLAApiResult SRLADecoder_DecodeBlock( + struct SRLADecoder *decoder, + const uint8_t *data, uint32_t data_size, + int32_t **buffer, uint32_t buffer_num_channels, uint32_t buffer_num_samples, + uint32_t *decode_size, uint32_t *num_decode_samples); + +/* ヘッダを含めて全ブロックデコード */ +SRLAApiResult SRLADecoder_DecodeWhole( + struct SRLADecoder *decoder, + const uint8_t *data, uint32_t data_size, + int32_t **buffer, uint32_t buffer_num_channels, uint32_t buffer_num_samples); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SRLA_DECODER_H_INCLUDED */ diff --git a/include/srla_encoder.h b/include/srla_encoder.h new file mode 100644 index 0000000..e273503 --- /dev/null +++ b/include/srla_encoder.h @@ -0,0 +1,63 @@ +#ifndef SRLA_ENCODER_H_INCLUDED +#define SRLA_ENCODER_H_INCLUDED + +#include "srla.h" +#include "srla_stdint.h" + +/* エンコードパラメータ */ +struct SRLAEncodeParameter { + uint16_t num_channels; /* 入力波形のチャンネル数 */ + uint16_t bits_per_sample; /* 入力波形のサンプルあたりビット数 */ + uint32_t sampling_rate; /* 入力波形のサンプリングレート */ + uint32_t num_samples_per_block; /* ブロックあたりサンプル数 */ + uint8_t preset; /* エンコードパラメータプリセット */ +}; + +/* エンコーダコンフィグ */ +struct SRLAEncoderConfig { + uint32_t max_num_channels; /* 最大チャンネル数 */ + uint32_t max_num_samples_per_block; /* 最大のブロックあたりサンプル数 */ + uint32_t max_num_parameters; /* 最大のパラメータ数 */ +}; + +/* エンコーダハンドル */ +struct SRLAEncoder; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* ヘッダエンコード */ +SRLAApiResult SRLAEncoder_EncodeHeader( + const struct SRLAHeader *header, uint8_t *data, uint32_t data_size); + +/* エンコーダハンドル作成に必要なワークサイズ計算 */ +int32_t SRLAEncoder_CalculateWorkSize(const struct SRLAEncoderConfig *config); + +/* エンコーダハンドル作成 */ +struct SRLAEncoder *SRLAEncoder_Create(const struct SRLAEncoderConfig *config, void *work, int32_t work_size); + +/* エンコーダハンドルの破棄 */ +void SRLAEncoder_Destroy(struct SRLAEncoder *encoder); + +/* エンコードパラメータの設定 */ +SRLAApiResult SRLAEncoder_SetEncodeParameter( + struct SRLAEncoder *encoder, const struct SRLAEncodeParameter *parameter); + +/* 単一データブロックエンコード */ +SRLAApiResult SRLAEncoder_EncodeBlock( + struct SRLAEncoder *encoder, + const int32_t *const *input, uint32_t num_samples, + uint8_t *data, uint32_t data_size, uint32_t *output_size); + +/* ヘッダ含めファイル全体をエンコード */ +SRLAApiResult SRLAEncoder_EncodeWhole( + struct SRLAEncoder *encoder, + const int32_t *const *input, uint32_t num_samples, + uint8_t *data, uint32_t data_size, uint32_t *output_size); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SRLA_ENCODER_H_INCLUDED */ diff --git a/include/srla_stdint.h b/include/srla_stdint.h new file mode 100644 index 0000000..0f56c41 --- /dev/null +++ b/include/srla_stdint.h @@ -0,0 +1,11 @@ +#ifndef SRLA_STDINT_H_INCLUDED +#define SRLA_STDINT_H_INCLUDED + +/* stdint.hが無い環境向けに +* stdint.h相当の型定義を行う */ + +/* 現在のところは必ずstdint.hを使用する */ +/* 困った場合に定義を追加する */ +#include + +#endif /* SRLA_STDINT_H_INCLUDED */ diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt new file mode 100644 index 0000000..7b369dc --- /dev/null +++ b/libs/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.15) + +add_subdirectory(bit_stream) +add_subdirectory(byte_array) +add_subdirectory(command_line_parser) +add_subdirectory(srla_coder) +add_subdirectory(srla_decoder) +add_subdirectory(srla_encoder) +add_subdirectory(srla_internal) +add_subdirectory(lpc) +add_subdirectory(wav) diff --git a/libs/bit_stream/CMakeLists.txt b/libs/bit_stream/CMakeLists.txt new file mode 100644 index 0000000..a430e09 --- /dev/null +++ b/libs/bit_stream/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# プロジェクト名 +project(BitStream C) + +# ライブラリ名 +set(LIB_NAME bit_stream) + +# 静的ライブラリ指定 +add_library(${LIB_NAME} STATIC) + +# ソースディレクトリ +add_subdirectory(src) + +# インクルードパス +target_include_directories(${LIB_NAME} + PRIVATE + ${PROJECT_ROOT_PATH}/include + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + +# コンパイルオプション +if(MSVC) + target_compile_options(${LIB_NAME} PRIVATE /W4) +else() + target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=2 -Wconversion -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition) + set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +endif() +set_target_properties(${LIB_NAME} + PROPERTIES + C_STANDARD 90 C_EXTENSIONS OFF + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) diff --git a/libs/bit_stream/include/bit_stream.h b/libs/bit_stream/include/bit_stream.h new file mode 100644 index 0000000..75a3ca6 --- /dev/null +++ b/libs/bit_stream/include/bit_stream.h @@ -0,0 +1,446 @@ +#ifndef BITSTREAM_H_INCLUDED +#define BITSTREAM_H_INCLUDED + +#include +#include +#include +#include + +#if CHAR_BIT != 8 +#error "This program 8bit/byte system only." +#endif + +/* マクロを使うか否か? */ +#define BITSTREAM_USE_MACROS 1 + +/* BitStream_Seek関数の探索コード */ +#define BITSTREAM_SEEK_SET (int32_t)SEEK_SET +#define BITSTREAM_SEEK_CUR (int32_t)SEEK_CUR +#define BITSTREAM_SEEK_END (int32_t)SEEK_END + +/* 読みモードか?(0で書きモード) */ +#define BITSTREAM_FLAGS_MODE_READ (1 << 0) + +/* valの下位nbitsを取得 */ +#define BITSTREAM_GETLOWERBITS(val, nbits) ((val) & g_bitstream_lower_bits_mask[(nbits)]) + +/* ビットストリーム構造体 */ +struct BitStream { + uint32_t bit_buffer; /* ビットの一時バッファ */ + uint32_t bit_count; /* [Reader]バッファ残りビット数, [Writer]メモリ書き出しまでのビット数 */ + const uint8_t *memory_image; /* メモリ領域先頭 */ + const uint8_t *memory_tail;/* メモリ領域末尾 */ + size_t memory_size; /* メモリ領域サイズ */ + uint8_t *memory_p; /* メモリ読み書き位置 */ + uint8_t flags; /* 内部状態フラグ */ +}; + +/* NLZ(最上位ビットから1に当たるまでのビット数)の計算 */ +#if defined(__GNUC__) +/* ビルトイン関数を使用 */ +#define BITSTREAM_NLZ(x) (((x) > 0) ? (uint32_t)__builtin_clz(x) : 32U) +#elif defined(_MSC_VER) +/* ビルトイン関数を使用 */ +__inline uint32_t BITSTREAM_NLZ(uint32_t x) +{ + unsigned long result; + if (!x) { + return 32U; + } + _BitScanReverse(&result, x); + return result ^ 31U; +} +#else +/* ソフトウェア実装を使用 */ +#define BITSTREAM_NLZ(x) BitStream_NLZSoft(x) +#endif + +/* NLZのソフトウェア実装 */ +#ifdef __cplusplus +extern "C" uint32_t BitStream_NLZSoft(uint32_t x); +#else +uint32_t BitStream_NLZSoft(uint32_t x); +#endif + +#if !defined(BITSTREAM_USE_MACROS) + +#ifdef __cplusplus +extern "C" { +#endif + +/* ビットリーダのオープン */ +void BitReader_Open(struct BitStream *stream, const uint8_t *memory, size_t size); + +/* ビットライタのオープン */ +void BitWriter_Open(struct BitStream *stream, const uint8_t *memory, size_t size); + +/* ビットストリームのクローズ */ +void BitStream_Close(struct BitStream *stream); + +/* シーク(fseek準拠) */ +void BitStream_Seek(struct BitStream *stream, int32_t offset, int32_t origin); + +/* 現在位置(ftell)準拠 */ +void BitStream_Tell(struct BitStream *stream, int32_t *result); + +/* valの右側(下位)nbits 出力(最大32bit出力可能) */ +void BitWriter_PutBits(struct BitStream *stream, uint32_t val, uint32_t nbits); + +/* 0のランに続いて終わりの1を出力 */ +void BitWriter_PutZeroRun(struct BitStream *stream, uint32_t runlength); + +/* nbits 取得(最大32bit)し、その値を右詰めして出力 */ +void BitReader_GetBits(struct BitStream *stream, uint32_t *val, uint32_t nbits); + +/* つぎの1にぶつかるまで読み込み、その間に読み込んだ0のランレングスを取得 */ +void BitReader_GetZeroRunLength(struct BitStream *stream, uint32_t *runlength); + +/* バッファにたまったビットをクリア */ +void BitStream_Flush(struct BitStream *stream); + +#ifdef __cplusplus +} +#endif + +#else /* BITSTREAM_USE_MACROS */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* 下位ビットを取り出すためのマスク */ +extern const uint32_t g_bitstream_lower_bits_mask[33]; + +/* ラン長のパターンテーブル */ +extern const uint32_t g_bitstream_zerobit_runlength_table[0x100]; + +#ifdef __cplusplus +} +#endif + +/* ビットリーダのオープン */ +#define BitReader_Open(stream, memory, size)\ + do {\ + /* 引数チェック */\ + assert((void *)(stream) != NULL);\ + assert((void *)(memory) != NULL);\ + \ + /* 内部状態リセット */\ + (stream)->flags = 0;\ + \ + /* バッファ初期化 */\ + (stream)->bit_count = 0;\ + (stream)->bit_buffer = 0;\ + \ + /* メモリセット */\ + (stream)->memory_image = (memory);\ + (stream)->memory_size = (size);\ + (stream)->memory_tail = (memory) + (size);\ + \ + /* 読み出し位置は先頭に */\ + (stream)->memory_p = (memory);\ + \ + /* 読みモードとしてセット */\ + (stream)->flags |= (uint8_t)BITSTREAM_FLAGS_MODE_READ;\ + } while (0) + +/* ビットライタのオープン */ +#define BitWriter_Open(stream, memory, size)\ + do {\ + /* 引数チェック */\ + assert((void *)(stream) != NULL);\ + assert((void *)(memory) != NULL);\ + \ + /* 内部状態リセット */\ + (stream)->flags = 0;\ + \ + /* バッファ初期化 */\ + (stream)->bit_count = 32;\ + (stream)->bit_buffer = 0;\ + \ + /* メモリセット */\ + (stream)->memory_image = (memory);\ + (stream)->memory_size = (size);\ + (stream)->memory_tail = (memory) + (size);\ + \ + /* 読み出し位置は先頭に */\ + (stream)->memory_p = (memory);\ + \ + /* 書きモードとしてセット */\ + (stream)->flags &= (uint8_t)(~BITSTREAM_FLAGS_MODE_READ);\ + } while (0) + +/* ビットストリームのクローズ */ +#define BitStream_Close(stream)\ + do {\ + /* 引数チェック */\ + assert((void *)(stream) != NULL);\ + \ + /* 残ったデータをフラッシュ */\ + BitStream_Flush(stream);\ + \ + /* バッファのクリア */\ + (stream)->bit_buffer = 0;\ + \ + /* メモリ情報のクリア */\ + (stream)->memory_image = NULL;\ + (stream)->memory_size = 0;\ + \ + /* 内部状態のクリア */\ + (stream)->memory_p = NULL;\ + (stream)->flags = 0;\ + } while (0) + +/* シーク(fseek準拠) */ +#define BitStream_Seek(stream, offset, origin)\ + do {\ + uint8_t* __pos = NULL;\ + \ + /* 引数チェック */\ + assert((void *)(stream) != NULL);\ + \ + /* 内部バッファをクリア(副作用が起こる) */\ + BitStream_Flush(stream);\ + \ + /* 起点をまず定める */\ + switch (origin) {\ + case BITSTREAM_SEEK_CUR:\ + __pos = (stream)->memory_p;\ + break;\ + case BITSTREAM_SEEK_SET:\ + __pos = (uint8_t *)(stream)->memory_image;\ + break;\ + case BITSTREAM_SEEK_END:\ + __pos = (uint8_t *)((stream)->memory_tail - 1);\ + break;\ + default:\ + assert(0);\ + }\ + \ + /* オフセット分動かす */\ + __pos += (offset);\ + \ + /* 範囲チェック */\ + assert(__pos >= (stream)->memory_image);\ + assert(__pos < (stream)->memory_tail);\ + \ + /* 結果の保存 */\ + (stream)->memory_p = __pos;\ + } while (0) + +/* 現在位置(ftell)準拠 */ +#define BitStream_Tell(stream, result)\ + do {\ + /* 引数チェック */\ + assert((void *)(stream) != NULL);\ + assert((void *)(result) != NULL);\ + \ + /* アクセスオフセットを返す */\ + (*result) = (int32_t)\ + ((stream)->memory_p - (stream)->memory_image);\ + } while (0) + +/* valの右側(下位)nbits 出力(最大32bit出力可能) */ +#define BitWriter_PutBits(stream, val, nbits)\ + do {\ + uint32_t __nbits;\ + \ + /* 引数チェック */\ + assert((void *)(stream) != NULL);\ + \ + /* 読み込みモードでは実行不可能 */\ + assert(!((stream)->flags & BITSTREAM_FLAGS_MODE_READ));\ + \ + /* 出力可能な最大ビット数を越えてないか確認 */\ + assert((nbits) <= 32);\ + \ + /* 0ビット出力は何もせず終了 */\ + if ((nbits) == 0) { break; }\ + \ + /* valの上位ビットから順次出力 */\ + __nbits = (nbits);\ + if (__nbits >= (stream)->bit_count) {\ + __nbits -= (stream)->bit_count;\ + (stream)->bit_buffer |= BITSTREAM_GETLOWERBITS((val) >> __nbits, (stream)->bit_count);\ + \ + /* 終端に達していないかチェック */\ + assert((stream)->memory_p >= (stream)->memory_image);\ + assert(((stream)->memory_p + 3) < (stream)->memory_tail);\ + \ + /* メモリに書き出し */\ + (stream)->memory_p[0] = (((stream)->bit_buffer >> 24) & 0xFF);\ + (stream)->memory_p[1] = (((stream)->bit_buffer >> 16) & 0xFF);\ + (stream)->memory_p[2] = (((stream)->bit_buffer >> 8) & 0xFF);\ + (stream)->memory_p[3] = (((stream)->bit_buffer >> 0) & 0xFF);\ + (stream)->memory_p += 4;\ + \ + /* バッファをリセット */\ + (stream)->bit_buffer = 0;\ + (stream)->bit_count = 32;\ + }\ + \ + /* 端数ビットの処理: 残った分をバッファの上位ビットにセット */\ + assert(__nbits <= 32);\ + (stream)->bit_count -= __nbits;\ + (stream)->bit_buffer |= BITSTREAM_GETLOWERBITS(val, __nbits) << (stream)->bit_count;\ + } while (0) + +/* 0のランに続いて終わりの1を出力 */ +#define BitWriter_PutZeroRun(stream, runlength)\ + do {\ + uint32_t __run = ((runlength) + 1);\ + \ + /* 引数チェック */\ + assert((void *)(stream) != NULL);\ + \ + /* 読み込みモードでは実行不可能 */\ + assert(!((stream)->flags & BITSTREAM_FLAGS_MODE_READ));\ + \ + /* 31ビット単位で出力 */\ + while (__run > 31) {\ + BitWriter_PutBits(stream, 0, 31);\ + __run -= 31;\ + }\ + /* 終端の1を出力 */\ + BitWriter_PutBits(stream, 1, __run);\ + } while (0) + +/* nbits 取得(最大32bit)し、その値を右詰めして出力 */ +#define BitReader_GetBits(stream, val, nbits)\ + do {\ + uint32_t __tmp, __nbits;\ + \ + /* 引数チェック */\ + assert((void *)(stream) != NULL);\ + assert((void *)(val) != NULL);\ + \ + /* 読み込みモードでない場合はアサート */\ + assert((stream)->flags & BITSTREAM_FLAGS_MODE_READ);\ + \ + /* 入力可能な最大ビット数を越えてないか確認 */\ + assert((nbits) <= 32);\ + \ + /* 0ビット取得は0を返す */\ + if ((nbits) == 0) {\ + (*(val)) = 0;\ + break;\ + }\ + \ + /* バッファから取り出す */\ + if ((nbits) <= (stream)->bit_count) {\ + (stream)->bit_count -= (nbits);\ + (*(val)) = BITSTREAM_GETLOWERBITS((stream)->bit_buffer >> (stream)->bit_count, (nbits));\ + break;\ + }\ + \ + /* 現在のバッファ容量よりも多くのビットが要求されたらメモリから読み出し */\ + __nbits = (nbits);\ + /* 残りのビットを上位ビットにセット */\ + __nbits -= (stream)->bit_count;\ + __tmp = BITSTREAM_GETLOWERBITS((stream)->bit_buffer, (stream)->bit_count) << __nbits;\ + \ + /* 終端に達していないかチェック */\ + assert((stream)->memory_p >= (stream)->memory_image);\ + assert((stream)->memory_p < (stream)->memory_tail);\ + \ + /* メモリから読み出し */\ + (stream)->bit_buffer\ + = ((uint32_t)(stream)->memory_p[0] << 24) | ((uint32_t)(stream)->memory_p[1] << 16)\ + | ((uint32_t)(stream)->memory_p[2] << 8) | ((uint32_t)(stream)->memory_p[3] << 0);\ + (stream)->memory_p += 4;\ + (stream)->bit_count = 32;\ + \ + /* 端数ビットの処理 残ったビット分をtmpの最上位ビットにセット */\ + (stream)->bit_count -= __nbits;\ + __tmp |= BITSTREAM_GETLOWERBITS((stream)->bit_buffer >> (stream)->bit_count, __nbits);\ + \ + /* 正常終了 */\ + (*(val)) = __tmp;\ + } while (0) + +/* つぎの1にぶつかるまで読み込み、その間に読み込んだ0のランレングスを取得 */ +#define BitReader_GetZeroRunLength(stream, runlength)\ + do {\ + uint32_t __run;\ + \ + /* 引数チェック */\ + assert((void *)(stream) != NULL);\ + assert((void *)(runlength) != NULL);\ + \ + /* 上位ビットからの連続する0を計測 */\ + __run = BITSTREAM_NLZ(BITSTREAM_GETLOWERBITS((stream)->bit_buffer, (stream)->bit_count)) + (stream)->bit_count - 32;\ + \ + /* 読み込んだ分カウントを減らす */\ + assert((stream)->bit_count >= __run);\ + (stream)->bit_count -= __run;\ + \ + /* バッファが空の時 */\ + while ((stream)->bit_count == 0) {\ + /* 1バイト読み込み再度計測 */\ + uint32_t __tmp_run;\ + \ + /* 終端に達していないかチェック */\ + assert((stream)->memory_p >= (stream)->memory_image);\ + assert((stream)->memory_p < (stream)->memory_tail);\ + \ + /* メモリから読み出し ビットバッファにセットし直して再度ランを計測 */\ + (stream)->bit_buffer = (stream)->memory_p[0];\ + (stream)->memory_p++;\ + /* テーブルによりラン長を取得 */\ + __tmp_run = g_bitstream_zerobit_runlength_table[(stream)->bit_buffer];\ + (stream)->bit_count = 8 - __tmp_run;\ + /* ランを加算 */\ + __run += __tmp_run;\ + }\ + \ + /* 続く1を空読み */\ + assert((stream)->bit_count >= 1);\ + (stream)->bit_count -= 1;\ + \ + /* 正常終了 */\ + (*(runlength)) = __run;\ + } while (0) + +/* バッファにたまったビットをクリア */ +#define BitStream_Flush(stream)\ + do {\ + /* 引数チェック */\ + assert((void *)(stream) != NULL);\ + \ + if ((stream)->flags & BITSTREAM_FLAGS_MODE_READ) {\ + /* バッファに余ったバイト分だけ読み出し位置を戻し、バッファクリア */\ + (stream)->memory_p -= ((stream)->bit_count >> 3);\ + (stream)->bit_buffer = 0;\ + (stream)->bit_count = 0;\ + } else {\ + if ((stream)->bit_count < 32) {\ + /* 次のバイト境界まで出力 */\ + const uint32_t __remainbits = 32 - (stream)->bit_count;\ + if (__remainbits > 24) {\ + (stream)->memory_p[0] = (((stream)->bit_buffer >> 24) & 0xFF);\ + (stream)->memory_p[1] = (((stream)->bit_buffer >> 16) & 0xFF);\ + (stream)->memory_p[2] = (((stream)->bit_buffer >> 8) & 0xFF);\ + (stream)->memory_p[3] = (((stream)->bit_buffer >> 0) & 0xFF);\ + (stream)->memory_p += 4;\ + } else if (__remainbits > 16) {\ + (stream)->memory_p[0] = (((stream)->bit_buffer >> 24) & 0xFF);\ + (stream)->memory_p[1] = (((stream)->bit_buffer >> 16) & 0xFF);\ + (stream)->memory_p[2] = (((stream)->bit_buffer >> 8) & 0xFF);\ + (stream)->memory_p += 3;\ + } else if (__remainbits > 8) {\ + (stream)->memory_p[0] = (((stream)->bit_buffer >> 24) & 0xFF);\ + (stream)->memory_p[1] = (((stream)->bit_buffer >> 16) & 0xFF);\ + (stream)->memory_p += 2;\ + } else {\ + (stream)->memory_p[0] = (((stream)->bit_buffer >> 24) & 0xFF);\ + (stream)->memory_p += 1;\ + }\ + (stream)->bit_count = 32;\ + (stream)->bit_buffer = 0;\ + }\ + }\ + } while (0) + +#endif /* BITSTREAM_USE_MACROS */ + +#endif /* BITSTREAM_H_INCLUDED */ diff --git a/libs/bit_stream/src/CMakeLists.txt b/libs/bit_stream/src/CMakeLists.txt new file mode 100644 index 0000000..5e63ab7 --- /dev/null +++ b/libs/bit_stream/src/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/bit_stream.c + ) diff --git a/libs/bit_stream/src/bit_stream.c b/libs/bit_stream/src/bit_stream.c new file mode 100644 index 0000000..11ead91 --- /dev/null +++ b/libs/bit_stream/src/bit_stream.c @@ -0,0 +1,388 @@ +#include "bit_stream.h" +#include + +/* 下位ビットを取り出すマスク 32bitまで */ +const uint32_t g_bitstream_lower_bits_mask[33] = { + 0x00000000U, + 0x00000001U, 0x00000003U, 0x00000007U, 0x0000000FU, + 0x0000001FU, 0x0000003FU, 0x0000007FU, 0x000000FFU, + 0x000001FFU, 0x000003FFU, 0x000007FFU, 0x00000FFFU, + 0x00001FFFU, 0x00003FFFU, 0x00007FFFU, 0x0000FFFFU, + 0x0001FFFFU, 0x0003FFFFU, 0x0007FFFFU, 0x000FFFFFU, + 0x001FFFFFU, 0x003FFFFFU, 0x007FFFFFU, 0x00FFFFFFU, + 0x01FFFFFFU, 0x03FFFFFFU, 0x07FFFFFFU, 0x0FFFFFFFU, + 0x1FFFFFFFU, 0x3FFFFFFFU, 0x7FFFFFFFU, 0xFFFFFFFFU +}; + +/* 0のラン長パターンテーブル(注意:上位ビットからのラン長) */ +const uint32_t g_bitstream_zerobit_runlength_table[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* NLZ計算のためのテーブル */ +#define UNUSED 99 +static const uint32_t st_nlz10_table[64] = { + 32, 20, 19, UNUSED, UNUSED, 18, UNUSED, 7, + 10, 17, UNUSED, UNUSED, 14, UNUSED, 6, UNUSED, + UNUSED, 9, UNUSED, 16, UNUSED, UNUSED, 1, 26, + UNUSED, 13, UNUSED, UNUSED, 24, 5, UNUSED, UNUSED, + UNUSED, 21, UNUSED, 8, 11, UNUSED, 15, UNUSED, + UNUSED, UNUSED, UNUSED, 2, 27, 0, 25, UNUSED, + 22, UNUSED, 12, UNUSED, UNUSED, 3, 28, UNUSED, + 23, UNUSED, 4, 29, UNUSED, UNUSED, 30, 31 +}; +#undef UNUSED + +#if !defined(BITSTREAM_USE_MACROS) + +/* ビットリーダのオープン */ +void BitReader_Open(struct BitStream *stream, const uint8_t *memory, size_t size) +{ + /* 引数チェック */ + assert(stream != NULL); + assert(memory != NULL); + + /* 内部状態リセット */ + stream->flags = 0; + + /* バッファ初期化 */ + stream->bit_count = 0; + stream->bit_buffer = 0; + + /* メモリセット */ + stream->memory_image = memory; + stream->memory_size = size; + stream->memory_tail = memory + size; + + /* 読み出し位置は先頭に */ + stream->memory_p = (uint8_t *)(memory); + + /* 読みモードとしてセット */ + stream->flags |= (uint8_t)BITSTREAM_FLAGS_MODE_READ; +} + +/* ビットライタのオープン */ +void BitWriter_Open(struct BitStream *stream, const uint8_t *memory, size_t size) +{ + /* 引数チェック */ + assert(stream != NULL); + assert(memory != NULL); + + /* 内部状態リセット */ + stream->flags = 0; + + /* バッファ初期化 */ + stream->bit_count = 32; + stream->bit_buffer = 0; + + /* メモリセット */ + stream->memory_image = memory; + stream->memory_size = size; + stream->memory_tail = memory + size; + + /* 読み出し位置は先頭に */ + stream->memory_p = (uint8_t *)(memory); + + /* 書きモードとしてセット */ + stream->flags &= (uint8_t)(~BITSTREAM_FLAGS_MODE_READ); +} + +/* ビットストリームのクローズ */ +void BitStream_Close(struct BitStream *stream) +{ + /* 引数チェック */ + assert(stream != NULL); + + /* 残ったデータをフラッシュ */ + BitStream_Flush(stream); + + /* バッファのクリア */ + stream->bit_buffer = 0; + + /* メモリ情報のクリア */ + stream->memory_image = NULL; + stream->memory_size = 0; + + /* 内部状態のクリア */ + stream->memory_p = NULL; + stream->flags = 0; +} + +/* シーク(fseek準拠) */ +void BitStream_Seek(struct BitStream *stream, int32_t offset, int32_t origin) +{ + uint8_t *pos = NULL; + + /* 引数チェック */ + assert(stream != NULL); + + /* 内部バッファをクリア(副作用が起こる) */ + BitStream_Flush(stream); + + /* 起点をまず定める */ + switch (origin) { + case BITSTREAM_SEEK_CUR: + pos = stream->memory_p; + break; + case BITSTREAM_SEEK_SET: + pos = (uint8_t *)stream->memory_image; + break; + case BITSTREAM_SEEK_END: + pos = (uint8_t *)((stream)->memory_tail - 1); + break; + default: + assert(0); + } + + /* オフセット分動かす */ + pos += (offset); + + /* 範囲チェック */ + assert(pos >= stream->memory_image); + assert(pos < (stream)->memory_tail); + + /* 結果の保存 */ + stream->memory_p = pos; +} + +/* 現在位置(ftell)準拠 */ +void BitStream_Tell(struct BitStream *stream, int32_t *result) +{ + /* 引数チェック */ + assert(stream != NULL); + assert(result != NULL); + + /* アクセスオフセットを返す */ + (*result) = (int32_t)(stream->memory_p - stream->memory_image); +} + +/* valの右側(下位)nbits 出力(最大32bit出力可能) */ +void BitWriter_PutBits(struct BitStream *stream, uint32_t val, uint32_t nbits) +{ + /* 引数チェック */ + assert(stream != NULL); + + /* 読み込みモードでは実行不可能 */ + assert(!(stream->flags & BITSTREAM_FLAGS_MODE_READ)); + + /* 出力可能な最大ビット数を越えてないか確認 */ + assert(nbits <= 32); + + /* 0ビット出力は何もせず終了 */ + if (nbits == 0) { return; } + + /* valの上位ビットから順次出力 */ + if (nbits >= stream->bit_count) { + nbits -= stream->bit_count; + stream->bit_buffer |= BITSTREAM_GETLOWERBITS(val >> nbits, stream->bit_count); + + /* 終端に達していないかチェック */ + assert(stream->memory_p >= stream->memory_image); + assert((stream->memory_p + 3) < stream->memory_tail); + + /* メモリに書き出し */ + stream->memory_p[0] = ((stream->bit_buffer >> 24) & 0xFF); + stream->memory_p[1] = ((stream->bit_buffer >> 16) & 0xFF); + stream->memory_p[2] = ((stream->bit_buffer >> 8) & 0xFF); + stream->memory_p[3] = ((stream->bit_buffer >> 0) & 0xFF); + stream->memory_p += 4; + + /* バッファをリセット */ + stream->bit_buffer = 0; + stream->bit_count = 32; + } + + /* 端数ビットの処理: 残った分をバッファの上位ビットにセット */ + assert(nbits <= 32); + stream->bit_count -= nbits; + stream->bit_buffer |= BITSTREAM_GETLOWERBITS(val, nbits) << stream->bit_count; +} + +/* 0のランに続いて終わりの1を出力 */ +void BitWriter_PutZeroRun(struct BitStream *stream, uint32_t runlength) +{ + uint32_t run = runlength + 1; + + /* 引数チェック */ + assert(stream != NULL); + + /* 読み込みモードでは実行不可能 */ + assert(!(stream->flags & BITSTREAM_FLAGS_MODE_READ)); + + /* 31ビット単位で出力 */ + while (run > 31) { + BitWriter_PutBits(stream, 0, 31); + run -= 31; + } + + /* 終端の1を出力 */ + BitWriter_PutBits(stream, 1, run); +} + +/* nbits 取得(最大32bit)し、その値を右詰めして出力 */ +void BitReader_GetBits(struct BitStream *stream, uint32_t *val, uint32_t nbits) +{ + uint32_t tmp = 0; + + /* 引数チェック */ + assert(stream != NULL); + assert(val != NULL); + + /* 読み込みモードでない場合はアサート */ + assert(stream->flags & BITSTREAM_FLAGS_MODE_READ); + + /* 入力可能な最大ビット数を越えてないか確認 */ + assert(nbits <= 32); + + /* 0ビット取得は0を返す */ + if (nbits == 0) { + (*val) = 0; + return; + } + + /* バッファから取り出す */ + if (nbits <= stream->bit_count) { + stream->bit_count -= nbits; + (*val) = BITSTREAM_GETLOWERBITS(stream->bit_buffer >> stream->bit_count, nbits); + return; + } + + /* 現在のバッファ容量よりも多くのビットが要求されたらメモリから読み出し */ + + /* 残りのビットを上位ビットにセット */ + nbits -= stream->bit_count; + tmp = BITSTREAM_GETLOWERBITS(stream->bit_buffer, stream->bit_count) << nbits; + + /* 終端に達していないかチェック */ + assert(stream->memory_p >= stream->memory_image); + assert(stream->memory_p < stream->memory_tail); + + /* メモリから読み出し */ + stream->bit_buffer + = ((uint32_t)stream->memory_p[0] << 24) | ((uint32_t)stream->memory_p[1] << 16) + | ((uint32_t)stream->memory_p[2] << 8) | ((uint32_t)stream->memory_p[3] << 0); + stream->memory_p += 4; + stream->bit_count = 32; + + /* 端数ビットの処理 残ったビット分をtmpの最上位ビットにセット */ + stream->bit_count -= nbits; + tmp |= BITSTREAM_GETLOWERBITS(stream->bit_buffer >> stream->bit_count, nbits); + + /* 正常終了 */ + (*val) = tmp; +} + +/* つぎの1にぶつかるまで読み込み、その間に読み込んだ0のランレングスを取得 */ +void BitReader_GetZeroRunLength(struct BitStream *stream, uint32_t *runlength) +{ + uint32_t run; + + /* 引数チェック */ + assert(stream != NULL); + assert(runlength != NULL); + + /* 上位ビットからの連続する0を計測 */ + run = BITSTREAM_NLZ(BITSTREAM_GETLOWERBITS(stream->bit_buffer, stream->bit_count)) + stream->bit_count - 32; + + /* 読み込んだ分カウントを減らす */ + assert(stream->bit_count >= run); + stream->bit_count -= run; + + /* バッファが空の時 */ + while (stream->bit_count == 0) { + /* 1バイト読み込み再度計測 */ + uint32_t tmp_run; + + /* 終端に達していないかチェック */ + assert(stream->memory_p >= stream->memory_image); + assert(stream->memory_p < stream->memory_tail); + + /* メモリから読み出し ビットバッファにセットし直して再度ランを計測 */ + stream->bit_buffer = stream->memory_p[0]; + stream->memory_p++; + /* テーブルによりラン長を取得 */ + tmp_run = g_bitstream_zerobit_runlength_table[stream->bit_buffer]; + stream->bit_count = 8 - tmp_run; + /* ランを加算 */ + run += tmp_run; + } + + /* 続く1を空読み */ + assert(stream->bit_count >= 1); + stream->bit_count -= 1; + + /* 正常終了 */ + (*runlength) = run; +} + +/* バッファにたまったビットをクリア(読み込み/書き込み位置を次のバイト境界に移動) */ +void BitStream_Flush(struct BitStream *stream) +{ + /* 引数チェック */ + assert(stream != NULL); + + if (stream->flags & BITSTREAM_FLAGS_MODE_READ) { + /* バッファに余ったバイト分だけ読み出し位置を戻し、バッファクリア */ + stream->memory_p -= (stream->bit_count >> 3); + stream->bit_buffer = 0; + stream->bit_count = 0; + } else { + if (stream->bit_count < 32) { + /* 次のバイト境界まで出力 */ + const uint32_t remainbits = 32 - stream->bit_count; + if (remainbits > 24) { + stream->memory_p[0] = ((stream->bit_buffer >> 24) & 0xFF); + stream->memory_p[1] = ((stream->bit_buffer >> 16) & 0xFF); + stream->memory_p[2] = ((stream->bit_buffer >> 8) & 0xFF); + stream->memory_p[3] = ((stream->bit_buffer >> 0) & 0xFF); + stream->memory_p += 4; + } else if (remainbits > 16) { + stream->memory_p[0] = ((stream->bit_buffer >> 24) & 0xFF); + stream->memory_p[1] = ((stream->bit_buffer >> 16) & 0xFF); + stream->memory_p[2] = ((stream->bit_buffer >> 8) & 0xFF); + stream->memory_p += 3; + } else if (remainbits > 8) { + stream->memory_p[0] = ((stream->bit_buffer >> 24) & 0xFF); + stream->memory_p[1] = ((stream->bit_buffer >> 16) & 0xFF); + stream->memory_p += 2; + } else { + stream->memory_p[0] = ((stream->bit_buffer >> 24) & 0xFF); + stream->memory_p += 1; + } + stream->bit_count = 32; + stream->bit_buffer = 0; + } + } +} + +#endif /* BITSTREAM_USE_MACROS */ + +/* NLZ(最上位ビットから1に当たるまでのビット数)の計算 */ +uint32_t BitStream_NLZSoft(uint32_t x) +{ + /* ハッカーのたのしみ参照 */ + x = x | (x >> 1); + x = x | (x >> 2); + x = x | (x >> 4); + x = x | (x >> 8); + x = x & ~(x >> 16); + x = (x << 9) - x; + x = (x << 11) - x; + x = (x << 14) - x; + return st_nlz10_table[x >> 26]; +} diff --git a/libs/byte_array/CMakeLists.txt b/libs/byte_array/CMakeLists.txt new file mode 100644 index 0000000..3e9d924 --- /dev/null +++ b/libs/byte_array/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.15) + +# プロジェクト名 +project(ByteArray C) + +# ライブラリ名 +set(LIB_NAME byte_array) + +# ライブラリ +add_library(${LIB_NAME} INTERFACE) + +# インクルードパス +target_include_directories(${LIB_NAME} + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) diff --git a/libs/byte_array/include/byte_array.h b/libs/byte_array/include/byte_array.h new file mode 100644 index 0000000..5bdb437 --- /dev/null +++ b/libs/byte_array/include/byte_array.h @@ -0,0 +1,210 @@ +#ifndef BYTEARRAY_H_INCLUDED +#define BYTEARRAY_H_INCLUDED + +#include + +/* 1バイト読み出し */ +#define ByteArray_ReadUint8(p_array)\ + (uint8_t)((p_array)[0]) + +/* 2バイト読み出し(ビッグエンディアン) */ +#define ByteArray_ReadUint16BE(p_array)\ + (uint16_t)(\ + (((uint16_t)((p_array)[0])) << 8) |\ + (((uint16_t)((p_array)[1])) << 0)\ + ) + +/* 3バイト読み出し(ビッグエンディアン) */ +#define ByteArray_ReadUint24BE(p_array)\ + (uint32_t)(\ + (((uint32_t)((p_array)[0])) << 16) |\ + (((uint32_t)((p_array)[1])) << 8) |\ + (((uint32_t)((p_array)[2])) << 0)\ + ) + +/* 4バイト読み出し(ビッグエンディアン) */ +#define ByteArray_ReadUint32BE(p_array)\ + (uint32_t)(\ + (((uint32_t)((p_array)[0])) << 24) |\ + (((uint32_t)((p_array)[1])) << 16) |\ + (((uint32_t)((p_array)[2])) << 8) |\ + (((uint32_t)((p_array)[3])) << 0)\ + ) + +/* 2バイト読み出し(リトルエンディアン) */ +#define ByteArray_ReadUint16LE(p_array)\ + (uint16_t)(\ + (((uint16_t)((p_array)[0])) << 0) |\ + (((uint16_t)((p_array)[1])) << 8)\ + ) + +/* 3バイト読み出し(リトルエンディアン) */ +#define ByteArray_ReadUint24LE(p_array)\ + (uint32_t)(\ + (((uint32_t)((p_array)[0])) << 0) |\ + (((uint32_t)((p_array)[1])) << 8) |\ + (((uint32_t)((p_array)[2])) << 16)\ + ) + +/* 4バイト読み出し(リトルエンディアン) */ +#define ByteArray_ReadUint32LE(p_array)\ + (uint32_t)(\ + (((uint32_t)((p_array)[0])) << 0) |\ + (((uint32_t)((p_array)[1])) << 8) |\ + (((uint32_t)((p_array)[2])) << 16) |\ + (((uint32_t)((p_array)[3])) << 24)\ + ) + +/* 1バイト取得 */ +#define ByteArray_GetUint8(p_array, p_u8val)\ + do {\ + (*(p_u8val)) = ByteArray_ReadUint8(p_array);\ + (p_array) += 1;\ + } while (0); + +/* 2バイト取得(ビッグエンディアン) */ +#define ByteArray_GetUint16BE(p_array, p_u16val)\ + do {\ + (*(p_u16val)) = ByteArray_ReadUint16BE(p_array);\ + (p_array) += 2;\ + } while (0); + +/* 3バイト取得(ビッグエンディアン) */ +#define ByteArray_GetUint24BE(p_array, p_u32val)\ + do {\ + (*(p_u32val)) = ByteArray_ReadUint24BE(p_array);\ + (p_array) += 3;\ + } while (0); + +/* 4バイト取得(ビッグエンディアン) */ +#define ByteArray_GetUint32BE(p_array, p_u32val)\ + do {\ + (*(p_u32val)) = ByteArray_ReadUint32BE(p_array);\ + (p_array) += 4;\ + } while (0); + +/* 2バイト取得(リトルエンディアン) */ +#define ByteArray_GetUint16LE(p_array, p_u16val)\ + do {\ + (*(p_u16val)) = ByteArray_ReadUint16LE(p_array);\ + (p_array) += 2;\ + } while (0); + +/* 3バイト取得(リトルエンディアン) */ +#define ByteArray_GetUint24LE(p_array, p_u32val)\ + do {\ + (*(p_u32val)) = ByteArray_ReadUint24LE(p_array);\ + (p_array) += 3;\ + } while (0); + +/* 4バイト取得(リトルエンディアン) */ +#define ByteArray_GetUint32LE(p_array, p_u32val)\ + do {\ + (*(p_u32val)) = ByteArray_ReadUint32LE(p_array);\ + (p_array) += 4;\ + } while (0); + +/* 1バイト書き出し */ +#define ByteArray_WriteUint8(p_array, u8val)\ + do {\ + ((p_array)[0]) = (uint8_t)(u8val);\ + } while (0); + +/* 2バイト書き出し(ビッグエンディアン) */ +#define ByteArray_WriteUint16BE(p_array, u16val)\ + do {\ + ((p_array)[0]) = (uint8_t)(((u16val) >> 8) & 0xFF);\ + ((p_array)[1]) = (uint8_t)(((u16val) >> 0) & 0xFF);\ + } while (0); + +/* 3バイト書き出し(ビッグエンディアン) */ +#define ByteArray_WriteUint24BE(p_array, u32val)\ + do {\ + ((p_array)[0]) = (uint8_t)(((u32val) >> 16) & 0xFF);\ + ((p_array)[1]) = (uint8_t)(((u32val) >> 8) & 0xFF);\ + ((p_array)[2]) = (uint8_t)(((u32val) >> 0) & 0xFF);\ + } while (0); + +/* 4バイト書き出し(ビッグエンディアン) */ +#define ByteArray_WriteUint32BE(p_array, u32val)\ + do {\ + ((p_array)[0]) = (uint8_t)(((u32val) >> 24) & 0xFF);\ + ((p_array)[1]) = (uint8_t)(((u32val) >> 16) & 0xFF);\ + ((p_array)[2]) = (uint8_t)(((u32val) >> 8) & 0xFF);\ + ((p_array)[3]) = (uint8_t)(((u32val) >> 0) & 0xFF);\ + } while (0); + +/* 2バイト書き出し(リトルエンディアン) */ +#define ByteArray_WriteUint16LE(p_array, u16val)\ + do {\ + ((p_array)[0]) = (uint8_t)(((u16val) >> 0) & 0xFF);\ + ((p_array)[1]) = (uint8_t)(((u16val) >> 8) & 0xFF);\ + } while (0); + +/* 3バイト書き出し(リトルエンディアン) */ +#define ByteArray_WriteUint24LE(p_array, u32val)\ + do {\ + ((p_array)[0]) = (uint8_t)(((u32val) >> 0) & 0xFF);\ + ((p_array)[1]) = (uint8_t)(((u32val) >> 8) & 0xFF);\ + ((p_array)[2]) = (uint8_t)(((u32val) >> 16) & 0xFF);\ + } while (0); + +/* 4バイト書き出し(リトルエンディアン) */ +#define ByteArray_WriteUint32LE(p_array, u32val)\ + do {\ + ((p_array)[0]) = (uint8_t)(((u32val) >> 0) & 0xFF);\ + ((p_array)[1]) = (uint8_t)(((u32val) >> 8) & 0xFF);\ + ((p_array)[2]) = (uint8_t)(((u32val) >> 16) & 0xFF);\ + ((p_array)[3]) = (uint8_t)(((u32val) >> 24) & 0xFF);\ + } while (0); + +/* 1バイト出力 */ +#define ByteArray_PutUint8(p_array, u8val)\ + do {\ + ByteArray_WriteUint8(p_array, u8val);\ + (p_array) += 1;\ + } while (0); + +/* 2バイト出力(ビッグエンディアン) */ +#define ByteArray_PutUint16BE(p_array, u16val)\ + do {\ + ByteArray_WriteUint16BE(p_array, u16val);\ + (p_array) += 2;\ + } while (0); + +/* 3バイト出力(ビッグエンディアン) */ +#define ByteArray_PutUint24BE(p_array, u32val)\ + do {\ + ByteArray_WriteUint24BE(p_array, u32val);\ + (p_array) += 3;\ + } while (0); + +/* 4バイト出力(ビッグエンディアン) */ +#define ByteArray_PutUint32BE(p_array, u32val)\ + do {\ + ByteArray_WriteUint32BE(p_array, u32val);\ + (p_array) += 4;\ + } while (0); + +/* 2バイト出力(リトルエンディアン) */ +#define ByteArray_PutUint16LE(p_array, u16val)\ + do {\ + ByteArray_WriteUint16LE(p_array, u16val);\ + (p_array) += 2;\ + } while (0); + +/* 3バイト出力(リトルエンディアン) */ +#define ByteArray_PutUint24LE(p_array, u32val)\ + do {\ + ByteArray_WriteUint24LE(p_array, u32val);\ + (p_array) += 3;\ + } while (0); + +/* 4バイト出力(リトルエンディアン) */ +#define ByteArray_PutUint32LE(p_array, u32val)\ + do {\ + ByteArray_WriteUint32LE(p_array, u32val);\ + (p_array) += 4;\ + } while (0); + +#endif /* BYTEARRAY_H_INCLUDED */ diff --git a/libs/command_line_parser/CMakeLists.txt b/libs/command_line_parser/CMakeLists.txt new file mode 100644 index 0000000..8810271 --- /dev/null +++ b/libs/command_line_parser/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.15) + +# プロジェクト名 +project(CommandLineParser C) + +# ライブラリ名 +set(LIB_NAME command_line_parser) + +# 静的ライブラリ指定 +add_library(${LIB_NAME} STATIC) + +# ソースディレクトリ +add_subdirectory(src) + +# インクルードパス +target_include_directories(${LIB_NAME} + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + +# コンパイルオプション +if(MSVC) + target_compile_options(${LIB_NAME} PRIVATE /W4) +else() + target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=2 -Wconversion -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition) + set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +endif() +set_target_properties(${LIB_NAME} + PROPERTIES + C_STANDARD 90 C_EXTENSIONS OFF + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) diff --git a/libs/command_line_parser/include/command_line_parser.h b/libs/command_line_parser/include/command_line_parser.h new file mode 100644 index 0000000..42a3c14 --- /dev/null +++ b/libs/command_line_parser/include/command_line_parser.h @@ -0,0 +1,61 @@ +#ifndef COMMAND_LINE_PARSER_H_INCLDED +#define COMMAND_LINE_PARSER_H_INCLDED + +#include + +/* 取得結果 */ +typedef enum CommandLineParserResultTag { + COMMAND_LINE_PARSER_RESULT_OK, /* 正常終了 */ + COMMAND_LINE_PARSER_RESULT_INVALID_ARGUMENT, /* 不正な引数 */ + COMMAND_LINE_PARSER_RESULT_INSUFFICIENT_OTHER_STRING_ARRAY_SIZE, /* その他の文字列が入った配列サイズが足らない */ + COMMAND_LINE_PARSER_RESULT_NOT_SPECIFY_ARGUMENT_TO_OPTION, /* 引数の指定が必須のオプションで引数の指定がない */ + COMMAND_LINE_PARSER_RESULT_UNKNOWN_OPTION, /* 定義にないオプションが指定された */ + COMMAND_LINE_PARSER_RESULT_OPTION_MULTIPLY_SPECIFIED, /* オプションが複数回指定された */ + COMMAND_LINE_PARSER_RESULT_INVALID_SPECIFICATION, /* 無効な仕様 */ + COMMAND_LINE_PARSER_RESULT_INVAILD_SHORT_OPTION_ARGUMENT /* ショートオプションの引数の指定が不適切 */ +} CommandLineParserResult; + +/* 論理定数 */ +typedef enum CommandLineParserBoolTag { + COMMAND_LINE_PARSER_FALSE = 0, /* 偽 */ + COMMAND_LINE_PARSER_TRUE /* 真 */ +} CommandLineParserBool; + +/* コマンドラインパーサ仕様 */ +/* 補足)コマンドラインパーサ仕様の配列の最後の要素の短いオプションに0, 長いオプションにNULLを指定して下さい */ +struct CommandLineParserSpecification { + char short_option; /* [in] 短いオプション文字列 */ + const char* long_option; /* [in] 長いオプション文字列 */ + const char* description; /* [in] 引数の説明 */ + CommandLineParserBool need_argument; /* [in] オプションに引数は必要か? */ + const char* argument_string; /* [in,out] 得られた文字列 */ + CommandLineParserBool acquired; /* [out] オプションが指定されたか? */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* 引数説明の印字 */ +void CommandLineParser_PrintDescription( + const struct CommandLineParserSpecification* clps); + +/* オプション名からそのオプションが指定されたか取得 */ +CommandLineParserBool CommandLineParser_GetOptionAcquired( + const struct CommandLineParserSpecification* clps, const char* option_name); + +/* オプション名からそのオプション引数を取得 */ +const char* CommandLineParser_GetArgumentString( + const struct CommandLineParserSpecification* clps, const char* option_name); + +/* 引数のパース */ +CommandLineParserResult CommandLineParser_ParseArguments( + struct CommandLineParserSpecification* clps, + int32_t argc, const char* const* argv, + const char** other_string_array, uint32_t other_string_array_size); + +#ifdef __cplusplus +} +#endif + +#endif /* COMMAND_LINE_PARSER_H_INCLDED */ diff --git a/libs/command_line_parser/src/CMakeLists.txt b/libs/command_line_parser/src/CMakeLists.txt new file mode 100644 index 0000000..eaad33b --- /dev/null +++ b/libs/command_line_parser/src/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/command_line_parser.c + ) diff --git a/libs/command_line_parser/src/command_line_parser.c b/libs/command_line_parser/src/command_line_parser.c new file mode 100644 index 0000000..a77b123 --- /dev/null +++ b/libs/command_line_parser/src/command_line_parser.c @@ -0,0 +1,331 @@ +#include "command_line_parser.h" + +#include +#include +#include + +/* 仕様リストサイズを計測 */ +static uint32_t CommandLineParser_GetNumSpecifications( + const struct CommandLineParserSpecification* clps) +{ + uint32_t num_specs; + + assert(clps != NULL); + + /* リスト終端の0にぶつかるまでポインタを進める */ + num_specs = 0; + while ((clps->short_option != 0) || (clps->long_option != NULL)) { + num_specs++; + clps++; + } + + return num_specs; +} + +/* コマンドラインパーサ仕様のチェック */ +static CommandLineParserBool CommandLineParser_CheckSpecification( + const struct CommandLineParserSpecification* clps) +{ + uint32_t spec_no; + uint32_t num_specs; + + assert(clps != NULL); + + /* 仕様数の取得 */ + num_specs = CommandLineParser_GetNumSpecifications(clps); + + for (spec_no = 0; spec_no < num_specs; spec_no++) { + uint32_t j; + for (j = 0; j < num_specs; j++) { + if (j == spec_no) { + continue; + } + /* 同じオプション文字列を持つものがいたら不正 */ + if (clps[j].short_option == clps[spec_no].short_option) { + return COMMAND_LINE_PARSER_FALSE; + } else if ((clps[j].long_option != NULL) && (clps[spec_no].long_option != NULL)) { + if (strcmp(clps[j].long_option, clps[spec_no].long_option) == 0) { + return COMMAND_LINE_PARSER_FALSE; + } + } + } + } + + /* 問題なし */ + return COMMAND_LINE_PARSER_TRUE; +} + +/* 引数説明の印字 */ +void CommandLineParser_PrintDescription(const struct CommandLineParserSpecification* clps) +{ + uint32_t spec_no; + char arg_option_attr[256]; + char command_str[256]; + uint32_t num_specs; + + /* 引数チェック */ + if (clps == NULL) { + fprintf(stderr, "Pointer to command-line specification is NULL. \n"); + return; + } + + /* 仕様をチェックしておく */ + if (CommandLineParser_CheckSpecification(clps) != COMMAND_LINE_PARSER_TRUE) { + fprintf(stderr, "Warning: Command-line specification is invalid. (Unable to parse) \n"); + } + + /* 仕様数の取得 */ + num_specs = CommandLineParser_GetNumSpecifications(clps); + + /* 仕様を順番に表示 */ + for (spec_no = 0; spec_no < num_specs; spec_no++) { + const struct CommandLineParserSpecification* pspec = &clps[spec_no]; + /* 引数の属性文字列を作成 */ + if (pspec->need_argument == COMMAND_LINE_PARSER_TRUE) { + sprintf(arg_option_attr, "(needs argument)"); + } else { + strcpy(arg_option_attr, ""); + } + + /* コマンド文字列を作成 */ + if (pspec->long_option != NULL) { + sprintf(command_str, " -%c, --%s", pspec->short_option, pspec->long_option); + } else { + sprintf(command_str, " -%c", pspec->short_option); + } + + /* 説明を付加して全てを印字 */ + printf("%-20s %-18s %s \n", + command_str, arg_option_attr, + (pspec->description != NULL) ? pspec->description : ""); + } +} + +/* オプション名からインデックスを取得 */ +static CommandLineParserResult CommandLineParser_GetSpecificationIndex( + const struct CommandLineParserSpecification* clps, + const char* option_name, uint32_t* index) +{ + uint32_t spec_no; + uint32_t num_specs; + + /* 引数チェック */ + if (clps == NULL || option_name == NULL || index == NULL) { + return COMMAND_LINE_PARSER_RESULT_INVALID_ARGUMENT; + } + + /* 仕様数の取得 */ + num_specs = CommandLineParser_GetNumSpecifications(clps); + + /* ショートオプションから検索 */ + if (strlen(option_name) == 1) { + for (spec_no = 0; spec_no < num_specs; spec_no++) { + if (option_name[0] == clps[spec_no].short_option) { + *index = spec_no; + return COMMAND_LINE_PARSER_RESULT_OK; + } + } + } + + /* ロングオプションから検索 */ + for (spec_no = 0; spec_no < num_specs; spec_no++) { + if (strcmp(option_name, clps[spec_no].long_option) == 0) { + *index = spec_no; + return COMMAND_LINE_PARSER_RESULT_OK; + } + } + + /* 見つからなかった */ + return COMMAND_LINE_PARSER_RESULT_UNKNOWN_OPTION; +} + +/* オプション名からそのオプションが指定されたか取得 */ +CommandLineParserBool CommandLineParser_GetOptionAcquired( + const struct CommandLineParserSpecification* clps, + const char* option_name) +{ + uint32_t spec_no; + + /* インデックス取得 */ + if (CommandLineParser_GetSpecificationIndex(clps, option_name, &spec_no) != COMMAND_LINE_PARSER_RESULT_OK) { + return COMMAND_LINE_PARSER_FALSE; + } + + return clps[spec_no].acquired; +} + +/* オプション名からそのオプション引数を取得 */ +const char* CommandLineParser_GetArgumentString( + const struct CommandLineParserSpecification* clps, + const char* option_name) +{ + uint32_t spec_no; + + /* インデックス取得 */ + if (CommandLineParser_GetSpecificationIndex(clps, option_name, &spec_no) != COMMAND_LINE_PARSER_RESULT_OK) { + return NULL; + } + + return clps[spec_no].argument_string; +} + +/* 引数のパース */ +CommandLineParserResult CommandLineParser_ParseArguments( + struct CommandLineParserSpecification* clps, + int32_t argc, const char* const* argv, + const char** other_string_array, uint32_t other_string_array_size) +{ + int32_t count; + uint32_t spec_no; + const char* arg_str; + uint32_t other_string_index; + uint32_t num_specs; + + /* 引数チェック */ + if (argv == NULL || clps == NULL) { + return COMMAND_LINE_PARSER_RESULT_INVALID_ARGUMENT; + } + + /* 仕様数の取得 */ + num_specs = CommandLineParser_GetNumSpecifications(clps); + + /* コマンドライン仕様のチェック */ + if (CommandLineParser_CheckSpecification(clps) != COMMAND_LINE_PARSER_TRUE) { + return COMMAND_LINE_PARSER_RESULT_INVALID_SPECIFICATION; + } + + /* 全てのオプションを未取得状態にセット */ + for (spec_no = 0; spec_no < num_specs; spec_no++) { + clps[spec_no].acquired = COMMAND_LINE_PARSER_FALSE; + } + + /* argv[0]はプログラム名だから飛ばす */ + other_string_index = 0; + for (count = 1; count < argc; count++) { + /* 文字列配列の要素を取得 */ + arg_str = argv[count]; + /* オプション文字列を検査 */ + if (strncmp(arg_str, "--", 2) == 0) { + /* ロングオプション */ + for (spec_no = 0; spec_no < num_specs; spec_no++) { + uint32_t long_option_len; + struct CommandLineParserSpecification* pspec = &clps[spec_no]; + /* ロングオプション文字列がNULLなら飛ばす */ + if (pspec->long_option == NULL) { + continue; + } + long_option_len = (uint32_t)strlen(pspec->long_option); + if (strncmp(&arg_str[2], pspec->long_option, long_option_len) == 0) { + /* ロングオプションの後はナル終端かオプション指定のための'='が来なければならない */ + if (arg_str[2 + long_option_len] == '\0') { + /* 既に取得済みのオプションが指定された */ + if (pspec->acquired == COMMAND_LINE_PARSER_TRUE) { + fprintf(stderr, "%s: Option \"%s\" multiply specified. \n", argv[0], pspec->long_option); + return COMMAND_LINE_PARSER_RESULT_OPTION_MULTIPLY_SPECIFIED; + } + if (pspec->need_argument == COMMAND_LINE_PARSER_TRUE) { + /* 引数を取るオプションの場合は、そのまま引数を取りに行く */ + if ((count + 1) == argc) { + /* 終端に達している */ + fprintf(stderr, "%s: Option \"%s\" needs argument. \n", argv[0], pspec->long_option); + return COMMAND_LINE_PARSER_RESULT_NOT_SPECIFY_ARGUMENT_TO_OPTION; + } else if ((strncmp(argv[count + 1], "--", 2) == 0) || argv[count + 1][0] == '-') { + /* 他のオプション指定が入っている */ + /* (オプション引数文字列として"--", '-'が先頭にくるものは認めない) */ + fprintf(stderr, "%s: Option \"%s\" needs argument. \n", argv[0], pspec->long_option); + return COMMAND_LINE_PARSER_RESULT_NOT_SPECIFY_ARGUMENT_TO_OPTION; + } + /* オプション文字列を取得しつつ次の引数文字列に移動 */ + count++; + pspec->argument_string = argv[count]; + } + } else if (arg_str[2 + long_option_len] == '=') { + if (pspec->need_argument != COMMAND_LINE_PARSER_TRUE) { + /* '='を含むオプションかもしれない... */ + continue; + } + /* 既に取得済みのオプションが指定された */ + if (pspec->acquired == COMMAND_LINE_PARSER_TRUE) { + fprintf(stderr, "%s: Option \"%s\" multiply specified. \n", argv[0], pspec->long_option); + return COMMAND_LINE_PARSER_RESULT_OPTION_MULTIPLY_SPECIFIED; + } + /* オプション文字列を取得 */ + pspec->argument_string = &arg_str[2 + long_option_len + 1]; + } else { + /* より長い文字が指定されている. 他のオプションで一致するかもしれないので読み飛ばす. */ + continue; + } + /* 取得済み状態にセット */ + pspec->acquired = COMMAND_LINE_PARSER_TRUE; + break; + } + } + /* オプションが見つからなかった */ + if (spec_no == num_specs) { + fprintf(stderr, "%s: Unknown long option - \"%s\" \n", argv[0], &arg_str[2]); + return COMMAND_LINE_PARSER_RESULT_UNKNOWN_OPTION; + } + } else if (arg_str[0] == '-') { + /* ショートオプション(の連なり) */ + uint32_t str_index; + for (str_index = 1; arg_str[str_index] != '\0'; str_index++) { + for (spec_no = 0; spec_no < num_specs; spec_no++) { + struct CommandLineParserSpecification* pspec = &clps[spec_no]; + if (arg_str[str_index] == pspec->short_option) { + /* 既に取得済みのオプションが指定された */ + if (pspec->acquired == COMMAND_LINE_PARSER_TRUE) { + fprintf(stderr, "%s: Option \'%c\' multiply specified. \n", argv[0], pspec->short_option); + return COMMAND_LINE_PARSER_RESULT_OPTION_MULTIPLY_SPECIFIED; + } + /* 引数ありのオプション */ + if (pspec->need_argument == COMMAND_LINE_PARSER_TRUE) { + /* 引数を取るオプションの場合は、そのまま引数を取りに行く */ + if (arg_str[str_index + 1] != '\0') { + /* 引数を取るに当たり、現在注目しているオプションが末尾である必要がある */ + fprintf(stderr, "%s: Option \'%c\' needs argument. " + "Please specify tail of short option sequence.\n", argv[0], pspec->short_option); + return COMMAND_LINE_PARSER_RESULT_INVAILD_SHORT_OPTION_ARGUMENT; + } + if ((count + 1) == argc) { + /* 終端に達している */ + fprintf(stderr, "%s: Option \'%c\' needs argument. \n", argv[0], pspec->short_option); + return COMMAND_LINE_PARSER_RESULT_NOT_SPECIFY_ARGUMENT_TO_OPTION; + } else if ((strncmp(argv[count + 1], "--", 2) == 0) || argv[count + 1][0] == '-') { + /* 他のオプション指定が入っている */ + /* (引数として"--", '-'が先頭にくるものは認めない) */ + fprintf(stderr, "%s: Option \'%c\' needs argument. \n", argv[0], pspec->short_option); + return COMMAND_LINE_PARSER_RESULT_NOT_SPECIFY_ARGUMENT_TO_OPTION; + } + /* オプション文字列を取得しつつ次の引数文字列に移動 */ + count++; + pspec->argument_string = argv[count]; + } + /* 取得済み状態にセット */ + pspec->acquired = COMMAND_LINE_PARSER_TRUE; + break; + } + } + /* オプションが見つからなかった */ + if (spec_no == num_specs) { + fprintf(stderr, "%s: Unknown short option - \'%c\' \n", argv[0], arg_str[str_index]); + return COMMAND_LINE_PARSER_RESULT_UNKNOWN_OPTION; + } + } + } else { + /* オプションでもオプション引数でもない文字列 */ + if (other_string_array == NULL) { + /* バッファにアクセスできない */ + return COMMAND_LINE_PARSER_RESULT_INVALID_ARGUMENT; + } else if (other_string_index >= other_string_array_size) { + /* バッファサイズ不足 */ + fprintf(stderr, "%s: Too many strings specified. \n", argv[0]); + return COMMAND_LINE_PARSER_RESULT_INSUFFICIENT_OTHER_STRING_ARRAY_SIZE; + } + /* 文字列取得 */ + other_string_array[other_string_index] = arg_str; + other_string_index++; + } + } + + return COMMAND_LINE_PARSER_RESULT_OK; +} diff --git a/libs/lpc/CMakeLists.txt b/libs/lpc/CMakeLists.txt new file mode 100644 index 0000000..2690b74 --- /dev/null +++ b/libs/lpc/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# プロジェクト名 +project(LPC C) + +# ライブラリ名 +set(LIB_NAME lpc) + +# 静的ライブラリ指定 +add_library(${LIB_NAME} STATIC) + +# ソースディレクトリ +add_subdirectory(src) + +# インクルードパス +target_include_directories(${LIB_NAME} + PRIVATE + ${PROJECT_ROOT_PATH}/include + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + +# コンパイルオプション +if(MSVC) + target_compile_options(${LIB_NAME} PRIVATE /W4) +else() + target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=2 -Wconversion -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition) + set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +endif() +set_target_properties(${LIB_NAME} + PROPERTIES + C_STANDARD 90 C_EXTENSIONS OFF + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) diff --git a/libs/lpc/include/lpc.h b/libs/lpc/include/lpc.h new file mode 100644 index 0000000..84b1c71 --- /dev/null +++ b/libs/lpc/include/lpc.h @@ -0,0 +1,118 @@ +#ifndef LPC_H_INCLUDED +#define LPC_H_INCLUDED + +#include + +/* API結果型 */ +typedef enum LPCApiResultTag { + LPC_APIRESULT_OK = 0, /* OK */ + LPC_APIRESULT_NG, /* 分類不能なエラー */ + LPC_APIRESULT_INVALID_ARGUMENT, /* 不正な引数 */ + LPC_APIRESULT_EXCEED_MAX_ORDER, /* 最大次数を超えた */ + LPC_APIRESULT_EXCEED_MAX_NUM_SAMPLES, /* 最大入力サンプル数を超えた */ + LPC_APIRESULT_FAILED_TO_CALCULATION /* 計算に失敗 */ +} LPCApiResult; + +/* 窓関数の種類 */ +typedef enum LPCWindowTypeTag { + LPC_WINDOWTYPE_RECTANGULAR = 0, /* 矩形窓 */ + LPC_WINDOWTYPE_SIN, /* サイン窓 */ + LPC_WINDOWTYPE_WELCH /* Welch窓 */ +} LPCWindowType; + +/* LPC係数計算ハンドル */ +struct LPCCalculator; + +/* 初期化コンフィグ */ +struct LPCCalculatorConfig { + uint32_t max_order; /* 最大次数 */ + uint32_t max_num_samples; /* 最大入力サンプル数 */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* LPC係数計算ハンドルのワークサイズ計算 */ +int32_t LPCCalculator_CalculateWorkSize(const struct LPCCalculatorConfig *config); + +/* LPC係数計算ハンドルの作成 */ +struct LPCCalculator *LPCCalculator_Create(const struct LPCCalculatorConfig *config, void *work, int32_t work_size); + +/* LPC係数計算ハンドルの破棄 */ +void LPCCalculator_Destroy(struct LPCCalculator *lpcc); + +/* Levinson-Durbin再帰計算によりLPC係数を求める */ +LPCApiResult LPCCalculator_CalculateLPCCoefficients( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, double *coef, uint32_t coef_order, + LPCWindowType window_type, double regular_term); + +/* Levinson-Durbin再帰計算により与えられた次数まで全てのLPC係数を求める(倍精度) */ +LPCApiResult LPCCalculator_CalculateMultipleLPCCoefficients( + struct LPCCalculator* lpcc, + const double* data, uint32_t num_samples, double **lpc_coefs, uint32_t max_coef_order, + LPCWindowType window_type, double regular_term); + +/* Levinson-Durbin再帰計算により与えられた次数までの残差分散を求める(倍精度) */ +/* 0次の誤差分散(分散)からmax_coef_order次の分散まで求めるためerror_varsのサイズはmax_coef_order+1要する */ +LPCApiResult LPCCalculator_CalculateErrorVariances( + struct LPCCalculator* lpcc, + const double* data, uint32_t num_samples, double *error_vars, uint32_t max_coef_order, + LPCWindowType window_type, double regular_term); + +/* 補助関数法よりLPC係数を求める(倍精度) */ +LPCApiResult LPCCalculator_CalculateLPCCoefficientsAF( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, double *coef, uint32_t coef_order, + uint32_t max_num_iteration, LPCWindowType window_type, double regular_term); + +/* Burg法によりLPC係数を求める(倍精度) */ +LPCApiResult LPCCalculator_CalculateLPCCoefficientsBurg( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, double *coef, uint32_t coef_order); + +/* SVRよりLPC係数を求める(倍精度) */ +LPCApiResult LPCCalculator_CalculateLPCCoefficientsSVR( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, double *coef, uint32_t coef_order, + uint32_t max_num_iteration, LPCWindowType window_type, + double regular_term, const double *margin_list, uint32_t margin_list_size); + +/* 入力データからサンプルあたりの推定符号長を求める */ +LPCApiResult LPCCalculator_EstimateCodeLength( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, uint32_t bits_per_sample, + uint32_t coef_order, double *length_per_sample_bits, LPCWindowType window_type); + +/* MDL(最小記述長)を計算 */ +LPCApiResult LPCCalculator_CalculateMDL( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, uint32_t coef_order, double *mdl, + LPCWindowType window_type); + +/* LPC係数をPARCOR係数に変換して量子化する */ +LPCApiResult LPC_QuantizeCoefficientsAsPARCOR( + struct LPCCalculator *lpcc, + const double *lpc_coef, uint32_t coef_order, uint32_t nbits_precision, int32_t *int_coef); + +/* LPC係数の整数量子化 */ +LPCApiResult LPC_QuantizeCoefficients( + const double *double_coef, uint32_t coef_order, uint32_t nbits_precision, uint32_t max_bits, + int32_t *int_coef, uint32_t *coef_rshift); + +/* LPC係数により予測/誤差出力 */ +LPCApiResult LPC_Predict( + const int32_t *data, uint32_t num_samples, + const int32_t *coef, uint32_t coef_order, int32_t *residual, uint32_t coef_rshift); + +/* LPC係数により合成(in-place) */ +LPCApiResult LPC_Synthesize( + int32_t *data, uint32_t num_samples, + const int32_t *coef, uint32_t coef_order, uint32_t coef_rshift); + +#ifdef __cplusplus +} +#endif + +#endif /* LPC_H_INCLUDED */ diff --git a/libs/lpc/src/CMakeLists.txt b/libs/lpc/src/CMakeLists.txt new file mode 100644 index 0000000..63e5ae0 --- /dev/null +++ b/libs/lpc/src/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/lpc.c + ) diff --git a/libs/lpc/src/lpc.c b/libs/lpc/src/lpc.c new file mode 100644 index 0000000..b1b35ad --- /dev/null +++ b/libs/lpc/src/lpc.c @@ -0,0 +1,1440 @@ +#include "lpc.h" + +#include +#include +#include +#include +#include +#include + +/* メモリアラインメント */ +#define LPC_ALIGNMENT 16 +/* 円周率 */ +#define LPC_PI 3.1415926535897932384626433832795029 +/* 残差絶対値の最小値 */ +#define LPCAF_RESIDUAL_EPSILON 1e-6 + +/* nの倍数切り上げ */ +#define LPC_ROUNDUP(val, n) ((((val) + ((n) - 1)) / (n)) * (n)) +/* 符号関数 */ +#define LPC_SIGN(val) (((val) > 0) - ((val) < 0)) +/* 最大値の取得 */ +#define LPC_MAX(a,b) (((a) > (b)) ? (a) : (b)) +/* 絶対値の取得 */ +#define LPC_ABS(val) (((val) > 0) ? (val) : -(val)) +/* 軟閾値作用素 */ +#define LPC_SOFT_THRESHOLD(in, epsilon) (LPC_SIGN(in) * LPC_MAX(LPC_ABS(in) - (epsilon), 0.0)) + +/* 内部エラー型 */ +typedef enum LPCErrorTag { + LPC_ERROR_OK = 0, + LPC_ERROR_NG, + LPC_ERROR_SINGULAR_MATRIX, + LPC_ERROR_INVALID_ARGUMENT +} LPCError; + +/* LPC計算ハンドル */ +struct LPCCalculator { + uint32_t max_order; /* 最大次数 */ + uint32_t max_num_buffer_samples; /* 最大バッファサンプル数 */ + /* 内部的な計算結果は精度を担保するため全てdoubleで持つ */ + /* floatだとサンプル数を増やすと標本自己相関値の誤差に起因して出力の計算結果がnanになる */ + double **a_vecs; /* 各次数の係数ベクトル */ + double *u_vec; /* 計算用ベクトル3 */ + double *v_vec; /* 計算用ベクトル4 */ + double **r_mat; /* 補助関数法/Burg法で使用する行列((max_order + 1)次) */ + double *auto_corr; /* 標本自己相関 */ + double **lpc_coefs; /* 各次数のLPC係数ベクトル */ + double *parcor_coef; /* PARCOR係数ベクトル */ + double *error_vars; /* 残差分散 */ + double *buffer; /* 入力信号のバッファ領域 */ + uint8_t alloced_by_own; /* 自分で領域確保したか? */ + void *work; /* ワーク領域先頭ポインタ */ +}; + +/* round関数(C89で定義されていない) */ +static double LPC_Round(double d) +{ + return (d >= 0.0) ? floor(d + 0.5) : -floor(-d + 0.5); +} + +/* log2関数(C89で定義されていない) */ +static double LPC_Log2(double d) +{ +#define INV_LOGE2 (1.4426950408889634) /* 1 / log(2) */ + return log(d) * INV_LOGE2; +#undef INV_LOGE2 +} + +/* LPC係数計算ハンドルのワークサイズ計算 */ +int32_t LPCCalculator_CalculateWorkSize(const struct LPCCalculatorConfig *config) +{ + int32_t work_size; + + /* 引数チェック */ + if (config == NULL) { + return -1; + } + + work_size = sizeof(struct LPCCalculator) + LPC_ALIGNMENT; + /* a_vecで使用する領域 */ + work_size += (int32_t)(sizeof(double *) * (config->max_order + 1)); + work_size += (int32_t)(sizeof(double) * (config->max_order + 1) * (config->max_order + 2)); + /* u, v ベクトル分の領域 */ + work_size += (int32_t)(sizeof(double) * (config->max_order + 2) * 2); + /* 標本自己相関の領域 */ + work_size += (int32_t)(sizeof(double) * (config->max_order + 1)); + /* LPC係数ベクトルの領域 */ + work_size += (int32_t)(sizeof(double *) * (config->max_order + 1)); + work_size += (int32_t)(sizeof(double) * (config->max_order + 1) * (config->max_order + 1)); + /* PARCOR係数ベクトルの領域 */ + work_size += (int32_t)(sizeof(double) * (config->max_order + 1)); + /* 残差分散の領域 */ + work_size += (int32_t)(sizeof(double) * (config->max_order + 1)); + /* 補助関数法で使用する行列領域 */ + work_size += (int32_t)(sizeof(double *) * (config->max_order + 1)); + work_size += (int32_t)(sizeof(double) * (config->max_order + 1) * (config->max_order + 1)); + /* 入力信号バッファ領域 */ + work_size += (int32_t)(sizeof(double) * config->max_num_samples); + + return work_size; +} + +/* LPC係数計算ハンドルの作成 */ +struct LPCCalculator* LPCCalculator_Create(const struct LPCCalculatorConfig *config, void *work, int32_t work_size) +{ + struct LPCCalculator *lpcc; + uint8_t *work_ptr; + uint8_t tmp_alloc_by_own = 0; + + /* 自前でワーク領域確保 */ + if ((work == NULL) && (work_size == 0)) { + if ((work_size = LPCCalculator_CalculateWorkSize(config)) < 0) { + return NULL; + } + work = malloc((uint32_t)work_size); + tmp_alloc_by_own = 1; + } + + /* 引数チェック */ + if ((config == NULL) || (work == NULL) + || (work_size < LPCCalculator_CalculateWorkSize(config)) + || (config->max_order == 0) || (config->max_num_samples == 0)) { + if (tmp_alloc_by_own == 1) { + free(work); + } + return NULL; + } + + /* ワーク領域取得 */ + work_ptr = (uint8_t *)work; + + /* ハンドル領域確保 */ + work_ptr = (uint8_t *)LPC_ROUNDUP((uintptr_t)work_ptr, LPC_ALIGNMENT); + lpcc = (struct LPCCalculator *)work_ptr; + work_ptr += sizeof(struct LPCCalculator); + + /* ハンドルメンバの設定 */ + lpcc->max_order = config->max_order; + lpcc->max_num_buffer_samples = config->max_num_samples; + lpcc->work = work; + lpcc->alloced_by_own = tmp_alloc_by_own; + + /* 計算用ベクトルの領域割当 */ + { + uint32_t ord; + lpcc->a_vecs = (double **)work_ptr; + work_ptr += sizeof(double *) * (config->max_order + 1); + for (ord = 0; ord < config->max_order + 1; ord++) { + lpcc->a_vecs[ord] = (double *)work_ptr; + work_ptr += sizeof(double) * (config->max_order + 2); /* a_0, a_k+1を含めるとmax_order+2 */ + } + } + lpcc->u_vec = (double *)work_ptr; + work_ptr += sizeof(double) * (config->max_order + 2); + lpcc->v_vec = (double *)work_ptr; + work_ptr += sizeof(double) * (config->max_order + 2); + + /* 標本自己相関の領域割当 */ + lpcc->auto_corr = (double *)work_ptr; + work_ptr += sizeof(double) * (config->max_order + 1); + + /* LPC係数ベクトルの領域割当 */ + { + uint32_t ord; + lpcc->lpc_coefs = (double **)work_ptr; + work_ptr += sizeof(double *) * (config->max_order + 1); + for (ord = 0; ord < config->max_order + 1; ord++) { + lpcc->lpc_coefs[ord] = (double *)work_ptr; + work_ptr += sizeof(double) * (config->max_order + 1); + } + } + + /* PARCOR係数ベクトルの領域割当 */ + lpcc->parcor_coef = (double *)work_ptr; + work_ptr += sizeof(double) * (config->max_order + 1); + + /* 残差分散の領域割り当て */ + lpcc->error_vars = (double *)work_ptr; + work_ptr += sizeof(double) * (config->max_order + 1); + + /* 補助関数法/Burg法で使用する行列領域 */ + { + uint32_t ord; + lpcc->r_mat = (double **)work_ptr; + work_ptr += sizeof(double *) * (config->max_order + 1); + for (ord = 0; ord < config->max_order + 1; ord++) { + lpcc->r_mat[ord] = (double *)work_ptr; + work_ptr += sizeof(double) * (config->max_order + 1); + } + } + + /* 入力信号バッファの領域 */ + lpcc->buffer = (double *)work_ptr; + work_ptr += sizeof(double) * config->max_num_samples; + + /* バッファオーバーフローチェック */ + assert((work_ptr - (uint8_t *)work) <= work_size); + + return lpcc; +} + +/* LPC係数計算ハンドルの破棄 */ +void LPCCalculator_Destroy(struct LPCCalculator *lpcc) +{ + if (lpcc != NULL) { + /* ワーク領域を時前確保していたときは開放 */ + if (lpcc->alloced_by_own == 1) { + free(lpcc->work); + } + } +} + +/* 窓関数の適用 */ +static LPCError LPC_ApplyWindow( + LPCWindowType window_type, const double *input, uint32_t num_samples, double *output) +{ + /* 引数チェック */ + if (input == NULL || output == NULL) { + return LPC_ERROR_INVALID_ARGUMENT; + } + + switch (window_type) { + case LPC_WINDOWTYPE_RECTANGULAR: + memcpy(output, input, sizeof(double) * num_samples); + break; + case LPC_WINDOWTYPE_SIN: + { + uint32_t smpl; + for (smpl = 0; smpl < num_samples; smpl++) { + output[smpl] = input[smpl] * sin((LPC_PI * smpl) / (num_samples - 1)); + } + } + break; + case LPC_WINDOWTYPE_WELCH: + { + uint32_t smpl; + const double divisor = 4.0 * pow(num_samples - 1, -2.0); + for (smpl = 0; smpl < (num_samples >> 1); smpl++) { + const double weight = divisor * smpl * (num_samples - 1 - smpl); + output[smpl] = input[smpl] * weight; + output[num_samples - smpl - 1] = input[num_samples - smpl - 1] * weight; + } + } + break; + default: + return LPC_ERROR_NG; + } + + return LPC_ERROR_OK; +} + +/*(標本)自己相関の計算 */ +static LPCError LPC_CalculateAutoCorrelation( + const double *data, uint32_t num_samples, double *auto_corr, uint32_t order) +{ + uint32_t i, lag; + double tmp; + + assert(num_samples >= order); + + /* 引数チェック */ + if (data == NULL || auto_corr == NULL) { + return LPC_ERROR_INVALID_ARGUMENT; + } + + /* 係数初期化 */ + for (lag = 0; lag < order; lag++) { + auto_corr[lag] = 0.0; + } + + /* 次数の代わりにデータ側のラグに注目した自己相関係数計算 */ + for (i = 0; i <= num_samples - order; i++) { + tmp = data[i]; + /* 同じラグを持ったデータ積和を取る */ + for (lag = 0; lag < order; lag++) { + auto_corr[lag] += tmp * data[i + lag]; + } + } + for (; i < num_samples; i++) { + tmp = data[i]; + for (lag = 0; lag < num_samples - i; lag++) { + auto_corr[lag] += tmp * data[i + lag]; + } + } + + return LPC_ERROR_OK; +} + +/* Levinson-Durbin再帰計算 */ +static LPCError LPC_LevinsonDurbinRecursion(struct LPCCalculator *lpcc, + const double *auto_corr, uint32_t coef_order, double **lpc_coefs, double *parcor_coef, double *error_vars) +{ + uint32_t k, i; + double gamma; /* 反射係数 */ + + /* オート変数にポインタをコピー */ + double **a_vecs = lpcc->a_vecs; + double *u_vec = lpcc->u_vec; + double *v_vec = lpcc->v_vec; + + /* 引数チェック */ + if ((lpcc == NULL) || (auto_corr == NULL) || (lpc_coefs == NULL) || (parcor_coef == NULL)) { + return LPC_ERROR_INVALID_ARGUMENT; + } + + /* 0次自己相関(信号の二乗和)が小さい場合 + * => 係数は全て0として無音出力システムを予測 */ + if (fabs(auto_corr[0]) < FLT_EPSILON) { + for (i = 0; i < coef_order + 1; i++) { + parcor_coef[i] = 0.0; + } + for (k = 0; k < coef_order; k++) { + for (i = 0; i < coef_order + 1; i++) { + lpc_coefs[k][i] = 0.0; + } + } + return LPC_ERROR_OK; + } + + /* 初期化 */ + for (i = 0; i < coef_order + 2; i++) { + u_vec[i] = v_vec[i] = 0.0; + } + for (k = 0; k < coef_order + 1; k++) { + for (i = 0; i < coef_order + 2; i++) { + a_vecs[k][i] = 0.0; + } + } + + /* 最初のステップの係数をセット */ + a_vecs[0][0] = 1.0; + error_vars[0] = auto_corr[0]; + a_vecs[0][1] = - auto_corr[1] / auto_corr[0]; + parcor_coef[0] = auto_corr[1] / error_vars[0]; + error_vars[1] = error_vars[0] + auto_corr[1] * a_vecs[0][1]; + u_vec[0] = 1.0; u_vec[1] = 0.0; + v_vec[0] = 0.0; v_vec[1] = 1.0; + + /* 再帰処理 */ + for (k = 1; k < coef_order; k++) { + gamma = 0.0; + for (i = 0; i < k + 1; i++) { + gamma += a_vecs[k - 1][i] * auto_corr[k + 1 - i]; + } + gamma /= -error_vars[k]; + error_vars[k + 1] = error_vars[k] * (1.0 - gamma * gamma); + /* 誤差分散(パワー)は非負 */ + assert(error_vars[k + 1] >= 0.0); + + /* u_vec, v_vecの更新 */ + for (i = 0; i < k; i++) { + u_vec[i + 1] = v_vec[k - i] = a_vecs[k - 1][i + 1]; + } + u_vec[0] = 1.0; u_vec[k + 1] = 0.0; + v_vec[0] = 0.0; v_vec[k + 1] = 1.0; + + /* 係数の更新 */ + for (i = 0; i < k + 2; i++) { + a_vecs[k][i] = u_vec[i] + gamma * v_vec[i]; + } + /* PARCOR係数は反射係数の符号反転 */ + parcor_coef[k] = -gamma; + /* PARCOR係数の絶対値は1未満(収束条件) */ + assert(fabs(gamma) < 1.0); + } + + /* 結果を取得 */ + for (k = 0; k < coef_order; k++) { + memcpy(lpc_coefs[k], &a_vecs[k][1], sizeof(double) * coef_order); + } + + return LPC_ERROR_OK; +} + +/* 係数計算の共通関数 */ +static LPCError LPC_CalculateCoef( + struct LPCCalculator *lpcc, const double *data, uint32_t num_samples, uint32_t coef_order, + LPCWindowType window_type, double regular_term) +{ + /* 引数チェック */ + if (lpcc == NULL) { + return LPC_ERROR_INVALID_ARGUMENT; + } + + /* 窓関数を適用 */ + if (LPC_ApplyWindow(window_type, data, num_samples, lpcc->buffer) != LPC_ERROR_OK) { + return LPC_ERROR_NG; + } + + /* 自己相関を計算 */ + if (LPC_CalculateAutoCorrelation( + lpcc->buffer, num_samples, lpcc->auto_corr, coef_order + 1) != LPC_ERROR_OK) { + return LPC_ERROR_NG; + } + + /* 入力サンプル数が少ないときは、係数が発散することが多数 + * => 無音データとして扱い、係数はすべて0とする */ + if (num_samples < coef_order) { + uint32_t i, k; + for (i = 0; i < coef_order + 1; i++) { + lpcc->parcor_coef[i] = 0.0; + } + for (k = 0; k < coef_order + 1; k++) { + for (i = 0; i < coef_order + 1; i++) { + lpcc->lpc_coefs[k][i] = 0.0; + } + } + return LPC_ERROR_OK; + } + + /* 0次相関を強調(Ridge正則化) */ + lpcc->auto_corr[0] *= (1.0 + regular_term); + + /* 再帰計算を実行 */ + if (LPC_LevinsonDurbinRecursion(lpcc, lpcc->auto_corr, coef_order, lpcc->lpc_coefs, lpcc->parcor_coef, lpcc->error_vars) != LPC_ERROR_OK) { + return LPC_ERROR_NG; + } + + return LPC_ERROR_OK; +} + +/* Levinson-Durbin再帰計算によりLPC係数を求める(倍精度) */ +LPCApiResult LPCCalculator_CalculateLPCCoefficients( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, double *lpc_coef, uint32_t coef_order, + LPCWindowType window_type, double regular_term) +{ + /* 引数チェック */ + if ((data == NULL) || (lpc_coef == NULL)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + /* 次数チェック */ + if (coef_order > lpcc->max_order) { + return LPC_APIRESULT_EXCEED_MAX_ORDER; + } + + /* 入力サンプル数チェック */ + if (num_samples > lpcc->max_num_buffer_samples) { + return LPC_APIRESULT_EXCEED_MAX_NUM_SAMPLES; + } + + /* 係数計算 */ + if (LPC_CalculateCoef(lpcc, data, num_samples, coef_order, window_type, regular_term) != LPC_ERROR_OK) { + return LPC_APIRESULT_FAILED_TO_CALCULATION; + } + + /* 計算成功時は結果をコピー */ + memmove(lpc_coef, lpcc->lpc_coefs[coef_order - 1], sizeof(double) * coef_order); + + return LPC_APIRESULT_OK; +} + +/* Levinson-Durbin再帰計算により与えられた次数まで全てのLPC係数を求める(倍精度) */ +LPCApiResult LPCCalculator_CalculateMultipleLPCCoefficients( + struct LPCCalculator* lpcc, + const double* data, uint32_t num_samples, double **lpc_coefs, uint32_t max_coef_order, + LPCWindowType window_type, double regular_term) +{ + uint32_t k; + + /* 引数チェック */ + if ((data == NULL) || (lpc_coefs == NULL)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + /* 次数チェック */ + if (max_coef_order > lpcc->max_order) { + return LPC_APIRESULT_EXCEED_MAX_ORDER; + } + + /* 入力サンプル数チェック */ + if (num_samples > lpcc->max_num_buffer_samples) { + return LPC_APIRESULT_EXCEED_MAX_NUM_SAMPLES; + } + + /* 係数計算 */ + if (LPC_CalculateCoef(lpcc, data, num_samples, max_coef_order, window_type, regular_term) != LPC_ERROR_OK) { + return LPC_APIRESULT_FAILED_TO_CALCULATION; + } + + /* 計算成功時は結果をコピー */ + for (k = 0; k < max_coef_order; k++) { + memmove(lpc_coefs[k], lpcc->lpc_coefs[k], sizeof(double) * max_coef_order); + } + + return LPC_APIRESULT_OK; +} + +/* Levinson-Durbin再帰計算により与えられた次数までの残差分散を求める(倍精度) */ +LPCApiResult LPCCalculator_CalculateErrorVariances( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, double *error_vars, uint32_t max_coef_order, + LPCWindowType window_type, double regular_term) +{ + /* 引数チェック */ + if ((data == NULL) || (error_vars == NULL)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + /* 次数チェック */ + if (max_coef_order > lpcc->max_order) { + return LPC_APIRESULT_EXCEED_MAX_ORDER; + } + + /* 入力サンプル数チェック */ + if (num_samples > lpcc->max_num_buffer_samples) { + return LPC_APIRESULT_EXCEED_MAX_NUM_SAMPLES; + } + + /* 係数計算 */ + if (LPC_CalculateCoef(lpcc, data, num_samples, max_coef_order, window_type, regular_term) != LPC_ERROR_OK) { + return LPC_APIRESULT_FAILED_TO_CALCULATION; + } + + /* 計算成功時は結果をコピー */ + memmove(error_vars, lpcc->error_vars, sizeof(double) * (max_coef_order + 1)); + + return LPC_APIRESULT_OK; +} + +/* コレスキー分解 */ +static LPCError LPC_CholeskyDecomposition( + double **Amat, int32_t dim, double *inv_diag) +{ + int32_t i, j, k; + double sum; + + /* 引数チェック */ + assert((Amat != NULL) && (inv_diag != NULL)); + + for (i = 0; i < dim; i++) { + sum = Amat[i][i]; + for (k = i - 1; k >= 0; k--) { + sum -= Amat[i][k] * Amat[i][k]; + } + if (sum <= 0.0) { + return LPC_ERROR_SINGULAR_MATRIX; + } + /* 1.0 / sqrt(sum) は除算により桁落ちするためpowを使用 */ + inv_diag[i] = pow(sum, -0.5); + for (j = i + 1; j < dim; j++) { + sum = Amat[i][j]; + for (k = i - 1; k >= 0; k--) { + sum -= Amat[i][k] * Amat[j][k]; + } + Amat[j][i] = sum * inv_diag[i]; + } + } + + return LPC_ERROR_OK; +} + +/* コレスキー分解により Amat * xvec = bvec を解く */ +static LPCError LPC_SolveByCholeskyDecomposition( + const double * const* Amat, int32_t dim, double *xvec, const double *bvec, const double *inv_diag) +{ + int32_t i, j; + double sum; + + /* 引数チェック */ + assert((Amat != NULL) && (inv_diag != NULL) && (bvec != NULL) && (xvec != NULL)); + + /* 分解を用いて線形一次方程式を解く */ + for (i = 0; i < dim; i++) { + sum = bvec[i]; + for (j = i - 1; j >= 0; j--) { + sum -= Amat[i][j] * xvec[j]; + } + xvec[i] = sum * inv_diag[i]; + } + for (i = dim - 1; i >= 0; i--) { + sum = xvec[i]; + for (j = i + 1; j < dim; j++) { + sum -= Amat[j][i] * xvec[j]; + } + xvec[i] = sum * inv_diag[i]; + } + + return LPC_ERROR_OK; +} + +#if 1 +/* 補助関数法(前向き残差)による係数行列計算 */ +static LPCError LPCAF_CalculateCoefMatrixAndVector( + const double *data, uint32_t num_samples, + const double *a_vec, double **r_mat, double *r_vec, + uint32_t coef_order, double *pobj_value) +{ + double obj_value; + uint32_t smpl, i, j; + + assert(data != NULL); + assert(a_vec != NULL); + assert(r_mat != NULL); + assert(r_vec != NULL); + assert(pobj_value != NULL); + assert(num_samples > coef_order); + + /* 行列を0初期化 */ + for (i = 0; i < coef_order; i++) { + r_vec[i] = 0.0; + for (j = 0; j < coef_order; j++) { + r_mat[i][j] = 0.0; + } + } + + obj_value = 0.0; + + for (smpl = coef_order; smpl < num_samples; smpl++) { + /* 残差計算 */ + double residual = data[smpl]; + double inv_residual; + for (i = 0; i < coef_order; i++) { + residual += a_vec[i] * data[smpl - i - 1]; + } + residual = fabs(residual); + obj_value += residual; + /* 小さすぎる残差は丸め込む(ゼERO割回避、正則化) */ + residual = (residual < LPCAF_RESIDUAL_EPSILON) ? LPCAF_RESIDUAL_EPSILON : residual; + inv_residual = 1.0 / residual; + /* 係数行列に蓄積 */ + for (i = 0; i < coef_order; i++) { + r_vec[i] -= data[smpl] * data[smpl - i - 1] * inv_residual; + for (j = i; j < coef_order; j++) { + r_mat[i][j] += data[smpl - i - 1] * data[smpl - j - 1] * inv_residual; + } + } + } + + /* 対称要素に拡張 */ + for (i = 0; i < coef_order; i++) { + for (j = i + 1; j < coef_order; j++) { + r_mat[j][i] = r_mat[i][j]; + } + } + + /* 目的関数値のセット */ + (*pobj_value) = obj_value / (num_samples - coef_order); + + return LPC_ERROR_OK; +} +#else +/* 補助関数法(前向き後ろ向き残差)による係数行列計算 */ +static LPCError LPCAF_CalculateCoefMatrixAndVector( + const double *data, uint32_t num_samples, + const double *a_vec, double **r_mat, double *r_vec, + uint32_t coef_order, double *pobj_value) +{ + double obj_value; + uint32_t smpl, i, j; + + assert(data != NULL); + assert(a_vec != NULL); + assert(r_mat != NULL); + assert(r_vec != NULL); + assert(pobj_value != NULL); + assert(num_samples > coef_order); + + /* 行列を0初期化 */ + for (i = 0; i < coef_order; i++) { + r_vec[i] = 0.0; + for (j = 0; j < coef_order; j++) { + r_mat[i][j] = 0.0; + } + } + + obj_value = 0.0; + + for (smpl = coef_order; smpl < num_samples - coef_order; smpl++) { + /* 残差計算 */ + double forward = data[smpl], backward = data[smpl]; + double inv_forward, inv_backward; + for (i = 0; i < coef_order; i++) { + forward += a_vec[i] * data[smpl - i - 1]; + backward += a_vec[i] * data[smpl + i + 1]; + } + forward = fabs(forward); + backward = fabs(backward); + obj_value += (forward + backward); + /* 小さすぎる残差は丸め込む(ゼERO割回避、正則化) */ + forward = (forward < LPCAF_RESIDUAL_EPSILON) ? LPCAF_RESIDUAL_EPSILON : forward; + backward = (backward < LPCAF_RESIDUAL_EPSILON) ? LPCAF_RESIDUAL_EPSILON : backward; + inv_forward = 1.0 / forward; + inv_backward = 1.0 / backward; + /* 係数行列に足し込み */ + for (i = 0; i < coef_order; i++) { + r_vec[i] -= data[smpl] * data[smpl - i - 1] * inv_forward; + r_vec[i] -= data[smpl] * data[smpl + i + 1] * inv_backward; + for (j = i; j < coef_order; j++) { + r_mat[i][j] += data[smpl - i - 1] * data[smpl - j - 1] * inv_forward; + r_mat[i][j] += data[smpl + i + 1] * data[smpl + j + 1] * inv_backward; + } + } + } + + /* 対称要素に拡張 */ + for (i = 0; i < coef_order; i++) { + for (j = i + 1; j < coef_order; j++) { + r_mat[j][i] = r_mat[i][j]; + } + } + + (*pobj_value) = obj_value / (2.0 * (num_samples - (2.0 * coef_order))); + + return LPC_ERROR_OK; +} +#endif + +/* 補助関数法による係数計算 */ +static LPCError LPC_CalculateCoefAF( + struct LPCCalculator *lpcc, const double *data, uint32_t num_samples, uint32_t coef_order, + const uint32_t max_num_iteration, const double obj_epsilon, LPCWindowType window_type, double regular_term) +{ + uint32_t itr, i; + double *a_vec = lpcc->a_vecs[0]; + double *r_vec = lpcc->u_vec; + double **r_mat = lpcc->r_mat; + double obj_value, prev_obj_value; + LPCError err; + + /* 係数をLebinson-Durbin法で初期化 */ + if ((err = LPC_CalculateCoef(lpcc, data, num_samples, coef_order, window_type, regular_term)) != LPC_ERROR_OK) { + return err; + } + memcpy(a_vec, lpcc->lpc_coefs[coef_order - 1], sizeof(double) * coef_order); + + /* 0次自己相関(信号の二乗和)が小さい場合 + * => 係数は全て0として無音出力システムを予測 */ + if (fabs(lpcc->auto_corr[0]) < FLT_EPSILON) { + for (i = 0; i < coef_order + 1; i++) { + lpcc->lpc_coefs[coef_order - 1][i] = 0.0; + } + return LPC_ERROR_OK; + } + + prev_obj_value = FLT_MAX; + for (itr = 0; itr < max_num_iteration; itr++) { + /* 係数行列要素の計算 */ + if ((err = LPCAF_CalculateCoefMatrixAndVector( + data, num_samples, a_vec, r_mat, r_vec, coef_order, &obj_value)) != LPC_ERROR_OK) { + return err; + } + /* コレスキー分解 */ + if ((err = LPC_CholeskyDecomposition( + r_mat, (int32_t)coef_order, lpcc->v_vec)) == LPC_ERROR_SINGULAR_MATRIX) { + /* 特異行列になるのは理論上入力が全部0のとき。係数を0クリアして終わる */ + for (i = 0; i < coef_order; i++) { + lpcc->lpc_coefs[coef_order - 1][i] = 0.0; + } + return LPC_ERROR_OK; + } + /* コレスキー分解で r_mat @ avec = r_vec を解く */ + if ((err = LPC_SolveByCholeskyDecomposition( + r_mat, (int32_t)coef_order, a_vec, r_vec, lpcc->v_vec)) != LPC_ERROR_OK) { + return err; + } + assert(err == LPC_ERROR_OK); + /* 収束判定 */ + if (fabs(prev_obj_value - obj_value) < obj_epsilon) { + break; + } + prev_obj_value = obj_value; + } + + /* 解を設定 */ + memmove(lpcc->lpc_coefs[coef_order - 1], a_vec, sizeof(double) * coef_order); + + return LPC_ERROR_OK; +} + +/* 補助関数法よりLPC係数を求める(倍精度) */ +LPCApiResult LPCCalculator_CalculateLPCCoefficientsAF( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, double *coef, uint32_t coef_order, + uint32_t max_num_iteration, LPCWindowType window_type, double regular_term) +{ + /* 引数チェック */ + if ((lpcc == NULL) || (data == NULL) || (coef == NULL)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + /* 次数チェック */ + if (coef_order > lpcc->max_order) { + return LPC_APIRESULT_EXCEED_MAX_ORDER; + } + + /* 係数計算 */ + if (LPC_CalculateCoefAF(lpcc, data, num_samples, coef_order, + max_num_iteration, 1e-8, window_type, regular_term) != LPC_ERROR_OK) { + return LPC_APIRESULT_FAILED_TO_CALCULATION; + } + + /* 計算成功時は結果をコピー */ + memmove(coef, lpcc->lpc_coefs[coef_order - 1], sizeof(double) * coef_order); + + return LPC_APIRESULT_OK; +} + +/* Burg法による係数計算 */ +static LPCError LPC_CalculateCoefBurg( + struct LPCCalculator *lpcc, const double *data, uint32_t num_samples, uint32_t coef_order) +{ +#if 1 + uint32_t i, j, k; + double *a_vec = lpcc->a_vecs[0]; + double **cov = lpcc->r_mat; + LPCError err; + + /* 自己共分散行列計算 */ + for (i = 0; i <= coef_order; i++) { + if ((err = LPC_CalculateAutoCorrelation( + data, num_samples - i, &cov[i][i], coef_order + 1 - i)) != LPC_ERROR_OK) { + return err; + } + for (j = i + 1; j <= coef_order; j++) { + cov[j][i] = cov[i][j]; + } + } + + /* 係数初期化 */ + for (i = 0; i <= coef_order; i++) { + a_vec[i] = 0.0; + } + a_vec[0] = 1.0; + + /* 次数ごとに計算 */ + for (k = 0; k < coef_order; k++) { + double mu; + double FkpBk = 0.0, sum = 0.0, Ck = 0.0; + /* Fk + Bk */ + for (i = 0; i <= k; i++) { + FkpBk += a_vec[i] * a_vec[i] * (cov[i][i] + cov[k + 1 - i][k + 1 - i]); + /* 対角成分以外は対称性を使って半分だけ計算 */ + for (j = i + 1; j <= k; j++) { + sum += a_vec[i] * a_vec[j] * (cov[i][j] + cov[k + 1 - i][k + 1 - j]); + } + } + FkpBk += 2.0 * sum; + /* Ck */ + for (i = 0; i <= k; i++) { + for (j = 0; j <= k; j++) { + Ck += a_vec[i] * a_vec[j] * cov[i][k + 1 - j]; + } + } + /* 反射係数の負号 */ + mu = - 2.0 * Ck / FkpBk; + assert(fabs(mu) <= 1.0); + /* 係数更新 */ + for (i = 0; i <= (k + 1) / 2; i++) { + double tmp1, tmp2; + tmp1 = a_vec[i]; tmp2 = a_vec[k + 1 - i]; + a_vec[i] = tmp1 + mu * tmp2; + a_vec[k + 1 - i] = mu * tmp1 + tmp2; + } + } + + /* 解を設定 */ + memmove(lpcc->lpc_coefs[coef_order - 1], &a_vec[1], sizeof(double) * coef_order); +#else + uint32_t i, k; + double *a_vec = lpcc->a_vec; + double *f_vec, *b_vec; + double Dk, mu; + double tmp1, tmp2; + + /* ベクトル領域割り当て */ + f_vec = malloc(sizeof(double) * num_samples); + b_vec = malloc(sizeof(double) * num_samples); + + /* 各ベクトル初期化 */ + for (k = 0; k < coef_order + 1; k++) { + a_vec[k] = 0.0; + } + a_vec[0] = 1.0; + memcpy(f_vec, data, sizeof(double) * num_samples); + memcpy(b_vec, data, sizeof(double) * num_samples); + + /* Dkの初期化 */ + Dk = 0.0; + for (i = 0; i < num_samples; i++) { + Dk += 2.0 * f_vec[i] * f_vec[i]; + } + Dk -= f_vec[0] * f_vec[0] + f_vec[num_samples - 1] * f_vec[num_samples - 1]; + + /* Burg 再帰アルゴリズム */ + for (k = 0; k < coef_order; k++) { + /* 反射(PARCOR)係数の計算 */ + mu = 0.0; + for (i = 0; i < num_samples - k - 1; i++) { + mu += f_vec[i + k + 1] * b_vec[i]; + } + mu *= -2.0 / Dk; + assert(fabs(mu) < 1.0); + /* a_vecの更新 */ + for (i = 0; i <= (k + 1) / 2; i++) { + tmp1 = a_vec[i]; tmp2 = a_vec[k + 1 - i]; + a_vec[i] = tmp1 + mu * tmp2; + a_vec[k + 1 - i] = mu * tmp1 + tmp2; + } + /* f_vec, b_vecの更新 */ + for (i = 0; i < num_samples - k - 1; i++) { + tmp1 = f_vec[i + k + 1]; tmp2 = b_vec[i]; + f_vec[i + k + 1] = tmp1 + mu * tmp2; + b_vec[i] = mu * tmp1 + tmp2; + } + /* Dkの更新 */ + Dk = (1.0 - mu * mu) * Dk - f_vec[k + 1] * f_vec[k + 1] - b_vec[num_samples - k - 2] * b_vec[num_samples - k - 2]; + } + + /* 係数コピー */ + memcpy(lpcc->lpc_coef, &a_vec[1], sizeof(double) * coef_order); + + free(b_vec); + free(f_vec); +#endif + return LPC_ERROR_OK; +} + +/* Burg法によりLPC係数を求める(倍精度) */ +LPCApiResult LPCCalculator_CalculateLPCCoefficientsBurg( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, double *coef, uint32_t coef_order) +{ + /* 引数チェック */ + if ((lpcc == NULL) || (data == NULL) || (coef == NULL)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + /* 次数チェック */ + if (coef_order > lpcc->max_order) { + return LPC_APIRESULT_EXCEED_MAX_ORDER; + } + + /* 係数計算 */ + if (LPC_CalculateCoefBurg(lpcc, data, num_samples, coef_order) != LPC_ERROR_OK) { + return LPC_APIRESULT_FAILED_TO_CALCULATION; + } + + /* 計算成功時は結果をコピー */ + memmove(coef, lpcc->lpc_coefs[coef_order - 1], sizeof(double) * coef_order); + + return LPC_APIRESULT_OK; +} + +/* 共分散行列の計算 */ +static LPCError LPCSVR_CalculateCovarianceMatrix( + const double *data, uint32_t num_samples, double **cov, uint32_t dim) +{ + uint32_t i, j, smpl; + + /* 引数チェック */ + if ((data == NULL) || (cov == NULL)) { + return LPC_ERROR_INVALID_ARGUMENT; + } + + for (i = 0; i < dim; i++) { + for (j = i; j < dim; j++) { + cov[i][j] = 0.0; + } + } + + for (smpl = 0; smpl < num_samples - dim; smpl++) { + const double *pdata = &data[smpl]; + for (i = 0; i < dim; i++) { + const double s = pdata[i]; + for (j = i; j < dim; j++) { + cov[i][j] += s * pdata[j]; + } + } + } + + for (i = 0; i < dim; i++) { + for (j = i + 1; j < dim; j++) { + cov[j][i] = cov[i][j]; + } + } + + return LPC_ERROR_OK; +} + +/* Recursive Golomb-Rice符号の平均符号長 */ +static double LPCSVR_CalculateRGRMeanCodeLength(double mean_abs_error, uint32_t bps) +{ + const double intmean = mean_abs_error * (1 << bps); /* 整数量子化した時の平均値 */ + const double rho = 1.0 / (1.0 + intmean); + const uint32_t k2 = (uint32_t)LPC_MAX(0, LPC_Log2(log(0.5127629514) / log(1.0 - rho))); + const uint32_t k1 = k2 + 1; + const double k1factor = pow(1.0 - rho, (double)(1 << k1)); + const double k2factor = pow(1.0 - rho, (double)(1 << k2)); + return (1.0 + k1) * (1.0 - k1factor) + (1.0 + k2 + (1.0 / (1.0 - k2factor))) * k1factor; +} + +/* SVRによる係数計算 */ +static LPCError LPC_CalculateCoefSVR( + struct LPCCalculator *lpcc, const double *data, uint32_t num_samples, uint32_t coef_order, + const uint32_t max_num_iteration, const double obj_epsilon, LPCWindowType window_type, + double regular_term, const double *margin_list, uint32_t margin_list_size) +{ +#define BITS_PER_SAMPLE 16 + uint32_t itr, i, j, smpl; + double *coef = lpcc->a_vecs[0]; + double *r_vec = lpcc->u_vec; + double *low = lpcc->v_vec; + double *best_coef = lpcc->lpc_coefs[0]; + double *delta = lpcc->parcor_coef; + double *init_coef = lpcc->auto_corr; + double **cov = lpcc->r_mat; + double *residual = lpcc->buffer; + double obj_value, prev_obj_value, min_obj_value; + LPCError err; + + /* 引数チェック */ + if ((lpcc == NULL) || (data == NULL) || (margin_list == NULL) || (margin_list_size == 0)) { + return LPC_ERROR_INVALID_ARGUMENT; + } + + /* Levinson-Durbin法で係数を求める */ + if ((err = LPC_CalculateCoef(lpcc, data, num_samples, coef_order, window_type, 0.0)) != LPC_ERROR_OK) { + return err; + } + /* 0次自己相関(信号の二乗和)が小さい場合 + * => 係数は全て0として無音出力システムを予測 */ + if (fabs(lpcc->auto_corr[0]) < FLT_EPSILON) { + for (i = 0; i < coef_order + 1; i++) { + lpcc->lpc_coefs[coef_order - 1][i] = 0.0; + } + return LPC_ERROR_OK; + } + + /* 学習しない場合はLevinson-Durbin法の結果をそのまま採用 */ + if (max_num_iteration == 0) { + return LPC_ERROR_OK; + } + + /* 共分散行列の計算 */ + if ((err = LPCSVR_CalculateCovarianceMatrix(data, num_samples, cov, coef_order)) != LPC_ERROR_OK) { + return err; + } + /* Ridge正則化 */ + for (i = 0; i < coef_order; i++) { + cov[i][i] += regular_term; + } + /* コレスキー分解 */ + if ((err = LPC_CholeskyDecomposition(cov, (int32_t)coef_order, low)) == LPC_ERROR_SINGULAR_MATRIX) { + /* 特異行列になるのは理論上入力が全部0のとき。係数を0クリアして終わる */ + for (i = 0; i < coef_order; i++) { + lpcc->lpc_coefs[coef_order - 1][i] = 0.0; + } + return LPC_ERROR_OK; + } + + /* 初期値をLevinson-Durbin法の係数に設定 */ + memcpy(init_coef, lpcc->lpc_coefs[coef_order - 1], sizeof(double) * coef_order); + + /* TODO: 係数は順序反転した方がresidualの計算が早そう(要検証) */ + + min_obj_value = FLT_MAX; + for (j = 0; j < margin_list_size; j++) { + const double margin = margin_list[j]; + prev_obj_value = FLT_MAX; + memcpy(coef, init_coef, sizeof(double) * coef_order); + for (itr = 0; itr < max_num_iteration; itr++) { + double mabse = 0.0; + /* 残差計算/残差ソフトスレッショルド */ + memcpy(residual, data, sizeof(double) * num_samples); + for (i = 0; i < coef_order; i++) { + r_vec[i] = 0.0; + } + for (smpl = coef_order; smpl < num_samples; smpl++) { + for (i = 0; i < coef_order; i++) { + residual[smpl] += coef[i] * data[smpl - i - 1]; + } + mabse += LPC_ABS(residual[smpl]); + residual[smpl] = LPC_SOFT_THRESHOLD(residual[smpl], margin); + for (i = 0; i < coef_order; i++) { + r_vec[i] += residual[smpl] * data[smpl - i - 1]; + } + } + obj_value = LPCSVR_CalculateRGRMeanCodeLength(mabse / num_samples, BITS_PER_SAMPLE); + /* コレスキー分解で cov @ delta = r_vec を解く */ + if ((err = LPC_SolveByCholeskyDecomposition( + cov, (int32_t)coef_order, delta, r_vec, low)) != LPC_ERROR_OK) { + return err; + } + /* 最善係数の更新 */ + if (obj_value < min_obj_value) { + memcpy(best_coef, coef, sizeof(double) * coef_order); + min_obj_value = obj_value; + } + /* 収束判定 */ + if ((prev_obj_value < obj_value) || (fabs(prev_obj_value - obj_value) < obj_epsilon)) { + break; + } + /* 係数更新 */ + for (i = 0; i < coef_order; i++) { + coef[i] += delta[i]; + } + prev_obj_value = obj_value; + } + } + + /* 解を設定 */ + memmove(lpcc->lpc_coefs[coef_order - 1], best_coef, sizeof(double) * coef_order); + + return LPC_ERROR_OK; +#undef BITS_PER_SAMPLE +} + +/* SVRよりLPC係数を求める(倍精度) */ +LPCApiResult LPCCalculator_CalculateLPCCoefficientsSVR( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, double *coef, uint32_t coef_order, + uint32_t max_num_iteration, LPCWindowType window_type, + double regular_term, const double *margin_list, uint32_t margin_list_size) +{ + /* 引数チェック */ + if ((lpcc == NULL) || (data == NULL) || (coef == NULL) + || (margin_list == NULL) || (margin_list_size == 0)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + /* 次数チェック */ + if (coef_order > lpcc->max_order) { + return LPC_APIRESULT_EXCEED_MAX_ORDER; + } + + /* 係数計算 */ + if (LPC_CalculateCoefSVR(lpcc, data, num_samples, coef_order, + max_num_iteration, 1e-8, window_type, regular_term, margin_list, margin_list_size) != LPC_ERROR_OK) { + return LPC_APIRESULT_FAILED_TO_CALCULATION; + } + + /* 計算成功時は結果をコピー */ + memmove(coef, lpcc->lpc_coefs[coef_order - 1], sizeof(double) * coef_order); + + return LPC_APIRESULT_OK; +} + +/* 入力データからサンプルあたりの推定符号長を求める */ +LPCApiResult LPCCalculator_EstimateCodeLength( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, uint32_t bits_per_sample, + uint32_t coef_order, double *length_per_sample_bits, LPCWindowType window_type) +{ + uint32_t ord; + double log2_mean_res_power, log2_var_ratio; + + /* 定数値 */ +#define BETA_CONST_FOR_LAPLACE_DIST (1.9426950408889634) /* sqrt(2 * E * E) */ +#define BETA_CONST_FOR_GAUSS_DIST (2.047095585180641) /* sqrt(2 * E * PI) */ + + /* 引数チェック */ + if ((lpcc == NULL) || (data == NULL) || (length_per_sample_bits == NULL)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + /* 係数計算 */ + if (LPC_CalculateCoef(lpcc, data, num_samples, coef_order, window_type, 0.0) != LPC_ERROR_OK) { + return LPC_APIRESULT_FAILED_TO_CALCULATION; + } + + /* log2(パワー平均)の計算 */ + log2_mean_res_power = lpcc->auto_corr[0]; /* 0次標本自己相関はパワー */ + /* 整数PCMの振幅に変換(doubleの密度保障) */ + log2_mean_res_power *= pow(2, (double)(2.0 * (bits_per_sample - 1))); + if (fabs(log2_mean_res_power) <= FLT_MIN) { + /* ほぼ無音だった場合は符号長を0とする */ + (*length_per_sample_bits) = 0.0; + return LPC_APIRESULT_OK; + } + log2_mean_res_power = LPC_Log2((double)log2_mean_res_power) - LPC_Log2((double)num_samples); + + /* sum(log2(1 - (parcor * parcor)))の計算 */ + log2_var_ratio = 0.0; + for (ord = 0; ord < coef_order; ord++) { + log2_var_ratio += LPC_Log2(1.0 - lpcc->parcor_coef[ord] * lpcc->parcor_coef[ord]); + } + + /* エントロピー計算 */ + /* →サンプルあたりの最小のビット数が得られる */ + (*length_per_sample_bits) = BETA_CONST_FOR_LAPLACE_DIST + 0.5f * (log2_mean_res_power + log2_var_ratio); + + /* 推定ビット数が負値の場合は、1サンプルあたり1ビットで符号化できることを期待する */ + /* 補足)このケースは入力音声パワーが非常に低い */ + if ((*length_per_sample_bits) <= 0) { + (*length_per_sample_bits) = 1.0; + return LPC_APIRESULT_OK; + } + +#undef BETA_CONST_FOR_LAPLACE_DIST +#undef BETA_CONST_FOR_GAUSS_DIST + + return LPC_APIRESULT_OK; +} + +/* MDL(最小記述長)を計算 */ +LPCApiResult LPCCalculator_CalculateMDL( + struct LPCCalculator *lpcc, + const double *data, uint32_t num_samples, uint32_t coef_order, double *mdl, + LPCWindowType window_type) +{ + uint32_t k; + double tmp; + + /* 引数チェック */ + if ((lpcc == NULL) || (data == NULL) || (mdl == NULL)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + /* 係数計算 */ + if (LPC_CalculateCoef(lpcc, data, num_samples, coef_order, window_type, 0.0) != LPC_ERROR_OK) { + return LPC_APIRESULT_FAILED_TO_CALCULATION; + } + + /* 第一項の計算 */ + /* 1次の係数は0で確定だから飛ばす */ + tmp = 0.0; + for (k = 1; k <= coef_order; k++) { + tmp += log(1.0 - lpcc->parcor_coef[k] * lpcc->parcor_coef[k]); + } + tmp *= num_samples; + + /* 第二項の計算 */ + tmp += coef_order * log(num_samples); + + (*mdl) = tmp; + + return LPC_APIRESULT_OK; +} + +/* LPC係数をPARCOR係数に変換 */ +static LPCError LPC_ConvertLPCtoPARCORDouble( + struct LPCCalculator *lpcc, const double *lpc_coef, uint32_t coef_order, double *parcor_coef) +{ + int32_t i, k; + double *tmplpc_coef, *a_vec; + + /* 引数チェック */ + if ((lpcc == NULL) || (lpc_coef == NULL) || (parcor_coef == NULL)) { + return LPC_ERROR_INVALID_ARGUMENT; + } + + /* 次数チェック */ + assert(coef_order <= lpcc->max_order); + + /* 作業領域を割り当て */ + tmplpc_coef = lpcc->lpc_coefs[0]; + a_vec = lpcc->a_vecs[0]; + + memcpy(tmplpc_coef, lpc_coef, sizeof(double) * coef_order); + + /* PARCOR係数に変換 */ + for (i = (int32_t)(coef_order - 1); i >= 0; i--) { + const double gamma = tmplpc_coef[i]; + assert(fabs(gamma) < 1.0); + parcor_coef[i] = -gamma; + for (k = 0; k < i; k++) { + a_vec[k] = tmplpc_coef[k]; + } + for (k = 0; k < i; k++) { + tmplpc_coef[k] = (a_vec[k] - gamma * a_vec[i - k - 1]) / (1.0 - gamma * gamma); + } + } + + return LPC_ERROR_OK; +} + +/* LPC係数をPARCOR係数に変換して量子化 */ +LPCApiResult LPC_QuantizeCoefficientsAsPARCOR( + struct LPCCalculator *lpcc, + const double *lpc_coef, uint32_t coef_order, uint32_t nbits_precision, int32_t *int_coef) +{ + uint32_t ord; + int32_t qtmp; + const int32_t qmax = (1 << (nbits_precision - 1)); + + /* 引数チェック */ + if ((lpcc == NULL) || (lpc_coef == NULL) + || (int_coef == NULL) || (nbits_precision == 0)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + /* 次数チェック */ + if (coef_order > lpcc->max_order) { + return LPC_APIRESULT_EXCEED_MAX_ORDER; + } + + /* PARCOR係数に変換 */ + if (LPC_ConvertLPCtoPARCORDouble(lpcc, lpc_coef, coef_order, lpcc->parcor_coef) != LPC_ERROR_OK) { + return LPC_APIRESULT_NG; + } + + /* PARCOR係数を量子化して出力 */ + for (ord = 0; ord < coef_order; ord++) { + assert(fabs(lpcc->parcor_coef[ord]) < 1.0); + qtmp = (int32_t)LPC_Round(lpcc->parcor_coef[ord] * pow(2.0, nbits_precision - 1)); + /* 正負境界の丸め込み */ + if (qtmp >= qmax) { + qtmp = qmax - 1; + } else if (qtmp < -qmax) { + qtmp = -qmax; + } + int_coef[ord] = qtmp; + } + + return LPC_APIRESULT_OK; +} + +/* LPC係数の整数量子化 */ +LPCApiResult LPC_QuantizeCoefficients( + const double *double_coef, uint32_t coef_order, uint32_t nbits_precision, uint32_t max_bits, + int32_t *int_coef, uint32_t *coef_rshift) +{ + uint32_t rshift; + int32_t ord, ndigit, qtmp; + double max, qerror; + const int32_t qmax = (1 << (nbits_precision - 1)); + + /* 引数チェック */ + if ((double_coef == NULL) || (int_coef == NULL) + || (coef_rshift == NULL) || (nbits_precision == 0)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + /* 係数絶対値の計算 */ + max = 0.0; + for (ord = 0; ord < (int32_t)coef_order; ord++) { + if (max < fabs(double_coef[ord])) { + max = fabs(double_coef[ord]); + } + } + + /* 与えられたビット数で表現できないほど小さいときは0とみなす */ + if (max <= pow(2.0, -(int32_t)(nbits_precision - 1))) { + (*coef_rshift) = nbits_precision; + memset(int_coef, 0, sizeof(int32_t) * coef_order); + return LPC_APIRESULT_OK; + } + + /* 最大値を[1/2, 1)に収めるための右シフト量の計算 */ + /* max = x * 2^ndigit, |x| in [1/2, 1)を計算 */ + (void)frexp(max, &ndigit); + /* 符号ビットを落とす */ + nbits_precision--; + /* nbits_precisionで表現可能にするためのシフト量計算 */ + assert((int32_t)nbits_precision >= ndigit); + rshift = (uint32_t)((int32_t)nbits_precision - ndigit); + + /* 右シフト量が最大を越えている場合は切り捨て(係数最大値が小さい場合) */ + if (rshift >= max_bits) { + rshift = max_bits - 1; + } + + /* 量子化 */ + qerror = 0.0; + for (ord = (int32_t)coef_order - 1; ord >= 0; ord--) { + /* 前の係数の誤差を取り込んで量子化 */ + /* インパルスの先頭部分には誤差を入れたくないため、末尾から処理 */ + qerror += double_coef[ord] * pow(2.0, rshift); + qtmp = (int32_t)LPC_Round(qerror); + /* 正負境界の丸め込み */ + if (qtmp >= qmax) { + qtmp = qmax - 1; + } else if (qtmp < -qmax) { + qtmp = -qmax; + } + /* 引いた分が量子化誤差として残る */ + qerror -= qtmp; + int_coef[ord] = qtmp; + } + (*coef_rshift) = rshift; + + return LPC_APIRESULT_OK; +} + +/* LPC係数により予測/誤差出力 */ +LPCApiResult LPC_Predict( + const int32_t *data, uint32_t num_samples, + const int32_t *coef, uint32_t coef_order, int32_t *residual, uint32_t coef_rshift) +{ + uint32_t smpl, ord; + + /* 引数チェック */ + if ((data == NULL) || (coef == NULL) + || (residual == NULL) || (coef_rshift == 0)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + memcpy(residual, data, sizeof(int32_t) * num_samples); + + /* LPC係数による予測 */ + for (smpl = 1; smpl < coef_order; smpl++) { + int32_t predict = (1 << (coef_rshift - 1)); + for (ord = 0; ord < smpl; ord++) { + predict += (coef[ord] * data[smpl - ord - 1]); + } + residual[smpl] += (predict >> coef_rshift); + } + for (smpl = coef_order; smpl < num_samples; smpl++) { + int32_t predict = (1 << (coef_rshift - 1)); + for (ord = 0; ord < coef_order; ord++) { + predict += (coef[ord] * data[smpl - ord - 1]); + } + residual[smpl] += (predict >> coef_rshift); + } + + return LPC_APIRESULT_OK; +} + +/* LPC係数により合成(in-place) */ +LPCApiResult LPC_Synthesize( + int32_t *data, uint32_t num_samples, + const int32_t *coef, uint32_t coef_order, uint32_t coef_rshift) +{ + uint32_t smpl, ord; + + /* 引数チェック */ + if ((data == NULL) || (coef == NULL) || (coef_rshift == 0)) { + return LPC_APIRESULT_INVALID_ARGUMENT; + } + + /* LPC係数による予測 */ + for (smpl = 1; smpl < coef_order; smpl++) { + int32_t predict = (1 << (coef_rshift - 1)); + for (ord = 0; ord < smpl; ord++) { + predict += (coef[ord] * data[smpl - ord - 1]); + } + data[smpl] -= (predict >> coef_rshift); + } + for (smpl = coef_order; smpl < num_samples; smpl++) { + int32_t predict = (1 << (coef_rshift - 1)); + for (ord = 0; ord < coef_order; ord++) { + predict += (coef[ord] * data[smpl - ord - 1]); + } + data[smpl] -= (predict >> coef_rshift); + } + + return LPC_APIRESULT_OK; +} diff --git a/libs/srla_coder/CMakeLists.txt b/libs/srla_coder/CMakeLists.txt new file mode 100644 index 0000000..d11c687 --- /dev/null +++ b/libs/srla_coder/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# プロジェクト名 +project(SRLACoder C) + +# ライブラリ名 +set(LIB_NAME srla_coder) + +# 静的ライブラリ指定 +add_library(${LIB_NAME} STATIC) + +# ソースディレクトリ +add_subdirectory(src) + +# インクルードパス +target_include_directories(${LIB_NAME} + PRIVATE + ${PROJECT_ROOT_PATH}/include + ${PROJECT_ROOT_PATH}/libs/bit_stream/include + ${PROJECT_ROOT_PATH}/libs/srla_internal/include + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + +# コンパイルオプション +if(MSVC) + target_compile_options(${LIB_NAME} PRIVATE /W4) +else() + target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=2 -Wconversion -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition) + set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +endif() +set_target_properties(${LIB_NAME} + PROPERTIES + C_STANDARD 90 C_EXTENSIONS OFF + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) diff --git a/libs/srla_coder/include/srla_coder.h b/libs/srla_coder/include/srla_coder.h new file mode 100644 index 0000000..dd69940 --- /dev/null +++ b/libs/srla_coder/include/srla_coder.h @@ -0,0 +1,33 @@ +#ifndef SRLACODER_H_INCLUDED +#define SRLACODER_H_INCLUDED + +#include +#include "bit_stream.h" + +/* 符号化ハンドル */ +struct SRLACoder; + +#ifdef __cplusplus +extern "C" { +#endif + +/* 符号化ハンドルの作成に必要なワークサイズの計算 */ +int32_t SRLACoder_CalculateWorkSize(void); + +/* 符号化ハンドルの作成 */ +struct SRLACoder* SRLACoder_Create(void *work, int32_t work_size); + +/* 符号化ハンドルの破棄 */ +void SRLACoder_Destroy(struct SRLACoder *coder); + +/* 符号付き整数配列の符号化 */ +void SRLACoder_Encode(struct SRLACoder *coder, struct BitStream *stream, const int32_t *data, uint32_t num_samples); + +/* 符号付き整数配列の復号 */ +void SRLACoder_Decode(struct BitStream *stream, int32_t *data, uint32_t num_samples); + +#ifdef __cplusplus +} +#endif + +#endif /* SRLACODER_H_INCLUDED */ diff --git a/libs/srla_coder/src/CMakeLists.txt b/libs/srla_coder/src/CMakeLists.txt new file mode 100644 index 0000000..368db87 --- /dev/null +++ b/libs/srla_coder/src/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/srla_coder.c + ) diff --git a/libs/srla_coder/src/srla_coder.c b/libs/srla_coder/src/srla_coder.c new file mode 100644 index 0000000..a00ba9c --- /dev/null +++ b/libs/srla_coder/src/srla_coder.c @@ -0,0 +1,586 @@ +#include "srla_coder.h" + +#include +#include +#include +#include + +#include "srla_internal.h" +#include "srla_utility.h" + +/* マクロ展開を使用する */ +#define SRLACODER_USE_MACROS 1 + +/* メモリアラインメント */ +#define SRLACODER_MEMORY_ALIGNMENT 16 +/* log2(最大分割数) */ +#define SRLACODER_LOG2_MAX_NUM_PARTITIONS 10 +/* 最大分割数 */ +#define SRLACODER_MAX_NUM_PARTITIONS (1 << SRLACODER_LOG2_MAX_NUM_PARTITIONS) +/* パラメータ記録領域ビット数 */ +#define SRLACODER_RICE_PARAMETER_BITS 5 +/* ガンマ符号長サイズ */ +#define SRLACODER_GAMMA_BITS(uint) (((uint) == 0) ? 1 : ((2 * SRLAUTILITY_LOG2CEIL(uint + 2)) - 1)) + +/* 符号の区別 */ +typedef enum SRLACoderCodeTypeTag { + SRLACODER_CODE_TYPE_RICE = 0, /* TODO: 将来的にGolombにするべきかも */ + SRLACODER_CODE_TYPE_RECURSIVE_RICE = 1, + SRLACODER_CODE_TYPE_INVALID +} SRLACoderCodeType; + + +/* 符号化ハンドル */ +struct SRLACoder { + uint8_t alloced_by_own; + double part_mean[SRLACODER_LOG2_MAX_NUM_PARTITIONS + 1][SRLACODER_MAX_NUM_PARTITIONS]; + void *work; +}; + +/* 符号化ハンドルの作成に必要なワークサイズの計算 */ +int32_t SRLACoder_CalculateWorkSize(void) +{ + int32_t work_size; + + /* ハンドル分のサイズ */ + work_size = sizeof(struct SRLACoder) + SRLACODER_MEMORY_ALIGNMENT; + + return work_size; +} + +/* 符号化ハンドルの作成 */ +struct SRLACoder* SRLACoder_Create(void *work, int32_t work_size) +{ + struct SRLACoder *coder; + uint8_t tmp_alloc_by_own = 0; + uint8_t *work_ptr; + + /* ワーク領域時前確保の場合 */ + if ((work == NULL) && (work_size == 0)) { + /* 引数を自前の計算値に差し替える */ + if ((work_size = SRLACoder_CalculateWorkSize()) < 0) { + return NULL; + } + work = malloc((uint32_t)work_size); + tmp_alloc_by_own = 1; + } + + /* 引数チェック */ + if ((work == NULL) || (work_size < SRLACoder_CalculateWorkSize())) { + return NULL; + } + + /* ワーク領域先頭取得 */ + work_ptr = (uint8_t *)work; + + /* ハンドル領域確保 */ + work_ptr = (uint8_t *)SRLAUTILITY_ROUNDUP((uintptr_t)work_ptr, SRLACODER_MEMORY_ALIGNMENT); + coder = (struct SRLACoder *)work_ptr; + work_ptr += sizeof(struct SRLACoder); + + /* ハンドルメンバ設定 */ + coder->alloced_by_own = tmp_alloc_by_own; + coder->work = work; + + return coder; +} + +/* 符号化ハンドルの破棄 */ +void SRLACoder_Destroy(struct SRLACoder *coder) +{ + if (coder != NULL) { + /* 自前確保していたら領域開放 */ + if (coder->alloced_by_own == 1) { + free(coder->work); + } + } +} + +/* ガンマ符号の出力 */ +static void Gamma_PutCode(struct BitStream *stream, uint32_t val) +{ + uint32_t ndigit; + + SRLA_ASSERT(stream != NULL); + + if (val == 0) { + /* 符号化対象が0ならば1を出力して終了 */ + BitWriter_PutBits(stream, 1, 1); + return; + } + + /* 桁数を取得 */ + ndigit = SRLAUTILITY_LOG2CEIL(val + 2); + /* 桁数-1だけ0を続ける */ + BitWriter_PutBits(stream, 0, ndigit - 1); + /* 桁数を使用して符号語を2進数で出力 */ + BitWriter_PutBits(stream, val + 1, ndigit); +} + +/* ガンマ符号の取得 */ +static uint32_t Gamma_GetCode(struct BitStream *stream) +{ + uint32_t ndigit; + uint32_t bitsbuf; + + SRLA_ASSERT(stream != NULL); + + /* 桁数を取得 */ + /* 1が出現するまで桁数を増加 */ + BitReader_GetZeroRunLength(stream, &ndigit); + /* 最低でも1のため下駄を履かせる */ + ndigit++; + + /* 桁数が1のときは0 */ + if (ndigit == 1) { + return 0; + } + + /* 桁数から符号語を出力 */ + BitReader_GetBits(stream, &bitsbuf, ndigit - 1); + return (uint32_t)((1UL << (ndigit - 1)) + bitsbuf - 1); +} + +/* Rice符号の出力 */ +static void Rice_PutCode(struct BitStream *stream, uint32_t k, uint32_t uval) +{ + const uint32_t mask = (1U << k) - 1; + + SRLA_ASSERT(stream != NULL); + + BitWriter_PutZeroRun(stream, uval >> k); + BitWriter_PutBits(stream, uval & mask, k); +} + +/* 再帰的Rice符号の出力 */ +static void RecursiveRice_PutCode(struct BitStream *stream, uint32_t k1, uint32_t k2, uint32_t uval) +{ + const uint32_t k1pow = 1U << k1; + const uint32_t k2mask = (1U << k2) - 1; + + SRLA_ASSERT(stream != NULL); + + if (uval < k1pow) { + /* 1段目で符号化 */ + BitWriter_PutBits(stream, 1, 1); + BitWriter_PutBits(stream, uval, k1); + } else { + /* 1段目のパラメータで引き、2段目のパラメータでRice符号化 */ + uval -= k1pow; + BitWriter_PutZeroRun(stream, 1 + (uval >> k2)); + BitWriter_PutBits(stream, uval & k2mask, k2); + } +} + +/* Rice符号の取得 */ +static uint32_t Rice_GetCode(struct BitStream *stream, uint32_t k) +{ + uint32_t quot, uval; + + SRLA_ASSERT(stream != NULL); + + /* 商(alpha符号)の取得 */ + BitReader_GetZeroRunLength(stream, "); + + /* 剰余の取得 */ + BitReader_GetBits(stream, &uval, k); + + return (quot << k) + uval; +} + +/* 再帰的Rice符号の取得 */ +#if defined(SRLACODER_USE_MACROS) +#define RecursiveRice_GetCode(stream, k1, k2, uval)\ + do {\ + uint32_t __quot;\ + \ + SRLA_ASSERT(stream != NULL);\ + SRLA_ASSERT(uval != NULL);\ + \ + /* 商(alpha符号)の取得 */\ + BitReader_GetZeroRunLength(stream, &__quot);\ + \ + /* 商で場合分け */\ + if (__quot == 0) {\ + BitReader_GetBits(stream, uval, k1);\ + } else {\ + BitReader_GetBits(stream, uval, k2);\ + (*(uval)) += (1U << (k1)) + ((__quot - 1) << (k2));\ + }\ + } while (0); +#else +static void RecursiveRice_GetCode(struct BitStream *stream, uint32_t k1, uint32_t k2, uint32_t *uval) +{ + uint32_t quot; + + SRLA_ASSERT(stream != NULL); + SRLA_ASSERT(uval != NULL); + + /* 商(alpha符号)の取得 */ + BitReader_GetZeroRunLength(stream, "); + + /* 商で場合分け */ + if (quot == 0) { + BitReader_GetBits(stream, uval, k1); + } else { + BitReader_GetBits(stream, uval, k2); + (*uval) += (1U << k1) + ((quot - 1) << k2); + } +} +#endif + +/* 最適な符号化パラメータの計算 */ +static void SRLACoder_CalculateOptimalRiceParameter( + const double mean, uint32_t *optk, double *bits_per_sample) +{ + uint32_t k; + double rho, fk, bps; +#define OPTX 0.5127629514437670454896078808815218508243560791015625 /* (x - 1)^2 + ln(2) x ln(x) = 0 の解 */ + + /* 幾何分布のパラメータを最尤推定 */ + rho = 1.0 / (1.0 + mean); + + /* 最適なパラメータの計算 */ + k = (uint32_t)SRLAUTILITY_MAX(0, SRLAUtility_Round(SRLAUtility_Log2(log(OPTX) / log(1.0 - rho)))); + + /* 平均符号長の計算 */ + fk = pow(1.0 - rho, (double)(1 << k)); + bps = k + (1.0 / (1.0 - fk)); + + /* 結果出力 */ + (*optk) = k; + + if (bits_per_sample != NULL) { + (*bits_per_sample) = bps; + } + +#undef OPTX +} + +/* k1に関する偏微分係数の計算 */ +static double SRLACoder_CalculateMeanCodelength(double rho, uint32_t k1, uint32_t k2) +{ + const double fk1 = pow(1.0 - rho, (double)(1 << k1)); + const double fk2 = pow(1.0 - rho, (double)(1 << k2)); + return (1.0 + k1) * (1.0 - fk1) + (1.0 + k2 + (1.0 / (1.0 - fk2))) * fk1; +} + +/* k1に関する偏微分係数の計算 */ +static double SRLACoder_CalculateDiffk1(double rho, double k1, double k2) +{ + const double k1pow = pow(2.0, k1); + const double fk1 = pow(1.0 - rho, k1pow); + const double fk2 = pow(1.0 - rho, pow(2.0, k2)); + const double fk1d = k1pow * fk1 * log(1.0 - rho) * log(2.0); + return 1.0 - fk1 + (k2 - k1 + 1.0 / (1.0 - fk2)) * fk1d; +} + +/* 最適な符号化パラメータの計算 */ +static void SRLACoder_CalculateOptimalRecursiveRiceParameter( + const double mean, uint32_t *optk1, uint32_t *optk2, double *bits_per_sample) +{ + uint32_t k1, k2; + double rho, fk1, fk2, bps; +#define OPTX 0.5127629514437670454896078808815218508243560791015625 /* (x - 1)^2 + ln(2) x ln(x) = 0 の解 */ + + /* 幾何分布のパラメータを最尤推定 */ + rho = 1.0 / (1.0 + mean); + + /* 最適なパラメータの計算 */ +#if 1 + /* 簡易版 */ + k2 = (uint32_t)SRLAUTILITY_MAX(0, floor(SRLAUtility_Log2(log(OPTX) / log(1.0 - rho)))); + k1 = k2 + 1; +#else + /* k1を2分法で求める */ + /* note: 一般にk1 = k2 + 1が成り立たなくなり符号化で不利! */ + { + uint32_t i; + double k1tmp, d1, d2; + const double k2tmp = SRLAUtility_Log2(log(OPTX) / log(1.0 - rho)); + double k1min = k2tmp - 1.5; + double k1max = k2tmp + 1.5; + for (i = 0; i < 5; i++) { + k1tmp = (k1max + k1min) / 2.0; + d1 = SRLACoder_CalculateDiffk1(rho, k1tmp, k2tmp); + d2 = SRLACoder_CalculateDiffk1(rho, k1min, k2tmp); + if (SRLAUTILITY_SIGN(d1) == SRLAUTILITY_SIGN(d2)) { + k1min = k1tmp; + } else { + k1max = k1tmp; + } + } + k1 = (uint32_t)SRLAUTILITY_MAX(0, ceil(k1tmp)); + k2 = (uint32_t)SRLAUTILITY_MAX(0, floor(k2tmp)); + } +#endif + + /* 平均符号長の計算 */ + bps = SRLACoder_CalculateMeanCodelength(rho, k1, k2); + + /* 結果出力 */ + (*optk1) = k1; + (*optk2) = k2; + + if (bits_per_sample != NULL) { + (*bits_per_sample) = bps; + } + +#undef OPTX +} + +/* Rice符号長の出力 */ +static uint32_t Rice_GetCodeLength(uint32_t k, uint32_t uval) +{ + return 1 + k + (uval >> k); +} + +/* 再帰的Rice符号長の出力 */ +static uint32_t RecursiveRice_GetCodeLength(uint32_t k1, uint32_t k2, uint32_t uval) +{ + const uint32_t k1pow = 1U << k1; + + if (uval < k1pow) { + /* 1段目で符号化 */ + return k1 + 1; + } else { + /* 1段目のパラメータで引き、2段目のパラメータでRice符号化 */ + return k2 + 2 + ((uval - k1pow) >> k2); + } +} + +/* 符号付き整数配列の符号化 */ +static void SRLACoder_EncodePartitionedRecursiveRice(struct SRLACoder *coder, struct BitStream *stream, const int32_t *data, uint32_t num_samples) +{ + uint32_t max_porder, max_num_partitions; + uint32_t porder, part, best_porder, smpl; + SRLACoderCodeType code_type = SRLACODER_CODE_TYPE_INVALID; + + /* 最大分割数の決定 */ + max_porder = 1; + while ((num_samples % (1 << max_porder)) == 0) { + max_porder++; + } + max_porder = SRLAUTILITY_MIN(max_porder - 1, SRLACODER_LOG2_MAX_NUM_PARTITIONS); + max_num_partitions = (1 << max_porder); + + /* 各分割での平均を計算 */ + { + int32_t i; + + /* 最も細かい分割時の平均値 */ + for (part = 0; part < max_num_partitions; part++) { + const uint32_t nsmpl = num_samples / max_num_partitions; + double part_sum = 0.0; + for (smpl = 0; smpl < nsmpl; smpl++) { + part_sum += SRLAUTILITY_SINT32_TO_UINT32(data[part * nsmpl + smpl]); + } + coder->part_mean[max_porder][part] = part_sum / nsmpl; + } + + /* より大きい分割の平均は、小さい分割の平均をマージして計算 */ + for (i = (int32_t)(max_porder - 1); i >= 0; i--) { + for (part = 0; part < (1 << i); part++) { + coder->part_mean[i][part] = (coder->part_mean[i + 1][2 * part] + coder->part_mean[i + 1][2 * part + 1]) / 2.0; + } + } + } + + /* 全体平均を元に符号を切り替え */ + if (coder->part_mean[0][0] < 2) { + code_type = SRLACODER_CODE_TYPE_RICE; + } else { + code_type = SRLACODER_CODE_TYPE_RECURSIVE_RICE; + } + + /* 各分割での符号長を計算し、最適な分割を探索 */ + { + uint32_t min_bits = UINT32_MAX; + best_porder = max_porder + 1; + + switch (code_type) { + case SRLACODER_CODE_TYPE_RICE: + { + for (porder = 0; porder <= max_porder; porder++) { + const uint32_t nsmpl = (num_samples >> porder); + uint32_t k, prevk; + uint32_t bits = 0; + for (part = 0; part < (1 << porder); part++) { + SRLACoder_CalculateOptimalRiceParameter(coder->part_mean[porder][part], &k, NULL); + for (smpl = 0; smpl < nsmpl; smpl++) { + bits += Rice_GetCodeLength(k, SRLAUTILITY_SINT32_TO_UINT32(data[part * nsmpl + smpl])); + } + if (part == 0) { + bits += SRLACODER_RICE_PARAMETER_BITS; + } else { + const int32_t diff = (int32_t)k - (int32_t)prevk; + const uint32_t udiff = SRLAUTILITY_SINT32_TO_UINT32(diff); + bits += SRLACODER_GAMMA_BITS(udiff); + } + prevk = k; + } + if (min_bits > bits) { + min_bits = bits; + best_porder = porder; + } + } + } + break; + case SRLACODER_CODE_TYPE_RECURSIVE_RICE: + { + for (porder = 0; porder <= max_porder; porder++) { + const uint32_t nsmpl = (num_samples >> porder); + uint32_t k1, k2, prevk2; + uint32_t bits = 0; + for (part = 0; part < (1 << porder); part++) { + SRLACoder_CalculateOptimalRecursiveRiceParameter(coder->part_mean[porder][part], &k1, &k2, NULL); + for (smpl = 0; smpl < nsmpl; smpl++) { + bits += RecursiveRice_GetCodeLength(k1, k2, SRLAUTILITY_SINT32_TO_UINT32(data[part * nsmpl + smpl])); + } + if (part == 0) { + bits += SRLACODER_RICE_PARAMETER_BITS; + } else { + const int32_t diff = (int32_t)k2 - (int32_t)prevk2; + const uint32_t udiff = SRLAUTILITY_SINT32_TO_UINT32(diff); + bits += SRLACODER_GAMMA_BITS(udiff); + } + prevk2 = k2; + } + if (min_bits > bits) { + min_bits = bits; + best_porder = porder; + } + } + } + break; + default: + SRLA_ASSERT(0); + } + + SRLA_ASSERT(best_porder != (max_porder + 1)); + } + + /* 最適な分割を用いて符号化 */ + { + uint32_t smpl; + const uint32_t nsmpl = num_samples >> best_porder; + + SRLA_ASSERT(code_type != SRLACODER_CODE_TYPE_INVALID); + BitWriter_PutBits(stream, code_type, 1); + BitWriter_PutBits(stream, best_porder, SRLACODER_LOG2_MAX_NUM_PARTITIONS); + + switch (code_type) { + case SRLACODER_CODE_TYPE_RICE: + { + uint32_t k, prevk; + for (part = 0; part < (1 << best_porder); part++) { + SRLACoder_CalculateOptimalRiceParameter(coder->part_mean[best_porder][part], &k, NULL); + if (part == 0) { + BitWriter_PutBits(stream, k, SRLACODER_RICE_PARAMETER_BITS); + } else { + const int32_t diff = (int32_t)k - (int32_t)prevk; + Gamma_PutCode(stream, SRLAUTILITY_SINT32_TO_UINT32(diff)); + } + prevk = k; + for (smpl = 0; smpl < nsmpl; smpl++) { + const uint32_t uval = SRLAUTILITY_SINT32_TO_UINT32(data[part * nsmpl + smpl]); + Rice_PutCode(stream, k, uval); + } + } + } + break; + case SRLACODER_CODE_TYPE_RECURSIVE_RICE: + { + uint32_t k1, k2, prevk2; + for (part = 0; part < (1 << best_porder); part++) { + SRLACoder_CalculateOptimalRecursiveRiceParameter(coder->part_mean[best_porder][part], &k1, &k2, NULL); + if (part == 0) { + BitWriter_PutBits(stream, k2, SRLACODER_RICE_PARAMETER_BITS); + } else { + const int32_t diff = (int32_t)k2 - (int32_t)prevk2; + Gamma_PutCode(stream, SRLAUTILITY_SINT32_TO_UINT32(diff)); + } + prevk2 = k2; + for (smpl = 0; smpl < nsmpl; smpl++) { + const uint32_t uval = SRLAUTILITY_SINT32_TO_UINT32(data[part * nsmpl + smpl]); + RecursiveRice_PutCode(stream, k1, k2, uval); + } + } + } + break; + default: + SRLA_ASSERT(0); + } + } +} + +/* 符号付き整数配列の復号 */ +static void SRLACoder_DecodePartitionedRecursiveRice(struct BitStream *stream, int32_t *data, uint32_t num_samples) +{ + uint32_t smpl, part, nsmpl, best_porder; + + SRLACoderCodeType code_type = SRLACODER_CODE_TYPE_INVALID; + + BitReader_GetBits(stream, (uint32_t *)&code_type, 1); + BitReader_GetBits(stream, &best_porder, SRLACODER_LOG2_MAX_NUM_PARTITIONS); + nsmpl = num_samples >> best_porder; + + switch (code_type) { + case SRLACODER_CODE_TYPE_RICE: + { + uint32_t k; + for (part = 0; part < (1 << best_porder); part++) { + if (part == 0) { + BitReader_GetBits(stream, &k, SRLACODER_RICE_PARAMETER_BITS); + } else { + const uint32_t udiff = Gamma_GetCode(stream); + k = (uint32_t)((int32_t)k + SRLAUTILITY_UINT32_TO_SINT32(udiff)); + } + for (smpl = 0; smpl < nsmpl; smpl++) { + const uint32_t uval = Rice_GetCode(stream, k); + data[part * nsmpl + smpl] = SRLAUTILITY_UINT32_TO_SINT32(uval); + } + } + } + break; + case SRLACODER_CODE_TYPE_RECURSIVE_RICE: + { + uint32_t k1, k2; + for (part = 0; part < (1 << best_porder); part++) { + if (part == 0) { + BitReader_GetBits(stream, &k2, SRLACODER_RICE_PARAMETER_BITS); + } else { + const uint32_t udiff = Gamma_GetCode(stream); + k2 = (uint32_t)((int32_t)k2 + SRLAUTILITY_UINT32_TO_SINT32(udiff)); + } + k1 = k2 + 1; + for (smpl = 0; smpl < nsmpl; smpl++) { + uint32_t uval; + RecursiveRice_GetCode(stream, k1, k2, &uval); + data[part * nsmpl + smpl] = SRLAUTILITY_UINT32_TO_SINT32(uval); + } + } + } + break; + default: + SRLA_ASSERT(0); + } +} + +/* 符号付き整数配列の符号化 */ +void SRLACoder_Encode(struct SRLACoder *coder, struct BitStream *stream, const int32_t *data, uint32_t num_samples) +{ + SRLA_ASSERT((stream != NULL) && (data != NULL) && (coder != NULL)); + SRLA_ASSERT(num_samples != 0); + + SRLACoder_EncodePartitionedRecursiveRice(coder, stream, data, num_samples); +} + +/* 符号付き整数配列の復号 */ +void SRLACoder_Decode(struct BitStream *stream, int32_t *data, uint32_t num_samples) +{ + SRLA_ASSERT((stream != NULL) && (data != NULL)); + SRLA_ASSERT(num_samples != 0); + + SRLACoder_DecodePartitionedRecursiveRice(stream, data, num_samples); +} diff --git a/libs/srla_decoder/CMakeLists.txt b/libs/srla_decoder/CMakeLists.txt new file mode 100644 index 0000000..3ba92de --- /dev/null +++ b/libs/srla_decoder/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# プロジェクト名 +project(SRLADecoder C) + +# ライブラリ名 +set(LIB_NAME srla_decoder) + +# 静的ライブラリ指定 +add_library(${LIB_NAME} STATIC) + +# ソースディレクトリ +add_subdirectory(src) + +# インクルードパス +target_include_directories(${LIB_NAME} + PRIVATE + ${PROJECT_ROOT_PATH}/include + ${PROJECT_ROOT_PATH}/libs/byte_array/include + ${PROJECT_ROOT_PATH}/libs/bit_stream/include + ${PROJECT_ROOT_PATH}/libs/lpc/include + ${PROJECT_ROOT_PATH}/libs/srla_internal/include + ${PROJECT_ROOT_PATH}/libs/srla_coder/include + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + +# コンパイルオプション +if(MSVC) + target_compile_options(${LIB_NAME} PRIVATE /W4) +else() + target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=2 -Wconversion -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition) + set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +endif() +set_target_properties(${LIB_NAME} + PROPERTIES + C_STANDARD 90 C_EXTENSIONS OFF + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) diff --git a/libs/srla_decoder/src/CMakeLists.txt b/libs/srla_decoder/src/CMakeLists.txt new file mode 100644 index 0000000..071cdc1 --- /dev/null +++ b/libs/srla_decoder/src/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(${LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/srla_decoder.c + ${CMAKE_CURRENT_SOURCE_DIR}/srla_lpc_synthesize.c + ${CMAKE_CURRENT_SOURCE_DIR}/srla_lpc_synthesize.h + ) diff --git a/libs/srla_decoder/src/srla_decoder.c b/libs/srla_decoder/src/srla_decoder.c new file mode 100644 index 0000000..e634027 --- /dev/null +++ b/libs/srla_decoder/src/srla_decoder.c @@ -0,0 +1,714 @@ +#include "srla_decoder.h" + +#include +#include +#include "srla_lpc_synthesize.h" +#include "srla_internal.h" +#include "srla_utility.h" +#include "srla_coder.h" +#include "byte_array.h" +#include "bit_stream.h" +#include "lpc.h" + +/* 内部状態フラグ */ +#define SRLADECODER_STATUS_FLAG_ALLOCED_BY_OWN (1 << 0) /* 領域を自己割当した */ +#define SRLADECODER_STATUS_FLAG_SET_HEADER (1 << 1) /* ヘッダセット済み */ +#define SRLADECODER_STATUS_FLAG_CHECKSUM_CHECK (1 << 2) /* チェックサムの検査を行う */ + +/* 内部状態フラグ操作マクロ */ +#define SRLADECODER_SET_STATUS_FLAG(decoder, flag) ((decoder->status_flags) |= (flag)) +#define SRLADECODER_CLEAR_STATUS_FLAG(decoder, flag) ((decoder->status_flags) &= ~(flag)) +#define SRLADECODER_GET_STATUS_FLAG(decoder, flag) ((decoder->status_flags) & (flag)) + +/* デコーダハンドル */ +struct SRLADecoder { + struct SRLAHeader header; /* ヘッダ */ + uint32_t max_num_channels; /* デコード可能な最大チャンネル数 */ + uint32_t max_num_parameters; /* 最大パラメータ数 */ + struct SRLAPreemphasisFilter **de_emphasis; /* デエンファシスフィルタ */ + int32_t **params_int; /* 各チャンネルのLPC係数(int) */ + uint32_t *rshifts; /* 各チャンネルのLPC係数右シフト量 */ + uint32_t *coef_order; /* 各チャンネルのLPC係数次数 */ + const struct SRLAParameterPreset *parameter_preset; /* パラメータプリセット */ + uint8_t status_flags; /* 内部状態フラグ */ + void *work; /* ワーク領域先頭ポインタ */ +}; + +/* 生データブロックデコード */ +static SRLAApiResult SRLADecoder_DecodeRawData( + struct SRLADecoder *decoder, + const uint8_t *data, uint32_t data_size, + int32_t **buffer, uint32_t num_channels, uint32_t num_decode_samples, + uint32_t *decode_size); +/* 圧縮データブロックデコード */ +static SRLAApiResult SRLADecoder_DecodeCompressData( + struct SRLADecoder *decoder, + const uint8_t *data, uint32_t data_size, + int32_t **buffer, uint32_t num_channels, uint32_t num_decode_samples, + uint32_t *decode_size); +/* 無音データブロックデコード */ +static SRLAApiResult SRLADecoder_DecodeSilentData( + struct SRLADecoder *decoder, + const uint8_t *data, uint32_t data_size, + int32_t **buffer, uint32_t num_channels, uint32_t num_decode_samples, + uint32_t *decode_size); + +/* ヘッダデコード */ +SRLAApiResult SRLADecoder_DecodeHeader( + const uint8_t *data, uint32_t data_size, struct SRLAHeader *header) +{ + const uint8_t *data_pos; + uint32_t u32buf; + uint16_t u16buf; + uint8_t u8buf; + struct SRLAHeader tmp_header; + + /* 引数チェック */ + if ((data == NULL) || (header == NULL)) { + return SRLA_APIRESULT_INVALID_ARGUMENT; + } + + /* データサイズが足りない */ + if (data_size < SRLA_HEADER_SIZE) { + return SRLA_APIRESULT_INSUFFICIENT_DATA; + } + + /* 読み出し用ポインタ設定 */ + data_pos = data; + + /* シグネチャ */ + { + uint8_t buf[4]; + ByteArray_GetUint8(data_pos, &buf[0]); + ByteArray_GetUint8(data_pos, &buf[1]); + ByteArray_GetUint8(data_pos, &buf[2]); + ByteArray_GetUint8(data_pos, &buf[3]); + if ((buf[0] != '1') || (buf[1] != '2') + || (buf[2] != '4') || (buf[3] != '9')) { + return SRLA_APIRESULT_INVALID_FORMAT; + } + } + + /* シグネチャ検査に通ったら、エラーを起こさずに読み切る */ + + /* フォーマットバージョン */ + ByteArray_GetUint32BE(data_pos, &u32buf); + tmp_header.format_version = u32buf; + /* エンコーダバージョン */ + ByteArray_GetUint32BE(data_pos, &u32buf); + tmp_header.codec_version = u32buf; + /* チャンネル数 */ + ByteArray_GetUint16BE(data_pos, &u16buf); + tmp_header.num_channels = u16buf; + /* サンプル数 */ + ByteArray_GetUint32BE(data_pos, &u32buf); + tmp_header.num_samples = u32buf; + /* サンプリングレート */ + ByteArray_GetUint32BE(data_pos, &u32buf); + tmp_header.sampling_rate = u32buf; + /* サンプルあたりビット数 */ + ByteArray_GetUint16BE(data_pos, &u16buf); + tmp_header.bits_per_sample = u16buf; + /* ブロックあたりサンプル数 */ + ByteArray_GetUint32BE(data_pos, &u32buf); + tmp_header.num_samples_per_block = u32buf; + /* パラメータプリセット */ + ByteArray_GetUint8(data_pos, &u8buf); + tmp_header.preset = u8buf; + + /* ヘッダサイズチェック */ + SRLA_ASSERT((data_pos - data) == SRLA_HEADER_SIZE); + + /* 成功終了 */ + (*header) = tmp_header; + return SRLA_APIRESULT_OK; +} + +/* ヘッダのフォーマットチェック */ +static SRLAError SRLADecoder_CheckHeaderFormat(const struct SRLAHeader *header) +{ + /* 内部モジュールなのでNULLが渡されたら落とす */ + SRLA_ASSERT(header != NULL); + + /* フォーマットバージョン */ + /* 補足)今のところは不一致なら無条件でエラー */ + if (header->format_version != SRLA_FORMAT_VERSION) { + return SRLA_ERROR_INVALID_FORMAT; + } + /* コーデックバージョン */ + /* 補足)今のところは不一致なら無条件でエラー */ + if (header->codec_version != SRLA_CODEC_VERSION) { + return SRLA_ERROR_INVALID_FORMAT; + } + /* チャンネル数 */ + if (header->num_channels == 0) { + return SRLA_ERROR_INVALID_FORMAT; + } + /* サンプル数 */ + if (header->num_samples == 0) { + return SRLA_ERROR_INVALID_FORMAT; + } + /* サンプリングレート */ + if (header->sampling_rate == 0) { + return SRLA_ERROR_INVALID_FORMAT; + } + /* ビット深度 */ + if (header->bits_per_sample == 0) { + return SRLA_ERROR_INVALID_FORMAT; + } + /* ブロックあたりサンプル数 */ + if (header->num_samples_per_block == 0) { + return SRLA_ERROR_INVALID_FORMAT; + } + /* パラメータプリセット */ + if (header->preset >= SRLA_NUM_PARAMETER_PRESETS) { + return SRLA_ERROR_INVALID_FORMAT; + } + + return SRLA_ERROR_OK; +} + +/* デコーダハンドルの作成に必要なワークサイズの計算 */ +int32_t SRLADecoder_CalculateWorkSize(const struct SRLADecoderConfig *config) +{ + int32_t work_size; + + /* 引数チェック */ + if (config == NULL) { + return -1; + } + + /* コンフィグチェック */ + if ((config->max_num_channels == 0) + || (config->max_num_parameters == 0)) { + return -1; + } + + /* 構造体サイズ(+メモリアラインメント) */ + work_size = sizeof(struct SRLADecoder) + SRLA_MEMORY_ALIGNMENT; + /* デエンファシスフィルタのサイズ */ + work_size += SRLA_CALCULATE_2DIMARRAY_WORKSIZE(struct SRLAPreemphasisFilter, config->max_num_channels, SRLA_NUM_PREEMPHASIS_FILTERS); + /* パラメータ領域 */ + /* LPC係数(int) */ + work_size += SRLA_CALCULATE_2DIMARRAY_WORKSIZE(int32_t, config->max_num_channels, config->max_num_parameters); + /* 各チャンネルのLPC係数右シフト量 */ + work_size += SRLA_MEMORY_ALIGNMENT + sizeof(uint32_t) * config->max_num_channels; + /* 各チャンネルのLPC係数次数 */ + work_size += SRLA_MEMORY_ALIGNMENT + sizeof(uint32_t) * config->max_num_channels; + + return work_size; +} + +/* デコーダハンドル作成 */ +struct SRLADecoder *SRLADecoder_Create(const struct SRLADecoderConfig *config, void *work, int32_t work_size) +{ + uint32_t ch, l; + struct SRLADecoder *decoder; + uint8_t *work_ptr; + uint8_t tmp_alloc_by_own = 0; + + /* 領域自前確保の場合 */ + if ((work == NULL) && (work_size == 0)) { + if ((work_size = SRLADecoder_CalculateWorkSize(config)) < 0) { + return NULL; + } + work = malloc((uint32_t)work_size); + tmp_alloc_by_own = 1; + } + + /* 引数チェック */ + if ((config == NULL) || (work == NULL) + || (work_size < SRLADecoder_CalculateWorkSize(config))) { + return NULL; + } + + /* コンフィグチェック */ + if ((config->max_num_channels == 0) + || (config->max_num_parameters == 0)) { + return NULL; + } + + /* ワーク領域先頭ポインタ取得 */ + work_ptr = (uint8_t *)work; + + /* 構造体領域確保 */ + work_ptr = (uint8_t *)SRLAUTILITY_ROUNDUP((uintptr_t)work_ptr, SRLA_MEMORY_ALIGNMENT); + decoder = (struct SRLADecoder *)work_ptr; + work_ptr += sizeof(struct SRLADecoder); + + /* 構造体メンバセット */ + decoder->work = work; + decoder->max_num_channels = config->max_num_channels; + decoder->max_num_parameters = config->max_num_parameters; + decoder->status_flags = 0; /* 状態クリア */ + if (tmp_alloc_by_own == 1) { + SRLADECODER_SET_STATUS_FLAG(decoder, SRLADECODER_STATUS_FLAG_ALLOCED_BY_OWN); + } + if (config->check_checksum == 1) { + SRLADECODER_SET_STATUS_FLAG(decoder, SRLADECODER_STATUS_FLAG_CHECKSUM_CHECK); + } + + /* デエンファシスフィルタの作成 */ + SRLA_ALLOCATE_2DIMARRAY(decoder->de_emphasis, + work_ptr, struct SRLAPreemphasisFilter, config->max_num_channels, SRLA_NUM_PREEMPHASIS_FILTERS); + + /* バッファ領域の確保 全てのポインタをアラインメント */ + /* LPC係数(int) */ + SRLA_ALLOCATE_2DIMARRAY(decoder->params_int, + work_ptr, int32_t, config->max_num_channels, config->max_num_parameters); + /* 各層のLPC係数右シフト量 */ + work_ptr = (uint8_t*)SRLAUTILITY_ROUNDUP((uintptr_t)work_ptr, SRLA_MEMORY_ALIGNMENT); + decoder->rshifts = (uint32_t*)work_ptr; + work_ptr += config->max_num_channels * sizeof(uint32_t); + /* 各層のLPC係数次数 */ + work_ptr = (uint8_t *)SRLAUTILITY_ROUNDUP((uintptr_t)work_ptr, SRLA_MEMORY_ALIGNMENT); + decoder->coef_order = (uint32_t *)work_ptr; + work_ptr += config->max_num_channels * sizeof(uint32_t); + + /* バッファオーバーランチェック */ + /* 補足)既にメモリを破壊している可能性があるので、チェックに失敗したら落とす */ + SRLA_ASSERT((work_ptr - (uint8_t *)work) <= work_size); + + /* プリエンファシスフィルタ初期化 */ + for (ch = 0; ch < config->max_num_channels; ch++) { + for (l = 0; l < SRLA_NUM_PREEMPHASIS_FILTERS; l++) { + SRLAPreemphasisFilter_Initialize(&decoder->de_emphasis[ch][l]); + } + } + + return decoder; +} + +/* デコーダハンドルの破棄 */ +void SRLADecoder_Destroy(struct SRLADecoder *decoder) +{ + if (decoder != NULL) { + if (SRLADECODER_GET_STATUS_FLAG(decoder, SRLADECODER_STATUS_FLAG_ALLOCED_BY_OWN)) { + free(decoder->work); + } + } +} + +/* デコーダにヘッダをセット */ +SRLAApiResult SRLADecoder_SetHeader( + struct SRLADecoder *decoder, const struct SRLAHeader *header) +{ + /* 引数チェック */ + if ((decoder == NULL) || (header == NULL)) { + return SRLA_APIRESULT_INVALID_ARGUMENT; + } + + /* ヘッダの有効性チェック */ + if (SRLADecoder_CheckHeaderFormat(header) != SRLA_ERROR_OK) { + return SRLA_APIRESULT_INVALID_FORMAT; + } + + /* デコーダの容量を越えてないかチェック */ + if (decoder->max_num_channels < header->num_channels) { + return SRLA_APIRESULT_INSUFFICIENT_BUFFER; + } + + /* 最大レイヤー数/パラメータ数のチェック */ + { + const struct SRLAParameterPreset* preset = &g_srla_parameter_preset[header->preset]; + if (decoder->max_num_parameters < preset->max_num_parameters) { + return SRLA_APIRESULT_INSUFFICIENT_BUFFER; + } + } + + /* エンコードプリセットを取得 */ + SRLA_ASSERT(header->preset < SRLA_NUM_PARAMETER_PRESETS); + decoder->parameter_preset = &g_srla_parameter_preset[header->preset]; + + /* ヘッダセット */ + decoder->header = (*header); + SRLADECODER_SET_STATUS_FLAG(decoder, SRLADECODER_STATUS_FLAG_SET_HEADER); + + return SRLA_APIRESULT_OK; +} + +/* 生データブロックデコード */ +static SRLAApiResult SRLADecoder_DecodeRawData( + struct SRLADecoder *decoder, + const uint8_t *data, uint32_t data_size, + int32_t **buffer, uint32_t num_channels, uint32_t num_decode_samples, + uint32_t *decode_size) +{ + uint32_t ch, smpl; + const struct SRLAHeader *header; + const uint8_t *read_ptr; + + /* 内部関数なので不正な引数はアサートで落とす */ + SRLA_ASSERT(decoder != NULL); + SRLA_ASSERT(data != NULL); + SRLA_ASSERT(data_size > 0); + SRLA_ASSERT(buffer != NULL); + SRLA_ASSERT(buffer[0] != NULL); + SRLA_ASSERT(num_decode_samples > 0); + SRLA_ASSERT(decode_size != NULL); + + /* ヘッダ取得 */ + header = &(decoder->header); + + /* チャンネル数不足もアサートで落とす */ + SRLA_ASSERT(num_channels >= header->num_channels); + + /* データサイズチェック */ + if (data_size < (header->bits_per_sample * num_decode_samples * header->num_channels) / 8) { + return SRLA_APIRESULT_INSUFFICIENT_DATA; + } + + /* 生データをチャンネルインターリーブで取得 */ + read_ptr = data; + switch (header->bits_per_sample) { + case 8: + for (smpl = 0; smpl < num_decode_samples; smpl++) { + for (ch = 0; ch < header->num_channels; ch++) { + uint8_t buf; + ByteArray_GetUint8(read_ptr, &buf); + buffer[ch][smpl] = SRLAUTILITY_UINT32_TO_SINT32(buf); + SRLA_ASSERT((uint32_t)(read_ptr - data) <= data_size); + } + } + break; + case 16: + for (smpl = 0; smpl < num_decode_samples; smpl++) { + for (ch = 0; ch < header->num_channels; ch++) { + uint16_t buf; + ByteArray_GetUint16BE(read_ptr, &buf); + buffer[ch][smpl] = SRLAUTILITY_UINT32_TO_SINT32(buf); + SRLA_ASSERT((uint32_t)(read_ptr - data) <= data_size); + } + } + break; + case 24: + for (smpl = 0; smpl < num_decode_samples; smpl++) { + for (ch = 0; ch < header->num_channels; ch++) { + uint32_t buf; + ByteArray_GetUint24BE(read_ptr, &buf); + buffer[ch][smpl] = SRLAUTILITY_UINT32_TO_SINT32(buf); + SRLA_ASSERT((uint32_t)(read_ptr - data) <= data_size); + } + } + break; + default: SRLA_ASSERT(0); + } + + /* 読み取りサイズ取得 */ + (*decode_size) = (uint32_t)(read_ptr - data); + + return SRLA_APIRESULT_OK; +} + +/* 圧縮データブロックデコード */ +static SRLAApiResult SRLADecoder_DecodeCompressData( + struct SRLADecoder *decoder, + const uint8_t *data, uint32_t data_size, + int32_t **buffer, uint32_t num_channels, uint32_t num_decode_samples, + uint32_t *decode_size) +{ + uint32_t ch; + int32_t l; + struct BitStream reader; + const struct SRLAHeader *header; + SRLAChannelProcessMethod ch_process_method; + + /* 内部関数なので不正な引数はアサートで落とす */ + SRLA_ASSERT(decoder != NULL); + SRLA_ASSERT(data != NULL); + SRLA_ASSERT(data_size > 0); + SRLA_ASSERT(buffer != NULL); + SRLA_ASSERT(buffer[0] != NULL); + SRLA_ASSERT(num_decode_samples > 0); + SRLA_ASSERT(decode_size != NULL); + + /* ヘッダ取得 */ + header = &(decoder->header); + + /* チャンネル数不足もアサートで落とす */ + SRLA_ASSERT(num_channels >= header->num_channels); + + /* ビットリーダ作成 */ + BitReader_Open(&reader, (uint8_t *)data, data_size); + + /* マルチチャンネル処理法の取得 */ + BitReader_GetBits(&reader, (uint32_t *)&ch_process_method, 2); + + /* パラメータ復号 */ + /* プリエンファシス */ + for (ch = 0; ch < num_channels; ch++) { + uint32_t uval; + for (l = 0; l < SRLA_NUM_PREEMPHASIS_FILTERS; l++) { + BitReader_GetBits(&reader, &uval, header->bits_per_sample + 1); + decoder->de_emphasis[ch][l].prev = SRLAUTILITY_UINT32_TO_SINT32(uval); + /* プリエンファシス係数は正値に制限しているため1bitケチれる */ + BitReader_GetBits(&reader, &uval, SRLA_PREEMPHASIS_COEF_SHIFT - 1); + decoder->de_emphasis[ch][l].coef = (int32_t)uval; + } + } + /* LPC係数次数/LPC係数右シフト量/LPC係数 */ + for (ch = 0; ch < num_channels; ch++) { + uint32_t i, uval; + /* LPC係数次数 */ + BitReader_GetBits(&reader, &decoder->coef_order[ch], SRLA_LPC_COEFFICIENT_ORDER_BITWIDTH); + decoder->coef_order[ch] += 1; /* -1してエンコードしてあるので戻す */ + /* 各レイヤーでのLPC係数右シフト量 */ + BitReader_GetBits(&reader, &decoder->rshifts[ch], SRLA_RSHIFT_LPC_COEFFICIENT_BITWIDTH); + /* LPC係数 */ + for (i = 0; i < decoder->coef_order[ch]; i++) { + BitReader_GetBits(&reader, &uval, SRLA_LPC_COEFFICIENT_BITWIDTH); + decoder->params_int[ch][i] = SRLAUTILITY_UINT32_TO_SINT32(uval); + } + } + + /* 残差復号 */ + for (ch = 0; ch < header->num_channels; ch++) { + SRLACoder_Decode(&reader, buffer[ch], num_decode_samples); + } + + /* バイト境界に揃える */ + BitStream_Flush(&reader); + + /* 読み出しサイズの取得 */ + BitStream_Tell(&reader, (int32_t *)decode_size); + + /* ビットライタ破棄 */ + BitStream_Close(&reader); + + /* チャンネル毎に合成処理 */ + for (ch = 0; ch < header->num_channels; ch++) { + /* LPC合成 */ + SRLALPC_Synthesize(buffer[ch], + num_decode_samples, decoder->params_int[ch], decoder->coef_order[ch], decoder->rshifts[ch]); + /* デエンファシス */ + SRLAPreemphasisFilter_MultiStageDeemphasis( + decoder->de_emphasis[ch], SRLA_NUM_PREEMPHASIS_FILTERS, buffer[ch], num_decode_samples); + } + + /* マルチチャンネル処理 */ + switch (ch_process_method) { + case SRLA_CH_PROCESS_METHOD_NONE: + break; + case SRLA_CH_PROCESS_METHOD_MS: + SRLA_ASSERT(header->num_channels >= 2); + SRLAUtility_MStoLRConversion(buffer, num_decode_samples); + break; + case SRLA_CH_PROCESS_METHOD_LS: + SRLA_ASSERT(header->num_channels >= 2); + SRLAUtility_LStoLRConversion(buffer, num_decode_samples); + break; + case SRLA_CH_PROCESS_METHOD_RS: + SRLA_ASSERT(header->num_channels >= 2); + SRLAUtility_RStoLRConversion(buffer, num_decode_samples); + break; + default: + SRLA_ASSERT(0); + } + + /* 成功終了 */ + return SRLA_APIRESULT_OK; +} + +/* 無音データブロックデコード */ +static SRLAApiResult SRLADecoder_DecodeSilentData( + struct SRLADecoder *decoder, + const uint8_t *data, uint32_t data_size, + int32_t **buffer, uint32_t num_channels, uint32_t num_decode_samples, + uint32_t *decode_size) +{ + uint32_t ch; + const struct SRLAHeader *header; + + SRLAUTILITY_UNUSED_ARGUMENT(data_size); + + /* 内部関数なので不正な引数はアサートで落とす */ + SRLA_ASSERT(decoder != NULL); + SRLA_ASSERT(data != NULL); + SRLA_ASSERT(buffer != NULL); + SRLA_ASSERT(buffer[0] != NULL); + SRLA_ASSERT(num_decode_samples > 0); + SRLA_ASSERT(decode_size != NULL); + + /* ヘッダ取得 */ + header = &(decoder->header); + + /* チャンネル数不足もアサートで落とす */ + SRLA_ASSERT(num_channels >= header->num_channels); + + /* 全て無音で埋める */ + for (ch = 0; ch < header->num_channels; ch++) { + memset(buffer[ch], 0, sizeof(int32_t) * num_decode_samples); + } + + (*decode_size) = 0; + return SRLA_APIRESULT_OK; +} + +/* 単一データブロックデコード */ +SRLAApiResult SRLADecoder_DecodeBlock( + struct SRLADecoder *decoder, + const uint8_t *data, uint32_t data_size, + int32_t **buffer, uint32_t buffer_num_channels, uint32_t buffer_num_samples, + uint32_t *decode_size, uint32_t *num_decode_samples) +{ + uint8_t buf8; + uint16_t buf16; + uint32_t buf32; + uint16_t num_block_samples; + uint32_t block_header_size, block_data_size; + SRLAApiResult ret; + SRLABlockDataType block_type; + const struct SRLAHeader *header; + const uint8_t *read_ptr; + + /* 引数チェック */ + if ((decoder == NULL) || (data == NULL) + || (buffer == NULL) || (decode_size == NULL) + || (num_decode_samples == NULL)) { + return SRLA_APIRESULT_INVALID_ARGUMENT; + } + + /* ヘッダがまだセットされていない */ + if (!SRLADECODER_GET_STATUS_FLAG(decoder, SRLADECODER_STATUS_FLAG_SET_HEADER)) { + return SRLA_APIRESULT_PARAMETER_NOT_SET; + } + + /* ヘッダ取得 */ + header = &(decoder->header); + + /* バッファチャンネル数チェック */ + if (buffer_num_channels < header->num_channels) { + return SRLA_APIRESULT_INSUFFICIENT_BUFFER; + } + + /* ブロックヘッダデコード */ + read_ptr = data; + + /* 同期コード */ + ByteArray_GetUint16BE(read_ptr, &buf16); + /* 同期コード不一致 */ + if (buf16 != SRLA_BLOCK_SYNC_CODE) { + return SRLA_APIRESULT_INVALID_FORMAT; + } + /* ブロックサイズ */ + ByteArray_GetUint32BE(read_ptr, &buf32); + SRLA_ASSERT(buf32 > 0); + /* データサイズ不足 */ + if ((buf32 + 6) > data_size) { + return SRLA_APIRESULT_INSUFFICIENT_DATA; + } + /* ブロックチェックサム */ + ByteArray_GetUint16BE(read_ptr, &buf16); + /* チェックするならばチェックサム計算を行い取得値との一致を確認 */ + if (SRLADECODER_GET_STATUS_FLAG(decoder, SRLADECODER_STATUS_FLAG_CHECKSUM_CHECK)) { + /* チェックサム自体の領域は外すために-2 */ + uint16_t checksum = SRLAUtility_CalculateFletcher16CheckSum(read_ptr, buf32 - 2); + if (checksum != buf16) { + return SRLA_APIRESULT_DETECT_DATA_CORRUPTION; + } + } + /* ブロックデータタイプ */ + ByteArray_GetUint8(read_ptr, &buf8); + block_type = (SRLABlockDataType)buf8; + /* ブロックチャンネルあたりサンプル数 */ + ByteArray_GetUint16BE(read_ptr, &num_block_samples); + if (num_block_samples > buffer_num_samples) { + return SRLA_APIRESULT_INSUFFICIENT_BUFFER; + } + /* ブロックヘッダサイズ */ + block_header_size = (uint32_t)(read_ptr - data); + + /* データ部のデコード */ + switch (block_type) { + case SRLA_BLOCK_DATA_TYPE_RAWDATA: + ret = SRLADecoder_DecodeRawData(decoder, + read_ptr, data_size - block_header_size, buffer, header->num_channels, num_block_samples, &block_data_size); + break; + case SRLA_BLOCK_DATA_TYPE_COMPRESSDATA: + ret = SRLADecoder_DecodeCompressData(decoder, + read_ptr, data_size - block_header_size, buffer, header->num_channels, num_block_samples, &block_data_size); + break; + case SRLA_BLOCK_DATA_TYPE_SILENT: + ret = SRLADecoder_DecodeSilentData(decoder, + read_ptr, data_size - block_header_size, buffer, header->num_channels, num_block_samples, &block_data_size); + break; + default: + return SRLA_APIRESULT_INVALID_FORMAT; + } + + /* データデコードに失敗している */ + if (ret != SRLA_APIRESULT_OK) { + return ret; + } + + /* デコードサイズ */ + (*decode_size) = block_header_size + block_data_size; + + /* デコードサンプル数 */ + (*num_decode_samples) = num_block_samples; + + /* デコード成功 */ + return SRLA_APIRESULT_OK; +} + +/* ヘッダを含めて全ブロックデコード */ +SRLAApiResult SRLADecoder_DecodeWhole( + struct SRLADecoder *decoder, + const uint8_t *data, uint32_t data_size, + int32_t **buffer, uint32_t buffer_num_channels, uint32_t buffer_num_samples) +{ + SRLAApiResult ret; + uint32_t progress, ch, read_offset, read_block_size, num_decode_samples; + const uint8_t *read_pos; + int32_t *buffer_ptr[SRLA_MAX_NUM_CHANNELS]; + struct SRLAHeader tmp_header; + const struct SRLAHeader *header; + + /* 引数チェック */ + if ((decoder == NULL) || (data == NULL) || (buffer == NULL)) { + return SRLA_APIRESULT_INVALID_ARGUMENT; + } + + /* ヘッダデコードとデコーダへのセット */ + if ((ret = SRLADecoder_DecodeHeader(data, data_size, &tmp_header)) + != SRLA_APIRESULT_OK) { + return ret; + } + if ((ret = SRLADecoder_SetHeader(decoder, &tmp_header)) + != SRLA_APIRESULT_OK) { + return ret; + } + header = &(decoder->header); + + /* バッファサイズチェック */ + if ((buffer_num_channels < header->num_channels) + || (buffer_num_samples < header->num_samples)) { + return SRLA_APIRESULT_INSUFFICIENT_BUFFER; + } + + progress = 0; + read_offset = SRLA_HEADER_SIZE; + read_pos = data + SRLA_HEADER_SIZE; + while ((progress < header->num_samples) && (read_offset < data_size)) { + /* サンプル書き出し位置のセット */ + for (ch = 0; ch < header->num_channels; ch++) { + buffer_ptr[ch] = &buffer[ch][progress]; + } + /* ブロックデコード */ + if ((ret = SRLADecoder_DecodeBlock(decoder, + read_pos, data_size - read_offset, + buffer_ptr, buffer_num_channels, buffer_num_samples - progress, + &read_block_size, &num_decode_samples)) != SRLA_APIRESULT_OK) { + return ret; + } + /* 進捗更新 */ + read_pos += read_block_size; + read_offset += read_block_size; + progress += num_decode_samples; + SRLA_ASSERT(progress <= buffer_num_samples); + SRLA_ASSERT(read_offset <= data_size); + } + + /* 成功終了 */ + return SRLA_APIRESULT_OK; +} diff --git a/libs/srla_decoder/src/srla_lpc_synthesize.c b/libs/srla_decoder/src/srla_lpc_synthesize.c new file mode 100644 index 0000000..b27b95a --- /dev/null +++ b/libs/srla_decoder/src/srla_lpc_synthesize.c @@ -0,0 +1,128 @@ +#include "srla_lpc_synthesize.h" + +#include +#include "srla_internal.h" +#include "srla_utility.h" + +/* SSE4.1を使うか +#define USE_SSE41 +*/ + +/* LPC係数により合成(in-place) */ +#if !defined(USE_SSE41) +void SRLALPC_Synthesize( + int32_t *data, uint32_t num_samples, + const int32_t *coef, uint32_t coef_order, uint32_t coef_rshift) +{ + uint32_t smpl, ord; + const int32_t half = 1 << (coef_rshift - 1); /* 固定小数の0.5 */ + int32_t predict; + + /* 引数チェック */ + SRLA_ASSERT(data != NULL); + SRLA_ASSERT(coef != NULL); + + for (smpl = 0; smpl < num_samples - coef_order; smpl++) { + predict = half; + for (ord = 0; ord < coef_order; ord++) { + predict += (coef[ord] * data[smpl + ord]); + } + data[smpl + ord] -= (predict >> coef_rshift); + } +} +#else +#ifdef _MSC_VER +#include +#else +#include +#endif +void SRLALPC_Synthesize( + int32_t *data, uint32_t num_samples, + const int32_t *coef, uint32_t coef_order, uint32_t coef_rshift) +{ + int32_t smpl, ord; + const int32_t half = 1 << (coef_rshift - 1); /* 固定小数の0.5 */ + int32_t predict; + + /* 引数チェック */ + SRLA_ASSERT(data != NULL); + SRLA_ASSERT(coef != NULL); + + smpl = coef_order; + + if (coef_order >= 4) { + uint32_t i; + __m128i vcoef[SRLA_MAX_COEFFICIENT_ORDER]; + /* 係数をベクトル化 */ + for (i = 0; i < coef_order; i++) { + vcoef[i] = _mm_set1_epi32(coef[i]); + } + for (; smpl < num_samples - coef_order - 4; smpl += 4) { + /* 4サンプル並列に処理 + int32_t predict[4] = { half, half, half, half } + for (ord = 0; ord < coef_order - 3; ord++) { + predict[0] += (coef[ord] * data[smpl - coef_order + ord + 0]); + predict[1] += (coef[ord] * data[smpl - coef_order + ord + 1]); + predict[2] += (coef[ord] * data[smpl - coef_order + ord + 2]); + predict[3] += (coef[ord] * data[smpl - coef_order + ord + 3]); + } + */ + __declspec(align(16)) int32_t predict[4]; + __m128i vdata; + __m128i vpred = _mm_set1_epi32(half); + for (ord = 0; ord < (int32_t)coef_order - 3 - 4; ord += 4) { + const int32_t *dat = &data[smpl - coef_order + ord]; + vdata = _mm_loadu_epi32(&dat[0]); + vpred = _mm_add_epi32(vpred, _mm_mullo_epi32(vcoef[ord + 0], vdata)); + vdata = _mm_loadu_epi32(&dat[1]); + vpred = _mm_add_epi32(vpred, _mm_mullo_epi32(vcoef[ord + 1], vdata)); + vdata = _mm_loadu_epi32(&dat[2]); + vpred = _mm_add_epi32(vpred, _mm_mullo_epi32(vcoef[ord + 2], vdata)); + vdata = _mm_loadu_epi32(&dat[3]); + vpred = _mm_add_epi32(vpred, _mm_mullo_epi32(vcoef[ord + 3], vdata)); + } + for (; ord < coef_order - 3; ord++) { + vdata = _mm_loadu_epi32(&data[smpl - coef_order + ord]); + vpred = _mm_add_epi32(vpred, _mm_mullo_epi32(vcoef[ord], vdata)); + } + _mm_store_si128(&predict, vpred); + + /* ord = coef_order - 3 */ + /* data[smpl + 0] .. data[smpl + 2]に依存関係があるため処理 + * TODO: ここもSSEでやり切ってdataに結果を直接storeしたい + predict[0] += (coef[ord + 0] * data[smpl - 3 + 0]); + predict[0] += (coef[ord + 1] * data[smpl - 3 + 1]); + predict[0] += (coef[ord + 2] * data[smpl - 3 + 2]); + data[smpl + 0] -= (predict[0] >> coef_rshift); + predict[1] += (coef[ord + 0] * data[smpl - 3 + 1]); + predict[1] += (coef[ord + 1] * data[smpl - 3 + 2]); + predict[1] += (coef[ord + 2] * data[smpl - 3 + 3]); + data[smpl + 1] -= (predict[1] >> coef_rshift); + predict[2] += (coef[ord + 0] * data[smpl - 3 + 2]); + predict[2] += (coef[ord + 1] * data[smpl - 3 + 3]); + predict[2] += (coef[ord + 2] * data[smpl - 3 + 4]); + data[smpl + 2] -= (predict[2] >> coef_rshift); + predict[3] += (coef[ord + 0] * data[smpl - 3 + 3]); + predict[3] += (coef[ord + 1] * data[smpl - 3 + 4]); + predict[3] += (coef[ord + 2] * data[smpl - 3 + 5]); + data[smpl + 3] -= (predict[3] >> coef_rshift); + */ + for (i = 0; i < 4; i++) { + predict[i] += (coef[ord + 0] * data[smpl - 3 + i + 0]); + predict[i] += (coef[ord + 1] * data[smpl - 3 + i + 1]); + predict[i] += (coef[ord + 2] * data[smpl - 3 + i + 2]); + data[smpl + i] -= (predict[i] >> coef_rshift); + } + } + } + + /* 余ったサンプル分の処理 */ + for (; smpl < num_samples; smpl++) { + int32_t predict = half; + for (ord = 0; ord < coef_order; ord++) { + predict += (coef[ord] * data[smpl - coef_order + ord]); + } + data[smpl] -= (predict >> coef_rshift); + } +} +#endif diff --git a/libs/srla_decoder/src/srla_lpc_synthesize.h b/libs/srla_decoder/src/srla_lpc_synthesize.h new file mode 100644 index 0000000..832a872 --- /dev/null +++ b/libs/srla_decoder/src/srla_lpc_synthesize.h @@ -0,0 +1,18 @@ +#ifndef SRLA_LPCSYNTHESIZE_H_INCLUDED +#define SRLA_LPCSYNTHESIZE_H_INCLUDED + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* LPC係数により合成(in-place) */ +void SRLALPC_Synthesize( + int32_t *data, uint32_t num_samples, const int32_t *coef, uint32_t coef_order, uint32_t coef_rshift); + +#ifdef __cplusplus +} +#endif + +#endif /* SRLA_LPCSYNTHESIZE_H_INCLUDED */ diff --git a/libs/srla_encoder/CMakeLists.txt b/libs/srla_encoder/CMakeLists.txt new file mode 100644 index 0000000..24db87f --- /dev/null +++ b/libs/srla_encoder/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# プロジェクト名 +project(SRLAEncoder C) + +# ライブラリ名 +set(LIB_NAME srla_encoder) + +# 静的ライブラリ指定 +add_library(${LIB_NAME} STATIC) + +# ソースディレクトリ +add_subdirectory(src) + +# インクルードパス +target_include_directories(${LIB_NAME} + PRIVATE + ${PROJECT_ROOT_PATH}/include + ${PROJECT_ROOT_PATH}/libs/byte_array/include + ${PROJECT_ROOT_PATH}/libs/bit_stream/include + ${PROJECT_ROOT_PATH}/libs/lpc/include + ${PROJECT_ROOT_PATH}/libs/srla_internal/include + ${PROJECT_ROOT_PATH}/libs/srla_coder/include + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + +# コンパイルオプション +if(MSVC) + target_compile_options(${LIB_NAME} PRIVATE /W4) +else() + target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=2 -Wconversion -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition) + set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +endif() +set_target_properties(${LIB_NAME} + PROPERTIES + C_STANDARD 90 C_EXTENSIONS OFF + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) diff --git a/libs/srla_encoder/src/CMakeLists.txt b/libs/srla_encoder/src/CMakeLists.txt new file mode 100644 index 0000000..86180ce --- /dev/null +++ b/libs/srla_encoder/src/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(${LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/srla_encoder.c + ${CMAKE_CURRENT_SOURCE_DIR}/srla_lpc_predict.c + ${CMAKE_CURRENT_SOURCE_DIR}/srla_lpc_predict.h + ) diff --git a/libs/srla_encoder/src/srla_encoder.c b/libs/srla_encoder/src/srla_encoder.c new file mode 100644 index 0000000..112cb26 --- /dev/null +++ b/libs/srla_encoder/src/srla_encoder.c @@ -0,0 +1,1025 @@ +#include "srla_encoder.h" + +#include +#include +#include +#include +#include "srla_lpc_predict.h" +#include "srla_internal.h" +#include "srla_utility.h" +#include "byte_array.h" +#include "bit_stream.h" +#include "lpc.h" +#include "srla_coder.h" + +/* エンコーダハンドル */ +struct SRLAEncoder { + struct SRLAHeader header; /* ヘッダ */ + struct SRLACoder *coder; /* 符号化ハンドル */ + uint32_t max_num_channels; /* バッファチャンネル数 */ + uint32_t max_num_samples_per_block; /* バッファサンプル数 */ + uint32_t max_num_parameters; /* 最大パラメータ数 */ + uint8_t set_parameter; /* パラメータセット済み? */ + struct LPCCalculator *lpcc; /* LPC計算ハンドル */ + struct SRLAPreemphasisFilter **pre_emphasis; /* プリエンファシスフィルタ */ + int32_t **pre_emphasis_prev; /* プリエンファシスフィルタの直前のサンプル */ + double **params_double; /* 各チャンネルのLPC係数(double) */ + int32_t **params_int; /* 各チャンネルのLPC係数(int) */ + uint32_t *rshifts; /* 各チャンネルのLPC係数右シフト量 */ + uint32_t *coef_order; /* 各チャンネルのLPC係数次数 */ + int32_t **buffer_int; /* 信号バッファ(int) */ + int32_t **residual; /* 残差信号 */ + double *buffer_double; /* 信号バッファ(double) */ + const struct SRLAParameterPreset *parameter_preset; /* パラメータプリセット */ + uint8_t alloced_by_own; /* 領域を自前確保しているか? */ + void *work; /* ワーク領域先頭ポインタ */ +}; + +/* エンコードパラメータをヘッダに変換 */ +static SRLAError SRLAEncoder_ConvertParameterToHeader( + const struct SRLAEncodeParameter *parameter, uint32_t num_samples, + struct SRLAHeader *header); +/* ブロックデータタイプの判定 */ +static SRLABlockDataType SRLAEncoder_DecideBlockDataType( + struct SRLAEncoder *encoder, const int32_t *const *input, uint32_t num_samples); + +/* ヘッダエンコード */ +SRLAApiResult SRLAEncoder_EncodeHeader( + const struct SRLAHeader *header, uint8_t *data, uint32_t data_size) +{ + uint8_t *data_pos; + + /* 引数チェック */ + if ((header == NULL) || (data == NULL)) { + return SRLA_APIRESULT_INVALID_ARGUMENT; + } + + /* 出力先バッファサイズ不足 */ + if (data_size < SRLA_HEADER_SIZE) { + return SRLA_APIRESULT_INSUFFICIENT_BUFFER; + } + + /* ヘッダ異常値のチェック */ + /* データに書き出す(副作用)前にできる限りのチェックを行う */ + /* チャンネル数 */ + if (header->num_channels == 0) { + return SRLA_APIRESULT_INVALID_FORMAT; + } + /* サンプル数 */ + if (header->num_samples == 0) { + return SRLA_APIRESULT_INVALID_FORMAT; + } + /* サンプリングレート */ + if (header->sampling_rate == 0) { + return SRLA_APIRESULT_INVALID_FORMAT; + } + /* ビット深度 */ + if (header->bits_per_sample == 0) { + return SRLA_APIRESULT_INVALID_FORMAT; + } + /* ブロックあたりサンプル数 */ + if (header->num_samples_per_block == 0) { + return SRLA_APIRESULT_INVALID_FORMAT; + } + /* パラメータプリセット */ + if (header->preset >= SRLA_NUM_PARAMETER_PRESETS) { + return SRLA_APIRESULT_INVALID_FORMAT; + } + + /* 書き出し用ポインタ設定 */ + data_pos = data; + + /* シグネチャ */ + ByteArray_PutUint8(data_pos, '1'); + ByteArray_PutUint8(data_pos, '2'); + ByteArray_PutUint8(data_pos, '4'); + ByteArray_PutUint8(data_pos, '9'); + /* フォーマットバージョン + * 補足)ヘッダの設定値は無視してマクロ値を書き込む */ + ByteArray_PutUint32BE(data_pos, SRLA_FORMAT_VERSION); + /* コーデックバージョン + * 補足)ヘッダの設定値は無視してマクロ値を書き込む */ + ByteArray_PutUint32BE(data_pos, SRLA_CODEC_VERSION); + /* チャンネル数 */ + ByteArray_PutUint16BE(data_pos, header->num_channels); + /* サンプル数 */ + ByteArray_PutUint32BE(data_pos, header->num_samples); + /* サンプリングレート */ + ByteArray_PutUint32BE(data_pos, header->sampling_rate); + /* サンプルあたりビット数 */ + ByteArray_PutUint16BE(data_pos, header->bits_per_sample); + /* ブロックあたりサンプル数 */ + ByteArray_PutUint32BE(data_pos, header->num_samples_per_block); + /* パラメータプリセット */ + ByteArray_PutUint8(data_pos, header->preset); + + /* ヘッダサイズチェック */ + SRLA_ASSERT((data_pos - data) == SRLA_HEADER_SIZE); + + /* 成功終了 */ + return SRLA_APIRESULT_OK; +} + +/* エンコードパラメータをヘッダに変換 */ +static SRLAError SRLAEncoder_ConvertParameterToHeader( + const struct SRLAEncodeParameter *parameter, uint32_t num_samples, + struct SRLAHeader *header) +{ + struct SRLAHeader tmp_header = { 0, }; + + /* 引数チェック */ + if ((parameter == NULL) || (header == NULL)) { + return SRLA_ERROR_INVALID_ARGUMENT; + } + + /* パラメータのチェック */ + if (parameter->num_channels == 0) { + return SRLA_ERROR_INVALID_FORMAT; + } + if (parameter->bits_per_sample == 0) { + return SRLA_ERROR_INVALID_FORMAT; + } + if (parameter->sampling_rate == 0) { + return SRLA_ERROR_INVALID_FORMAT; + } + if (parameter->preset >= SRLA_NUM_PARAMETER_PRESETS) { + return SRLA_ERROR_INVALID_FORMAT; + } + + /* 総サンプル数 */ + tmp_header.num_samples = num_samples; + + /* 対応するメンバをコピー */ + tmp_header.num_channels = parameter->num_channels; + tmp_header.sampling_rate = parameter->sampling_rate; + tmp_header.bits_per_sample = parameter->bits_per_sample; + tmp_header.preset = parameter->preset; + tmp_header.num_samples_per_block = parameter->num_samples_per_block; + + /* 成功終了 */ + (*header) = tmp_header; + return SRLA_ERROR_OK; +} + +/* エンコーダハンドル作成に必要なワークサイズ計算 */ +int32_t SRLAEncoder_CalculateWorkSize(const struct SRLAEncoderConfig *config) +{ + int32_t work_size, tmp_work_size; + + /* 引数チェック */ + if (config == NULL) { + return -1; + } + + /* コンフィグチェック */ + if ((config->max_num_samples_per_block == 0) + || (config->max_num_channels == 0) + || (config->max_num_parameters == 0)) { + return -1; + } + + /* ブロックサイズはパラメータ数より大きくなるべき */ + if (config->max_num_parameters > config->max_num_samples_per_block) { + return -1; + } + + /* ハンドル本体のサイズ */ + work_size = sizeof(struct SRLAEncoder) + SRLA_MEMORY_ALIGNMENT; + + /* LPC計算ハンドルのサイズ */ + { + struct LPCCalculatorConfig lpcc_config; + lpcc_config.max_num_samples = config->max_num_samples_per_block; + lpcc_config.max_order = config->max_num_parameters; + if ((tmp_work_size = LPCCalculator_CalculateWorkSize(&lpcc_config)) < 0) { + return -1; + } + work_size += tmp_work_size; + } + + /* 符号化ハンドルのサイズ */ + if ((tmp_work_size = SRLACoder_CalculateWorkSize()) < 0) { + return -1; + } + work_size += tmp_work_size; + + /* プリエンファシスフィルタのサイズ */ + work_size += SRLA_CALCULATE_2DIMARRAY_WORKSIZE(struct SRLAPreemphasisFilter, config->max_num_channels, SRLA_NUM_PREEMPHASIS_FILTERS); + work_size += SRLA_CALCULATE_2DIMARRAY_WORKSIZE(int32_t, config->max_num_channels, SRLA_NUM_PREEMPHASIS_FILTERS); + /* パラメータバッファ領域 */ + /* LPC係数(int) */ + work_size += SRLA_CALCULATE_2DIMARRAY_WORKSIZE(int32_t, config->max_num_channels, config->max_num_parameters); + /* LPC係数(double) */ + work_size += SRLA_CALCULATE_2DIMARRAY_WORKSIZE(double, config->max_num_channels, config->max_num_parameters); + /* 各チャンネルのLPC係数右シフト量 */ + work_size += SRLA_MEMORY_ALIGNMENT + sizeof(uint32_t) * config->max_num_channels; + /* 各チャンネルのLPC係数次数 */ + work_size += SRLA_MEMORY_ALIGNMENT + sizeof(uint32_t) * config->max_num_channels; + /* 信号処理バッファのサイズ */ + work_size += SRLA_CALCULATE_2DIMARRAY_WORKSIZE(int32_t, config->max_num_channels, config->max_num_samples_per_block); + work_size += config->max_num_samples_per_block * sizeof(double) + SRLA_MEMORY_ALIGNMENT; + /* 残差信号のサイズ */ + work_size += SRLA_CALCULATE_2DIMARRAY_WORKSIZE(int32_t, config->max_num_channels, config->max_num_samples_per_block); + + return work_size; +} + +/* エンコーダハンドル作成 */ +struct SRLAEncoder* SRLAEncoder_Create(const struct SRLAEncoderConfig* config, void* work, int32_t work_size) +{ + uint32_t ch, l; + struct SRLAEncoder* encoder; + uint8_t tmp_alloc_by_own = 0; + uint8_t* work_ptr; + + /* ワーク領域時前確保の場合 */ + if ((work == NULL) && (work_size == 0)) { + if ((work_size = SRLAEncoder_CalculateWorkSize(config)) < 0) { + return NULL; + } + work = malloc((uint32_t)work_size); + tmp_alloc_by_own = 1; + } + + /* 引数チェック */ + if ((config == NULL) || (work == NULL) + || (work_size < SRLAEncoder_CalculateWorkSize(config))) { + return NULL; + } + + /* コンフィグチェック */ + if ((config->max_num_channels == 0) + || (config->max_num_samples_per_block == 0) + || (config->max_num_parameters == 0)) { + return NULL; + } + + /* ブロックサイズはパラメータ数より大きくなるべき */ + if (config->max_num_parameters > config->max_num_samples_per_block) { + return NULL; + } + + /* ワーク領域先頭ポインタ取得 */ + work_ptr = (uint8_t*)work; + + /* エンコーダハンドル領域確保 */ + work_ptr = (uint8_t*)SRLAUTILITY_ROUNDUP((uintptr_t)work_ptr, SRLA_MEMORY_ALIGNMENT); + encoder = (struct SRLAEncoder*)work_ptr; + work_ptr += sizeof(struct SRLAEncoder); + + /* エンコーダメンバ設定 */ + encoder->set_parameter = 0; + encoder->alloced_by_own = tmp_alloc_by_own; + encoder->work = work; + encoder->max_num_channels = config->max_num_channels; + encoder->max_num_samples_per_block = config->max_num_samples_per_block; + encoder->max_num_parameters = config->max_num_parameters; + + /* LPC計算ハンドルの作成 */ + { + int32_t lpcc_size; + struct LPCCalculatorConfig lpcc_config; + lpcc_config.max_num_samples = config->max_num_samples_per_block; + lpcc_config.max_order = config->max_num_parameters; + lpcc_size = LPCCalculator_CalculateWorkSize(&lpcc_config); + if ((encoder->lpcc = LPCCalculator_Create(&lpcc_config, work_ptr, lpcc_size)) == NULL) { + return NULL; + } + work_ptr += lpcc_size; + } + + /* 符号化ハンドルの作成 */ + { + const int32_t coder_size = SRLACoder_CalculateWorkSize(); + if ((encoder->coder = SRLACoder_Create(work_ptr, coder_size)) == NULL) { + return NULL; + } + work_ptr += coder_size; + } + + /* プリエンファシスフィルタの作成 */ + SRLA_ALLOCATE_2DIMARRAY(encoder->pre_emphasis, + work_ptr, struct SRLAPreemphasisFilter, config->max_num_channels, SRLA_NUM_PREEMPHASIS_FILTERS); + /* プリエンファシスフィルタのバッファ領域 */ + SRLA_ALLOCATE_2DIMARRAY(encoder->pre_emphasis_prev, + work_ptr, int32_t, config->max_num_channels, SRLA_NUM_PREEMPHASIS_FILTERS); + + /* バッファ領域の確保 全てのポインタをアラインメント */ + /* LPC係数(int) */ + SRLA_ALLOCATE_2DIMARRAY(encoder->params_int, + work_ptr, int32_t, config->max_num_channels, config->max_num_parameters); + /* LPC係数(double) */ + SRLA_ALLOCATE_2DIMARRAY(encoder->params_double, + work_ptr, double, config->max_num_channels, config->max_num_parameters); + /* 各層のLPC係数右シフト量 */ + work_ptr = (uint8_t *)SRLAUTILITY_ROUNDUP((uintptr_t)work_ptr, SRLA_MEMORY_ALIGNMENT); + encoder->rshifts = (uint32_t *)work_ptr; + work_ptr += config->max_num_channels * sizeof(uint32_t); + /* 各層のLPC係数次数 */ + work_ptr = (uint8_t *)SRLAUTILITY_ROUNDUP((uintptr_t)work_ptr, SRLA_MEMORY_ALIGNMENT); + encoder->coef_order = (uint32_t *)work_ptr; + work_ptr += config->max_num_channels * sizeof(uint32_t); + + /* 信号処理用バッファ領域 */ + SRLA_ALLOCATE_2DIMARRAY(encoder->buffer_int, + work_ptr, int32_t, config->max_num_channels, config->max_num_samples_per_block); + SRLA_ALLOCATE_2DIMARRAY(encoder->residual, + work_ptr, int32_t, config->max_num_channels, config->max_num_samples_per_block); + + /* doubleバッファ */ + work_ptr = (uint8_t *)SRLAUTILITY_ROUNDUP((uintptr_t)work_ptr, SRLA_MEMORY_ALIGNMENT); + encoder->buffer_double = (double *)work_ptr; + work_ptr += config->max_num_samples_per_block * sizeof(double); + + /* バッファオーバーランチェック */ + /* 補足)既にメモリを破壊している可能性があるので、チェックに失敗したら落とす */ + SRLA_ASSERT((work_ptr - (uint8_t *)work) <= work_size); + + /* プリエンファシスフィルタ初期化 */ + for (ch = 0; ch < config->max_num_channels; ch++) { + for (l = 0; l < SRLA_NUM_PREEMPHASIS_FILTERS; l++) { + SRLAPreemphasisFilter_Initialize(&encoder->pre_emphasis[ch][l]); + } + } + + return encoder; +} + +/* エンコーダハンドルの破棄 */ +void SRLAEncoder_Destroy(struct SRLAEncoder *encoder) +{ + if (encoder != NULL) { + SRLACoder_Destroy(encoder->coder); + if (encoder->alloced_by_own == 1) { + free(encoder->work); + } + } +} + +/* エンコードパラメータの設定 */ +SRLAApiResult SRLAEncoder_SetEncodeParameter( + struct SRLAEncoder *encoder, const struct SRLAEncodeParameter *parameter) +{ + struct SRLAHeader tmp_header; + + /* 引数チェック */ + if ((encoder == NULL) || (parameter == NULL)) { + return SRLA_APIRESULT_INVALID_ARGUMENT; + } + + /* パラメータ設定がおかしくないか、ヘッダへの変換を通じて確認 */ + /* 総サンプル数はダミー値を入れる */ + if (SRLAEncoder_ConvertParameterToHeader(parameter, 0, &tmp_header) != SRLA_ERROR_OK) { + return SRLA_APIRESULT_INVALID_FORMAT; + } + + /* エンコーダの容量を越えてないかチェック */ + if ((encoder->max_num_samples_per_block < parameter->num_samples_per_block) + || (encoder->max_num_channels < parameter->num_channels)) { + return SRLA_APIRESULT_INSUFFICIENT_BUFFER; + } + /* ブロックあたりサンプル数のセット */ + tmp_header.num_samples_per_block = parameter->num_samples_per_block; + + /* ヘッダ設定 */ + encoder->header = tmp_header; + + /* エンコードプリセットを取得 */ + SRLA_ASSERT(parameter->preset < SRLA_NUM_PARAMETER_PRESETS); + encoder->parameter_preset = &g_srla_parameter_preset[parameter->preset]; + + /* パラメータ設定済みフラグを立てる */ + encoder->set_parameter = 1; + + return SRLA_APIRESULT_OK; +} + +/* ブロックデータタイプの判定 */ +static SRLABlockDataType SRLAEncoder_DecideBlockDataType( + struct SRLAEncoder *encoder, const int32_t *const *input, uint32_t num_samples) +{ + uint32_t ch, smpl; + double mean_length; + const struct SRLAHeader *header; + + SRLA_ASSERT(encoder != NULL); + SRLA_ASSERT(input != NULL); + SRLA_ASSERT(encoder->set_parameter == 1); + + header = &encoder->header; + + /* 平均符号長の計算 */ + mean_length = 0.0; + for (ch = 0; ch < header->num_channels; ch++) { + double len; + LPCApiResult ret; + /* 入力をdouble化 */ + for (smpl = 0; smpl < num_samples; smpl++) { + encoder->buffer_double[smpl] = input[ch][smpl] * pow(2.0, -(int32_t)(header->bits_per_sample - 1)); + } + /* 推定符号長計算 */ + ret = LPCCalculator_EstimateCodeLength(encoder->lpcc, + encoder->buffer_double, num_samples, + header->bits_per_sample, encoder->parameter_preset->max_num_parameters, &len, LPC_WINDOWTYPE_RECTANGULAR); + SRLA_ASSERT(ret == LPC_APIRESULT_OK); + mean_length += len; + } + mean_length /= header->num_channels; + + /* ビット幅に占める比に変換 */ + mean_length /= header->bits_per_sample; + + /* データタイプ判定 */ + + /* 圧縮が効きにくい: 生データ出力 */ + if (mean_length >= SRLA_ESTIMATED_CODELENGTH_THRESHOLD) { + return SRLA_BLOCK_DATA_TYPE_RAWDATA; + } + + /* 無音判定 */ + for (ch = 0; ch < header->num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + if (input[ch][smpl] != 0) { + goto NOT_SILENCE; + } + } + } + return SRLA_BLOCK_DATA_TYPE_SILENT; + +NOT_SILENCE: + /* それ以外は圧縮データ */ + return SRLA_BLOCK_DATA_TYPE_COMPRESSDATA; +} + +/* 生データブロックエンコード */ +static SRLAApiResult SRLAEncoder_EncodeRawData( + struct SRLAEncoder *encoder, + const int32_t *const *input, uint32_t num_samples, + uint8_t *data, uint32_t data_size, uint32_t *output_size) +{ + uint32_t ch, smpl; + const struct SRLAHeader *header; + uint8_t *data_ptr; + + /* 内部関数なので不正な引数はアサートで落とす */ + SRLA_ASSERT(encoder != NULL); + SRLA_ASSERT(input != NULL); + SRLA_ASSERT(num_samples > 0); + SRLA_ASSERT(data != NULL); + SRLA_ASSERT(data_size > 0); + SRLA_ASSERT(output_size != NULL); + + header = &(encoder->header); + + /* 書き込み先のバッファサイズチェック */ + if (data_size < (header->bits_per_sample * num_samples * header->num_channels) / 8) { + return SRLA_APIRESULT_INSUFFICIENT_BUFFER; + } + + /* 生データをチャンネルインターリーブして出力 */ + data_ptr = data; + switch (header->bits_per_sample) { + case 8: + for (smpl = 0; smpl < num_samples; smpl++) { + for (ch = 0; ch < header->num_channels; ch++) { + ByteArray_PutUint8(data_ptr, SRLAUTILITY_SINT32_TO_UINT32(input[ch][smpl])); + SRLA_ASSERT((uint32_t)(data_ptr - data) < data_size); + } + } + break; + case 16: + for (smpl = 0; smpl < num_samples; smpl++) { + for (ch = 0; ch < header->num_channels; ch++) { + ByteArray_PutUint16BE(data_ptr, SRLAUTILITY_SINT32_TO_UINT32(input[ch][smpl])); + SRLA_ASSERT((uint32_t)(data_ptr - data) < data_size); + } + } + break; + case 24: + for (smpl = 0; smpl < num_samples; smpl++) { + for (ch = 0; ch < header->num_channels; ch++) { + ByteArray_PutUint24BE(data_ptr, SRLAUTILITY_SINT32_TO_UINT32(input[ch][smpl])); + SRLA_ASSERT((uint32_t)(data_ptr - data) < data_size); + } + } + break; + default: + SRLA_ASSERT(0); + } + + /* 書き込みサイズ取得 */ + (*output_size) = (uint32_t)(data_ptr - data); + + return SRLA_APIRESULT_OK; +} + +/* Recursive Golomb-Rice符号の平均符号長 */ +static double SRLAEncoder_CalculateRGRMeanCodeLength(double mean_abs_error, uint32_t bps) +{ + const double intmean = mean_abs_error * (1 << bps); /* 整数量子化した時の平均値 */ + const double rho = 1.0 / (1.0 + intmean); + const uint32_t k2 = (uint32_t)SRLAUTILITY_MAX(0, SRLAUtility_Log2(log(0.5127629514) / log(1.0 - rho))); + const uint32_t k1 = k2 + 1; + const double k1factor = pow(1.0 - rho, (double)(1 << k1)); + const double k2factor = pow(1.0 - rho, (double)(1 << k2)); + return (1.0 + k1) * (1.0 - k1factor) + (1.0 + k2 + (1.0 / (1.0 - k2factor))) * k1factor; +} + +/* 最適なLPC次数の選択 */ +static SRLAError SRLAEncoder_SelectBestLPCOrder(struct SRLAEncoder *encoder, + const double *input, uint32_t num_samples, SRLAChannelLPCOrderDecisionTactics tactics, + uint32_t max_coef_order, uint32_t *best_coef_order) +{ + SRLA_ASSERT(encoder != NULL); + SRLA_ASSERT(input != NULL); + SRLA_ASSERT(input == encoder->buffer_double); /* 現状エンコーダハンドルの領域の使用を想定 */ + SRLA_ASSERT(best_coef_order != NULL); + + switch (tactics) { + case SRLA_LPC_ORDER_DECISION_TACTICS_MAX_FIXED: + /* 最大次数を常に選択 */ + (*best_coef_order) = max_coef_order; + return SRLA_ERROR_OK; + case SRLA_LPC_ORDER_DECISION_TACTICS_BRUTEFORCE_SEARCH: + /* 網羅探索 */ + { + LPCApiResult ret; + double minlen, len, mabse; + uint32_t i, order, smpl, tmp_best_order = 0; + double coefs[SRLA_MAX_COEFFICIENT_ORDER][SRLA_MAX_COEFFICIENT_ORDER]; + double *pcoefs[SRLA_MAX_COEFFICIENT_ORDER]; + for (i = 0; i < SRLA_MAX_COEFFICIENT_ORDER; i++) { + pcoefs[i] = &coefs[i][0]; + } + /* 次数選択のため係数計算 */ + ret = LPCCalculator_CalculateMultipleLPCCoefficients(encoder->lpcc, + input, num_samples, pcoefs, max_coef_order, LPC_WINDOWTYPE_WELCH, 1e-6); + SRLA_ASSERT(ret == LPC_APIRESULT_OK); + + minlen = FLT_MAX; + for (order = 1; order <= max_coef_order; order++) { + const double *coef = pcoefs[order - 1]; + mabse = 0.0; + for (smpl = order; smpl < num_samples; smpl++) { + double residual = input[smpl]; + for (i = 0; i < order; i++) { + residual += coef[i] * input[smpl - i - 1]; + } + mabse += SRLAUTILITY_ABS(residual); + } + /* 残差符号のサイズ */ + len = SRLAEncoder_CalculateRGRMeanCodeLength(mabse / num_samples, encoder->header.bits_per_sample) * num_samples; + /* 係数のサイズ */ + len += SRLA_LPC_COEFFICIENT_BITWIDTH * order; + if (minlen > len) { + minlen = len; + tmp_best_order = order; + } + } + /* 結果を設定 */ + SRLA_ASSERT(tmp_best_order != 0); + (*best_coef_order) = tmp_best_order; + return SRLA_ERROR_OK; + } + case SRLA_LPC_ORDER_DECISION_TACTICS_BRUTEFORCE_ESTIMATION: + /* 残差分散の推測による網羅探索 */ + { + LPCApiResult ret; + double minlen, len, mabse; + uint32_t order, tmp_best_order = 0; + double error_vars[SRLA_MAX_COEFFICIENT_ORDER + 1]; + + /* 残差分散の計算 */ + ret = LPCCalculator_CalculateErrorVariances(encoder->lpcc, + input, num_samples, error_vars, max_coef_order, LPC_WINDOWTYPE_WELCH, 1e-6); + SRLA_ASSERT(ret == LPC_APIRESULT_OK); + + minlen = FLT_MAX; + for (order = 1; order <= max_coef_order; order++) { + /* Laplace分布の仮定で残差分散から平均絶対値を推定 */ + mabse = sqrt(error_vars[order] / 2.0); + /* 残差符号のサイズ */ + len = SRLAEncoder_CalculateRGRMeanCodeLength(mabse, encoder->header.bits_per_sample) * num_samples; + /* 係数のサイズ */ + len += SRLA_LPC_COEFFICIENT_BITWIDTH * order; + if (minlen > len) { + minlen = len; + tmp_best_order = order; + } + } + /* 結果を設定 */ + SRLA_ASSERT(tmp_best_order != 0); + (*best_coef_order) = tmp_best_order; + return SRLA_ERROR_OK; + } + default: + SRLA_ASSERT(0); + } + + return SRLA_ERROR_NG; +} + +/* 圧縮データブロックエンコード */ +static SRLAApiResult SRLAEncoder_EncodeCompressData( + struct SRLAEncoder *encoder, + const int32_t *const *input, uint32_t num_samples, + uint8_t *data, uint32_t data_size, uint32_t *output_size) +{ + uint32_t ch; + struct BitStream writer; + const struct SRLAHeader *header; + SRLAChannelProcessMethod ch_process_method = SRLA_CH_PROCESS_METHOD_INVALID; + + /* 内部関数なので不正な引数はアサートで落とす */ + SRLA_ASSERT(encoder != NULL); + SRLA_ASSERT(input != NULL); + SRLA_ASSERT(num_samples > 0); + SRLA_ASSERT(data != NULL); + SRLA_ASSERT(data_size > 0); + SRLA_ASSERT(output_size != NULL); + + /* ヘッダ取得 */ + header = &(encoder->header); + + /* マルチチャンネル処理法の決定 */ + if (header->num_channels == 1) { + ch_process_method = SRLA_CH_PROCESS_METHOD_NONE; + } else { + switch (encoder->parameter_preset->ch_process_method_tactics) { + case SRLA_CH_PROCESS_METHOD_TACTICS_NONE: + ch_process_method = SRLA_CH_PROCESS_METHOD_NONE; + break; + case SRLA_CH_PROCESS_METHOD_TACTICS_MS_FIXED: + ch_process_method = SRLA_CH_PROCESS_METHOD_MS; + break; + case SRLA_CH_PROCESS_METHOD_TACTICS_ADAPTIVE: + if (header->num_channels >= 2) { + uint32_t smpl; + double est_len[4]; + /* 入力をバッファにコピー */ + for (ch = 0; ch < 2; ch++) { + memcpy(encoder->buffer_int[ch], input[ch], sizeof(int32_t) * num_samples); + } + /* MS信号の生成 */ + SRLAUtility_LRtoMSConversion(encoder->buffer_int, num_samples); + + /* 各チャンネルの推定符号長を計算 */ + for (ch = 0; ch < 2; ch++) { + /* L,R */ + for (smpl = 0; smpl < num_samples; smpl++) { + encoder->buffer_double[smpl] = input[ch][smpl] * pow(2.0, -(int32_t)(header->bits_per_sample - 1)); + } + LPCCalculator_EstimateCodeLength(encoder->lpcc, + encoder->buffer_double, num_samples, header->bits_per_sample, encoder->parameter_preset->max_num_parameters, + &est_len[ch], LPC_WINDOWTYPE_WELCH); + /* M,S */ + for (smpl = 0; smpl < num_samples; smpl++) { + encoder->buffer_double[smpl] = encoder->buffer_int[ch][smpl] * pow(2.0, -(int32_t)(header->bits_per_sample - 1)); + } + LPCCalculator_EstimateCodeLength(encoder->lpcc, + encoder->buffer_double, num_samples, header->bits_per_sample, encoder->parameter_preset->max_num_parameters, + &est_len[2 + ch], LPC_WINDOWTYPE_WELCH); + } + /* 最小の符号長を選択 */ + { + uint32_t i; + SRLAChannelProcessMethod argmin; + double len[4], min; + SRLA_STATIC_ASSERT((SRLA_CH_PROCESS_METHOD_NONE == 0) && (SRLA_CH_PROCESS_METHOD_MS == 1) + && (SRLA_CH_PROCESS_METHOD_LS == 2) && (SRLA_CH_PROCESS_METHOD_RS == 3)); + len[SRLA_CH_PROCESS_METHOD_NONE] = est_len[0] + est_len[1]; + len[SRLA_CH_PROCESS_METHOD_MS] = est_len[2] + est_len[3]; + len[SRLA_CH_PROCESS_METHOD_LS] = est_len[0] + est_len[3]; + len[SRLA_CH_PROCESS_METHOD_RS] = est_len[1] + est_len[3]; + min = len[SRLA_CH_PROCESS_METHOD_NONE]; argmin = SRLA_CH_PROCESS_METHOD_NONE; + for (i = 1; i < 4; i++) { + if (min > len[i]) { + min = len[i]; + argmin = (SRLAChannelProcessMethod)i; + } + } + ch_process_method = argmin; + } + } + break; + default: + SRLA_ASSERT(0); + } + } + + /* 入力をバッファにコピー */ + for (ch = 0; ch < header->num_channels; ch++) { + memcpy(encoder->buffer_int[ch], input[ch], sizeof(int32_t) * num_samples); + /* バッファサイズより小さい入力のときは、末尾を0埋め */ + if (num_samples < encoder->max_num_samples_per_block) { + const uint32_t remain = encoder->max_num_samples_per_block - num_samples; + memset(&encoder->buffer_int[ch][num_samples], 0, sizeof(int32_t) * remain); + } + } + + /* マルチチャンネル処理 */ + switch (ch_process_method) { + case SRLA_CH_PROCESS_METHOD_NONE: + break; + case SRLA_CH_PROCESS_METHOD_MS: + SRLA_ASSERT(header->num_channels >= 2); + SRLAUtility_LRtoMSConversion(encoder->buffer_int, num_samples); + break; + case SRLA_CH_PROCESS_METHOD_LS: + SRLA_ASSERT(header->num_channels >= 2); + SRLAUtility_LRtoLSConversion(encoder->buffer_int, num_samples); + break; + case SRLA_CH_PROCESS_METHOD_RS: + SRLA_ASSERT(header->num_channels >= 2); + SRLAUtility_LRtoRSConversion(encoder->buffer_int, num_samples); + break; + default: + SRLA_ASSERT(0); + } + + /* プリエンファシス */ + for (ch = 0; ch < header->num_channels; ch++) { + uint32_t p; + for (p = 0; p < SRLA_NUM_PREEMPHASIS_FILTERS; p++) { + /* 直前値には先頭の同一値が続くと考える */ + encoder->pre_emphasis[ch][p].prev = encoder->pre_emphasis_prev[ch][p] = encoder->buffer_int[ch][0]; + SRLAPreemphasisFilter_CalculateCoefficient(&encoder->pre_emphasis[ch][p], encoder->buffer_int[ch], num_samples); + SRLAPreemphasisFilter_Preemphasis(&encoder->pre_emphasis[ch][p], encoder->buffer_int[ch], num_samples); + } + } + + /* チャンネル毎にパラメータ計算 */ + for (ch = 0; ch < header->num_channels; ch++) { + uint32_t smpl, p; + LPCApiResult ret; + /* double精度の信号に変換([-1,1]の範囲に正規化) */ + for (smpl = 0; smpl < num_samples; smpl++) { + encoder->buffer_double[smpl] = encoder->buffer_int[ch][smpl] * pow(2.0, -(int32_t)(header->bits_per_sample - 1)); + } + /* 次数選択 */ + SRLAEncoder_SelectBestLPCOrder(encoder, + encoder->buffer_double, num_samples, encoder->parameter_preset->lpc_order_tactics, + encoder->parameter_preset->max_num_parameters, &encoder->coef_order[ch]); + /* LPC係数計算 */ + ret = LPCCalculator_CalculateLPCCoefficientsSVR(encoder->lpcc, + encoder->buffer_double, num_samples, + encoder->params_double[ch], encoder->coef_order[ch], encoder->parameter_preset->svr_max_num_iterations, + LPC_WINDOWTYPE_WELCH, 1e-6, encoder->parameter_preset->margin_list, encoder->parameter_preset->margin_list_size); + SRLA_ASSERT(ret == LPC_APIRESULT_OK); + /* 畳み込み演算でインデックスが増える方向にしたい都合上パラメータ順序を変転 */ + for (p = 0; p < encoder->coef_order[ch] / 2; p++) { + double tmp = encoder->params_double[ch][p]; + encoder->params_double[ch][p] = encoder->params_double[ch][encoder->coef_order[ch] - p - 1]; + encoder->params_double[ch][encoder->coef_order[ch] - p - 1] = tmp; + } + ret = LPC_QuantizeCoefficients(encoder->params_double[ch], encoder->coef_order[ch], + SRLA_LPC_COEFFICIENT_BITWIDTH, (1 << SRLA_RSHIFT_LPC_COEFFICIENT_BITWIDTH), + encoder->params_int[ch], &encoder->rshifts[ch]); + SRLA_ASSERT(ret == LPC_APIRESULT_OK); + } + + /* チャンネル毎にLPC予測 */ + for (ch = 0; ch < header->num_channels; ch++) { + /* LPC予測 */ + SRLALPC_Predict(encoder->buffer_int[ch], + num_samples, encoder->params_int[ch], encoder->coef_order[ch], encoder->residual[ch], encoder->rshifts[ch]); + } + + /* ビットライタ作成 */ + BitWriter_Open(&writer, data, data_size); + + /* マルチチャンネル処理法の書き込み */ + SRLA_ASSERT(ch_process_method != SRLA_CH_PROCESS_METHOD_INVALID); + SRLA_ASSERT(ch_process_method < 4); + BitWriter_PutBits(&writer, ch_process_method, 2); + + /* パラメータ符号化 */ + /* プリエンファシス */ + for (ch = 0; ch < header->num_channels; ch++) { + uint32_t p, uval; + for (p = 0; p < SRLA_NUM_PREEMPHASIS_FILTERS; p++) { + /* プリエンファシスフィルタのバッファ */ + uval = SRLAUTILITY_SINT32_TO_UINT32(encoder->pre_emphasis_prev[ch][p]); + SRLA_ASSERT(uval < (1 << (header->bits_per_sample + 1))); + BitWriter_PutBits(&writer, uval, header->bits_per_sample + 1); + /* プリエンファシス係数は正値に制限しているため1bitケチれる */ + SRLA_ASSERT(encoder->pre_emphasis[ch][p].coef >= 0); + uval = (uint32_t)encoder->pre_emphasis[ch][p].coef; + SRLA_ASSERT(uval < (1 << (SRLA_PREEMPHASIS_COEF_SHIFT - 1))); + BitWriter_PutBits(&writer, uval, SRLA_PREEMPHASIS_COEF_SHIFT - 1); + } + } + /* LPC係数次数/LPC係数右シフト量/LPC係数 */ + for (ch = 0; ch < header->num_channels; ch++) { + uint32_t i, uval; + /* LPC係数次数 */ + SRLA_ASSERT(encoder->coef_order[ch] > 0); + SRLA_ASSERT(encoder->coef_order[ch] <= (1 << SRLA_LPC_COEFFICIENT_ORDER_BITWIDTH)); + BitWriter_PutBits(&writer, encoder->coef_order[ch] - 1, SRLA_LPC_COEFFICIENT_ORDER_BITWIDTH); + /* LPC係数右シフト量 */ + SRLA_ASSERT(encoder->rshifts[ch] < (1 << SRLA_RSHIFT_LPC_COEFFICIENT_BITWIDTH)); + BitWriter_PutBits(&writer, encoder->rshifts[ch], SRLA_RSHIFT_LPC_COEFFICIENT_BITWIDTH); + /* LPC係数 */ + for (i = 0; i < encoder->coef_order[ch]; i++) { + uval = SRLAUTILITY_SINT32_TO_UINT32(encoder->params_int[ch][i]); + SRLA_ASSERT(uval < (1 << SRLA_LPC_COEFFICIENT_BITWIDTH)); + BitWriter_PutBits(&writer, uval, SRLA_LPC_COEFFICIENT_BITWIDTH); + } + } + + /* 残差符号化 */ + for (ch = 0; ch < header->num_channels; ch++) { + SRLACoder_Encode(encoder->coder, &writer, encoder->residual[ch], num_samples); + } + + /* バイト境界に揃える */ + BitStream_Flush(&writer); + + /* 書き込みサイズの取得 */ + BitStream_Tell(&writer, (int32_t *)output_size); + + /* ビットライタ破棄 */ + BitStream_Close(&writer); + + return SRLA_APIRESULT_OK; +} + +/* 無音データブロックエンコード */ +static SRLAApiResult SRLAEncoder_EncodeSilentData( + struct SRLAEncoder *encoder, + const int32_t *const *input, uint32_t num_samples, + uint8_t *data, uint32_t data_size, uint32_t *output_size) +{ + /* 内部関数なので不正な引数はアサートで落とす */ + SRLA_ASSERT(encoder != NULL); + SRLA_ASSERT(input != NULL); + SRLA_ASSERT(num_samples > 0); + SRLA_ASSERT(data != NULL); + SRLA_ASSERT(data_size > 0); + SRLA_ASSERT(output_size != NULL); + + /* データサイズなし */ + (*output_size) = 0; + return SRLA_APIRESULT_OK; +} + +/* 単一データブロックエンコード */ +SRLAApiResult SRLAEncoder_EncodeBlock( + struct SRLAEncoder *encoder, + const int32_t *const *input, uint32_t num_samples, + uint8_t *data, uint32_t data_size, uint32_t *output_size) +{ + uint8_t *data_ptr; + const struct SRLAHeader *header; + SRLABlockDataType block_type; + SRLAApiResult ret; + uint32_t block_header_size, block_data_size; + + /* 引数チェック */ + if ((encoder == NULL) || (input == NULL) || (num_samples == 0) + || (data == NULL) || (data_size == 0) || (output_size == NULL)) { + return SRLA_APIRESULT_INVALID_ARGUMENT; + } + header = &(encoder->header); + + /* パラメータがセットされてない */ + if (encoder->set_parameter != 1) { + return SRLA_APIRESULT_PARAMETER_NOT_SET; + } + + /* エンコードサンプル数チェック */ + if (num_samples > header->num_samples_per_block) { + return SRLA_APIRESULT_INSUFFICIENT_BUFFER; + } + + /* 圧縮手法の判定 */ + block_type = SRLAEncoder_DecideBlockDataType(encoder, input, num_samples); + SRLA_ASSERT(block_type != SRLA_BLOCK_DATA_TYPE_INVALID); + + /* ブロックヘッダをエンコード */ + data_ptr = data; + /* ブロック先頭の同期コード */ + ByteArray_PutUint16BE(data_ptr, SRLA_BLOCK_SYNC_CODE); + /* ブロックサイズ: 仮値で埋めておく */ + ByteArray_PutUint32BE(data_ptr, 0); + /* ブロックチェックサム: 仮値で埋めておく */ + ByteArray_PutUint16BE(data_ptr, 0); + /* ブロックデータタイプ */ + ByteArray_PutUint8(data_ptr, block_type); + /* ブロックチャンネルあたりサンプル数 */ + ByteArray_PutUint16BE(data_ptr, num_samples); + /* ブロックヘッダサイズ */ + block_header_size = (uint32_t)(data_ptr - data); + + /* データ部のエンコード */ + /* 手法によりエンコードする関数を呼び分け */ + switch (block_type) { + case SRLA_BLOCK_DATA_TYPE_RAWDATA: + ret = SRLAEncoder_EncodeRawData(encoder, input, num_samples, + data_ptr, data_size - block_header_size, &block_data_size); + break; + case SRLA_BLOCK_DATA_TYPE_COMPRESSDATA: + ret = SRLAEncoder_EncodeCompressData(encoder, input, num_samples, + data_ptr, data_size - block_header_size, &block_data_size); + break; + case SRLA_BLOCK_DATA_TYPE_SILENT: + ret = SRLAEncoder_EncodeSilentData(encoder, input, num_samples, + data_ptr, data_size - block_header_size, &block_data_size); + break; + default: + ret = SRLA_APIRESULT_INVALID_FORMAT; + break; + } + + /* エンコードに失敗している */ + if (ret != SRLA_APIRESULT_OK) { + return ret; + } + + /* ブロックサイズ書き込み: + * チェックサム(2byte) + ブロックチャンネルあたりサンプル数(2byte) + ブロックデータタイプ(1byte) */ + ByteArray_WriteUint32BE(&data[2], block_data_size + 5); + + /* チェックサムの領域以降のチェックサムを計算し書き込み */ + { + /* ブロックチャンネルあたりサンプル数(2byte) + ブロックデータタイプ(1byte) を加算 */ + const uint16_t checksum = SRLAUtility_CalculateFletcher16CheckSum(&data[8], block_data_size + 3); + ByteArray_WriteUint16BE(&data[6], checksum); + } + + /* 出力サイズ */ + (*output_size) = block_header_size + block_data_size; + + /* エンコード成功 */ + return SRLA_APIRESULT_OK; +} + +/* ヘッダ含めファイル全体をエンコード */ +SRLAApiResult SRLAEncoder_EncodeWhole( + struct SRLAEncoder *encoder, + const int32_t *const *input, uint32_t num_samples, + uint8_t *data, uint32_t data_size, uint32_t *output_size) +{ + SRLAApiResult ret; + uint32_t progress, ch, write_size, write_offset, num_encode_samples; + uint8_t *data_pos; + const int32_t *input_ptr[SRLA_MAX_NUM_CHANNELS]; + const struct SRLAHeader *header; + + /* 引数チェック */ + if ((encoder == NULL) || (input == NULL) + || (data == NULL) || (output_size == NULL)) { + return SRLA_APIRESULT_INVALID_ARGUMENT; + } + + /* パラメータがセットされてない */ + if (encoder->set_parameter != 1) { + return SRLA_APIRESULT_PARAMETER_NOT_SET; + } + + /* 書き出し位置を取得 */ + data_pos = data; + + /* ヘッダエンコード */ + encoder->header.num_samples = num_samples; + if ((ret = SRLAEncoder_EncodeHeader(&(encoder->header), data_pos, data_size)) + != SRLA_APIRESULT_OK) { + return ret; + } + header = &(encoder->header); + + /* 進捗状況初期化 */ + progress = 0; + write_offset = SRLA_HEADER_SIZE; + data_pos = data + SRLA_HEADER_SIZE; + + /* ブロックを時系列順にエンコード */ + while (progress < num_samples) { + + /* エンコードサンプル数の確定 */ + num_encode_samples + = SRLAUTILITY_MIN(header->num_samples_per_block, num_samples - progress); + + /* サンプル参照位置のセット */ + for (ch = 0; ch < header->num_channels; ch++) { + input_ptr[ch] = &input[ch][progress]; + } + + /* ブロックエンコード */ + if ((ret = SRLAEncoder_EncodeBlock(encoder, + input_ptr, num_encode_samples, + data_pos, data_size - write_offset, &write_size)) != SRLA_APIRESULT_OK) { + return ret; + } + + /* 進捗更新 */ + data_pos += write_size; + write_offset += write_size; + progress += num_encode_samples; + SRLA_ASSERT(write_offset <= data_size); + } + + /* 成功終了 */ + (*output_size) = write_offset; + return SRLA_APIRESULT_OK; +} diff --git a/libs/srla_encoder/src/srla_lpc_predict.c b/libs/srla_encoder/src/srla_lpc_predict.c new file mode 100644 index 0000000..5c59392 --- /dev/null +++ b/libs/srla_encoder/src/srla_lpc_predict.c @@ -0,0 +1,30 @@ +#include "srla_lpc_predict.h" + +#include +#include "srla_internal.h" + +/* LPC係数により予測/誤差出力 */ +void SRLALPC_Predict( + const int32_t *data, uint32_t num_samples, + const int32_t *coef, uint32_t coef_order, int32_t *residual, uint32_t coef_rshift) +{ + uint32_t smpl, ord; + int32_t predict; + const int32_t half = 1 << (coef_rshift - 1); /* 固定小数の0.5 */ + + /* 引数チェック */ + SRLA_ASSERT(data != NULL); + SRLA_ASSERT(coef != NULL); + SRLA_ASSERT(residual != NULL); + + memcpy(residual, data, sizeof(int32_t) * num_samples); + + /* 予測 */ + for (smpl = 0; smpl < num_samples - coef_order; smpl++) { + predict = half; + for (ord = 0; ord < coef_order; ord++) { + predict += (coef[ord] * data[smpl + ord]); + } + residual[smpl + ord] += (predict >> coef_rshift); + } +} diff --git a/libs/srla_encoder/src/srla_lpc_predict.h b/libs/srla_encoder/src/srla_lpc_predict.h new file mode 100644 index 0000000..e5c125a --- /dev/null +++ b/libs/srla_encoder/src/srla_lpc_predict.h @@ -0,0 +1,19 @@ +#ifndef SRLA_LPCPREDICTOR_H_INCLUDED +#define SRLA_LPCPREDICTOR_H_INCLUDED + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* LPC係数により予測/誤差出力 */ +void SRLALPC_Predict( + const int32_t *data, uint32_t num_samples, + const int32_t *coef, uint32_t coef_order, int32_t *residual, uint32_t coef_rshift); + +#ifdef __cplusplus +} +#endif + +#endif /* SRLA_LPCPREDICTOR_H_INCLUDED */ diff --git a/libs/srla_internal/CMakeLists.txt b/libs/srla_internal/CMakeLists.txt new file mode 100644 index 0000000..7093cc9 --- /dev/null +++ b/libs/srla_internal/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# プロジェクト名 +project(SRLAInternal C) + +# ライブラリ名 +set(LIB_NAME srla_internal) + +# 静的ライブラリ指定 +add_library(${LIB_NAME} STATIC) + +# ソースディレクトリ +add_subdirectory(src) + +# インクルードパス +target_include_directories(${LIB_NAME} + PRIVATE + ${PROJECT_ROOT_PATH}/include + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + +# コンパイルオプション +if(MSVC) + target_compile_options(${LIB_NAME} PRIVATE /W4) +else() + target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=2 -Wconversion -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition) + set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +endif() +set_target_properties(${LIB_NAME} + PROPERTIES + C_STANDARD 90 C_EXTENSIONS OFF + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) diff --git a/libs/srla_internal/include/srla_internal.h b/libs/srla_internal/include/srla_internal.h new file mode 100644 index 0000000..ab31821 --- /dev/null +++ b/libs/srla_internal/include/srla_internal.h @@ -0,0 +1,102 @@ +#ifndef SRLA_INTERNAL_H_INCLUDED +#define SRLA_INTERNAL_H_INCLUDED + +#include "srla.h" +#include "srla_stdint.h" + +/* 本ライブラリのメモリアラインメント */ +#define SRLA_MEMORY_ALIGNMENT 16 +/* ブロック先頭の同期コード */ +#define SRLA_BLOCK_SYNC_CODE 0xFFFF + +/* 内部エンコードパラメータ */ +/* プリエンファシスの係数シフト量 */ +#define SRLA_PREEMPHASIS_COEF_SHIFT 5 +/* プリエンファシスフィルタの適用回数 */ +#define SRLA_NUM_PREEMPHASIS_FILTERS 2 +/* LPC係数のビット幅 */ +#define SRLA_LPC_COEFFICIENT_BITWIDTH 8 +/* LPC係数右シフト量のビット幅 */ +#define SRLA_RSHIFT_LPC_COEFFICIENT_BITWIDTH 4 +/* (LPC係数次数-1)のビット幅 */ +#define SRLA_LPC_COEFFICIENT_ORDER_BITWIDTH 5 +/* 圧縮をやめて生データを出力するときの閾値(サンプルあたりビット数に占める比率) */ +#define SRLA_ESTIMATED_CODELENGTH_THRESHOLD 0.95f + +/* アサートマクロ */ +#ifdef NDEBUG +/* 未使用変数警告を明示的に回避 */ +#define SRLA_ASSERT(condition) ((void)(condition)) +#else +#include +#define SRLA_ASSERT(condition) assert(condition) +#endif + +/* 静的アサートマクロ */ +#define SRLA_STATIC_ASSERT(expr) extern void assertion_failed(char dummy[(expr) ? 1 : -1]) + +/* ブロックデータタイプ */ +typedef enum SRLABlockDataTypeTag { + SRLA_BLOCK_DATA_TYPE_COMPRESSDATA = 0, /* 圧縮済みデータ */ + SRLA_BLOCK_DATA_TYPE_SILENT = 1, /* 無音データ */ + SRLA_BLOCK_DATA_TYPE_RAWDATA = 2, /* 生データ */ + SRLA_BLOCK_DATA_TYPE_INVALID = 3 /* 無効 */ +} SRLABlockDataType; + +/* マルチチャンネル処理の決定方法 */ +typedef enum SRLAChannelProcessMethodTacticsTag { + SRLA_CH_PROCESS_METHOD_TACTICS_NONE = 0, /* 何もしない */ + SRLA_CH_PROCESS_METHOD_TACTICS_MS_FIXED, /* ステレオMS処理を常に選択 */ + SRLA_CH_PROCESS_METHOD_TACTICS_ADAPTIVE, /* 適応的にLR,LS,RS,MSを選択 */ + SRLA_CH_PROCESS_METHOD_TACTICS_INVALID /* 無効値 */ +} SRLAChannelProcessMethodTactics; + +/* マルチチャンネル処理法 */ +typedef enum SRLAChannelProcessMethodTag { + SRLA_CH_PROCESS_METHOD_NONE = 0, /* 何もしない */ + SRLA_CH_PROCESS_METHOD_MS = 1, /* ステレオMS処理 */ + SRLA_CH_PROCESS_METHOD_LS = 2, /* ステレオLS処理 */ + SRLA_CH_PROCESS_METHOD_RS = 3, /* ステレオRS処理 */ + SRLA_CH_PROCESS_METHOD_INVALID /* 無効値 */ +} SRLAChannelProcessMethod; + +/* LPCの次数決定方法 */ +typedef enum SRLAChannelLPCOrderDecisionTacticsTag { + SRLA_LPC_ORDER_DECISION_TACTICS_MAX_FIXED = 0, /* 最大次数を常に選択 */ + SRLA_LPC_ORDER_DECISION_TACTICS_BRUTEFORCE_SEARCH, /* 素朴な網羅探索 */ + SRLA_LPC_ORDER_DECISION_TACTICS_BRUTEFORCE_ESTIMATION, /* 残差分散の推定による網羅探索 */ + SRLA_LPC_ORDER_DECISION_TACTICS_INVALID /* 無効値 */ +} SRLAChannelLPCOrderDecisionTactics; + +/* 内部エラー型 */ +typedef enum SRLAErrorTag { + SRLA_ERROR_OK = 0, /* OK */ + SRLA_ERROR_NG, /* 分類不能な失敗 */ + SRLA_ERROR_INVALID_ARGUMENT, /* 不正な引数 */ + SRLA_ERROR_INVALID_FORMAT, /* 不正なフォーマット */ + SRLA_ERROR_INSUFFICIENT_BUFFER, /* バッファサイズが足りない */ + SRLA_ERROR_INSUFFICIENT_DATA /* データサイズが足りない */ +} SRLAError; + +/* パラメータプリセット */ +struct SRLAParameterPreset { + uint32_t max_num_parameters; /* 最大パラメータ数 */ + SRLAChannelProcessMethodTactics ch_process_method_tactics; /* マルチチャンネル処理の決定法 */ + SRLAChannelLPCOrderDecisionTactics lpc_order_tactics; /* LPCの次数決定法 */ + uint32_t svr_max_num_iterations; /* SVRの最大繰り返し回数 */ + const double *margin_list; /* マージンリスト */ + uint32_t margin_list_size; /* マージンリストサイズ */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* パラメータプリセット配列 */ +extern const struct SRLAParameterPreset g_srla_parameter_preset[]; + +#ifdef __cplusplus +} +#endif + +#endif /* SRLA_INTERNAL_H_INCLUDED */ diff --git a/libs/srla_internal/include/srla_utility.h b/libs/srla_internal/include/srla_utility.h new file mode 100644 index 0000000..cb60db6 --- /dev/null +++ b/libs/srla_internal/include/srla_utility.h @@ -0,0 +1,148 @@ +#ifndef SRLAUTILITY_H_INCLUDED +#define SRLAUTILITY_H_INCLUDED + +#include "srla_stdint.h" +#include + +/* 未使用引数警告回避 */ +#define SRLAUTILITY_UNUSED_ARGUMENT(arg) ((void)(arg)) +/* 算術右シフト */ +#if ((((int32_t)-1) >> 1) == ((int32_t)-1)) +/* 算術右シフトが有効な環境では、そのまま右シフト */ +#define SRLAUTILITY_SHIFT_RIGHT_ARITHMETIC(sint32, rshift) ((sint32) >> (rshift)) +#else +/* 算術右シフトが無効な環境では、自分で定義する ハッカーのたのしみのより引用 */ +/* 注意)有効範囲:0 <= rshift <= 32 */ +#define SRLAUTILITY_SHIFT_RIGHT_ARITHMETIC(sint32, rshift) ((((uint64_t)(sint32) + 0x80000000UL) >> (rshift)) - (0x80000000UL >> (rshift))) +#endif +/* 符号関数 ハッカーのたのしみより引用 補足)val==0の時は0を返す */ +#define SRLAUTILITY_SIGN(val) (((val) > 0) - ((val) < 0)) +/* nの倍数への切り上げ */ +#define SRLAUTILITY_ROUNDUP(val, n) ((((val) + ((n) - 1)) / (n)) * (n)) +/* 最大値の取得 */ +#define SRLAUTILITY_MAX(a,b) (((a) > (b)) ? (a) : (b)) +/* 最小値の取得 */ +#define SRLAUTILITY_MIN(a,b) (((a) < (b)) ? (a) : (b)) +/* 最小値以上最小値以下に制限 */ +#define SRLAUTILITY_INNER_VALUE(val, min, max) (SRLAUTILITY_MIN((max), SRLAUTILITY_MAX((min), (val)))) +/* 2の冪乗か? */ +#define SRLAUTILITY_IS_POWERED_OF_2(val) (!((val) & ((val) - 1))) +/* 符号付き32bit数値を符号なし32bit数値に一意変換 */ +#define SRLAUTILITY_SINT32_TO_UINT32(sint) (((int32_t)(sint) < 0) ? ((uint32_t)((-((sint) << 1)) - 1)) : ((uint32_t)(((sint) << 1)))) +/* 符号なし32bit数値を符号付き32bit数値に一意変換 */ +#define SRLAUTILITY_UINT32_TO_SINT32(uint) ((int32_t)((uint) >> 1) ^ -(int32_t)((uint) & 1)) +/* 絶対値の取得 */ +#define SRLAUTILITY_ABS(val) (((val) > 0) ? (val) : -(val)) + +/* NLZ(最上位ビットから1に当たるまでのビット数)の計算 */ +#if defined(__GNUC__) +/* ビルトイン関数を使用 */ +#define SRLAUTILITY_NLZ(x) (((x) > 0) ? (uint32_t)__builtin_clz(x) : 32U) +#elif defined(_MSC_VER) +/* ビルトイン関数を使用 */ +__inline uint32_t SRLAUTILITY_NLZ(uint32_t x) +{ + unsigned long result; + return (_BitScanReverse(&result, x) != 0) ? (31U - result) : 32U; +} +#else +/* ソフトウェア実装を使用 */ +#define SRLAUTILITY_NLZ(x) SRLAUtility_NLZSoft(x) +#endif + +/* ceil(log2(val))の計算 */ +#define SRLAUTILITY_LOG2CEIL(x) (32U - SRLAUTILITY_NLZ((uint32_t)((x) - 1U))) +/* floor(log2(val))の計算 */ +#define SRLAUTILITY_LOG2FLOOR(x) (31U - SRLAUTILITY_NLZ(x)) + +/* 2の冪乗数(1,2,4,8,16,...)への切り上げ */ +#if defined(__GNUC__) || defined(_MSC_VER) +/* ビルトイン関数を使用 */ +#define SRLAUTILITY_ROUNDUP2POWERED(x) (1U << SRLAUTILITY_LOG2CEIL(x)) +#else +/* ソフトウェア実装を使用 */ +#define SRLAUTILITY_ROUNDUP2POWERED(x) SRLAUtility_RoundUp2PoweredSoft(x) +#endif + +/* 2次元配列の領域ワークサイズ計算 */ +#define SRLA_CALCULATE_2DIMARRAY_WORKSIZE(type, size1, size2)\ + ((size1) * ((int32_t)sizeof(type *) + SRLA_MEMORY_ALIGNMENT\ + + (size2) * (int32_t)sizeof(type) + SRLA_MEMORY_ALIGNMENT)) + +/* 2次元配列の領域割当て */ +#define SRLA_ALLOCATE_2DIMARRAY(ptr, work_ptr, type, size1, size2)\ + do {\ + uint32_t i;\ + (work_ptr) = (uint8_t *)SRLAUTILITY_ROUNDUP((uintptr_t)work_ptr, SRLA_MEMORY_ALIGNMENT);\ + (ptr) = (type **)work_ptr;\ + (work_ptr) += sizeof(type *) * (size1);\ + for (i = 0; i < (size1); i++) {\ + (work_ptr) = (uint8_t *)SRLAUTILITY_ROUNDUP((uintptr_t)work_ptr, SRLA_MEMORY_ALIGNMENT);\ + (ptr)[i] = (type *)work_ptr;\ + (work_ptr) += sizeof(type) * (size2);\ + }\ + } while (0) + +/* プリエンファシス/デエンファシスフィルタ */ +struct SRLAPreemphasisFilter { + int32_t prev; + int32_t coef; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* round関数(C89で用意されていない) */ +double SRLAUtility_Round(double d); + +/* log2関数(C89で定義されていない) */ +double SRLAUtility_Log2(double d); + +/* フレッチャーのチェックサム計算 */ +uint16_t SRLAUtility_CalculateFletcher16CheckSum(const uint8_t *data, size_t data_size); + +/* NLZ(最上位ビットから1に当たるまでのビット数)の計算 */ +uint32_t SRLAUtility_NLZSoft(uint32_t val); + +/* 2の冪乗に切り上げる */ +uint32_t SRLAUtility_RoundUp2PoweredSoft(uint32_t val); + +/* LR -> MS (in-place) */ +void SRLAUtility_LRtoMSConversion(int32_t **buffer, uint32_t num_samples); + +/* MS -> LR (in-place) */ +void SRLAUtility_MStoLRConversion(int32_t **buffer, uint32_t num_samples); + +/* LR -> LS (in-place) */ +void SRLAUtility_LRtoLSConversion(int32_t **buffer, uint32_t num_samples); + +/* LS -> LR (in-place) */ +void SRLAUtility_LStoLRConversion(int32_t **buffer, uint32_t num_samples); + +/* LR -> RS (in-place) */ +void SRLAUtility_LRtoRSConversion(int32_t **buffer, uint32_t num_samples); + +/* RS -> LR (in-place) */ +void SRLAUtility_RStoLRConversion(int32_t **buffer, uint32_t num_samples); + +/* プリエンファシスフィルタ初期化 */ +void SRLAPreemphasisFilter_Initialize(struct SRLAPreemphasisFilter *preem); + +/* プリエンファシスフィルタ係数計算 */ +void SRLAPreemphasisFilter_CalculateCoefficient( + struct SRLAPreemphasisFilter *preem, const int32_t *buffer, uint32_t num_samples); + +/* プリエンファシス */ +void SRLAPreemphasisFilter_Preemphasis( + struct SRLAPreemphasisFilter *preem, int32_t *buffer, uint32_t num_samples); + +/* デエンファシスを複数回適用 */ +void SRLAPreemphasisFilter_MultiStageDeemphasis( + struct SRLAPreemphasisFilter *preem, uint32_t num_preem, int32_t *buffer, uint32_t num_samples); + +#ifdef __cplusplus +} +#endif + +#endif /* SRLAUTILITY_H_INCLUDED */ diff --git a/libs/srla_internal/src/CMakeLists.txt b/libs/srla_internal/src/CMakeLists.txt new file mode 100644 index 0000000..879b98a --- /dev/null +++ b/libs/srla_internal/src/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/srla_internal.c + ${CMAKE_CURRENT_SOURCE_DIR}/srla_utility.c + ) diff --git a/libs/srla_internal/src/srla_internal.c b/libs/srla_internal/src/srla_internal.c new file mode 100644 index 0000000..4d69441 --- /dev/null +++ b/libs/srla_internal/src/srla_internal.c @@ -0,0 +1,21 @@ +#include "srla_internal.h" + +/* 配列の要素数を取得 */ +#define SRLA_NUM_ARRAY_ELEMENTS(array) ((sizeof(array)) / (sizeof(array[0]))) +/* プリセットの要素定義 */ +#define SRLA_DEFINE_ARRAY_AND_NUM_ELEMTNS_TUPLE(array) array, SRLA_NUM_ARRAY_ELEMENTS(array) + +/* マージンリスト候補配列 */ +static const double margin_list[] = { 0.0, 1.0 / 4096, 1.0 / 1024, 1.0 / 256, 1.0 / 64, 1.0 / 16 }; + +/* パラメータプリセット配列 */ +const struct SRLAParameterPreset g_srla_parameter_preset[] = { + { 32, SRLA_CH_PROCESS_METHOD_TACTICS_ADAPTIVE, SRLA_LPC_ORDER_DECISION_TACTICS_MAX_FIXED, 0, SRLA_DEFINE_ARRAY_AND_NUM_ELEMTNS_TUPLE(margin_list) }, + { 32, SRLA_CH_PROCESS_METHOD_TACTICS_ADAPTIVE, SRLA_LPC_ORDER_DECISION_TACTICS_MAX_FIXED, 10, SRLA_DEFINE_ARRAY_AND_NUM_ELEMTNS_TUPLE(margin_list) }, + { 32, SRLA_CH_PROCESS_METHOD_TACTICS_ADAPTIVE, SRLA_LPC_ORDER_DECISION_TACTICS_BRUTEFORCE_ESTIMATION, 0, SRLA_DEFINE_ARRAY_AND_NUM_ELEMTNS_TUPLE(margin_list) }, + { 32, SRLA_CH_PROCESS_METHOD_TACTICS_ADAPTIVE, SRLA_LPC_ORDER_DECISION_TACTICS_BRUTEFORCE_ESTIMATION, 10, SRLA_DEFINE_ARRAY_AND_NUM_ELEMTNS_TUPLE(margin_list) }, + { 32, SRLA_CH_PROCESS_METHOD_TACTICS_ADAPTIVE, SRLA_LPC_ORDER_DECISION_TACTICS_BRUTEFORCE_SEARCH, 0, SRLA_DEFINE_ARRAY_AND_NUM_ELEMTNS_TUPLE(margin_list) }, + { 32, SRLA_CH_PROCESS_METHOD_TACTICS_ADAPTIVE, SRLA_LPC_ORDER_DECISION_TACTICS_BRUTEFORCE_SEARCH, 10, SRLA_DEFINE_ARRAY_AND_NUM_ELEMTNS_TUPLE(margin_list) } +}; + +SRLA_STATIC_ASSERT(SRLA_NUM_ARRAY_ELEMENTS(g_srla_parameter_preset) == SRLA_NUM_PARAMETER_PRESETS); diff --git a/libs/srla_internal/src/srla_utility.c b/libs/srla_internal/src/srla_utility.c new file mode 100644 index 0000000..ae50c82 --- /dev/null +++ b/libs/srla_internal/src/srla_utility.c @@ -0,0 +1,268 @@ +#include "srla_utility.h" + +#include +#include +#include "srla_internal.h" + +/* NLZ計算のためのテーブル */ +#define UNUSED 99 +static const uint32_t st_nlz10_table[64] = { + 32, 20, 19, UNUSED, UNUSED, 18, UNUSED, 7, + 10, 17, UNUSED, UNUSED, 14, UNUSED, 6, UNUSED, + UNUSED, 9, UNUSED, 16, UNUSED, UNUSED, 1, 26, + UNUSED, 13, UNUSED, UNUSED, 24, 5, UNUSED, UNUSED, + UNUSED, 21, UNUSED, 8, 11, UNUSED, 15, UNUSED, + UNUSED, UNUSED, UNUSED, 2, 27, 0, 25, UNUSED, + 22, UNUSED, 12, UNUSED, UNUSED, 3, 28, UNUSED, + 23, UNUSED, 4, 29, UNUSED, UNUSED, 30, 31 +}; +#undef UNUSED + +/* round関数(C89で用意されていない) */ +double SRLAUtility_Round(double d) +{ + return (d >= 0.0) ? floor(d + 0.5) : -floor(-d + 0.5); +} + +/* log2関数(C89で定義されていない) */ +double SRLAUtility_Log2(double d) +{ +#define INV_LOGE2 (1.4426950408889634) /* 1 / log(2) */ + return log(d) * INV_LOGE2; +#undef INV_LOGE2 +} + +/* フレッチャーのチェックサム計算 */ +uint16_t SRLAUtility_CalculateFletcher16CheckSum(const uint8_t *data, size_t data_size) +{ +#define MAX_BLOCK_SIZE 5802 /* c1のmodが変化しないブロックサイズ */ +#define MOD255(x) (((x) + ((x) / 255)) & 0xFF) /* 255の剰余計算 */ + uint32_t c0, c1; + + /* 引数チェック */ + SRLA_ASSERT(data != NULL); + + c0 = c1 = 0; + while (data_size > 0) { + size_t block_size = SRLAUTILITY_MIN(MAX_BLOCK_SIZE, data_size); + data_size -= block_size; + while (block_size--) { + c0 += *data++; + c1 += c0; + } + c0 = MOD255(c0); + c1 = MOD255(c1); + } + + return (uint16_t)((c1 << 8) | c0); +#undef MOD255 +#undef MAX_BLOCK_SIZE +} + +/* NLZ(最上位ビットから1に当たるまでのビット数)の計算 */ +uint32_t SRLAUtility_NLZSoft(uint32_t x) +{ + /* ハッカーのたのしみ参照 */ + x = x | (x >> 1); + x = x | (x >> 2); + x = x | (x >> 4); + x = x | (x >> 8); + x = x & ~(x >> 16); + x = (x << 9) - x; + x = (x << 11) - x; + x = (x << 14) - x; + return st_nlz10_table[x >> 26]; +} + +/* 2の冪乗数に切り上げる */ +uint32_t SRLAUtility_RoundUp2PoweredSoft(uint32_t val) +{ + /* ハッカーのたのしみ参照 */ + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + return val + 1; +} + +/* LR -> MS (in-place) */ +void SRLAUtility_LRtoMSConversion(int32_t **buffer, uint32_t num_samples) +{ + uint32_t smpl; + + SRLA_ASSERT(buffer != NULL); + SRLA_ASSERT(buffer[0] != NULL); + SRLA_ASSERT(buffer[1] != NULL); + + for (smpl = 0; smpl < num_samples; smpl++) { + buffer[1][smpl] -= buffer[0][smpl]; + buffer[0][smpl] += (buffer[1][smpl] >> 1); + } +} + +/* MS -> LR (in-place) */ +void SRLAUtility_MStoLRConversion(int32_t **buffer, uint32_t num_samples) +{ + uint32_t smpl; + + SRLA_ASSERT(buffer != NULL); + SRLA_ASSERT(buffer[0] != NULL); + SRLA_ASSERT(buffer[1] != NULL); + + for (smpl = 0; smpl < num_samples; smpl++) { + buffer[0][smpl] -= (buffer[1][smpl] >> 1); + buffer[1][smpl] += buffer[0][smpl]; + } +} + +/* LR -> LS (in-place) */ +void SRLAUtility_LRtoLSConversion(int32_t **buffer, uint32_t num_samples) +{ + uint32_t smpl; + + SRLA_ASSERT(buffer != NULL); + SRLA_ASSERT(buffer[0] != NULL); + SRLA_ASSERT(buffer[1] != NULL); + + for (smpl = 0; smpl < num_samples; smpl++) { + buffer[1][smpl] -= buffer[0][smpl]; + } +} + +/* LS -> LR (in-place) */ +void SRLAUtility_LStoLRConversion(int32_t **buffer, uint32_t num_samples) +{ + uint32_t smpl; + + SRLA_ASSERT(buffer != NULL); + SRLA_ASSERT(buffer[0] != NULL); + SRLA_ASSERT(buffer[1] != NULL); + + for (smpl = 0; smpl < num_samples; smpl++) { + buffer[1][smpl] += buffer[0][smpl]; + } +} + +/* LR -> RS (in-place) */ +void SRLAUtility_LRtoRSConversion(int32_t **buffer, uint32_t num_samples) +{ + uint32_t smpl; + + SRLA_ASSERT(buffer != NULL); + SRLA_ASSERT(buffer[0] != NULL); + SRLA_ASSERT(buffer[1] != NULL); + + for (smpl = 0; smpl < num_samples; smpl++) { + buffer[0][smpl] = buffer[1][smpl] - buffer[0][smpl]; + } +} + +/* RS -> LR (in-place) */ +void SRLAUtility_RStoLRConversion(int32_t **buffer, uint32_t num_samples) +{ + uint32_t smpl; + + SRLA_ASSERT(buffer != NULL); + SRLA_ASSERT(buffer[0] != NULL); + SRLA_ASSERT(buffer[1] != NULL); + + for (smpl = 0; smpl < num_samples; smpl++) { + buffer[0][smpl] = buffer[1][smpl] - buffer[0][smpl]; + } +} + +/* プリエンファシスフィルタ初期化 */ +void SRLAPreemphasisFilter_Initialize(struct SRLAPreemphasisFilter *preem) +{ + SRLA_ASSERT(preem != NULL); + preem->prev = 0; + preem->coef = 0; +} + +/* プリエンファシスフィルタ係数計算 */ +void SRLAPreemphasisFilter_CalculateCoefficient( + struct SRLAPreemphasisFilter *preem, const int32_t *buffer, uint32_t num_samples) +{ + uint32_t smpl; + int32_t coef; + double corr[2] = { 0.0, 0.0 }; + double curr; + + SRLA_ASSERT(preem != NULL); + SRLA_ASSERT(buffer != NULL); + + /* 相関の計算 */ + curr = buffer[0]; + for (smpl = 0; smpl < num_samples - 1; smpl++) { + const double succ = buffer[smpl + 1]; + corr[0] += curr * curr; + corr[1] += curr * succ; + curr = succ; + } + /* 分散(=0次相関)で正規化 */ + corr[1] /= corr[0]; + + /* 固定小数化 */ + if ((corr[0] < 1e-6) || (corr[1] < 0.0)) { + /* 1次相関が負の場合は振動しているためプリエンファシスの効果は薄い */ + coef = 0; + } else { + coef = (int32_t)SRLAUtility_Round(corr[1] * pow(2.0f, SRLA_PREEMPHASIS_COEF_SHIFT)); + /* 丸め込み */ + if (coef >= (1 << (SRLA_PREEMPHASIS_COEF_SHIFT - 1))) { + coef = (1 << (SRLA_PREEMPHASIS_COEF_SHIFT - 1)) - 1; + } + } + + preem->coef = coef; +} + +/* プリエンファシス */ +void SRLAPreemphasisFilter_Preemphasis( + struct SRLAPreemphasisFilter *preem, int32_t *buffer, uint32_t num_samples) +{ + uint32_t smpl; + int32_t prev, tmp; + + SRLA_ASSERT(buffer != NULL); + SRLA_ASSERT(preem != NULL); + + prev = preem->prev; + for (smpl = 0; smpl < num_samples; smpl++) { + tmp = buffer[smpl]; + buffer[smpl] -= (prev * preem->coef) >> SRLA_PREEMPHASIS_COEF_SHIFT; + prev = tmp; + } + preem->prev = prev; +} + +/* デエンファシスを複数回適用 */ +void SRLAPreemphasisFilter_MultiStageDeemphasis( + struct SRLAPreemphasisFilter *preem, uint32_t num_preem, int32_t *buffer, uint32_t num_samples) +{ + uint32_t smpl; + const int32_t c0 = preem[0].coef; + const int32_t c1 = preem[1].coef; + + /* 注意)現段階では2回を前提 */ + SRLA_STATIC_ASSERT(SRLA_NUM_PREEMPHASIS_FILTERS == 2); + SRLA_ASSERT(num_preem == 2); + + SRLA_ASSERT(buffer != NULL); + SRLA_ASSERT(preem != NULL); + + buffer[0] += (preem[1].prev * c1) >> SRLA_PREEMPHASIS_COEF_SHIFT; + buffer[1] += (buffer[0] * c1) >> SRLA_PREEMPHASIS_COEF_SHIFT; + buffer[0] += (preem[0].prev * c0) >> SRLA_PREEMPHASIS_COEF_SHIFT; + + for (smpl = 2; smpl < num_samples; smpl++) { + buffer[smpl] += (buffer[smpl - 1] * c1) >> SRLA_PREEMPHASIS_COEF_SHIFT; + buffer[smpl - 1] += (buffer[smpl - 2] * c0) >> SRLA_PREEMPHASIS_COEF_SHIFT; + } + + preem[0].prev = buffer[num_samples - 1]; + buffer[num_samples - 1] += (buffer[num_samples - 2] * c0) >> SRLA_PREEMPHASIS_COEF_SHIFT; + preem[1].prev = buffer[num_samples - 1]; +} diff --git a/libs/wav/CMakeLists.txt b/libs/wav/CMakeLists.txt new file mode 100644 index 0000000..8905d6a --- /dev/null +++ b/libs/wav/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.15) + +# プロジェクト名 +project(Wav C) + +# ライブラリ名 +set(LIB_NAME wav) + +# 静的ライブラリ指定 +add_library(${LIB_NAME} STATIC) + +# ソースディレクトリ +add_subdirectory(src) + +# インクルードパス +target_include_directories(${LIB_NAME} + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ) + +# コンパイルオプション +if(MSVC) + target_compile_options(${LIB_NAME} PRIVATE /W4) +else() + target_compile_options(${LIB_NAME} PRIVATE -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=2 -Wconversion -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition) + set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +endif() +set_target_properties(${LIB_NAME} + PROPERTIES + C_STANDARD 90 C_EXTENSIONS OFF + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) diff --git a/libs/wav/include/wav.h b/libs/wav/include/wav.h new file mode 100644 index 0000000..06e922a --- /dev/null +++ b/libs/wav/include/wav.h @@ -0,0 +1,66 @@ +#ifndef WAV_INCLUDED +#define WAV_INCLUDED + +#include + +/* PCM型 - ファイルのビット深度如何によらず、メモリ上では全て符号付き32bitで取り扱う */ +typedef int32_t WAVPcmData; + +/* WAVデータのフォーマット */ +typedef enum WAVDataFormatTag { + WAV_DATA_FORMAT_PCM /* PCMのみ対応 */ +} WAVDataFormat; + +/* API結果型 */ +typedef enum WAVApiResultTag { + WAV_APIRESULT_OK = 0, + WAV_APIRESULT_NG, + WAV_APIRESULT_INVALID_FORMAT, /* フォーマットが不正 */ + WAV_APIRESULT_IOERROR, /* ファイル入出力エラー */ + WAV_APIRESULT_INVALID_PARAMETER /* 引数が不正 */ +} WAVApiResult; + +/* WAVファイルフォーマット */ +struct WAVFileFormat { + WAVDataFormat data_format; /* データフォーマット */ + uint32_t num_channels; /* チャンネル数 */ + uint32_t sampling_rate; /* サンプリングレート */ + uint32_t bits_per_sample; /* 量子化ビット数 */ + uint32_t num_samples; /* サンプル数 */ +}; + +/* WAVファイルハンドル */ +struct WAVFile { + struct WAVFileFormat format; /* フォーマット */ + WAVPcmData** data; /* 実データ */ +}; + +/* アクセサ */ +#define WAVFile_PCM(wavfile, samp, ch) (wavfile->data[(ch)][(samp)]) + +#ifdef __cplusplus +extern "C" { +#endif + +/* ファイルからWAVファイルハンドルを作成 */ +struct WAVFile* WAV_CreateFromFile(const char* filename); + +/* フォーマットを指定して新規にWAVファイルハンドルを作成 */ +struct WAVFile* WAV_Create(const struct WAVFileFormat* format); + +/* WAVファイルハンドルを破棄 */ +void WAV_Destroy(struct WAVFile* wavfile); + +/* ファイル書き出し */ +WAVApiResult WAV_WriteToFile( + const char* filename, const struct WAVFile* wavfile); + +/* ファイルからWAVファイルフォーマットだけ読み取り */ +WAVApiResult WAV_GetWAVFormatFromFile( + const char* filename, struct WAVFileFormat* format); + +#ifdef __cplusplus +} +#endif + +#endif /* WAV_INCLUDED */ diff --git a/libs/wav/src/CMakeLists.txt b/libs/wav/src/CMakeLists.txt new file mode 100644 index 0000000..4b6ac15 --- /dev/null +++ b/libs/wav/src/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/wav.c + ) diff --git a/libs/wav/src/wav.c b/libs/wav/src/wav.c new file mode 100644 index 0000000..da7ba8f --- /dev/null +++ b/libs/wav/src/wav.c @@ -0,0 +1,990 @@ +#include "wav.h" + +#include +#include +#include +#include + +/* パーサの読み込みバッファサイズ */ +#define WAVBITBUFFER_BUFFER_SIZE (10 * 1024) + +/* 下位n_bitsを取得 */ +/* 補足)((1 << n_bits) - 1)は下位の数値だけ取り出すマスクになる */ +#define WAV_GetLowerBits(n_bits, val) ((val) & (uint32_t)((1 << (n_bits)) - 1)) + +/* a,bの内の小さい値を取得 */ +#define WAV_Min(a, b) (((a) < (b)) ? (a) : (b)) + +/* 内部エラー型 */ +typedef enum WAVErrorTag { + WAV_ERROR_OK = 0, /* OK */ + WAV_ERROR_NG, /* 分類不能な失敗 */ + WAV_ERROR_IO, /* 入出力エラー */ + WAV_ERROR_INVALID_PARAMETER, /* 不正な引数 */ + WAV_ERROR_INVALID_FORMAT /* 不正なフォーマット */ +} WAVError; + +/* ビットバッファ */ +struct WAVBitBuffer { + uint8_t bytes[WAVBITBUFFER_BUFFER_SIZE]; /* ビットバッファ */ + uint32_t bit_count; /* ビット入力カウント */ + int32_t byte_pos; /* バイト列読み込み位置 */ +}; + +/* パーサ */ +struct WAVParser { + FILE* fp; /* 読み込みファイルポインタ */ + struct WAVBitBuffer buffer; /* ビットバッファ */ +}; + +/* ライタ */ +struct WAVWriter { + FILE* fp; /* 書き込みファイルポインタ */ + uint32_t bit_buffer; /* 出力途中のビット */ + uint32_t bit_count; /* 出力カウント */ + struct WAVBitBuffer buffer; /* ビットバッファ */ +}; + +/* パーサの初期化 */ +static void WAVParser_Initialize(struct WAVParser* parser, FILE* fp); +/* パーサの使用終了 */ +static void WAVParser_Finalize(struct WAVParser* parser); +/* n_bit 取得し、結果を右詰めする */ +static WAVError WAVParser_GetBits(struct WAVParser* parser, uint32_t n_bits, uint64_t* bitsbuf); +/* シーク(fseek準拠) */ +static WAVError WAVParser_Seek(struct WAVParser* parser, int32_t offset, int32_t wherefrom); +/* ライタの初期化 */ +static void WAVWriter_Initialize(struct WAVWriter* writer, FILE* fp); +/* ライタの終了 */ +static void WAVWriter_Finalize(struct WAVWriter* writer); +/* valの下位n_bitを書き込む */ +static WAVError WAVWriter_PutBits(struct WAVWriter* writer, uint64_t val, uint32_t n_bits); +/* バッファにたまったビットをクリア */ +static WAVError WAVWriter_Flush(struct WAVWriter* writer); +/* リトルエンディアンでビットパターンを出力 */ +static WAVError WAVWriter_PutLittleEndianBytes( + struct WAVWriter* writer, uint32_t nbytes, uint64_t data); + +/* ライタを使用してファイルフォーマットに従ったヘッダ部を出力 */ +static WAVError WAVWriter_PutWAVHeader( + struct WAVWriter* writer, const struct WAVFileFormat* format); +/* ライタを使用してPCMデータ出力 */ +static WAVError WAVWriter_PutWAVPcmData( + struct WAVWriter* writer, const struct WAVFile* wavfile); + +/* リトルエンディアンでビットパターンを取得 */ +static WAVError WAVParser_GetLittleEndianBytes( + struct WAVParser* parser, uint32_t nbytes, uint64_t* bitsbuf); +/* パーサを使用して文字列取得 */ +static WAVError WAVParser_GetString( + struct WAVParser* parser, char* string_buffer, uint32_t string_length); +/* パーサを使用して文字列取得/一致チェック */ +static WAVError WAVParser_CheckSignatureString( + struct WAVParser* parser, const char* signature, uint32_t signature_length); +/* パーサを使用してファイルフォーマットを読み取り */ +static WAVError WAVParser_GetWAVFormat( + struct WAVParser* parser, struct WAVFileFormat* format); +/* パーサを使用してPCMデータを読み取り */ +static WAVError WAVParser_GetWAVPcmData( + struct WAVParser* parser, struct WAVFile* wavfile); + +/* 8bitPCM形式を32bit形式に変換 */ +static int32_t WAV_Convert8bitPCMto32bitPCM(int32_t in_8bitpcm); +/* 16bitPCM形式を32bit形式に変換 */ +static int32_t WAV_Convert16bitPCMto32bitPCM(int32_t in_16bitpcm); +/* 24bitPCM形式を32bit形式に変換 */ +static int32_t WAV_Convert24bitPCMto32bitPCM(int32_t in_24bitpcm); +/* 32bitPCM形式を32bit形式に変換 */ +static int32_t WAV_Convert32bitPCMto32bitPCM(int32_t in_32bitpcm); + +/* 32bitPCM形式を32bit形式に変換 */ +static int32_t WAV_Convert32bitPCMto32bitPCM(int32_t in_32bitpcm); + +/* パーサを使用してファイルフォーマットを読み取り */ +static WAVError WAVParser_GetWAVFormat( + struct WAVParser* parser, struct WAVFileFormat* format) +{ + uint64_t bitsbuf; + int32_t fmt_chunk_size; + struct WAVFileFormat tmp_format; + + /* 引数チェック */ + if (parser == NULL || format == NULL) { + return WAV_ERROR_INVALID_PARAMETER; + } + + /* ヘッダ 'R', 'I', 'F', 'F' をチェック */ + if (WAVParser_CheckSignatureString(parser, "RIFF", 4) != WAV_ERROR_OK) { + return WAV_ERROR_INVALID_FORMAT; + } + + /* ファイルサイズ-8(読み飛ばし) */ + if (WAVParser_GetLittleEndianBytes(parser, 4, &bitsbuf) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + + /* ヘッダ 'W', 'A', 'V', 'E' をチェック */ + if (WAVParser_CheckSignatureString(parser, "WAVE", 4) != WAV_ERROR_OK) { + return WAV_ERROR_INVALID_FORMAT; + } + + /* fmtチャンクのヘッダ 'f', 'm', 't', ' ' をチェック */ + if (WAVParser_CheckSignatureString(parser, "fmt ", 4) != WAV_ERROR_OK) { + return WAV_ERROR_INVALID_FORMAT; + } + + /* fmtチャンクのバイト数を取得 + * 補足/注意)16より大きいサイズのfmtチャンクの内容(拡張)は読み飛ばす */ + if (WAVParser_GetLittleEndianBytes(parser, 4, &bitsbuf) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + fmt_chunk_size = (int32_t)bitsbuf; + + /* フォーマットIDをチェック + * 補足)1(リニアPCM)以外対応していない */ + if (WAVParser_GetLittleEndianBytes(parser, 2, &bitsbuf) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + if (bitsbuf != 1) { + /* fprintf(stderr, "Unsupported format: fmt chunk format ID \n"); */ + return WAV_ERROR_INVALID_FORMAT; + } + tmp_format.data_format = WAV_DATA_FORMAT_PCM; + + /* チャンネル数 */ + if (WAVParser_GetLittleEndianBytes(parser, 2, &bitsbuf) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + tmp_format.num_channels = (uint32_t)bitsbuf; + + /* サンプリングレート */ + if (WAVParser_GetLittleEndianBytes(parser, 4, &bitsbuf) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + tmp_format.sampling_rate =(uint32_t) bitsbuf; + + /* データ速度(byte/sec)は読み飛ばし */ + if (WAVParser_GetLittleEndianBytes(parser, 4, &bitsbuf) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + + /* ブロックあたりサイズ数は読み飛ばし */ + if (WAVParser_GetLittleEndianBytes(parser, 2, &bitsbuf) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + + /* 量子化ビット数(サンプルあたりのビット数) */ + if (WAVParser_GetLittleEndianBytes(parser, 2, &bitsbuf) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + tmp_format.bits_per_sample = (uint32_t)bitsbuf; + + /* 拡張部分の読み取りには未対応: 読み飛ばしを行う */ + if (fmt_chunk_size > 16) { + fprintf(stderr, "Warning: skip fmt chunk extention (unsupported). \n"); + if (WAVParser_Seek(parser, fmt_chunk_size - 16, SEEK_CUR) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + } + + /* チャンク読み取り */ + while (1) { + char string_buf[4]; + /* チャンク文字列取得 */ + if (WAVParser_GetString(parser, string_buf, 4) != WAV_ERROR_OK) { + return WAV_ERROR_IO; + } + if (strncmp(string_buf, "data", 4) == 0) { + /* データチャンクを見つけたら終わり */ + break; + } else { + /* 他のチャンクはサイズだけ取得してシークにより読み飛ばす */ + if (WAVParser_GetLittleEndianBytes(parser, 4, &bitsbuf) != WAV_ERROR_OK) { + return WAV_ERROR_IO; + } + /* printf("chunk:%s size:%d \n", string_buf, (int32_t)bitsbuf); */ + WAVParser_Seek(parser, (int32_t)bitsbuf, SEEK_CUR); + } + } + + /* サンプル数: 波形データバイト数から算出 */ + if (WAVParser_GetLittleEndianBytes(parser, 4, &bitsbuf) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + tmp_format.num_samples = (uint32_t)bitsbuf; + assert(tmp_format.num_samples % ((tmp_format.bits_per_sample / 8) * tmp_format.num_channels) == 0); + tmp_format.num_samples /= ((tmp_format.bits_per_sample / 8) * tmp_format.num_channels); + + /* 構造体コピー */ + *format = tmp_format; + + return WAV_ERROR_OK; +} + +/* パーサを使用してPCMデータを読み取り */ +static WAVError WAVParser_GetWAVPcmData( + struct WAVParser* parser, struct WAVFile* wavfile) +{ + uint32_t ch, sample, bytes_per_sample; + uint64_t bitsbuf; + int32_t (*convert_to_sint32_func)(int32_t); + + /* 引数チェック */ + if (parser == NULL || wavfile == NULL) { + return WAV_ERROR_INVALID_PARAMETER; + } + + /* ビット深度に合わせてPCMデータの変換関数を決定 */ + switch (wavfile->format.bits_per_sample) { + case 8: + convert_to_sint32_func = WAV_Convert8bitPCMto32bitPCM; + break; + case 16: + convert_to_sint32_func = WAV_Convert16bitPCMto32bitPCM; + break; + case 24: + convert_to_sint32_func = WAV_Convert24bitPCMto32bitPCM; + break; + case 32: + convert_to_sint32_func = WAV_Convert32bitPCMto32bitPCM; + break; + default: + /* fprintf(stderr, "Unsupported bits per sample format(=%d). \n", wavfile->format.bits_per_sample); */ + return WAV_ERROR_INVALID_FORMAT; + } + + /* データ読み取り */ + bytes_per_sample = wavfile->format.bits_per_sample / 8; + for (sample = 0; sample < wavfile->format.num_samples; sample++) { + for (ch = 0; ch < wavfile->format.num_channels; ch++) { + if (WAVParser_GetLittleEndianBytes(parser, bytes_per_sample, &bitsbuf) != WAV_ERROR_OK) { + return WAV_ERROR_IO; + } + /* 32bit整数形式に変形してデータにセット */ + wavfile->data[ch][sample] = convert_to_sint32_func((int32_t)(bitsbuf)); + } + } + + return WAV_ERROR_OK; +} + +/* ファイルからWAVファイルフォーマットだけ読み取り */ +WAVApiResult WAV_GetWAVFormatFromFile( + const char* filename, struct WAVFileFormat* format) +{ + struct WAVParser parser; + FILE* fp; + + /* 引数チェック */ + if (filename == NULL || format == NULL) { + return WAV_APIRESULT_NG; + } + + /* wavファイルを開く */ + fp = fopen(filename, "rb"); + if (fp == NULL) { + /* fprintf(stderr, "Failed to open %s. \n", filename); */ + return WAV_APIRESULT_NG; + } + + /* パーサ初期化 */ + WAVParser_Initialize(&parser, fp); + + /* ヘッダ読み取り */ + if (WAVParser_GetWAVFormat(&parser, format) != WAV_ERROR_OK) { + return WAV_APIRESULT_NG; + } + + /* パーサ使用終了 */ + WAVParser_Finalize(&parser); + + /* ファイルを閉じる */ + fclose(fp); + + return WAV_APIRESULT_OK; +} + +/* ファイルからWAVファイルハンドルを作成 */ +struct WAVFile* WAV_CreateFromFile(const char* filename) +{ + struct WAVParser parser; + FILE* fp; + struct WAVFile* wavfile; + struct WAVFileFormat format; + + /* 引数チェック */ + if (filename == NULL) { + return NULL; + } + + /* wavファイルを開く */ + fp = fopen(filename, "rb"); + if (fp == NULL) { + /* fprintf(stderr, "Failed to open %s. \n", filename); */ + return NULL; + } + + /* パーサ初期化 */ + WAVParser_Initialize(&parser, fp); + + /* ヘッダ読み取り */ + if (WAVParser_GetWAVFormat(&parser, &format) != WAV_ERROR_OK) { + return NULL; + } + + /* ハンドル作成 */ + wavfile = WAV_Create(&format); + if (wavfile == NULL) { + return NULL; + } + + /* PCMデータ読み取り */ + if (WAVParser_GetWAVPcmData(&parser, wavfile) != WAV_ERROR_OK) { + goto EXIT_FAILURE_WITH_DATA_RELEASE; + } + + /* パーサ終了 */ + WAVParser_Finalize(&parser); + + /* ファイルを閉じる */ + fclose(fp); + + /* 正常終了 */ + return wavfile; + + /* ハンドルが確保したデータを全て解放して終了 */ +EXIT_FAILURE_WITH_DATA_RELEASE: + WAV_Destroy(wavfile); + WAVParser_Finalize(&parser); + fclose(fp); + return NULL; +} + +/* フォーマットを指定して新規にWAVファイルハンドルを作成 */ +struct WAVFile* WAV_Create(const struct WAVFileFormat* format) +{ + uint32_t ch; + struct WAVFile* wavfile; + + /* 引数チェック */ + if (format == NULL) { + return NULL; + } + + /* 現在はPCMフォーマット以外対応していない */ + if (format->data_format != WAV_DATA_FORMAT_PCM) { + /* fprintf(stderr, "Unsupported wav data format. \n"); */ + return NULL; + } + + /* ハンドル作成 */ + wavfile = (struct WAVFile *)malloc(sizeof(struct WAVFile)); + if (wavfile == NULL) { + goto EXIT_FAILURE_WITH_DATA_RELEASE; + } + + /* 構造体コピーによりフォーマット情報取得 */ + wavfile->format = (*format); + + /* データ領域の割り当て */ + wavfile->data = (WAVPcmData **)malloc(sizeof(WAVPcmData *) * format->num_channels); + if (wavfile->data == NULL) { + goto EXIT_FAILURE_WITH_DATA_RELEASE; + } + for (ch = 0; ch < format->num_channels; ch++) { + wavfile->data[ch] = (WAVPcmData *)calloc(format->num_samples, sizeof(WAVPcmData)); + if (wavfile->data[ch] == NULL) { + goto EXIT_FAILURE_WITH_DATA_RELEASE; + } + } + + return wavfile; + +EXIT_FAILURE_WITH_DATA_RELEASE: + WAV_Destroy(wavfile); + return NULL; +} + +/* 8bitPCM形式を32bit形式に変換 */ +static int32_t WAV_Convert8bitPCMto32bitPCM(int32_t in_8bitpcm) +{ + /* 無音に相当する128を引いてから32bit整数に切り上げる */ + return (in_8bitpcm - 128) << 24; +} + +/* 16bitPCM形式を32bit形式に変換 */ +static int32_t WAV_Convert16bitPCMto32bitPCM(int32_t in_16bitpcm) +{ + /* そのまま16bit左シフト */ + return in_16bitpcm << 16; +} + +/* 24bitPCM形式を32bit形式に変換 */ +static int32_t WAV_Convert24bitPCMto32bitPCM(int32_t in_24bitpcm) +{ + /* そのまま8bit左シフト */ + return in_24bitpcm << 8; +} + +/* 32bitPCM形式を32bit形式に変換 */ +static int32_t WAV_Convert32bitPCMto32bitPCM(int32_t in_32bitpcm) +{ + /* 何もしない */ + return in_32bitpcm; +} + +/* パーサの初期化 */ +static void WAVParser_Initialize(struct WAVParser* parser, FILE* fp) +{ + parser->fp = fp; + memset(&parser->buffer, 0, sizeof(struct WAVBitBuffer)); + parser->buffer.byte_pos = -1; +} + +/* パーサの使用終了 */ +static void WAVParser_Finalize(struct WAVParser* parser) +{ + parser->fp = NULL; + memset(&parser->buffer, 0, sizeof(struct WAVBitBuffer)); + parser->buffer.byte_pos = -1; +} + +/* n_bit 取得し、結果を右詰めする */ +static WAVError WAVParser_GetBits(struct WAVParser* parser, uint32_t n_bits, uint64_t* bitsbuf) +{ + uint64_t tmp; + struct WAVBitBuffer *buf = &(parser->buffer); + + /* 引数チェック */ + if (parser == NULL || bitsbuf == NULL || n_bits > 64) { + return WAV_ERROR_INVALID_PARAMETER; + } + + /* 初回読み込み */ + if (buf->byte_pos == -1) { + if (fread(buf->bytes, sizeof(uint8_t), WAVBITBUFFER_BUFFER_SIZE, parser->fp) == 0) { + return WAV_ERROR_IO; + } + buf->byte_pos = 0; + buf->bit_count = 8; + } + + /* 最上位ビットからデータを埋めていく + * 初回ループではtmpの上位ビットにセット + * 2回目以降は8bit単位で入力しtmpにセット */ + tmp = 0; + while (n_bits > buf->bit_count) { + /* 上位bitから埋めていく */ + n_bits -= buf->bit_count; + tmp |= (uint64_t)WAV_GetLowerBits(buf->bit_count, buf->bytes[buf->byte_pos]) << n_bits; + + /* 1バイト読み進める */ + buf->byte_pos++; + buf->bit_count = 8; + + /* バッファが一杯ならば、再度読み込み */ + if (buf->byte_pos == WAVBITBUFFER_BUFFER_SIZE) { + if (fread(buf->bytes, sizeof(uint8_t), WAVBITBUFFER_BUFFER_SIZE, parser->fp) == 0) { + return WAV_ERROR_IO; + } + buf->byte_pos = 0; + } + } + + /* 端数ビットの処理 + * 残ったビット分をtmpの最上位ビットにセット */ + buf->bit_count -= n_bits; + tmp |= (uint64_t)WAV_GetLowerBits(n_bits, (uint32_t)(buf->bytes[buf->byte_pos] >> buf->bit_count)); + + *bitsbuf = tmp; + return WAV_ERROR_OK; +} + +/* シーク(fseek準拠) */ +static WAVError WAVParser_Seek(struct WAVParser* parser, int32_t offset, int32_t wherefrom) +{ + if (parser->buffer.byte_pos != -1) { + /* バッファに取り込んだ分先読みしているので戻す */ + offset -= (WAVBITBUFFER_BUFFER_SIZE - (parser->buffer.byte_pos + 1)); + } + /* 移動 */ + fseek(parser->fp, offset, wherefrom); + /* バッファをクリア */ + parser->buffer.byte_pos = -1; + + return WAV_ERROR_OK; +} + +/* WAVファイルハンドルを破棄 */ +void WAV_Destroy(struct WAVFile* wavfile) +{ + uint32_t ch; + + /* NULLチェックして解放 */ +#define NULLCHECK_AND_FREE(ptr) { \ + if ((ptr) != NULL) { \ + free(ptr); \ + ptr = NULL; \ + } \ +} + + if (wavfile != NULL) { + for (ch = 0; ch < wavfile->format.num_channels; ch++) { + NULLCHECK_AND_FREE(wavfile->data[ch]); + } + NULLCHECK_AND_FREE(wavfile->data); + free(wavfile); + } + +#undef NULLCHECK_AND_FREE +} + +/* ライタを使用してファイルフォーマットに従ったヘッダ部を出力 */ +static WAVError WAVWriter_PutWAVHeader( + struct WAVWriter* writer, const struct WAVFileFormat* format) +{ + uint32_t filesize, pcm_data_size; + + /* 引数チェック */ + if (writer == NULL || format == NULL) { + return WAV_ERROR_INVALID_PARAMETER; + } + + /* フォーマットチェック */ + /* PCM以外は対応していない */ + if (format->data_format != WAV_DATA_FORMAT_PCM) { + return WAV_ERROR_INVALID_FORMAT; + } + + /* PCM データサイズ */ + pcm_data_size + = format->num_samples * (format->bits_per_sample / 8) * format->num_channels; + + /* ファイルサイズ */ + /* 44は"RIFF" から ("data"のサイズ) までのフィールドのバイト数(拡張部分を一切含まない) */ + filesize = pcm_data_size + 44; + + /* ヘッダ 'R', 'I', 'F', 'F' を出力 */ + if (WAVWriter_PutBits(writer, 'R', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, 'I', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, 'F', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, 'F', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + + /* ファイルサイズ-8(この要素以降のサイズ) */ + if (WAVWriter_PutLittleEndianBytes(writer, 4, filesize - 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + + /* ヘッダ 'W', 'A', 'V', 'E' を出力 */ + if (WAVWriter_PutBits(writer, 'W', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, 'A', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, 'V', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, 'E', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + + /* fmtチャンクのヘッダ 'f', 'm', 't', ' ' を出力 */ + if (WAVWriter_PutBits(writer, 'f', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, 'm', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, 't', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, ' ', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + + /* fmtチャンクのバイト数を出力 (補足)現在は16byte決め打ち */ + if (WAVWriter_PutLittleEndianBytes(writer, 4, 16) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + + /* フォーマットIDを出力 (補足)現在は1(リニアPCM)決め打ち */ + if (WAVWriter_PutLittleEndianBytes(writer, 2, 1) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + + /* チャンネル数 */ + if (WAVWriter_PutLittleEndianBytes(writer, 2, format->num_channels) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + + /* サンプリングレート */ + if (WAVWriter_PutLittleEndianBytes(writer, 4, format->sampling_rate) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + + /* データ速度(byte/sec) */ + if (WAVWriter_PutLittleEndianBytes(writer, 4, + format->sampling_rate * (format->bits_per_sample / 8) * format->num_channels) + != WAV_ERROR_OK) { return WAV_ERROR_IO; } + + /* ブロックあたりサイズ数 */ + if (WAVWriter_PutLittleEndianBytes(writer, 2, + (format->bits_per_sample / 8) * format->num_channels) + != WAV_ERROR_OK) { return WAV_ERROR_IO; } + + /* 量子化ビット数(サンプルあたりのビット数) */ + if (WAVWriter_PutLittleEndianBytes(writer, 2, format->bits_per_sample) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + + /* "data" チャンクのヘッダ出力 */ + if (WAVWriter_PutBits(writer, 'd', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, 'a', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, 't', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + if (WAVWriter_PutBits(writer, 'a', 8) != WAV_ERROR_OK) { return WAV_ERROR_IO; }; + + /* 波形データバイト数 */ + if (WAVWriter_PutLittleEndianBytes(writer, 4, pcm_data_size) != WAV_ERROR_OK) { return WAV_ERROR_IO; } + + return WAV_ERROR_OK; +} + +/* リトルエンディアンで書き出し +* 注意)dataはスワップされる可能性がある */ +static size_t WAVWrite_FWriteLittleEndian( + void *data, size_t size, size_t ndata, FILE *fp) +{ + int x = 1; + uint8_t *buffer; + uint32_t i; + + /* リトルエンディアン環境ではそのままfwrite */ + if ((size == 1) || (*((char *)&x) == 1)) { + return fwrite(data, size, ndata, fp); + } + + /* ビッグエンディアン環境では並び替えてから書き込む */ + buffer = (uint8_t *)data; + + switch (size) { + case 2: + for (i = 0; i < ndata; i++) { + uint8_t a = buffer[2 * i]; + buffer[2 * i + 0] = buffer[2 * i + 1]; + buffer[2 * i + 1] = a; + } + break; + case 3: + for (i = 0; i < ndata; i++) { + uint8_t a = buffer[3 * i]; + buffer[3 * i + 0] = buffer[3 * i + 2]; + buffer[3 * i + 2] = a; + } + break; + case 4: + for (i = 0; i < ndata; i++) { + uint8_t a = buffer[4 * i]; + uint8_t b = buffer[4 * i + 1]; + buffer[4 * i + 0] = buffer[4 * i + 3]; + buffer[4 * i + 1] = buffer[4 * i + 2]; + buffer[4 * i + 2] = b; + buffer[4 * i + 3] = a; + } + break; + default: + return 0; + } + + return fwrite(data, size, ndata, fp); +} + +/* ライタを使用してPCMデータ出力 */ +static WAVError WAVWriter_PutWAVPcmData( + struct WAVWriter* writer, const struct WAVFile* wavfile) +{ + uint32_t ch, smpl, progress; + + /* バッファは空に */ + WAVWriter_Flush(writer); + + /* チャンネルインターリーブしながら書き出し */ + switch (wavfile->format.bits_per_sample) { + case 8: + { + uint8_t *buffer; + const uint32_t num_output_smpls_per_buffer = WAVBITBUFFER_BUFFER_SIZE / (sizeof(uint8_t) * wavfile->format.num_channels); + progress = 0; + while (progress < wavfile->format.num_samples) { + const uint32_t num_process_smpls = WAV_Min(num_output_smpls_per_buffer, wavfile->format.num_samples - progress); + const uint32_t num_output_smpls = num_process_smpls * wavfile->format.num_channels; + buffer = (uint8_t *)writer->buffer.bytes; + for (smpl = 0; smpl < num_process_smpls; smpl++) { + for (ch = 0; ch < wavfile->format.num_channels; ch++) { + (*buffer++) = (uint8_t)(((WAVFile_PCM(wavfile, progress + smpl, ch) >> 24) + 128) & 0xFF); + } + } + if (WAVWrite_FWriteLittleEndian(writer->buffer.bytes, + sizeof(uint8_t), num_output_smpls, writer->fp) < num_output_smpls) { + return WAV_ERROR_IO; + } + progress += num_process_smpls; + } + } + break; + case 16: + { + int16_t *buffer; + const uint32_t num_output_smpls_per_buffer = WAVBITBUFFER_BUFFER_SIZE / (sizeof(int16_t) * wavfile->format.num_channels); + progress = 0; + while (progress < wavfile->format.num_samples) { + const uint32_t num_process_smpls = WAV_Min(num_output_smpls_per_buffer, wavfile->format.num_samples - progress); + const uint32_t num_output_smpls = num_process_smpls * wavfile->format.num_channels; + buffer = (int16_t *)writer->buffer.bytes; + for (smpl = 0; smpl < num_process_smpls; smpl++) { + for (ch = 0; ch < wavfile->format.num_channels; ch++) { + (*buffer++) = (int16_t)((WAVFile_PCM(wavfile, progress + smpl, ch) >> 16) & 0xFFFF); + } + } + if (WAVWrite_FWriteLittleEndian(writer->buffer.bytes, + sizeof(int16_t), num_output_smpls, writer->fp) < num_output_smpls) { + return WAV_ERROR_IO; + } + progress += num_process_smpls; + } + } + break; + case 24: + { + uint8_t *buffer; + const size_t int24_size = 3 * sizeof(uint8_t); + const uint32_t num_output_smpls_per_buffer = WAVBITBUFFER_BUFFER_SIZE / (int24_size * wavfile->format.num_channels); + progress = 0; + while (progress < wavfile->format.num_samples) { + const uint32_t num_process_smpls = WAV_Min(num_output_smpls_per_buffer, wavfile->format.num_samples - progress); + const uint32_t num_output_smpls = num_process_smpls * wavfile->format.num_channels; + const size_t output_size = num_output_smpls * int24_size; + buffer = (uint8_t *)writer->buffer.bytes; + for (smpl = 0; smpl < num_process_smpls; smpl++) { + for (ch = 0; ch < wavfile->format.num_channels; ch++) { + int32_t pcm = WAVFile_PCM(wavfile, progress + smpl, ch); + (*buffer++) = (uint8_t)((pcm >> 8) & 0xFF); + (*buffer++) = (uint8_t)((pcm >> 16) & 0xFF); + (*buffer++) = (uint8_t)((pcm >> 24) & 0xFF); + } + } + if (WAVWrite_FWriteLittleEndian(writer->buffer.bytes, + sizeof(uint8_t), output_size, writer->fp) < output_size) { + return WAV_ERROR_IO; + } + progress += num_process_smpls; + } + } + break; + case 32: + { + int32_t *buffer; + const uint32_t num_output_smpls_per_buffer = WAVBITBUFFER_BUFFER_SIZE / (sizeof(int32_t) * wavfile->format.num_channels); + progress = 0; + while (progress < wavfile->format.num_samples) { + const uint32_t num_process_smpls = WAV_Min(num_output_smpls_per_buffer, wavfile->format.num_samples - progress); + const uint32_t num_output_smpls = num_process_smpls * wavfile->format.num_channels; + buffer = (int32_t *)writer->buffer.bytes; + for (smpl = 0; smpl < num_process_smpls; smpl++) { + for (ch = 0; ch < wavfile->format.num_channels; ch++) { + (*buffer++) = WAVFile_PCM(wavfile, progress + smpl, ch); + } + } + if (WAVWrite_FWriteLittleEndian(writer->buffer.bytes, + sizeof(int32_t), num_output_smpls, writer->fp) < num_output_smpls) { + return WAV_ERROR_IO; + } + progress += num_process_smpls; + } + } + break; + default: + /* fprintf(stderr, "Unsupported bits per smpl format(=%d). \n", wavfile->format.bits_per_smpl); */ + return WAV_ERROR_INVALID_FORMAT; + } + + + return WAV_ERROR_OK; +} + +/* ファイル書き出し */ +WAVApiResult WAV_WriteToFile( + const char* filename, const struct WAVFile* wavfile) +{ + struct WAVWriter writer; + FILE* fp; + + /* 引数チェック */ + if (filename == NULL || wavfile == NULL) { + return WAV_APIRESULT_INVALID_PARAMETER; + } + + /* wavファイルを開く */ + fp = fopen(filename, "wb"); + if (fp == NULL) { + /* fprintf(stderr, "Failed to open %s. \n", filename); */ + return WAV_APIRESULT_NG; + } + + /* ライタ初期化 */ + WAVWriter_Initialize(&writer, fp); + + /* ヘッダ書き出し */ + if (WAVWriter_PutWAVHeader(&writer, &wavfile->format) != WAV_ERROR_OK) { + return WAV_APIRESULT_NG; + } + + /* データ書き出し */ + if (WAVWriter_PutWAVPcmData(&writer, wavfile) != WAV_ERROR_OK) { + return WAV_APIRESULT_NG; + } + + /* ライタ終了 */ + WAVWriter_Finalize(&writer); + + /* ファイルを閉じる */ + fclose(fp); + + /* 正常終了 */ + return WAV_APIRESULT_OK; +} + +/* ライタの初期化 */ +static void WAVWriter_Initialize(struct WAVWriter* writer, FILE* fp) +{ + writer->fp = fp; + writer->bit_count = 8; + writer->bit_buffer = 0; + memset(&writer->buffer, 0, sizeof(struct WAVBitBuffer)); + writer->buffer.byte_pos = 0; +} + +/* ライタの終了 */ +static void WAVWriter_Finalize(struct WAVWriter* writer) +{ + /* バッファに余っているデータを書き出し */ + WAVWriter_Flush(writer); + + /* メンバをクリア */ + writer->fp = NULL; + writer->bit_count = 8; + writer->bit_buffer = 0; + memset(&writer->buffer, 0, sizeof(struct WAVBitBuffer)); + writer->buffer.byte_pos = 0; +} + +/* valの下位n_bitを書き込む(ビッグエンディアンで) */ +static WAVError WAVWriter_PutBits(struct WAVWriter* writer, uint64_t val, uint32_t n_bits) +{ + /* 無効な引数 */ + if (writer == NULL) { + return WAV_ERROR_INVALID_PARAMETER; + } + + /* valの上位ビットから順次出力 + * 初回ループでは端数(出力に必要なビット数)分を埋め出力 + * 2回目以降は8bit単位で出力 */ + while (n_bits >= writer->bit_count) { + n_bits -= writer->bit_count; + writer->bit_buffer |= (uint8_t)WAV_GetLowerBits(writer->bit_count, val >> n_bits); + + /* バッファに追記 */ + writer->buffer.bytes[writer->buffer.byte_pos++] = (uint8_t)(writer->bit_buffer & 0xFF); + + /* バッファが一杯になったら書き出し */ + if (writer->buffer.byte_pos == WAVBITBUFFER_BUFFER_SIZE) { + if (fwrite(writer->buffer.bytes, + sizeof(uint8_t), WAVBITBUFFER_BUFFER_SIZE, + writer->fp) < WAVBITBUFFER_BUFFER_SIZE) { + return WAV_ERROR_IO; + } + /* 書き込み位置をリセット */ + writer->buffer.byte_pos = 0; + } + + writer->bit_buffer = 0; + writer->bit_count = 8; + } + + /* 端数ビットの処理: + * 残った分をバッファの上位ビットにセット */ + writer->bit_count -= n_bits; + writer->bit_buffer |= (uint8_t)(WAV_GetLowerBits(n_bits, (uint32_t)val) << writer->bit_count); + + return WAV_ERROR_OK; +} + +/* リトルエンディアンでビットパターンを出力 */ +static WAVError WAVWriter_PutLittleEndianBytes( + struct WAVWriter* writer, uint32_t nbytes, uint64_t data) +{ + uint64_t out; + uint32_t i_byte; + + /* リトルエンディアンに並び替え */ + out = 0; + for (i_byte = 0; i_byte < nbytes; i_byte++) { + out |= ((data >> (8 * (nbytes - i_byte - 1))) & 0xFFUL) << (8 * i_byte); + } + + /* 出力 */ + if (WAVWriter_PutBits(writer, out, (uint8_t)(nbytes * 8)) != WAV_ERROR_OK) { + return WAV_ERROR_IO; + } + + return WAV_ERROR_OK; +} + +/* バッファにたまったビットをクリア */ +static WAVError WAVWriter_Flush(struct WAVWriter* writer) +{ + /* 引数チェック */ + if (writer == NULL) { + return WAV_ERROR_INVALID_PARAMETER; + } + + /* 余ったビットを強制出力 */ + if (writer->bit_count != 8) { + if (WAVWriter_PutBits(writer, 0, (uint8_t)writer->bit_count) != WAV_ERROR_OK) { + return WAV_ERROR_IO; + } + writer->bit_buffer = 0; + writer->bit_count = 8; + } + + /* バッファに残っているデータをフラッシュ */ + if (fwrite(writer->buffer.bytes, + sizeof(uint8_t), (uint32_t)writer->buffer.byte_pos, + writer->fp) < (size_t)writer->buffer.byte_pos) { + return WAV_ERROR_IO; + } + /* バッファ残量は0に */ + writer->buffer.byte_pos = 0; + + return WAV_ERROR_OK; +} + +/* リトルエンディアンでビットパターンを取得 */ +static WAVError WAVParser_GetLittleEndianBytes( + struct WAVParser* parser, uint32_t nbytes, uint64_t* bitsbuf) +{ + uint64_t tmp, ret; + uint32_t i_byte; + + /* ビッグエンディアンで取得 */ + if (WAVParser_GetBits(parser, nbytes * 8, &tmp) != WAV_ERROR_OK) { + return WAV_ERROR_IO; + } + + /* リトルエンディアンに並び替え */ + ret = 0; + for (i_byte = 0; i_byte < nbytes; i_byte++) { + ret |= ((tmp >> (8 * (nbytes - i_byte - 1))) & 0xFFUL) << (8 * i_byte); + } + *bitsbuf = ret; + + return WAV_ERROR_OK; +} + +/* パーサを使用して文字列取得 */ +static WAVError WAVParser_GetString( + struct WAVParser* parser, char* string_buffer, uint32_t string_length) +{ + uint32_t i_byte; + uint64_t bitsbuf; + + assert(parser != NULL && string_buffer != NULL); + + /* 文字列取得 */ + for (i_byte = 0; i_byte < string_length; i_byte++) { + /* 1文字取得 */ + if (WAVParser_GetBits(parser, 8, &bitsbuf) != WAV_ERROR_OK) { + return WAV_ERROR_IO; + } + string_buffer[i_byte] = (char)bitsbuf; + } + + return WAV_ERROR_OK; +} + +/* パーサを使用して文字列取得/一致チェック */ +static WAVError WAVParser_CheckSignatureString( + struct WAVParser* parser, const char* signature, uint32_t signature_length) +{ + uint32_t i_byte; + uint64_t bitsbuf; + + assert(parser != NULL && signature != NULL); + + /* 文字列取得/検査 */ + for (i_byte = 0; i_byte < signature_length; i_byte++) { + /* 1文字取得 */ + if (WAVParser_GetBits(parser, 8, &bitsbuf) != WAV_ERROR_OK) { + return WAV_ERROR_IO; + } + /* シグネチャ検査 */ + if (signature[i_byte] != (char)bitsbuf) { + /* fprintf(stderr, "Failed to check %s header signature. \n", signature); */ + return WAV_ERROR_INVALID_FORMAT; + } + } + + return WAV_ERROR_OK; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..0e985ad --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.15) + +add_subdirectory(bit_stream) +add_subdirectory(byte_array) +add_subdirectory(command_line_parser) +add_subdirectory(srla_internal) +add_subdirectory(srla_coder) +add_subdirectory(srla_encoder) +add_subdirectory(srla_decoder) +add_subdirectory(srla_encode_decode) +add_subdirectory(lpc) +add_subdirectory(wav) diff --git a/test/bit_stream/CMakeLists.txt b/test/bit_stream/CMakeLists.txt new file mode 100644 index 0000000..eff9884 --- /dev/null +++ b/test/bit_stream/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# テスト名 +set(TEST_NAME bit_stream_test) + +# 実行形式ファイル +add_executable(${TEST_NAME} main.cpp bit_stream_macro_test.cpp bit_stream_function_test.cpp) + +# インクルードディレクトリ +include_directories(${PROJECT_ROOT_PATH}/libs/bit_stream/include) + +# リンクするライブラリ +target_link_libraries(${TEST_NAME} gtest gtest_main) +if (NOT MSVC) +target_link_libraries(${TEST_NAME} pthread) +endif() + +# コンパイルオプション +set_target_properties(${TEST_NAME} + PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) + +add_test( + NAME bit_stream + COMMAND $ + ) + +# run with: ctest -L lib +set_property( + TEST bit_stream + PROPERTY LABELS lib bit_stream + ) diff --git a/test/bit_stream/bit_stream_common_test.cpp b/test/bit_stream/bit_stream_common_test.cpp new file mode 100644 index 0000000..a698627 --- /dev/null +++ b/test/bit_stream/bit_stream_common_test.cpp @@ -0,0 +1,228 @@ +#include +#include + +#include + +/* インスタンス作成破棄テスト */ +TEST(BitStreamTest, CreateDestroyTest) +{ + /* インスタンス作成・破棄 */ + { + struct BitStream strm; + uint8_t test_memory[] = {'A', 'I', 'K', 'A', 'T', 'S', 'U'}; + const uint32_t test_memory_size = sizeof(test_memory) / sizeof(test_memory[0]); + + /* 書きモードでインスタンス作成 */ + BitWriter_Open(&strm, test_memory, test_memory_size); + EXPECT_TRUE(strm.memory_image == test_memory); + EXPECT_EQ(test_memory_size, strm.memory_size); + EXPECT_TRUE(strm.memory_p == test_memory); + EXPECT_EQ(0, strm.bit_buffer); + EXPECT_EQ(32, strm.bit_count); + EXPECT_TRUE(!(strm.flags & BITSTREAM_FLAGS_MODE_READ)); + BitStream_Close(&strm); + + /* 読みモードでインスタンス作成 */ + BitReader_Open(&strm, test_memory, test_memory_size); + EXPECT_TRUE(strm.memory_image == test_memory); + EXPECT_EQ(test_memory_size, strm.memory_size); + EXPECT_TRUE(strm.memory_p == test_memory); + EXPECT_EQ(0, strm.bit_buffer); + EXPECT_EQ(0, strm.bit_count); + EXPECT_TRUE(strm.flags & BITSTREAM_FLAGS_MODE_READ); + BitStream_Close(&strm); + } +} + +/* PutBit関数テスト */ +TEST(BitStreamTest, PutGetTest) +{ + { + struct BitStream strm; + uint8_t bit_pattern[] = { 1, 1, 1, 1, 0, 0, 0, 0 }; + uint8_t memory_image[256]; + uint32_t bit_pattern_length = sizeof(bit_pattern) / sizeof(bit_pattern[0]); + uint32_t i, is_ok; + + /* 書き込んでみる */ + BitWriter_Open(&strm, memory_image, sizeof(memory_image)); + for (i = 0; i < bit_pattern_length; i++) { + BitWriter_PutBits(&strm, bit_pattern[i], 1); + } + BitStream_Close(&strm); + + /* 正しく書き込めているか? */ + BitReader_Open(&strm, memory_image, sizeof(memory_image)); + is_ok = 1; + for (i = 0; i < bit_pattern_length; i++) { + uint32_t buf; + BitReader_GetBits(&strm, &buf, 1); + if ((uint8_t)buf != bit_pattern[i]) { + is_ok = 0; + break; + } + } + EXPECT_EQ(1, is_ok); + BitStream_Close(&strm); + } + + /* PutBit関数テスト2 8bitパターンチェック */ + { + struct BitStream strm; + uint8_t memory_image[256]; + uint32_t i, is_ok, nbits; + + for (nbits = 1; nbits <= 8; nbits++) { + /* 書き込んでみる */ + BitWriter_Open(&strm, memory_image, sizeof(memory_image)); + for (i = 0; i < (1 << nbits); i++) { + BitWriter_PutBits(&strm, i, nbits); + } + BitStream_Close(&strm); + + /* 正しく書き込めているか? */ + BitReader_Open(&strm, memory_image, sizeof(memory_image)); + is_ok = 1; + for (i = 0; i < (1 << nbits); i++) { + uint32_t buf; + BitReader_GetBits(&strm, &buf, nbits); + if (buf != i) { + is_ok = 0; + break; + } + } + EXPECT_EQ(1, is_ok); + BitStream_Close(&strm); + } + + } + + /* Flushテスト */ + { + struct BitStream strm; + uint8_t memory_image[256] = { 0, }; + uint32_t bits; + + BitWriter_Open(&strm, memory_image, sizeof(memory_image)); + BitWriter_PutBits(&strm, 1, 1); + BitWriter_PutBits(&strm, 1, 1); + /* 2bitしか書いていないがフラッシュ */ + BitStream_Flush(&strm); + EXPECT_EQ(0, strm.bit_buffer); + EXPECT_EQ(32, strm.bit_count); + BitStream_Close(&strm); + + /* 1バイトで先頭2bitだけが立っているはず */ + BitReader_Open(&strm, memory_image, sizeof(memory_image)); + BitReader_GetBits(&strm, &bits, 8); + EXPECT_EQ(0xC0, bits); + EXPECT_EQ(24, strm.bit_count); + EXPECT_EQ(0xC0000000, strm.bit_buffer); + EXPECT_EQ(&memory_image[4], strm.memory_p); + BitStream_Flush(&strm); + EXPECT_EQ(0, strm.bit_count); + EXPECT_EQ(0, strm.bit_buffer); + EXPECT_EQ(&memory_image[1], strm.memory_p); + BitStream_Close(&strm); + } + +} + +/* seek, tellなどのストリーム操作系APIテスト */ +TEST(BitStreamTest, StreamOperationTest) +{ + /* Seek/Tellテスト */ + { + struct BitStream strm; + int32_t tell_result; + uint8_t test_memory[8]; + + /* テスト用に適当にデータ作成 */ + BitWriter_Open(&strm, test_memory, sizeof(test_memory)); + BitWriter_PutBits(&strm, 0xDEADBEAF, 32); + BitWriter_PutBits(&strm, 0xABADCAFE, 32); + BitStream_Tell(&strm, &tell_result); + EXPECT_EQ(8, tell_result); + BitStream_Close(&strm); + + /* ビットリーダを使ったseek & tellテスト */ + BitReader_Open(&strm, test_memory, sizeof(test_memory)); + BitStream_Seek(&strm, 0, BITSTREAM_SEEK_SET); + BitStream_Tell(&strm, &tell_result); + EXPECT_EQ(0, tell_result); + BitStream_Seek(&strm, 1, BITSTREAM_SEEK_CUR); + BitStream_Tell(&strm, &tell_result); + EXPECT_EQ(1, tell_result); + BitStream_Seek(&strm, 2, BITSTREAM_SEEK_CUR); + BitStream_Tell(&strm, &tell_result); + EXPECT_EQ(3, tell_result); + BitStream_Seek(&strm, 0, BITSTREAM_SEEK_END); + BitStream_Tell(&strm, &tell_result); + EXPECT_EQ(7, tell_result); + BitStream_Close(&strm); + + /* ビットライタを使ったseek & tellテスト */ + BitWriter_Open(&strm, test_memory, sizeof(test_memory)); + BitStream_Seek(&strm, 0, BITSTREAM_SEEK_SET); + BitStream_Tell(&strm, &tell_result); + EXPECT_EQ(0, tell_result); + BitStream_Seek(&strm, 1, BITSTREAM_SEEK_CUR); + BitStream_Tell(&strm, &tell_result); + EXPECT_EQ(1, tell_result); + BitStream_Seek(&strm, 2, BITSTREAM_SEEK_CUR); + BitStream_Tell(&strm, &tell_result); + EXPECT_EQ(3, tell_result); + BitStream_Seek(&strm, 0, BITSTREAM_SEEK_END); + BitStream_Tell(&strm, &tell_result); + EXPECT_EQ(7, tell_result); + BitStream_Close(&strm); + } +} + +/* ランレングス取得テスト */ +TEST(BitStreamTest, GetZeroRunLengthTest) +{ + { + struct BitStream strm; + uint8_t data[256] = { 0, }; + uint32_t test_length, run; + + for (test_length = 0; test_length <= 65; test_length++) { + /* ラン長だけ0を書き込み、1で止める */ + BitWriter_Open(&strm, data, sizeof(data)); + for (run = 0; run < test_length; run++) { + BitWriter_PutBits(&strm, 0, 1); + } + BitWriter_PutBits(&strm, 1, 1); + BitStream_Close(&strm); + + BitReader_Open(&strm, data, sizeof(data)); + BitReader_GetZeroRunLength(&strm, &run); + EXPECT_EQ(test_length, run); + } + + /* ラン長出力APIを使用 */ + for (test_length = 0; test_length <= 65; test_length++) { + BitWriter_Open(&strm, data, sizeof(data)); + BitWriter_PutZeroRun(&strm, test_length); + BitStream_Close(&strm); + + BitReader_Open(&strm, data, sizeof(data)); + BitReader_GetZeroRunLength(&strm, &run); + EXPECT_EQ(test_length, run); + } + + /* 連続したラン */ + BitWriter_Open(&strm, data, sizeof(data)); + for (test_length = 0; test_length <= 32; test_length++) { + BitWriter_PutZeroRun(&strm, test_length); + } + BitStream_Close(&strm); + BitReader_Open(&strm, data, sizeof(data)); + for (test_length = 0; test_length <= 32; test_length++) { + BitReader_GetZeroRunLength(&strm, &run); + EXPECT_EQ(test_length, run); + } + BitStream_Close(&strm); + } +} diff --git a/test/bit_stream/bit_stream_function_test.cpp b/test/bit_stream/bit_stream_function_test.cpp new file mode 100644 index 0000000..9bfd9a8 --- /dev/null +++ b/test/bit_stream/bit_stream_function_test.cpp @@ -0,0 +1,44 @@ +#include "bit_stream.h" + +/* マクロ定義を削除 */ +#undef BitReader_Open +#undef BitWriter_Open +#undef BitStream_Close +#undef BitStream_Seek +#undef BitStream_Tell +#undef BitWriter_PutBits +#undef BitWriter_PutZeroRun +#undef BitReader_GetBits +#undef BitReader_GetZeroRunLength +#undef BitStream_Flush + +/* マクロの代わりに関数宣言 */ +extern "C" { +void BitReader_Open(struct BitStream *stream, const uint8_t *memory, size_t size); +void BitWriter_Open(struct BitStream *stream, const uint8_t *memory, size_t size); +void BitStream_Close(struct BitStream *stream); +void BitStream_Seek(struct BitStream *stream, int32_t offset, int32_t origin); +void BitStream_Tell(struct BitStream *stream, int32_t *result); +void BitWriter_PutBits(struct BitStream *stream, uint32_t val, uint32_t nbits); +void BitWriter_PutZeroRun(struct BitStream *stream, uint32_t runlength); +void BitReader_GetBits(struct BitStream *stream, uint32_t *val, uint32_t nbits); +void BitReader_GetZeroRunLength(struct BitStream *stream, uint32_t *runlength); +void BitStream_Flush(struct BitStream *stream); +} + +/* 多重定義防止 */ +#define BitStream_NLZSoft BitStream_NLZSoftTestDummy +#define g_bitstream_lower_bits_mask g_bitstream_lower_bits_mask_test_dummy +#define g_bitstream_zerobit_runlength_table g_bitstream_zerobit_runlength_table_test_dummy + +/* テスト対象のモジュール */ +extern "C" { +/* 関数定義を使用 */ +#undef BITSTREAM_USE_MACROS +#include "../../libs/bit_stream/src/bit_stream.c" +} + +/* マクロと同じテストソースを使用 */ +#define BitStreamTest BitStreamFunctionTest +#include "bit_stream_common_test.cpp" +#undef BitStreamTest diff --git a/test/bit_stream/bit_stream_macro_test.cpp b/test/bit_stream/bit_stream_macro_test.cpp new file mode 100644 index 0000000..d38ce46 --- /dev/null +++ b/test/bit_stream/bit_stream_macro_test.cpp @@ -0,0 +1,6 @@ +/* テスト対象のモジュール */ +extern "C" { +#include "../../libs/bit_stream/src/bit_stream.c" +} + +#include "bit_stream_common_test.cpp" diff --git a/test/bit_stream/main.cpp b/test/bit_stream/main.cpp new file mode 100644 index 0000000..96d7200 --- /dev/null +++ b/test/bit_stream/main.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/byte_array/CMakeLists.txt b/test/byte_array/CMakeLists.txt new file mode 100644 index 0000000..8029e86 --- /dev/null +++ b/test/byte_array/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# テスト名 +set(TEST_NAME byte_array_test) + +# 実行形式ファイル +add_executable(${TEST_NAME} main.cpp) + +# インクルードディレクトリ +include_directories(${PROJECT_ROOT_PATH}/libs/byte_array/include) + +# リンクするライブラリ +target_link_libraries(${TEST_NAME} gtest gtest_main) +if (NOT MSVC) +target_link_libraries(${TEST_NAME} pthread) +endif() + +# コンパイルオプション +set_target_properties(${TEST_NAME} + PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) + +add_test( + NAME byte_array + COMMAND $ + ) + +# run with: ctest -L lib +set_property( + TEST byte_array + PROPERTY LABELS lib byte_array + ) diff --git a/test/byte_array/main.cpp b/test/byte_array/main.cpp new file mode 100644 index 0000000..0207c5e --- /dev/null +++ b/test/byte_array/main.cpp @@ -0,0 +1,360 @@ +#include +#include + +#include + +/* テスト対象のモジュール */ +extern "C" { +#include "byte_array.h" +} + +/* 読み書きテスト */ +TEST(ByteArrayTest, ReadWriteTest) +{ +#define TEST_SIZE (16 * 1024) + + /* 1バイト読み/書き */ + { + uint8_t *pos; + uint8_t array[TEST_SIZE], answer[TEST_SIZE]; + uint32_t i; + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE; i++) { + answer[i] = (uint8_t)i; + ByteArray_WriteUint8(pos, (uint8_t)i); + pos += 1; + } + + /* リファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(array, answer, sizeof(uint8_t) * TEST_SIZE)); + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE; i++) { + array[i] = ByteArray_ReadUint8(pos); + pos += 1; + } + + /* リファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(array, answer, sizeof(uint8_t) * TEST_SIZE)); + } + /* 同じことをGet/Putでやる */ + { + uint8_t *pos; + uint8_t array[TEST_SIZE], answer[TEST_SIZE]; + uint32_t i; + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE; i++) { + answer[i] = (uint8_t)i; + ByteArray_PutUint8(pos, (uint8_t)i); + } + + /* リファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(array, answer, sizeof(uint8_t) * TEST_SIZE)); + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE; i++) { + ByteArray_GetUint8(pos, &array[i]); + } + + /* リファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(array, answer, sizeof(uint8_t) * TEST_SIZE)); + } + + /* 2バイト読み/書き */ + { +#define TEST_SIZE_UINT16 (TEST_SIZE / sizeof(uint16_t)) + uint8_t *pos; + uint8_t array[TEST_SIZE]; + uint16_t test[TEST_SIZE_UINT16], answer[TEST_SIZE_UINT16]; + uint32_t i; + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT16; i++) { + answer[i] = (uint16_t)i; + ByteArray_WriteUint16LE(pos, (uint16_t)i); + pos += 2; + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT16; i++) { + test[i] = ByteArray_ReadUint16LE(pos); + pos += 2; + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE)); + + /* ビッグエンディアンでも */ + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT16; i++) { + answer[i] = (uint16_t)i; + ByteArray_WriteUint16BE(pos, (uint16_t)i); + pos += 2; + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT16; i++) { + test[i] = ByteArray_ReadUint16BE(pos); + pos += 2; + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE)); + +#undef TEST_SIZE_UINT16 + } + /* 同じことをGet/Putでやる */ + { +#define TEST_SIZE_UINT16 (TEST_SIZE / sizeof(uint16_t)) + uint8_t *pos; + uint8_t array[TEST_SIZE]; + uint16_t test[TEST_SIZE_UINT16], answer[TEST_SIZE_UINT16]; + uint32_t i; + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT16; i++) { + answer[i] = (uint16_t)i; + ByteArray_PutUint16LE(pos, (uint16_t)i); + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT16; i++) { + ByteArray_GetUint16LE(pos, &test[i]); + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE)); + + /* ビッグエンディアンでも */ + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT16; i++) { + answer[i] = (uint16_t)i; + ByteArray_PutUint16BE(pos, (uint16_t)i); + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT16; i++) { + ByteArray_GetUint16BE(pos, &test[i]); + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE)); + +#undef TEST_SIZE_UINT16 + } + + /* 3バイト読み/書き */ + { +#define TEST_SIZE_FOR24 1021 +#define TEST_SIZE_UINT24 ((TEST_SIZE_FOR24 * 4) / (sizeof(uint32_t) * 3)) + uint8_t *pos; + uint8_t array[TEST_SIZE_FOR24]; + uint32_t test[TEST_SIZE_UINT24], answer[TEST_SIZE_UINT24]; + uint32_t i; + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT24; i++) { + answer[i] = (uint32_t)i; + ByteArray_WriteUint24LE(pos, (uint32_t)i); + pos += 3; + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT24; i++) { + test[i] = ByteArray_ReadUint24LE(pos); + pos += 3; + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE_FOR24)); + + /* ビッグエンディアンでも */ + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT24; i++) { + answer[i] = (uint32_t)i; + ByteArray_WriteUint24BE(pos, (uint32_t)i); + pos += 3; + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT24; i++) { + test[i] = ByteArray_ReadUint24BE(pos); + pos += 3; + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE_FOR24)); + +#undef TEST_SIZE_UINT24 +#undef TEST_SIZE_FOR24 + } + /* 同じことをGet/Putでやる */ + { +#define TEST_SIZE_FOR24 1021 +#define TEST_SIZE_UINT24 ((TEST_SIZE_FOR24 * 4) / (sizeof(uint32_t) * 3)) + uint8_t *pos; + uint8_t array[TEST_SIZE]; + uint32_t test[TEST_SIZE_UINT24], answer[TEST_SIZE_UINT24]; + uint32_t i; + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT24; i++) { + answer[i] = (uint32_t)i; + ByteArray_PutUint24LE(pos, (uint32_t)i); + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT24; i++) { + ByteArray_GetUint24LE(pos, &test[i]); + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE_FOR24)); + + /* ビッグエンディアンでも */ + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT24; i++) { + answer[i] = (uint32_t)i; + ByteArray_PutUint24BE(pos, (uint32_t)i); + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT24; i++) { + ByteArray_GetUint24BE(pos, &test[i]); + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE_FOR24)); + +#undef TEST_SIZE_UINT24 +#undef TEST_SIZE_FOR24 + } + + /* 4バイト読み/書き */ + { +#define TEST_SIZE_UINT32 (TEST_SIZE / sizeof(uint32_t)) + uint8_t *pos; + uint8_t array[TEST_SIZE]; + uint32_t test[TEST_SIZE_UINT32], answer[TEST_SIZE_UINT32]; + uint32_t i; + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT32; i++) { + answer[i] = (uint32_t)i; + ByteArray_WriteUint32LE(pos, (uint32_t)i); + pos += 4; + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT32; i++) { + test[i] = ByteArray_ReadUint32LE(pos); + pos += 4; + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE)); + + /* ビッグエンディアンでも */ + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT32; i++) { + answer[i] = (uint32_t)i; + ByteArray_WriteUint32BE(pos, (uint32_t)i); + pos += 4; + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT32; i++) { + test[i] = ByteArray_ReadUint32BE(pos); + pos += 4; + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE)); + +#undef TEST_SIZE_UINT32 + } + + /* 同じことをGet/Putでやる */ + { +#define TEST_SIZE_UINT32 (TEST_SIZE / sizeof(uint32_t)) + uint8_t *pos; + uint8_t array[TEST_SIZE]; + uint32_t test[TEST_SIZE_UINT32], answer[TEST_SIZE_UINT32]; + uint32_t i; + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT32; i++) { + answer[i] = (uint32_t)i; + ByteArray_PutUint32LE(pos, (uint32_t)i); + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT32; i++) { + ByteArray_GetUint32LE(pos, &test[i]); + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE)); + + /* ビッグエンディアンでもやる */ + + /* 書き出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT32; i++) { + answer[i] = (uint32_t)i; + ByteArray_PutUint32BE(pos, (uint32_t)i); + } + + /* 読み出し */ + pos = array; + for (i = 0; i < TEST_SIZE_UINT32; i++) { + ByteArray_GetUint32BE(pos, &test[i]); + } + + /* 読み出した結果がリファレンスと一致するか? */ + EXPECT_EQ(0, memcmp(test, answer, sizeof(uint8_t) * TEST_SIZE)); + +#undef TEST_SIZE_UINT16 + } + +#undef TEST_SIZE +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/command_line_parser/CMakeLists.txt b/test/command_line_parser/CMakeLists.txt new file mode 100644 index 0000000..2fac1c6 --- /dev/null +++ b/test/command_line_parser/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# テスト名 +set(TEST_NAME command_line_parser_test) + +# 実行形式ファイル +add_executable(${TEST_NAME} main.cpp) + +# インクルードディレクトリ +include_directories(${PROJECT_ROOT_PATH}/libs/command_line_parser/include) + +# リンクするライブラリ +target_link_libraries(${TEST_NAME} gtest gtest_main) +if (NOT MSVC) +target_link_libraries(${TEST_NAME} pthread) +endif() + +# コンパイルオプション +set_target_properties(${TEST_NAME} + PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) + +add_test( + NAME command_line_parser + COMMAND $ + ) + +# run with: ctest -L lib +set_property( + TEST command_line_parser + PROPERTY LABELS lib command_line_parser + ) diff --git a/test/command_line_parser/main.cpp b/test/command_line_parser/main.cpp new file mode 100644 index 0000000..43a4c8f --- /dev/null +++ b/test/command_line_parser/main.cpp @@ -0,0 +1,587 @@ +#include +#include + +#include + +/* テスト対象のモジュール */ +extern "C" { +#include "../../libs/command_line_parser/src/command_line_parser.c" +} + +/* ショートオプションの取得テスト */ +TEST(CommandLineParserTest, GetShortOptionTest) +{ + + /* 簡単な成功例 */ + { +#define INPUT_FILE_NAME "inputfile" + static const struct CommandLineParserSpecification specs[] = { + { 'i', NULL, "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'p', NULL, "output file", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + struct CommandLineParserSpecification get_specs[sizeof(specs) / sizeof(specs[0])]; + const char* test_argv1[] = { "progname", "-i", INPUT_FILE_NAME, "-p" }; + const char* test_argv2[] = { "progname", "-p", "-i", INPUT_FILE_NAME }; + const char* test_argv3[] = { "progname", "-pi", INPUT_FILE_NAME }; + + /* パースしてみる */ + memcpy(get_specs, specs, sizeof(get_specs)); + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OK, + CommandLineParser_ParseArguments( + get_specs, + sizeof(test_argv1) / sizeof(test_argv1[0]), test_argv1, + NULL, 0)); + + /* 正しく取得できたかチェック */ + EXPECT_EQ(COMMAND_LINE_PARSER_TRUE, get_specs[0].acquired); + EXPECT_TRUE(get_specs[0].argument_string != NULL); + if (get_specs[0].argument_string != NULL) { + EXPECT_EQ(strcmp(INPUT_FILE_NAME, get_specs[0].argument_string), 0); + } + EXPECT_EQ(COMMAND_LINE_PARSER_TRUE, get_specs[1].acquired); + + /* 引数順番を変えたものをパースしてみる */ + memcpy(get_specs, specs, sizeof(get_specs)); + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OK, + CommandLineParser_ParseArguments( + get_specs, + sizeof(test_argv2) / sizeof(test_argv2[0]), test_argv2, + NULL, 0)); + + /* 正しく取得できたかチェック */ + EXPECT_EQ(COMMAND_LINE_PARSER_TRUE, get_specs[0].acquired); + EXPECT_TRUE(get_specs[0].argument_string != NULL); + if (get_specs[0].argument_string != NULL) { + EXPECT_EQ(0, strcmp(get_specs[0].argument_string, INPUT_FILE_NAME)); + } + EXPECT_EQ(COMMAND_LINE_PARSER_TRUE, get_specs[1].acquired); + + /* ショートオプションの連なりを含むものをパースしてみる */ + memcpy(get_specs, specs, sizeof(get_specs)); + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OK, + CommandLineParser_ParseArguments( + get_specs, + sizeof(test_argv3) / sizeof(test_argv3[0]), test_argv3, + NULL, 0)); + + /* 正しく取得できたかチェック */ + EXPECT_EQ(COMMAND_LINE_PARSER_TRUE, get_specs[0].acquired); + EXPECT_TRUE(get_specs[0].argument_string != NULL); + if (get_specs[0].argument_string != NULL) { + EXPECT_EQ(0, strcmp(get_specs[0].argument_string, INPUT_FILE_NAME)); + } + EXPECT_EQ(COMMAND_LINE_PARSER_TRUE, get_specs[1].acquired); +#undef INPUT_FILE_NAME + } + + /* 失敗系 */ + + /* 引数が指定されずに末尾に達した */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', NULL, "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "-i" }; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_NOT_SPECIFY_ARGUMENT_TO_OPTION, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0)); + } + + /* 引数に他のオプションが指定されている */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', NULL, "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv1[] = { "progname", "-i", "-p" }; + const char* test_argv2[] = { "progname", "-i", "--pripara" }; + + EXPECT_EQ( + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv1) / sizeof(test_argv1[0]), test_argv1, + NULL, 0), + COMMAND_LINE_PARSER_RESULT_NOT_SPECIFY_ARGUMENT_TO_OPTION); + + EXPECT_EQ( + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv2) / sizeof(test_argv2[0]), test_argv2, + NULL, 0), + COMMAND_LINE_PARSER_RESULT_NOT_SPECIFY_ARGUMENT_TO_OPTION); + } + + /* 仕様にないオプションが指定された */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', NULL, "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'p', NULL, "output file", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "-i", "kiriya aoi", "-p", "-s" }; + + EXPECT_EQ( + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0), + COMMAND_LINE_PARSER_RESULT_UNKNOWN_OPTION); + } + + /* 同じオプションが複数回指定されている ケース1 */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', NULL, "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "-i", "kiriya aoi", "-i", "shibuki ran" }; + + EXPECT_EQ( + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0), + COMMAND_LINE_PARSER_RESULT_OPTION_MULTIPLY_SPECIFIED); + } + + /* 同じオプションが複数回指定されている ケース2 */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', NULL, "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'p', NULL, "aikatsu", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "-p", "-i", "kiriya aoi", "-p" }; + + EXPECT_EQ( + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0), + COMMAND_LINE_PARSER_RESULT_OPTION_MULTIPLY_SPECIFIED); + } + + /* ショートオプションの使い方が正しくない パート1 */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', NULL, "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'p', NULL, "aikatsu", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "-ip", "filename" }; + + EXPECT_EQ( + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0), + COMMAND_LINE_PARSER_RESULT_INVAILD_SHORT_OPTION_ARGUMENT); + } + + /* ショートオプションの使い方が正しくない パート2 */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', NULL, "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'p', NULL, "RL", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "-ip", "filename" }; + + EXPECT_EQ( + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0), + COMMAND_LINE_PARSER_RESULT_INVAILD_SHORT_OPTION_ARGUMENT); + } + +} + +/* ロングオプションの取得テスト */ +TEST(CommandLineParserTest, GetLongOptionTest) +{ + + /* 簡単な成功例 */ + { +#define INPUT_FILE_NAME "inputfile" + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'a', "aikatsu", "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "--input", INPUT_FILE_NAME, "--aikatsu" }; + + /* パースしてみる */ + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OK, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0)); + + /* 正しく取得できたかチェック */ + EXPECT_EQ(COMMAND_LINE_PARSER_TRUE, specs[0].acquired); + EXPECT_TRUE(specs[0].argument_string != NULL); + if (specs[0].argument_string != NULL) { + EXPECT_EQ(0, strcmp(specs[0].argument_string, INPUT_FILE_NAME)); + } + EXPECT_EQ(COMMAND_LINE_PARSER_TRUE, specs[1].acquired); + +#undef INPUT_FILE_NAME + } + + /* 簡単な成功例 パート2 */ + { +#define INPUT_FILE_NAME "inputfile" + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'a', "aikatsu", "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "--input=" INPUT_FILE_NAME, "--aikatsu" }; + + /* パースしてみる */ + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OK, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0)); + + /* 正しく取得できたかチェック */ + EXPECT_EQ(COMMAND_LINE_PARSER_TRUE, specs[0].acquired); + EXPECT_TRUE(specs[0].argument_string != NULL); + if (specs[0].argument_string != NULL) { + EXPECT_EQ(0, strcmp(specs[0].argument_string, INPUT_FILE_NAME)); + } + EXPECT_EQ(COMMAND_LINE_PARSER_TRUE, specs[1].acquired); + +#undef INPUT_FILE_NAME + } + + /* 失敗系 */ + + /* 引数が指定されずに末尾に達した */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "--input" }; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_NOT_SPECIFY_ARGUMENT_TO_OPTION, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0)); + } + + /* 引数に他のオプションが指定されている */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv1[] = { "progname", "--input", "-a" }; + const char* test_argv2[] = { "progname", "--input", "--aikatsu" }; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_NOT_SPECIFY_ARGUMENT_TO_OPTION, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv1) / sizeof(test_argv1[0]), test_argv1, + NULL, 0)); + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_NOT_SPECIFY_ARGUMENT_TO_OPTION, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv2) / sizeof(test_argv2[0]), test_argv2, + NULL, 0)); + } + + /* 仕様にないオプションが指定された */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'p', "aikatsu", "aikatsu mode", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "--input", "kiriya aoi", "--aikatsu", "--unknown" }; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_UNKNOWN_OPTION, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0)); + } + + /* 同じオプションが複数回指定されている ケース1 */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "--input", "kiriya aoi", "--input", "shibuki ran" }; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OPTION_MULTIPLY_SPECIFIED, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0)); + } + + /* 同じオプションが複数回指定されている ケース2 */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "--input=kiriya aoi", "--input=shibuki ran" }; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OPTION_MULTIPLY_SPECIFIED, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0)); + } + + /* 同じオプションが複数回指定されている ケース3 */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "--input=kiriya aoi", "--input", "shibuki ran" }; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OPTION_MULTIPLY_SPECIFIED, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0)); + } +} + +/* 他文字列オプションの取得テスト */ +TEST(CommandLineParserTest, GetOtherStringTest) +{ + + /* 簡単な成功例 */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "Ichgo Hoshimiya", "Aoi Kiriya", "Ran Shibuki" }; + const char* other_string_array[3]; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OK, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + other_string_array, sizeof(other_string_array) / sizeof(other_string_array[0]))); + + /* 順番含め取れたか確認 */ + EXPECT_EQ(0, strcmp(other_string_array[0], test_argv[1])); + EXPECT_EQ(0, strcmp(other_string_array[1], test_argv[2])); + EXPECT_EQ(0, strcmp(other_string_array[2], test_argv[3])); + } + + /* オプションを混ぜてみる */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "Ichgo Hoshimiya", "-i", "inputfile", "Aoi Kiriya", "Ran Shibuki" }; + const char* other_string_array[3]; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OK, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + other_string_array, sizeof(other_string_array) / sizeof(other_string_array[0]))); + + /* 順番含め取れたか確認 */ + EXPECT_EQ(0, strcmp(other_string_array[0], test_argv[1])); + EXPECT_EQ(0, strcmp(other_string_array[1], test_argv[4])); + EXPECT_EQ(0, strcmp(other_string_array[2], test_argv[5])); + EXPECT_EQ(0, strcmp(specs[0].argument_string, "inputfile")); + } + + /* 失敗系 */ + + /* バッファサイズが足らない */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "Ichgo Hoshimiya", "Aoi Kiriya", "Ran Shibuki" }; + const char* other_string_array[2]; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_INSUFFICIENT_OTHER_STRING_ARRAY_SIZE, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + other_string_array, sizeof(other_string_array) / sizeof(other_string_array[0]))); + } +} + +/* 様々な文字列設定に対するテスト */ +TEST(CommandLineParserTest, ParseVariousStringTest) +{ + + /* 失敗系 */ + + /* パーサ仕様が不正 ケース1(ショートオプション重複) */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'a', "aikatsu", "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'i', NULL, "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "--input", "inputfile", "--aikatsu" }; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_INVALID_SPECIFICATION, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0)); + } + + /* パーサ仕様が不正 ケース2(ロングオプション重複) */ + { + struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'a', "aikatsu", "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'p', "input", "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + const char* test_argv[] = { "progname", "--input", "inputfile", "--aikatsu" }; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_INVALID_SPECIFICATION, + CommandLineParser_ParseArguments( + specs, + sizeof(test_argv) / sizeof(test_argv[0]), test_argv, + NULL, 0)); + } +} + +/* インデックス取得テスト */ +TEST(CommandLineParserTest, GetSpecificationIndexTest) +{ + /* 簡単な成功例 */ + { + uint32_t get_index, spec_no; + const struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'a', "aikatsu", "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'p', "RL", "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, } + }; + uint32_t num_specs = CommandLineParser_GetNumSpecifications(specs); + + for (spec_no = 0; spec_no < num_specs; spec_no++) { + char short_option_str[2]; + /* ショートオプションに対してテスト */ + short_option_str[0] = specs[spec_no].short_option; + short_option_str[1] = '\0'; + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OK, + CommandLineParser_GetSpecificationIndex( + specs, short_option_str, &get_index)); + EXPECT_EQ(spec_no, get_index); + /* ロングオプションに対してテスト */ + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_OK, + CommandLineParser_GetSpecificationIndex( + specs, specs[spec_no].long_option, &get_index)); + EXPECT_EQ(spec_no, get_index); + } + } + + /* 失敗系 */ + + /* 引数が不正 */ + { + uint32_t get_index; + const struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'a', "aikatsu", "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'p', "RL", "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } + }; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_INVALID_ARGUMENT, + CommandLineParser_GetSpecificationIndex( + NULL, "aikatsu", &get_index)); + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_INVALID_ARGUMENT, + CommandLineParser_GetSpecificationIndex( + specs, NULL, &get_index)); + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_INVALID_ARGUMENT, + CommandLineParser_GetSpecificationIndex( + specs, "aikatsu", NULL)); + } + + /* 存在しないオプションのインデックスを問い合わせる */ + { + uint32_t get_index; + const struct CommandLineParserSpecification specs[] = { + { 'i', "input", "input file", COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'a', "aikatsu", "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'p', "RL", "aikatsu dakega boku no shinri datta...", COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, } + }; + + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_UNKNOWN_OPTION, + CommandLineParser_GetSpecificationIndex( + specs, "aikats", &get_index)); + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_UNKNOWN_OPTION, + CommandLineParser_GetSpecificationIndex( + specs, "naru ayase", &get_index)); + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_UNKNOWN_OPTION, + CommandLineParser_GetSpecificationIndex( + specs, "rinne ibara", &get_index)); + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_UNKNOWN_OPTION, + CommandLineParser_GetSpecificationIndex( + specs, "s", &get_index)); + EXPECT_EQ( + COMMAND_LINE_PARSER_RESULT_UNKNOWN_OPTION, + CommandLineParser_GetSpecificationIndex( + specs, "b", &get_index)); + } +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/lpc/CMakeLists.txt b/test/lpc/CMakeLists.txt new file mode 100644 index 0000000..587b7a8 --- /dev/null +++ b/test/lpc/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# テスト名 +set(TEST_NAME lpc_test) + +# 実行形式ファイル +add_executable(${TEST_NAME} main.cpp) + +# インクルードディレクトリ +include_directories(${PROJECT_ROOT_PATH}/libs/lpc/include) + +# リンクするライブラリ +target_link_libraries(${TEST_NAME} gtest gtest_main) +if (NOT MSVC) +target_link_libraries(${TEST_NAME} pthread) +endif() + +# コンパイルオプション +set_target_properties(${TEST_NAME} + PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) + +add_test( + NAME lpc + COMMAND $ + ) + +# run with: ctest -L lib +set_property( + TEST lpc + PROPERTY LABELS lib lpc + ) diff --git a/test/lpc/main.cpp b/test/lpc/main.cpp new file mode 100644 index 0000000..0b7fd45 --- /dev/null +++ b/test/lpc/main.cpp @@ -0,0 +1,229 @@ +#include +#include + +#include + +/* テスト対象のモジュール */ +extern "C" { +#include "../../libs/lpc/src/lpc.c" +} + +/* ハンドル作成破棄テスト */ +TEST(LPCCalculatorTest, CreateDestroyHandleTest) +{ + /* ワークサイズ計算テスト */ + { + int32_t work_size; + struct LPCCalculatorConfig config; + + /* 最低限構造体本体よりは大きいはず */ + config.max_order = 1; + config.max_num_samples = 1; + work_size = LPCCalculator_CalculateWorkSize(&config); + ASSERT_TRUE(work_size > sizeof(struct LPCCalculator)); + + /* 不正なコンフィグ */ + EXPECT_TRUE(LPCCalculator_CalculateWorkSize(0) < 0); + } + + /* ワーク領域渡しによるハンドル作成(成功例) */ + { + void *work; + int32_t work_size; + struct LPCCalculatorConfig config; + struct LPCCalculator *lpcc; + + config.max_order = 1; + config.max_num_samples = 1; + work_size = LPCCalculator_CalculateWorkSize(&config); + work = malloc(work_size); + + lpcc = LPCCalculator_Create(&config, work, work_size); + ASSERT_TRUE(lpcc != NULL); + EXPECT_TRUE(lpcc->work == work); + EXPECT_EQ(lpcc->alloced_by_own, 0); + + LPCCalculator_Destroy(lpcc); + free(work); + } + + /* 自前確保によるハンドル作成(成功例) */ + { + struct LPCCalculator *lpcc; + struct LPCCalculatorConfig config; + + config.max_order = 1; + config.max_num_samples = 1; + lpcc = LPCCalculator_Create(&config, NULL, 0); + ASSERT_TRUE(lpcc != NULL); + EXPECT_TRUE(lpcc->work != NULL); + EXPECT_EQ(lpcc->alloced_by_own, 1); + + LPCCalculator_Destroy(lpcc); + } + + /* ワーク領域渡しによるハンドル作成(失敗ケース) */ + { + void *work; + int32_t work_size; + struct LPCCalculator *lpcc; + struct LPCCalculatorConfig config; + + config.max_order = 1; + config.max_num_samples = 1; + work_size = LPCCalculator_CalculateWorkSize(&config); + work = malloc(work_size); + + /* 引数が不正 */ + lpcc = LPCCalculator_Create(NULL, work, work_size); + EXPECT_TRUE(lpcc == NULL); + lpcc = LPCCalculator_Create(&config, NULL, work_size); + EXPECT_TRUE(lpcc == NULL); + lpcc = LPCCalculator_Create(&config, work, 0); + EXPECT_TRUE(lpcc == NULL); + + /* コンフィグパラメータが不正 */ + config.max_order = 0; config.max_num_samples = 1; + lpcc = LPCCalculator_Create(&config, work, work_size); + EXPECT_TRUE(lpcc == NULL); + config.max_order = 1; config.max_num_samples = 0; + lpcc = LPCCalculator_Create(&config, work, work_size); + EXPECT_TRUE(lpcc == NULL); + + free(work); + } + + /* 自前確保によるハンドル作成(失敗ケース) */ + { + struct LPCCalculator *lpcc; + struct LPCCalculatorConfig config; + + /* コンフィグパラメータが不正 */ + config.max_order = 0; config.max_num_samples = 1; + lpcc = LPCCalculator_Create(&config, NULL, 0); + EXPECT_TRUE(lpcc == NULL); + config.max_order = 1; config.max_num_samples = 0; + lpcc = LPCCalculator_Create(&config, NULL, 0); + EXPECT_TRUE(lpcc == NULL); + } +} + +/* (テスト用)PARCOR係数をLPC係数に変換 */ +static LPCError LPC_ConvertPARCORtoLPCDouble( + struct LPCCalculator* lpcc, const double* parcor_coef, uint32_t coef_order, double *lpc_coef) +{ + int32_t i, k; + double *a_vec; + + /* 引数チェック */ + if ((lpcc == NULL) || (lpc_coef == NULL) || (parcor_coef == NULL)) { + return LPC_ERROR_INVALID_ARGUMENT; + } + + /* 次数チェック */ + assert(coef_order <= lpcc->max_order); + + /* 作業領域を割り当て */ + a_vec = lpcc->a_vecs[0]; + + /* 再帰計算 */ + lpc_coef[0] = -parcor_coef[0]; + for (i = 1; i < coef_order; i++) { + const double gamma = -parcor_coef[i]; + for (k = 0; k < i; k++) { + a_vec[k] = lpc_coef[k]; + } + for (k = 0; k < i; k++) { + lpc_coef[k] = a_vec[k] + (gamma * a_vec[i - k - 1]); + } + lpc_coef[i] = gamma; + } + + return LPC_ERROR_OK; +} + +/* LPC係数をPARCOR係数に変換するテスト */ +TEST(LPCCalculatorTest, LPC_ConvertLPCandPARCORTest) +{ + /* LPC->PARCORの簡単な成功例 */ + { +#define NUM_SAMPLES 32 +#define COEF_ORDER 16 + uint32_t i; + struct LPCCalculator* lpcc; + struct LPCCalculatorConfig config; + double data[NUM_SAMPLES], lpc_coef[COEF_ORDER], answer[COEF_ORDER], test[COEF_ORDER]; + + for (i = 0; i < NUM_SAMPLES; i++) { + data[i] = sin(0.1 * i); + } + + config.max_num_samples = NUM_SAMPLES; config.max_order = COEF_ORDER; + lpcc = LPCCalculator_Create(&config, NULL, 0); + ASSERT_TRUE(lpcc != NULL); + + /* 係数計算 */ + ASSERT_EQ(LPC_APIRESULT_OK, + LPCCalculator_CalculateLPCCoefficients(lpcc, + data, NUM_SAMPLES, lpc_coef, COEF_ORDER, LPC_WINDOWTYPE_RECTANGULAR, 0.0)); + memcpy(answer, lpcc->parcor_coef, sizeof(double) * COEF_ORDER); + + LPCCalculator_Destroy(lpcc); + + /* LPC->PARCOR変換 */ + lpcc = LPCCalculator_Create(&config, NULL, 0); + EXPECT_EQ(LPC_ERROR_OK, LPC_ConvertLPCtoPARCORDouble(lpcc, lpc_coef, COEF_ORDER, test)); + + /* 一致確認 */ + for (i = 0; i < COEF_ORDER; i++) { + EXPECT_FLOAT_EQ(answer[i], test[i]); + } + + LPCCalculator_Destroy(lpcc); +#undef NUM_SAMPLES +#undef COEF_ORDER + } + + /* PARCOR->LPCの簡単な成功例 */ + { +#define NUM_SAMPLES 32 +#define COEF_ORDER 16 + uint32_t i; + struct LPCCalculator* lpcc; + struct LPCCalculatorConfig config; + double data[NUM_SAMPLES], lpc_coef[COEF_ORDER], answer[COEF_ORDER], test[COEF_ORDER]; + + for (i = 0; i < NUM_SAMPLES; i++) { + data[i] = sin(0.1 * i); + } + + config.max_num_samples = NUM_SAMPLES; config.max_order = COEF_ORDER; + lpcc = LPCCalculator_Create(&config, NULL, 0); + ASSERT_TRUE(lpcc != NULL); + + ASSERT_EQ(LPC_APIRESULT_OK, + LPCCalculator_CalculateLPCCoefficients(lpcc, + data, NUM_SAMPLES, lpc_coef, COEF_ORDER, LPC_WINDOWTYPE_RECTANGULAR, 0.0)); + memcpy(answer, lpcc->parcor_coef, sizeof(double) * COEF_ORDER); + + LPCCalculator_Destroy(lpcc); + + /* PARCOR->LPC変換 */ + lpcc = LPCCalculator_Create(&config, NULL, 0); + EXPECT_EQ(LPC_ERROR_OK, LPC_ConvertPARCORtoLPCDouble(lpcc, answer, COEF_ORDER, test)); + + for (i = 0; i < COEF_ORDER; i++) { + EXPECT_FLOAT_EQ(lpc_coef[i], test[i]); + } + + LPCCalculator_Destroy(lpcc); +#undef NUM_SAMPLES +#undef COEF_ORDER + } +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/srla_coder/CMakeLists.txt b/test/srla_coder/CMakeLists.txt new file mode 100644 index 0000000..449a7fa --- /dev/null +++ b/test/srla_coder/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# テスト名 +set(TEST_NAME srla_coder_test) + +# 実行形式ファイル +add_executable(${TEST_NAME} main.cpp) + +# インクルードディレクトリ +include_directories(${PROJECT_ROOT_PATH}/libs/srla_coder/include) + +# リンクするライブラリ +target_link_libraries(${TEST_NAME} gtest gtest_main bit_stream srla_internal srla_coder) +if (NOT MSVC) +target_link_libraries(${TEST_NAME} pthread) +endif() + +# コンパイルオプション +set_target_properties(${TEST_NAME} + PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) + +# 実行パスをtmp以下に +add_test( + NAME srla_coder + WORKING_DIRECTORY $/tmp + COMMAND $ + ) + +# run with: ctest -L lib +set_property( + TEST srla_coder + PROPERTY LABELS lib srla_coder + ) + +# ビルド後にテストリソースを持ってくる +add_custom_command( + TARGET ${TEST_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory $/tmp + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/PriChanIcon.png $/tmp + ) diff --git a/test/srla_coder/PriChanIcon.png b/test/srla_coder/PriChanIcon.png new file mode 100644 index 0000000..e7f7136 Binary files /dev/null and b/test/srla_coder/PriChanIcon.png differ diff --git a/test/srla_coder/main.cpp b/test/srla_coder/main.cpp new file mode 100644 index 0000000..0d2dcfc --- /dev/null +++ b/test/srla_coder/main.cpp @@ -0,0 +1,314 @@ +#include +#include + +#include + +/* テスト対象のモジュール */ +extern "C" { +#include "../../libs/srla_coder/src/srla_coder.c" +} + +/* ハンドル作成破棄テスト */ +TEST(SRLACoderTest, CreateDestroyHandleTest) +{ + /* ワークサイズ計算テスト */ + { + int32_t work_size; + + /* 最低限構造体本体よりは大きいはず */ + work_size = SRLACoder_CalculateWorkSize(); + ASSERT_TRUE(work_size > sizeof(struct SRLACoder)); + } + + /* ワーク領域渡しによるハンドル作成(成功例) */ + { + void *work; + int32_t work_size; + struct SRLACoder *coder; + + work_size = SRLACoder_CalculateWorkSize(); + work = malloc(work_size); + + coder = SRLACoder_Create(work, work_size); + ASSERT_TRUE(coder != NULL); + EXPECT_TRUE(coder->work == work); + EXPECT_EQ(coder->alloced_by_own, 0); + + SRLACoder_Destroy(coder); + free(work); + } + + /* 自前確保によるハンドル作成(成功例) */ + { + struct SRLACoder *coder; + + coder = SRLACoder_Create(NULL, 0); + ASSERT_TRUE(coder != NULL); + EXPECT_TRUE(coder->work != NULL); + EXPECT_EQ(coder->alloced_by_own, 1); + + SRLACoder_Destroy(coder); + } + + /* ワーク領域渡しによるハンドル作成(失敗ケース) */ + { + void *work; + int32_t work_size; + struct SRLACoder *coder; + + work_size = SRLACoder_CalculateWorkSize(); + work = malloc(work_size); + + /* 引数が不正 */ + coder = SRLACoder_Create(NULL, work_size); + EXPECT_TRUE(coder == NULL); + coder = SRLACoder_Create(work, 0); + EXPECT_TRUE(coder == NULL); + + /* ワークサイズ不足 */ + coder = SRLACoder_Create(work, work_size - 1); + EXPECT_TRUE(coder == NULL); + } +} + +/* 再帰的ライス符号テスト */ +TEST(SRLACoderTest, RecursiveRiceTest) +{ + /* 簡単に出力テスト */ + { + uint32_t code; + uint8_t data[16]; + struct BitStream strm; + + /* 0を4回出力 */ + memset(data, 0, sizeof(data)); + BitWriter_Open(&strm, data, sizeof(data)); + RecursiveRice_PutCode(&strm, 1, 1, 0); + RecursiveRice_PutCode(&strm, 1, 1, 0); + RecursiveRice_PutCode(&strm, 1, 1, 0); + RecursiveRice_PutCode(&strm, 1, 1, 0); + BitStream_Close(&strm); + + /* 取得 */ + BitReader_Open(&strm, data, sizeof(data)); + RecursiveRice_GetCode(&strm, 1, 1, &code); + EXPECT_EQ(0, code); + RecursiveRice_GetCode(&strm, 1, 1, &code); + EXPECT_EQ(0, code); + RecursiveRice_GetCode(&strm, 1, 1, &code); + EXPECT_EQ(0, code); + RecursiveRice_GetCode(&strm, 1, 1, &code); + EXPECT_EQ(0, code); + BitStream_Close(&strm); + + /* 1を4回出力 */ + memset(data, 0, sizeof(data)); + BitWriter_Open(&strm, data, sizeof(data)); + RecursiveRice_PutCode(&strm, 1, 1, 1); + RecursiveRice_PutCode(&strm, 1, 1, 1); + RecursiveRice_PutCode(&strm, 1, 1, 1); + RecursiveRice_PutCode(&strm, 1, 1, 1); + BitStream_Close(&strm); + + /* 取得 */ + BitReader_Open(&strm, data, sizeof(data)); + RecursiveRice_GetCode(&strm, 1, 1, &code); + EXPECT_EQ(1, code); + RecursiveRice_GetCode(&strm, 1, 1, &code); + EXPECT_EQ(1, code); + RecursiveRice_GetCode(&strm, 1, 1, &code); + EXPECT_EQ(1, code); + RecursiveRice_GetCode(&strm, 1, 1, &code); + EXPECT_EQ(1, code); + BitStream_Close(&strm); + + /* パラメータを変えて0を4回出力 */ + memset(data, 0, sizeof(data)); + BitWriter_Open(&strm, data, sizeof(data)); + RecursiveRice_PutCode(&strm, 2, 2, 0); + RecursiveRice_PutCode(&strm, 2, 2, 0); + RecursiveRice_PutCode(&strm, 2, 2, 0); + RecursiveRice_PutCode(&strm, 2, 2, 0); + BitStream_Close(&strm); + + /* 取得 */ + BitReader_Open(&strm, data, sizeof(data)); + RecursiveRice_GetCode(&strm, 2, 2, &code); + EXPECT_EQ(0, code); + RecursiveRice_GetCode(&strm, 2, 2, &code); + EXPECT_EQ(0, code); + RecursiveRice_GetCode(&strm, 2, 2, &code); + EXPECT_EQ(0, code); + RecursiveRice_GetCode(&strm, 2, 2, &code); + EXPECT_EQ(0, code); + BitStream_Close(&strm); + + /* パラメータを変えて3を4回出力 */ + memset(data, 0, sizeof(data)); + BitWriter_Open(&strm, data, sizeof(data)); + RecursiveRice_PutCode(&strm, 2, 2, 3); + RecursiveRice_PutCode(&strm, 2, 2, 3); + RecursiveRice_PutCode(&strm, 2, 2, 3); + RecursiveRice_PutCode(&strm, 2, 2, 3); + BitStream_Close(&strm); + + /* 取得 */ + BitReader_Open(&strm, data, sizeof(data)); + RecursiveRice_GetCode(&strm, 2, 2, &code); + EXPECT_EQ(3, code); + RecursiveRice_GetCode(&strm, 2, 2, &code); + EXPECT_EQ(3, code); + RecursiveRice_GetCode(&strm, 2, 2, &code); + EXPECT_EQ(3, code); + RecursiveRice_GetCode(&strm, 2, 2, &code); + EXPECT_EQ(3, code); + BitStream_Close(&strm); + } + + /* 長めの信号を出力してみる */ + { +#define TEST_OUTPUT_LENGTH (128) + uint32_t i, code, is_ok, k1, k2; + struct BitStream strm; + int32_t test_output_pattern[TEST_OUTPUT_LENGTH]; + uint8_t data[TEST_OUTPUT_LENGTH * 2]; + double mean = 0.0; + + /* 出力の生成 */ + for (i = 0; i < TEST_OUTPUT_LENGTH; i++) { + test_output_pattern[i] = i; + mean += test_output_pattern[i]; + } + mean /= TEST_OUTPUT_LENGTH; + + /* 最適なパラメータの計算 */ + SRLACoder_CalculateOptimalRecursiveRiceParameter(mean, &k1, &k2, NULL); + + /* 出力 */ + BitWriter_Open(&strm, data, sizeof(data)); + for (i = 0; i < TEST_OUTPUT_LENGTH; i++) { + RecursiveRice_PutCode(&strm, k1, k2, test_output_pattern[i]); + } + BitStream_Close(&strm); + + /* 取得 */ + BitReader_Open(&strm, data, sizeof(data)); + is_ok = 1; + for (i = 0; i < TEST_OUTPUT_LENGTH; i++) { + uint32_t uval; + RecursiveRice_GetCode(&strm, k1, k2, &uval); + if (uval != test_output_pattern[i]) { + printf("actual:%d != test:%d \n", uval, test_output_pattern[i]); + is_ok = 0; + break; + } + } + EXPECT_EQ(1, is_ok); + BitStream_Close(&strm); +#undef TEST_OUTPUT_LENGTH + } + + /* 長めの信号を出力してみる(乱数) */ + { +#define TEST_OUTPUT_LENGTH (128) + uint32_t i, code, is_ok, k1, k2; + struct BitStream strm; + int32_t test_output_pattern[TEST_OUTPUT_LENGTH]; + uint8_t data[TEST_OUTPUT_LENGTH * 2]; + double mean = 0.0; + + /* 出力の生成 */ + srand(0); + for (i = 0; i < TEST_OUTPUT_LENGTH; i++) { + test_output_pattern[i] = rand() % 0xFF; + mean += test_output_pattern[i]; + } + mean /= TEST_OUTPUT_LENGTH; + + /* 最適なパラメータの計算 */ + SRLACoder_CalculateOptimalRecursiveRiceParameter(mean, &k1, &k2, NULL); + + /* 出力 */ + BitWriter_Open(&strm, data, sizeof(data)); + for (i = 0; i < TEST_OUTPUT_LENGTH; i++) { + RecursiveRice_PutCode(&strm, k1, k2, test_output_pattern[i]); + } + BitStream_Close(&strm); + + /* 取得 */ + BitReader_Open(&strm, data, sizeof(data)); + is_ok = 1; + for (i = 0; i < TEST_OUTPUT_LENGTH; i++) { + uint32_t uval; + RecursiveRice_GetCode(&strm, k1, k2, &uval); + if (uval != test_output_pattern[i]) { + printf("actual:%d != test:%d \n", uval, test_output_pattern[i]); + is_ok = 0; + break; + } + } + EXPECT_EQ(1, is_ok); + BitStream_Close(&strm); +#undef TEST_OUTPUT_LENGTH + } + + /* 実データを符号化してみる */ + { + uint32_t i, encsize, k1, k2; + struct stat fstat; + const char test_infile_name[] = "PriChanIcon.png"; + uint8_t *fileimg; + uint8_t *encimg; + uint8_t *decimg; + double mean; + FILE *fp; + struct BitStream strm; + + /* 入力データ読み出し */ + stat(test_infile_name, &fstat); + fileimg = (uint8_t *)malloc(fstat.st_size); + encimg = (uint8_t *)malloc(2 * fstat.st_size); /* PNG画像のため増えることを想定 */ + decimg = (uint8_t *)malloc(fstat.st_size); + fp = fopen(test_infile_name, "rb"); + fread(fileimg, sizeof(uint8_t), fstat.st_size, fp); + fclose(fp); + + /* 最適なパラメータ計算 */ + mean = 0.0; + for (i = 0; i < fstat.st_size; i++) { + mean += fileimg[i]; + } + mean /= fstat.st_size; + SRLACoder_CalculateOptimalRecursiveRiceParameter(mean, &k1, &k2, NULL); + + /* 書き込み */ + BitWriter_Open(&strm, encimg, 2 * fstat.st_size); + for (i = 0; i < fstat.st_size; i++) { + RecursiveRice_PutCode(&strm, k1, k2, fileimg[i]); + } + BitStream_Flush(&strm); + BitStream_Tell(&strm, (int32_t *)&encsize); + BitStream_Close(&strm); + + /* 読み込み */ + BitReader_Open(&strm, encimg, encsize); + for (i = 0; i < fstat.st_size; i++) { + uint32_t uval; + RecursiveRice_GetCode(&strm, k1, k2, &uval) + decimg[i] = (uint8_t)uval; + } + BitStream_Close(&strm); + + /* 一致確認 */ + EXPECT_EQ(0, memcmp(fileimg, decimg, sizeof(uint8_t) * fstat.st_size)); + + free(decimg); + free(fileimg); + } +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/srla_decoder/CMakeLists.txt b/test/srla_decoder/CMakeLists.txt new file mode 100644 index 0000000..f890a25 --- /dev/null +++ b/test/srla_decoder/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# テスト名 +set(TEST_NAME srla_decoder_test) + +# 実行形式ファイル +add_executable(${TEST_NAME} + srla_decoder_test.cpp + srla_lpc_synthesize_test.cpp + main.cpp + ) + +# インクルードディレクトリ +include_directories(${PROJECT_ROOT_PATH}/libs/srla_decoder/include) + +# リンクするライブラリ +target_link_libraries(${TEST_NAME} gtest gtest_main byte_array bit_stream srla_encoder srla_coder srla_internal lpc) +if (NOT MSVC) +target_link_libraries(${TEST_NAME} pthread) +endif() + +# コンパイルオプション +set_target_properties(${TEST_NAME} + PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) + +add_test( + NAME srla_decoder + COMMAND $ + ) + +# run with: ctest -L lib +set_property( + TEST srla_decoder + PROPERTY LABELS lib srla_decoder + ) diff --git a/test/srla_decoder/main.cpp b/test/srla_decoder/main.cpp new file mode 100644 index 0000000..96d7200 --- /dev/null +++ b/test/srla_decoder/main.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/srla_decoder/srla_decoder_test.cpp b/test/srla_decoder/srla_decoder_test.cpp new file mode 100644 index 0000000..f4f33d1 --- /dev/null +++ b/test/srla_decoder/srla_decoder_test.cpp @@ -0,0 +1,537 @@ +#include +#include + +#include + +#include "srla_encoder.h" + +/* テスト対象のモジュール */ +extern "C" { +#include "../../libs/srla_decoder/src/srla_decoder.c" +} + +/* 有効なヘッダをセット */ +#define SRLA_SetValidHeader(p_header)\ + do {\ + struct SRLAHeader *header__p = p_header;\ + header__p->format_version = SRLA_FORMAT_VERSION;\ + header__p->codec_version = SRLA_CODEC_VERSION;\ + header__p->num_channels = 1;\ + header__p->sampling_rate = 44100;\ + header__p->bits_per_sample = 16;\ + header__p->num_samples = 8192;\ + header__p->num_samples_per_block = 1024;\ + header__p->preset = 0;\ + } while (0); + +/* ヘッダにある情報からエンコードパラメータを作成 */ +#define SRLAEncoder_ConvertHeaderToParameter(p_header, p_parameter)\ + do {\ + const struct SRLAHeader *header__p = p_header;\ + struct SRLAEncodeParameter *param__p = p_parameter;\ + param__p->num_channels = header__p->num_channels;\ + param__p->sampling_rate = header__p->sampling_rate;\ + param__p->bits_per_sample = header__p->bits_per_sample;\ + param__p->num_samples_per_block = header__p->num_samples_per_block;\ + param__p->preset = header__p->preset;\ + } while (0); + +/* 有効なエンコードパラメータをセット */ +#define SRLAEncoder_SetValidEncodeParameter(p_parameter)\ + do {\ + struct SRLAEncodeParameter *param__p = p_parameter;\ + param__p->num_channels = 1;\ + param__p->bits_per_sample = 16;\ + param__p->sampling_rate = 44100;\ + param__p->num_samples_per_block = 1024;\ + param__p->preset = 0;\ + } while (0); + +/* 有効なエンコーダコンフィグをセット */ +#define SRLAEncoder_SetValidConfig(p_config)\ + do {\ + struct SRLAEncoderConfig *config__p = p_config;\ + config__p->max_num_channels = 8;\ + config__p->max_num_samples_per_block = 4096;\ + config__p->max_num_parameters = 32;\ + } while (0); + +/* 有効なデコーダコンフィグをセット */ +#define SRLADecoder_SetValidConfig(p_config)\ + do {\ + struct SRLADecoderConfig *config__p = p_config;\ + config__p->max_num_channels = 8;\ + config__p->max_num_parameters = 32;\ + config__p->check_checksum = 1;\ + } while (0); + +/* ヘッダデコードテスト */ +TEST(SRLADecoderTest, DecodeHeaderTest) +{ + /* 成功例 */ + { + uint8_t data[SRLA_HEADER_SIZE] = { 0, }; + struct SRLAHeader header, tmp_header; + + SRLA_SetValidHeader(&header); + + /* エンコード->デコード */ + EXPECT_EQ(SRLA_APIRESULT_OK, SRLAEncoder_EncodeHeader(&header, data, sizeof(data))); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, sizeof(data), &tmp_header)); + + /* デコードしたヘッダの一致確認 */ + EXPECT_EQ(SRLA_FORMAT_VERSION, tmp_header.format_version); + EXPECT_EQ(SRLA_CODEC_VERSION, tmp_header.codec_version); + EXPECT_EQ(header.num_channels, tmp_header.num_channels); + EXPECT_EQ(header.sampling_rate, tmp_header.sampling_rate); + EXPECT_EQ(header.bits_per_sample, tmp_header.bits_per_sample); + EXPECT_EQ(header.num_samples, tmp_header.num_samples); + EXPECT_EQ(header.num_samples_per_block, tmp_header.num_samples_per_block); + EXPECT_EQ(header.preset, tmp_header.preset); + } + + /* ヘッダデコード失敗ケース */ + { + struct SRLAHeader header, getheader; + uint8_t valid_data[SRLA_HEADER_SIZE] = { 0, }; + uint8_t data[SRLA_HEADER_SIZE]; + + /* 有効な内容を作っておく */ + SRLA_SetValidHeader(&header); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLAEncoder_EncodeHeader(&header, valid_data, sizeof(valid_data))); + /* 有効であることを確認 */ + ASSERT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(valid_data, sizeof(valid_data), &getheader)); + + /* シグネチャが不正 */ + memcpy(data, valid_data, sizeof(valid_data)); + memset(&getheader, 0xCD, sizeof(getheader)); + ByteArray_WriteUint8(&data[0], 'a'); + EXPECT_EQ(SRLA_APIRESULT_INVALID_FORMAT, SRLADecoder_DecodeHeader(data, sizeof(data), &getheader)); + + /* 以降のテストケースでは、ヘッダは取得できるが、内部のチェック関数で失敗する */ + + /* 異常なフォーマットバージョン */ + memcpy(data, valid_data, sizeof(valid_data)); + memset(&getheader, 0xCD, sizeof(getheader)); + ByteArray_WriteUint32BE(&data[4], 0); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, sizeof(data), &getheader)); + EXPECT_EQ(SRLA_ERROR_INVALID_FORMAT, SRLADecoder_CheckHeaderFormat(&getheader)); + memcpy(data, valid_data, sizeof(valid_data)); + memset(&getheader, 0xCD, sizeof(getheader)); + ByteArray_WriteUint32BE(&data[4], SRLA_FORMAT_VERSION + 1); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, sizeof(data), &getheader)); + EXPECT_EQ(SRLA_ERROR_INVALID_FORMAT, SRLADecoder_CheckHeaderFormat(&getheader)); + + /* 異常なエンコーダバージョン */ + memcpy(data, valid_data, sizeof(valid_data)); + memset(&getheader, 0xCD, sizeof(getheader)); + ByteArray_WriteUint32BE(&data[8], 0); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, sizeof(data), &getheader)); + EXPECT_EQ(SRLA_ERROR_INVALID_FORMAT, SRLADecoder_CheckHeaderFormat(&getheader)); + memcpy(data, valid_data, sizeof(valid_data)); + memset(&getheader, 0xCD, sizeof(getheader)); + ByteArray_WriteUint32BE(&data[8], SRLA_CODEC_VERSION + 1); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, sizeof(data), &getheader)); + EXPECT_EQ(SRLA_ERROR_INVALID_FORMAT, SRLADecoder_CheckHeaderFormat(&getheader)); + + /* 異常なチャンネル数 */ + memcpy(data, valid_data, sizeof(valid_data)); + memset(&getheader, 0xCD, sizeof(getheader)); + ByteArray_WriteUint16BE(&data[12], 0); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, sizeof(data), &getheader)); + EXPECT_EQ(SRLA_ERROR_INVALID_FORMAT, SRLADecoder_CheckHeaderFormat(&getheader)); + + /* 異常なサンプル数 */ + memcpy(data, valid_data, sizeof(valid_data)); + memset(&getheader, 0xCD, sizeof(getheader)); + ByteArray_WriteUint32BE(&data[14], 0); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, sizeof(data), &getheader)); + EXPECT_EQ(SRLA_ERROR_INVALID_FORMAT, SRLADecoder_CheckHeaderFormat(&getheader)); + + /* 異常なサンプリングレート */ + memcpy(data, valid_data, sizeof(valid_data)); + memset(&getheader, 0xCD, sizeof(getheader)); + ByteArray_WriteUint32BE(&data[18], 0); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, sizeof(data), &getheader)); + EXPECT_EQ(SRLA_ERROR_INVALID_FORMAT, SRLADecoder_CheckHeaderFormat(&getheader)); + + /* 異常なサンプルあたりビット数 */ + memcpy(data, valid_data, sizeof(valid_data)); + memset(&getheader, 0xCD, sizeof(getheader)); + ByteArray_WriteUint16BE(&data[22], 0); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, sizeof(data), &getheader)); + EXPECT_EQ(SRLA_ERROR_INVALID_FORMAT, SRLADecoder_CheckHeaderFormat(&getheader)); + + /* 異常なブロックあたりサンプル数 */ + memcpy(data, valid_data, sizeof(valid_data)); + memset(&getheader, 0xCD, sizeof(getheader)); + ByteArray_WriteUint32BE(&data[24], 0); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, sizeof(data), &getheader)); + EXPECT_EQ(SRLA_ERROR_INVALID_FORMAT, SRLADecoder_CheckHeaderFormat(&getheader)); + + /* 異常なプリセット */ + memcpy(data, valid_data, sizeof(valid_data)); + memset(&getheader, 0xCD, sizeof(getheader)); + ByteArray_WriteUint8(&data[28], SRLA_NUM_PARAMETER_PRESETS); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, sizeof(data), &getheader)); + EXPECT_EQ(SRLA_ERROR_INVALID_FORMAT, SRLADecoder_CheckHeaderFormat(&getheader)); + } +} + +/* デコーダハンドル生成破棄テスト */ +TEST(SRLADecoderTest, CreateDestroyHandleTest) +{ + /* ワークサイズ計算テスト */ + { + int32_t work_size; + struct SRLADecoderConfig config; + + /* 最低限構造体本体よりは大きいはず */ + SRLADecoder_SetValidConfig(&config); + work_size = SRLADecoder_CalculateWorkSize(&config); + ASSERT_TRUE(work_size > sizeof(struct SRLADecoder)); + + /* 不正な引数 */ + EXPECT_TRUE(SRLADecoder_CalculateWorkSize(NULL) < 0); + + /* 不正なコンフィグ */ + SRLADecoder_SetValidConfig(&config); + config.max_num_channels = 0; + EXPECT_TRUE(SRLADecoder_CalculateWorkSize(&config) < 0); + + SRLADecoder_SetValidConfig(&config); + config.max_num_parameters = 0; + EXPECT_TRUE(SRLADecoder_CalculateWorkSize(&config) < 0); + } + + /* ワーク領域渡しによるハンドル作成(成功例) */ + { + void *work; + int32_t work_size; + struct SRLADecoder *decoder; + struct SRLADecoderConfig config; + + SRLADecoder_SetValidConfig(&config); + work_size = SRLADecoder_CalculateWorkSize(&config); + work = malloc(work_size); + + decoder = SRLADecoder_Create(&config, work, work_size); + ASSERT_TRUE(decoder != NULL); + EXPECT_TRUE(decoder->work == work); + EXPECT_FALSE(SRLADECODER_GET_STATUS_FLAG(decoder, SRLADECODER_STATUS_FLAG_ALLOCED_BY_OWN)); + EXPECT_FALSE(SRLADECODER_GET_STATUS_FLAG(decoder, SRLADECODER_STATUS_FLAG_SET_HEADER)); + EXPECT_TRUE(decoder->params_int != NULL); + EXPECT_TRUE(decoder->params_int[0] != NULL); + EXPECT_TRUE(decoder->rshifts != NULL); + + SRLADecoder_Destroy(decoder); + free(work); + } + + /* 自前確保によるハンドル作成(成功例) */ + { + struct SRLADecoder *decoder; + struct SRLADecoderConfig config; + + SRLADecoder_SetValidConfig(&config); + + decoder = SRLADecoder_Create(&config, NULL, 0); + ASSERT_TRUE(decoder != NULL); + EXPECT_TRUE(decoder->work != NULL); + EXPECT_TRUE(SRLADECODER_GET_STATUS_FLAG(decoder, SRLADECODER_STATUS_FLAG_ALLOCED_BY_OWN)); + EXPECT_FALSE(SRLADECODER_GET_STATUS_FLAG(decoder, SRLADECODER_STATUS_FLAG_SET_HEADER)); + EXPECT_TRUE(decoder->params_int != NULL); + EXPECT_TRUE(decoder->params_int[0] != NULL); + EXPECT_TRUE(decoder->rshifts != NULL); + + SRLADecoder_Destroy(decoder); + } + + /* ワーク領域渡しによるハンドル作成(失敗ケース) */ + { + void *work; + int32_t work_size; + struct SRLADecoder *decoder; + struct SRLADecoderConfig config; + + SRLADecoder_SetValidConfig(&config); + work_size = SRLADecoder_CalculateWorkSize(&config); + work = malloc(work_size); + + /* 引数が不正 */ + decoder = SRLADecoder_Create(NULL, work, work_size); + EXPECT_TRUE(decoder == NULL); + decoder = SRLADecoder_Create(&config, NULL, work_size); + EXPECT_TRUE(decoder == NULL); + decoder = SRLADecoder_Create(&config, work, 0); + EXPECT_TRUE(decoder == NULL); + + /* ワークサイズ不足 */ + decoder = SRLADecoder_Create(&config, work, work_size - 1); + EXPECT_TRUE(decoder == NULL); + + /* コンフィグが不正 */ + SRLADecoder_SetValidConfig(&config); + config.max_num_channels = 0; + decoder = SRLADecoder_Create(&config, work, work_size); + EXPECT_TRUE(decoder == NULL); + + SRLADecoder_SetValidConfig(&config); + config.max_num_parameters = 0; + decoder = SRLADecoder_Create(&config, work, work_size); + EXPECT_TRUE(decoder == NULL); + } + + /* 自前確保によるハンドル作成(失敗ケース) */ + { + struct SRLADecoder *decoder; + struct SRLADecoderConfig config; + + SRLADecoder_SetValidConfig(&config); + + /* 引数が不正 */ + decoder = SRLADecoder_Create(NULL, NULL, 0); + EXPECT_TRUE(decoder == NULL); + + /* コンフィグが不正 */ + SRLADecoder_SetValidConfig(&config); + config.max_num_channels = 0; + decoder = SRLADecoder_Create(&config, NULL, 0); + EXPECT_TRUE(decoder == NULL); + + SRLADecoder_SetValidConfig(&config); + config.max_num_parameters = 0; + decoder = SRLADecoder_Create(&config, NULL, 0); + EXPECT_TRUE(decoder == NULL); + } +} + +/* 1ブロックデコードテスト */ +TEST(SRLADecoderTest, DecodeBlockTest) +{ + /* ヘッダ設定前にデコードしてエラー */ + { + struct SRLADecoder *decoder; + struct SRLADecoderConfig config; + struct SRLAHeader header; + uint8_t *data; + int32_t *output[SRLA_MAX_NUM_CHANNELS]; + uint32_t ch, sufficient_size, output_size, out_num_samples; + + SRLA_SetValidHeader(&header); + SRLADecoder_SetValidConfig(&config); + + /* 十分なデータサイズ */ + sufficient_size = (2 * header.num_channels * header.num_samples_per_block * header.bits_per_sample) / 8; + + /* データ領域確保 */ + data = (uint8_t *)malloc(sufficient_size); + for (ch = 0; ch < header.num_channels; ch++) { + output[ch] = (int32_t *)malloc(sizeof(int32_t) * header.num_samples_per_block); + } + + /* デコーダ作成 */ + decoder = SRLADecoder_Create(&config, NULL, 0); + ASSERT_TRUE(decoder != NULL); + + /* ヘッダセット前にデコーダしようとする */ + EXPECT_EQ(SRLA_APIRESULT_PARAMETER_NOT_SET, + SRLADecoder_DecodeBlock(decoder, data, sufficient_size, output, header.num_channels, header.num_samples_per_block, &output_size, &out_num_samples)); + + /* ヘッダをセット */ + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_SetHeader(decoder, &header)); + + /* 領域の開放 */ + for (ch = 0; ch < header.num_channels; ch++) { + free(output[ch]); + } + free(data); + SRLADecoder_Destroy(decoder); + } + + /* 無音データをエンコードデコードしてみる */ + { + struct SRLAEncoder *encoder; + struct SRLADecoder *decoder; + struct SRLAEncoderConfig encoder_config; + struct SRLADecoderConfig decoder_config; + struct SRLAEncodeParameter parameter; + struct SRLAHeader header, tmp_header; + uint8_t *data; + int32_t *input[SRLA_MAX_NUM_CHANNELS]; + int32_t *output[SRLA_MAX_NUM_CHANNELS]; + uint32_t ch, sufficient_size, output_size, decode_output_size, out_num_samples; + + SRLA_SetValidHeader(&header); + SRLAEncoder_SetValidConfig(&encoder_config); + SRLADecoder_SetValidConfig(&decoder_config); + + /* 十分なデータサイズ */ + sufficient_size = (2 * header.num_channels * header.num_samples_per_block * header.bits_per_sample) / 8; + + /* データ領域確保 */ + data = (uint8_t *)malloc(sufficient_size); + for (ch = 0; ch < header.num_channels; ch++) { + input[ch] = (int32_t *)malloc(sizeof(int32_t) * header.num_samples_per_block); + output[ch] = (int32_t *)malloc(sizeof(int32_t) * header.num_samples_per_block); + } + + /* エンコーダデコーダ作成 */ + encoder = SRLAEncoder_Create(&encoder_config, NULL, 0); + decoder = SRLADecoder_Create(&decoder_config, NULL, 0); + ASSERT_TRUE(encoder != NULL); + ASSERT_TRUE(decoder != NULL); + + /* 入力に無音セット */ + for (ch = 0; ch < header.num_channels; ch++) { + memset(input[ch], 0, sizeof(int32_t) * header.num_samples_per_block); + } + + /* ヘッダを元にパラメータを設定 */ + SRLAEncoder_ConvertHeaderToParameter(&header, ¶meter); + + /* 入力データをエンコード */ + EXPECT_EQ(SRLA_APIRESULT_OK, SRLAEncoder_SetEncodeParameter(encoder, ¶meter)); + EXPECT_EQ(SRLA_APIRESULT_OK, + SRLAEncoder_EncodeWhole(encoder, input, header.num_samples_per_block, data, sufficient_size, &output_size)); + + /* エンコードデータを簡易チェック */ + EXPECT_TRUE(output_size > SRLA_HEADER_SIZE); + EXPECT_TRUE(output_size < sufficient_size); + + /* デコード */ + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, output_size, &tmp_header)); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_SetHeader(decoder, &tmp_header)); + EXPECT_EQ(SRLA_APIRESULT_OK, + SRLADecoder_DecodeBlock(decoder, data + SRLA_HEADER_SIZE, output_size - SRLA_HEADER_SIZE, + output, header.num_channels, tmp_header.num_samples_per_block, &decode_output_size, &out_num_samples)); + + /* 出力チェック */ + EXPECT_EQ(output_size - SRLA_HEADER_SIZE, decode_output_size); + EXPECT_EQ(header.num_samples_per_block, out_num_samples); + for (ch = 0; ch < header.num_channels; ch++) { + EXPECT_EQ(0, memcmp(input[ch], output[ch], sizeof(int32_t) * header.num_samples_per_block)); + } + + /* 領域の開放 */ + for (ch = 0; ch < header.num_channels; ch++) { + free(output[ch]); + free(input[ch]); + } + free(data); + SRLADecoder_Destroy(decoder); + SRLAEncoder_Destroy(encoder); + } + + /* デコード失敗テスト */ + { + struct SRLAEncoder *encoder; + struct SRLADecoder *decoder; + struct SRLAEncoderConfig encoder_config; + struct SRLADecoderConfig decoder_config; + struct SRLAEncodeParameter parameter; + struct SRLAHeader header, tmp_header; + uint8_t *data; + int32_t *input[SRLA_MAX_NUM_CHANNELS]; + int32_t *output[SRLA_MAX_NUM_CHANNELS]; + uint32_t ch, sufficient_size, output_size, decode_output_size, out_num_samples; + + SRLA_SetValidHeader(&header); + SRLAEncoder_SetValidConfig(&encoder_config); + SRLADecoder_SetValidConfig(&decoder_config); + + /* 十分なデータサイズ */ + sufficient_size = (2 * header.num_channels * header.num_samples_per_block * header.bits_per_sample) / 8; + + /* データ領域確保 */ + data = (uint8_t *)malloc(sufficient_size); + for (ch = 0; ch < header.num_channels; ch++) { + input[ch] = (int32_t *)malloc(sizeof(int32_t) * header.num_samples_per_block); + output[ch] = (int32_t *)malloc(sizeof(int32_t) * header.num_samples_per_block); + } + + /* エンコーダデコーダ作成 */ + encoder = SRLAEncoder_Create(&encoder_config, NULL, 0); + decoder = SRLADecoder_Create(&decoder_config, NULL, 0); + ASSERT_TRUE(encoder != NULL); + ASSERT_TRUE(decoder != NULL); + + /* 入力に無音セット */ + for (ch = 0; ch < header.num_channels; ch++) { + memset(input[ch], 0, sizeof(int32_t) * header.num_samples_per_block); + } + + /* ヘッダを元にパラメータを設定 */ + SRLAEncoder_ConvertHeaderToParameter(&header, ¶meter); + + /* 入力データをエンコード */ + EXPECT_EQ(SRLA_APIRESULT_OK, SRLAEncoder_SetEncodeParameter(encoder, ¶meter)); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLAEncoder_EncodeWhole(encoder, input, header.num_samples_per_block, data, sufficient_size, &output_size)); + + /* エンコードデータを簡易チェック */ + EXPECT_TRUE(output_size > SRLA_HEADER_SIZE); + EXPECT_TRUE(output_size < sufficient_size); + + /* ヘッダデコード */ + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_DecodeHeader(data, output_size, &tmp_header)); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLADecoder_SetHeader(decoder, &tmp_header)); + + /* 不正な引数 */ + EXPECT_EQ(SRLA_APIRESULT_INVALID_ARGUMENT, + SRLADecoder_DecodeBlock(NULL, data, output_size, output, header.num_channels, tmp_header.num_samples_per_block, &decode_output_size, &out_num_samples)); + EXPECT_EQ(SRLA_APIRESULT_INVALID_ARGUMENT, + SRLADecoder_DecodeBlock(decoder, NULL, output_size, output, header.num_channels, tmp_header.num_samples_per_block, &decode_output_size, &out_num_samples)); + EXPECT_EQ(SRLA_APIRESULT_INVALID_ARGUMENT, + SRLADecoder_DecodeBlock(decoder, data, output_size, NULL, header.num_channels, tmp_header.num_samples_per_block, &decode_output_size, &out_num_samples)); + EXPECT_EQ(SRLA_APIRESULT_INVALID_ARGUMENT, + SRLADecoder_DecodeBlock(decoder, data, output_size, output, header.num_channels, tmp_header.num_samples_per_block, NULL, &out_num_samples)); + EXPECT_EQ(SRLA_APIRESULT_INVALID_ARGUMENT, + SRLADecoder_DecodeBlock(decoder, data, output_size, output, header.num_channels, tmp_header.num_samples_per_block, &decode_output_size, NULL)); + + /* データサイズ不足 */ + EXPECT_EQ(SRLA_APIRESULT_INSUFFICIENT_DATA, + SRLADecoder_DecodeBlock(decoder, data + SRLA_HEADER_SIZE, output_size - SRLA_HEADER_SIZE - 1, + output, header.num_channels, tmp_header.num_samples_per_block, &decode_output_size, &out_num_samples)); + + /* データを一部破壊した場合にエラーを返すか */ + + /* 同期コード(データ先頭16bit)破壊 */ + EXPECT_EQ(SRLA_APIRESULT_OK, SRLAEncoder_EncodeWhole(encoder, input, header.num_samples_per_block, data, sufficient_size, &output_size)); + data[SRLA_HEADER_SIZE] ^= 0xFF; + EXPECT_EQ(SRLA_APIRESULT_INVALID_FORMAT, + SRLADecoder_DecodeBlock(decoder, data + SRLA_HEADER_SIZE, output_size - SRLA_HEADER_SIZE, + output, header.num_channels, tmp_header.num_samples_per_block, &decode_output_size, &out_num_samples)); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLAEncoder_EncodeWhole(encoder, input, header.num_samples_per_block, data, sufficient_size, &output_size)); + data[SRLA_HEADER_SIZE + 1] ^= 0xFF; + EXPECT_EQ(SRLA_APIRESULT_INVALID_FORMAT, + SRLADecoder_DecodeBlock(decoder, data + SRLA_HEADER_SIZE, output_size - SRLA_HEADER_SIZE, + output, header.num_channels, tmp_header.num_samples_per_block, &decode_output_size, &out_num_samples)); + /* ブロックデータタイプ不正: データ破損検知 */ + EXPECT_EQ(SRLA_APIRESULT_OK, SRLAEncoder_EncodeWhole(encoder, input, header.num_samples_per_block, data, sufficient_size, &output_size)); + data[SRLA_HEADER_SIZE + 8] = 0xC0; + EXPECT_EQ(SRLA_APIRESULT_DETECT_DATA_CORRUPTION, + SRLADecoder_DecodeBlock(decoder, data + SRLA_HEADER_SIZE, output_size - SRLA_HEADER_SIZE, + output, header.num_channels, tmp_header.num_samples_per_block, &decode_output_size, &out_num_samples)); + /* ブロックチャンネルあたりサンプル数不正: データ破損検知 */ + EXPECT_EQ(SRLA_APIRESULT_OK, SRLAEncoder_EncodeWhole(encoder, input, header.num_samples_per_block, data, sufficient_size, &output_size)); + data[SRLA_HEADER_SIZE + 9] ^= 0xFF; + EXPECT_EQ(SRLA_APIRESULT_DETECT_DATA_CORRUPTION, + SRLADecoder_DecodeBlock(decoder, data + SRLA_HEADER_SIZE, output_size - SRLA_HEADER_SIZE, + output, header.num_channels, tmp_header.num_samples_per_block, &decode_output_size, &out_num_samples)); + /* データの末尾1byteがビット反転: データ破損検知 */ + EXPECT_EQ(SRLA_APIRESULT_OK, SRLAEncoder_EncodeWhole(encoder, input, header.num_samples_per_block, data, sufficient_size, &output_size)); + data[output_size - 1] ^= 0xEF; /* note: Fletcherは0xFFのビット反転の破損を検知できない(mod 255の都合上) */ + EXPECT_EQ(SRLA_APIRESULT_DETECT_DATA_CORRUPTION, + SRLADecoder_DecodeBlock(decoder, data + SRLA_HEADER_SIZE, output_size - SRLA_HEADER_SIZE, + output, header.num_channels, tmp_header.num_samples_per_block, &decode_output_size, &out_num_samples)); + + /* 領域の開放 */ + for (ch = 0; ch < header.num_channels; ch++) { + free(output[ch]); + free(input[ch]); + } + free(data); + SRLADecoder_Destroy(decoder); + SRLAEncoder_Destroy(encoder); + } +} diff --git a/test/srla_decoder/srla_lpc_synthesize_test.cpp b/test/srla_decoder/srla_lpc_synthesize_test.cpp new file mode 100644 index 0000000..5342ca7 --- /dev/null +++ b/test/srla_decoder/srla_lpc_synthesize_test.cpp @@ -0,0 +1,4 @@ +/* テスト対象のモジュール */ +extern "C" { +#include "../../libs/srla_decoder/src/srla_lpc_synthesize.c" +} diff --git a/test/srla_encode_decode/CMakeLists.txt b/test/srla_encode_decode/CMakeLists.txt new file mode 100644 index 0000000..5c8da6e --- /dev/null +++ b/test/srla_encode_decode/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# テスト名 +set(TEST_NAME srla_encode_decode_test) + +# 実行形式ファイル +add_executable(${TEST_NAME} + main.cpp + ) + +# インクルードディレクトリ +include_directories(${PROJECT_ROOT_PATH}/include) + +# リンクするライブラリ +target_link_libraries(${TEST_NAME} gtest gtest_main srla_encoder srla_decoder srla_coder srla_internal byte_array bit_stream lpc) +if (NOT MSVC) +target_link_libraries(${TEST_NAME} pthread) +endif() + +# コンパイルオプション +set_target_properties(${TEST_NAME} + PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) + +add_test( + NAME srla_encode_decode + COMMAND $ + ) + +# run with: ctest -L lib +set_property( + TEST srla_encode_decode + PROPERTY LABELS lib srla_encode_decode + ) diff --git a/test/srla_encode_decode/main.cpp b/test/srla_encode_decode/main.cpp new file mode 100644 index 0000000..ae115e7 --- /dev/null +++ b/test/srla_encode_decode/main.cpp @@ -0,0 +1,532 @@ +#include +#include +#include + +#include + +#include "srla_encoder.h" +#include "srla_decoder.h" +#include "srla_utility.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/* 様々な波形がエンコード -> デコードが元に戻るかを確認するテスト */ + +/* 波形生成関数 */ +typedef void (*GenerateWaveFunction)(double **data, uint32_t num_channels, uint32_t num_samples); + +/* テストケース */ +struct EncodeDecodeTestCase { + struct SRLAEncodeParameter encode_parameter; /* エンコードパラメータ */ + uint32_t offset_lshift; /* オフセット分の左シフト */ + uint32_t num_samples; /* サンプル数 */ + GenerateWaveFunction gen_wave_func; /* 波形生成関数 */ +}; + +/* 無音の生成 */ +static void SRLAEncodeDecodeTest_GenerateSilence(double **data, uint32_t num_channels, uint32_t num_samples); +/* サイン波の生成 */ +static void SRLAEncodeDecodeTest_GenerateSinWave(double **data, uint32_t num_channels, uint32_t num_samples); +/* サイン波(チャンネルごとに逆相)の部 */ +static void SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave(double **data, uint32_t num_channels, uint32_t num_samples); +/* 白色雑音の生成 */ +static void SRLAEncodeDecodeTest_GenerateWhiteNoise(double **data, uint32_t num_channels, uint32_t num_samples); +/* チャープ信号の生成 */ +static void SRLAEncodeDecodeTest_GenerateChirp(double **data, uint32_t num_channels, uint32_t num_samples); +/* 正定数信号の生成 */ +static void SRLAEncodeDecodeTest_GeneratePositiveConstant(double **data, uint32_t num_channels, uint32_t num_samples); +/* 負定数信号の生成 */ +static void SRLAEncodeDecodeTest_GenerateNegativeConstant(double **data, uint32_t num_channels, uint32_t num_samples); +/* ナイキスト周期の振動の生成 */ +static void SRLAEncodeDecodeTest_GenerateNyquistOsc(double **data, uint32_t num_channels, uint32_t num_samples); +/* ガウス雑音の生成 */ +static void SRLAEncodeDecodeTest_GenerateGaussNoise(double **data, uint32_t num_channels, uint32_t num_samples); + +/* 無音の生成 */ +static void SRLAEncodeDecodeTest_GenerateSilence( + double **data, uint32_t num_channels, uint32_t num_samples) +{ + uint32_t smpl, ch; + + assert(data != NULL); + + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + data[ch][smpl] = 0.0f; + } + } +} + +/* サイン波の生成 */ +static void SRLAEncodeDecodeTest_GenerateSinWave( + double **data, uint32_t num_channels, uint32_t num_samples) +{ + uint32_t smpl, ch; + + assert(data != NULL); + + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + data[ch][smpl] = sin(440.0f * 2 * M_PI * smpl / 44100.0f); + } + } +} + +/* サイン波(チャンネルごとに逆相)の部 */ +static void SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave( + double **data, uint32_t num_channels, uint32_t num_samples) +{ + uint32_t smpl, ch; + + assert(data != NULL); + + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + data[ch][smpl] = pow(-1, ch) * sin(440.0f * 2 * M_PI * smpl / 44100.0f); + } + } +} + +/* 白色雑音の生成 */ +static void SRLAEncodeDecodeTest_GenerateWhiteNoise( + double **data, uint32_t num_channels, uint32_t num_samples) +{ + uint32_t smpl, ch; + + assert(data != NULL); + + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + data[ch][smpl] = 2.0f * ((double)rand() / RAND_MAX - 0.5f); + } + } +} + +/* チャープ信号の生成 */ +static void SRLAEncodeDecodeTest_GenerateChirp( + double **data, uint32_t num_channels, uint32_t num_samples) +{ + uint32_t smpl, ch; + double period; + + assert(data != NULL); + + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + period = num_samples - smpl; + data[ch][smpl] = sin((2.0f * M_PI * smpl) / period); + } + } +} + +/* 正定数信号の生成 */ +static void SRLAEncodeDecodeTest_GeneratePositiveConstant( + double **data, uint32_t num_channels, uint32_t num_samples) +{ + uint32_t smpl, ch; + + assert(data != NULL); + + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + data[ch][smpl] = 1.0f; + } + } +} + +/* 負定数信号の生成 */ +static void SRLAEncodeDecodeTest_GenerateNegativeConstant( + double **data, uint32_t num_channels, uint32_t num_samples) +{ + uint32_t smpl, ch; + + assert(data != NULL); + + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + data[ch][smpl] = -1.0f; + } + } +} + +/* ナイキスト周期の振動の生成 */ +static void SRLAEncodeDecodeTest_GenerateNyquistOsc( + double **data, uint32_t num_channels, uint32_t num_samples) +{ + uint32_t smpl, ch; + + assert(data != NULL); + + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + data[ch][smpl] = (smpl % 2 == 0) ? 1.0f : -1.0f; + } + } +} + +/* ガウス雑音の生成 */ +static void SRLAEncodeDecodeTest_GenerateGaussNoise( + double **data, uint32_t num_channels, uint32_t num_samples) +{ + uint32_t smpl, ch; + double x, y; + + assert(data != NULL); + + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + /* ボックス-ミューラー法 */ + x = (double)rand() / RAND_MAX; + y = (double)rand() / RAND_MAX; + /* 分散は0.1f */ + data[ch][smpl] = 0.25f * sqrt(-2.0f * log(x)) * cos(2.0f * M_PI * y); + data[ch][smpl] = (data[ch][smpl] >= 1.0f) ? 1.0f : data[ch][smpl]; + data[ch][smpl] = (data[ch][smpl] <= -1.0f) ? -1.0f : data[ch][smpl]; + } + } +} + +/* double入力データの固定小数化 */ +static void SRLAEncodeDecodeTest_InputDoubleToInputFixedFloat( + const struct SRLAEncodeParameter *param, uint32_t offset_lshift, + double **input_double, uint32_t num_channels, uint32_t num_samples, int32_t **input_int32) +{ + uint32_t ch, smpl; + + assert((input_double != NULL) && (input_int32 != NULL)); + + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + assert(fabs(input_double[ch][smpl]) <= 1.0f); + /* まずはビット幅のデータを作る */ + input_int32[ch][smpl] + = (int32_t)SRLAUtility_Round(input_double[ch][smpl] * pow(2, param->bits_per_sample - 1)); + /* クリップ */ + if (input_int32[ch][smpl] >= (1L << (param->bits_per_sample - 1))) { + input_int32[ch][smpl] = (1L << (param->bits_per_sample - 1)) - 1; + } + /* 左シフト量だけ下位ビットのデータを消す */ + input_int32[ch][smpl] &= ~((1UL << offset_lshift) - 1); + } + } +} + +/* 単一のテストケースを実行 */ +static int32_t SRLAEncodeDecodeTest_ExecuteTestCase(const struct EncodeDecodeTestCase* test_case) +{ + int32_t ret; + uint32_t smpl, ch; + uint32_t num_samples, num_channels, data_size; + uint32_t output_size; + double **input_double; + int32_t **input; + uint8_t *data; + int32_t **output; + SRLAApiResult api_ret; + + struct SRLAEncoderConfig encoder_config; + struct SRLADecoderConfig decoder_config; + struct SRLAEncoder *encoder; + struct SRLADecoder *decoder; + + assert(test_case != NULL); + assert(test_case->num_samples <= (1UL << 14)); /* 長過ぎる入力はNG */ + + num_samples = test_case->num_samples; + num_channels = test_case->encode_parameter.num_channels; + /* 十分なデータサイズを用意(入力データPCMの2倍) */ + data_size = SRLA_HEADER_SIZE + (2 * num_channels * num_samples * test_case->encode_parameter.bits_per_sample) / 8; + + /* エンコード・デコードコンフィグ作成 */ + /* FIXME: 仮値 */ + encoder_config.max_num_channels = num_channels; + encoder_config.max_num_samples_per_block = test_case->encode_parameter.num_samples_per_block; + encoder_config.max_num_parameters = 32; + decoder_config.max_num_channels = num_channels; + decoder_config.max_num_parameters = 32; + decoder_config.check_checksum = 1; + + /* 一時領域の割り当て */ + input_double = (double **)malloc(sizeof(double*) * num_channels); + input = (int32_t **)malloc(sizeof(int32_t*) * num_channels); + output = (int32_t **)malloc(sizeof(int32_t*) * num_channels); + data = (uint8_t *)malloc(data_size); + for (ch = 0; ch < num_channels; ch++) { + input_double[ch] = (double *)malloc(sizeof(double) * num_samples); + input[ch] = (int32_t *)malloc(sizeof(int32_t) * num_samples); + output[ch] = (int32_t *)malloc(sizeof(int32_t) * num_samples); + } + + /* エンコード・デコードハンドル作成 */ + encoder = SRLAEncoder_Create(&encoder_config, NULL, 0); + decoder = SRLADecoder_Create(&decoder_config, NULL, 0); + if ((encoder == NULL) || (decoder == NULL)) { + ret = 1; + goto EXIT; + } + + /* 波形生成 */ + test_case->gen_wave_func(input_double, num_channels, num_samples); + + /* 固定小数化 */ + SRLAEncodeDecodeTest_InputDoubleToInputFixedFloat( + &test_case->encode_parameter, test_case->offset_lshift, input_double, num_channels, num_samples, input); + + /* 波形フォーマットと波形パラメータをセット */ + if ((api_ret = SRLAEncoder_SetEncodeParameter(encoder, &test_case->encode_parameter)) != SRLA_APIRESULT_OK) { + fprintf(stderr, "Failed to set encode parameter. ret:%d \n", api_ret); + ret = 2; + goto EXIT; + } + + /* エンコード */ + if ((api_ret = SRLAEncoder_EncodeWhole(encoder, + (const int32_t **)input, num_samples, data, data_size, &output_size)) != SRLA_APIRESULT_OK) { + fprintf(stderr, "Encode failed! ret:%d \n", api_ret); + ret = 3; + goto EXIT; + } + + /* デコード */ + if ((api_ret = SRLADecoder_DecodeWhole(decoder, data, output_size, output, num_channels, num_samples)) != SRLA_APIRESULT_OK) { + fprintf(stderr, "Decode failed! ret:%d \n", api_ret); + ret = 4; + goto EXIT; + } + + /* 一致確認 */ + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + if (input[ch][smpl] != output[ch][smpl]) { + printf("%5d %12d vs %12d \n", smpl, input[ch][smpl], output[ch][smpl]); + ret = 5; + goto EXIT; + } + } + } + + /* ここまで来れば成功 */ + ret = 0; + +EXIT: + /* ハンドル開放 */ + SRLADecoder_Destroy(decoder); + SRLAEncoder_Destroy(encoder); + + /* 一時領域の開放 */ + for (ch = 0; ch < num_channels; ch++) { + free(input_double[ch]); + free(input[ch]); + free(output[ch]); + } + free(input_double); + free(input); + free(output); + free(data); + + return ret; +} + +/* インスタンス作成破棄テスト */ +TEST(SRLAEncodeDecodeTest, EncodeDecodeCheckTest) +{ + int32_t test_ret; + uint32_t test_no; + + /* テストケース配列 */ + static const struct EncodeDecodeTestCase test_case[] = { + /* 無音の部 */ + { { 1, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 1, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 1, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 2, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 2, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 2, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 8, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 8, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 8, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 1, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 1, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 1, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 2, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 2, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 2, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 8, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 8, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + { { 8, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSilence }, + /* サイン波の部 */ + { { 1, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 1, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 1, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 2, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 2, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 2, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 8, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 8, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 8, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 1, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 1, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 1, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 2, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 2, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 2, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 8, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 8, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + { { 8, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinWave }, + /* サイン波(チャンネルごとに逆相)の部 */ + { { 1, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 1, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 1, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 2, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 2, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 2, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 8, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 8, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 8, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 1, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 1, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 1, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 2, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 2, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 2, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 8, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 8, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + { { 8, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateSinChSignFlippedWave }, + /* 白色雑音の部 */ + { { 1, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 1, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 1, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 2, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 2, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 2, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 8, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 8, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 8, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 1, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 1, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 1, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 2, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 2, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 2, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 8, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 8, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + { { 8, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateWhiteNoise }, + /* チャープ信号の部 */ + { { 1, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 1, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 1, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 2, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 2, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 2, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 8, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 8, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 8, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 1, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 1, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 1, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 2, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 2, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 2, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 8, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 8, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + { { 8, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateChirp }, + /* 正定数信号の部 */ + { { 1, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 1, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 1, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 2, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 2, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 2, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 8, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 8, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 8, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 1, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 1, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 1, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 2, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 2, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 2, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 8, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 8, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + { { 8, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GeneratePositiveConstant }, + /* 負定数信号の部 */ + { { 1, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 1, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 1, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 2, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 2, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 2, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 8, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 8, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 8, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 1, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 1, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 1, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 2, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 2, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 2, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 8, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 8, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + { { 8, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNegativeConstant }, + /* ナイキスト周期振動信号の部 */ + { { 1, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 1, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 1, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 2, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 2, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 2, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 8, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 8, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 8, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 1, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 1, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 1, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 2, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 2, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 2, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 8, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 8, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + { { 8, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateNyquistOsc }, + /* ガウス雑音信号の部 */ + { { 1, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 1, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 1, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 2, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 2, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 2, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 8, 8, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 8, 16, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 8, 24, 8000, 1024, 0 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 1, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 1, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 1, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 2, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 2, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 2, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 8, 8, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 8, 16, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + { { 8, 24, 8000, 1024, SRLA_NUM_PARAMETER_PRESETS - 1 }, 0, 8192, SRLAEncodeDecodeTest_GenerateGaussNoise }, + }; + + /* テストケース数 */ + const uint32_t num_test_case = sizeof(test_case) / sizeof(test_case[0]); + + /* デバッグのしやすさのため、乱数シードを固定 */ + srand(0); + + for (test_no = 0; test_no < num_test_case; test_no++) { + test_ret = SRLAEncodeDecodeTest_ExecuteTestCase(&test_case[test_no]); + EXPECT_EQ(0, test_ret); + if (test_ret != 0) { + fprintf(stderr, "Encode / Decode Test Failed at case %d. ret:%d \n", test_no, test_ret); + } + } +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/srla_encoder/CMakeLists.txt b/test/srla_encoder/CMakeLists.txt new file mode 100644 index 0000000..9c244ee --- /dev/null +++ b/test/srla_encoder/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# テスト名 +set(TEST_NAME srla_encoder_test) + +# 実行形式ファイル +add_executable(${TEST_NAME} + srla_encoder_test.cpp + srla_lpc_predict_test.cpp + main.cpp + ) + +# インクルードディレクトリ +include_directories(${PROJECT_ROOT_PATH}/libs/srla_encoder/include) + +# リンクするライブラリ +target_link_libraries(${TEST_NAME} gtest gtest_main byte_array bit_stream lpc srla_internal srla_coder) +if (NOT MSVC) +target_link_libraries(${TEST_NAME} pthread) +endif() + +# コンパイルオプション +set_target_properties(${TEST_NAME} + PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) + +add_test( + NAME srla_encoder + COMMAND $ + ) + +# run with: ctest -L lib +set_property( + TEST srla_encoder + PROPERTY LABELS lib srla_encoder + ) diff --git a/test/srla_encoder/main.cpp b/test/srla_encoder/main.cpp new file mode 100644 index 0000000..96d7200 --- /dev/null +++ b/test/srla_encoder/main.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/srla_encoder/srla_encoder_test.cpp b/test/srla_encoder/srla_encoder_test.cpp new file mode 100644 index 0000000..b9e88ff --- /dev/null +++ b/test/srla_encoder/srla_encoder_test.cpp @@ -0,0 +1,430 @@ +#include +#include + +#include + +/* テスト対象のモジュール */ +extern "C" { +#include "../../libs/srla_encoder/src/srla_encoder.c" +} + +/* 有効なヘッダをセット */ +#define SRLA_SetValidHeader(p_header)\ + do {\ + struct SRLAHeader *header__p = p_header;\ + header__p->format_version = SRLA_FORMAT_VERSION;\ + header__p->codec_version = SRLA_CODEC_VERSION;\ + header__p->num_channels = 1;\ + header__p->sampling_rate = 44100;\ + header__p->bits_per_sample = 16;\ + header__p->num_samples = 1024;\ + header__p->num_samples_per_block = 32;\ + header__p->preset = 0;\ + } while (0); + +/* 有効なエンコードパラメータをセット */ +#define SRLAEncoder_SetValidEncodeParameter(p_parameter)\ + do {\ + struct SRLAEncodeParameter *param__p = p_parameter;\ + param__p->num_channels = 1;\ + param__p->bits_per_sample = 16;\ + param__p->sampling_rate = 44100;\ + param__p->num_samples_per_block = 1024;\ + param__p->preset = 0;\ + } while (0); + +/* 有効なコンフィグをセット */ +#define SRLAEncoder_SetValidConfig(p_config)\ + do {\ + struct SRLAEncoderConfig *config__p = p_config;\ + config__p->max_num_channels = 8;\ + config__p->max_num_samples_per_block = 4096;\ + config__p->max_num_parameters = 32;\ + } while (0); + +/* ヘッダエンコードテスト */ +TEST(SRLAEncoderTest, EncodeHeaderTest) +{ + /* ヘッダエンコード成功ケース */ + { + struct SRLAHeader header; + uint8_t data[SRLA_HEADER_SIZE] = { 0, }; + + SRLA_SetValidHeader(&header); + EXPECT_EQ(SRLA_APIRESULT_OK, SRLAEncoder_EncodeHeader(&header, data, sizeof(data))); + + /* 簡易チェック */ + EXPECT_EQ('S', data[0]); + EXPECT_EQ('F', data[1]); + EXPECT_EQ('L', data[2]); + EXPECT_EQ('A', data[3]); + } + + /* ヘッダエンコード失敗ケース */ + { + struct SRLAHeader header; + uint8_t data[SRLA_HEADER_SIZE] = { 0, }; + + /* 引数が不正 */ + SRLA_SetValidHeader(&header); + EXPECT_EQ(SRLA_APIRESULT_INVALID_ARGUMENT, SRLAEncoder_EncodeHeader(NULL, data, sizeof(data))); + EXPECT_EQ(SRLA_APIRESULT_INVALID_ARGUMENT, SRLAEncoder_EncodeHeader(&header, NULL, sizeof(data))); + + /* データサイズ不足 */ + SRLA_SetValidHeader(&header); + EXPECT_EQ(SRLA_APIRESULT_INSUFFICIENT_BUFFER, SRLAEncoder_EncodeHeader(&header, data, sizeof(data) - 1)); + EXPECT_EQ(SRLA_APIRESULT_INSUFFICIENT_BUFFER, SRLAEncoder_EncodeHeader(&header, data, SRLA_HEADER_SIZE - 1)); + + /* 異常なチャンネル数 */ + SRLA_SetValidHeader(&header); + header.num_channels = 0; + EXPECT_EQ(SRLA_APIRESULT_INVALID_FORMAT, SRLAEncoder_EncodeHeader(&header, data, sizeof(data))); + + /* 異常なサンプル数 */ + SRLA_SetValidHeader(&header); + header.num_samples = 0; + EXPECT_EQ(SRLA_APIRESULT_INVALID_FORMAT, SRLAEncoder_EncodeHeader(&header, data, sizeof(data))); + + /* 異常なサンプリングレート */ + SRLA_SetValidHeader(&header); + header.sampling_rate = 0; + EXPECT_EQ(SRLA_APIRESULT_INVALID_FORMAT, SRLAEncoder_EncodeHeader(&header, data, sizeof(data))); + + /* 異常なビット深度 */ + SRLA_SetValidHeader(&header); + header.bits_per_sample = 0; + EXPECT_EQ(SRLA_APIRESULT_INVALID_FORMAT, SRLAEncoder_EncodeHeader(&header, data, sizeof(data))); + + /* 異常なブロックあたりサンプル数 */ + SRLA_SetValidHeader(&header); + header.num_samples_per_block = 0; + EXPECT_EQ(SRLA_APIRESULT_INVALID_FORMAT, SRLAEncoder_EncodeHeader(&header, data, sizeof(data))); + + /* 異常なプリセット */ + SRLA_SetValidHeader(&header); + header.preset = SRLA_NUM_PARAMETER_PRESETS; + EXPECT_EQ(SRLA_APIRESULT_INVALID_FORMAT, SRLAEncoder_EncodeHeader(&header, data, sizeof(data))); + } + +} + +/* エンコードハンドル作成破棄テスト */ +TEST(SRLAEncoderTest, CreateDestroyHandleTest) +{ + /* ワークサイズ計算テスト */ + { + int32_t work_size; + struct SRLAEncoderConfig config; + + /* 最低限構造体本体よりは大きいはず */ + SRLAEncoder_SetValidConfig(&config); + work_size = SRLAEncoder_CalculateWorkSize(&config); + ASSERT_TRUE(work_size > sizeof(struct SRLAEncoder)); + + /* 不正な引数 */ + EXPECT_TRUE(SRLAEncoder_CalculateWorkSize(NULL) < 0); + + /* 不正なコンフィグ */ + SRLAEncoder_SetValidConfig(&config); + config.max_num_channels = 0; + EXPECT_TRUE(SRLAEncoder_CalculateWorkSize(&config) < 0); + + SRLAEncoder_SetValidConfig(&config); + config.max_num_samples_per_block = 0; + EXPECT_TRUE(SRLAEncoder_CalculateWorkSize(&config) < 0); + + SRLAEncoder_SetValidConfig(&config); + config.max_num_parameters = 0; + EXPECT_TRUE(SRLAEncoder_CalculateWorkSize(&config) < 0); + } + + /* ワーク領域渡しによるハンドル作成(成功例) */ + { + void *work; + int32_t work_size; + struct SRLAEncoder *encoder; + struct SRLAEncoderConfig config; + + SRLAEncoder_SetValidConfig(&config); + work_size = SRLAEncoder_CalculateWorkSize(&config); + work = malloc(work_size); + + encoder = SRLAEncoder_Create(&config, work, work_size); + ASSERT_TRUE(encoder != NULL); + EXPECT_TRUE(encoder->work == work); + EXPECT_EQ(encoder->set_parameter, 0); + EXPECT_EQ(encoder->alloced_by_own, 0); + EXPECT_TRUE(encoder->coder != NULL); + + SRLAEncoder_Destroy(encoder); + free(work); + } + + /* 自前確保によるハンドル作成(成功例) */ + { + struct SRLAEncoder *encoder; + struct SRLAEncoderConfig config; + + SRLAEncoder_SetValidConfig(&config); + + encoder = SRLAEncoder_Create(&config, NULL, 0); + ASSERT_TRUE(encoder != NULL); + EXPECT_TRUE(encoder->work != NULL); + EXPECT_EQ(encoder->set_parameter, 0); + EXPECT_EQ(encoder->alloced_by_own, 1); + EXPECT_TRUE(encoder->coder != NULL); + + SRLAEncoder_Destroy(encoder); + } + + /* ワーク領域渡しによるハンドル作成(失敗ケース) */ + { + void *work; + int32_t work_size; + struct SRLAEncoder *encoder; + struct SRLAEncoderConfig config; + + SRLAEncoder_SetValidConfig(&config); + work_size = SRLAEncoder_CalculateWorkSize(&config); + work = malloc(work_size); + + /* 引数が不正 */ + encoder = SRLAEncoder_Create(NULL, work, work_size); + EXPECT_TRUE(encoder == NULL); + encoder = SRLAEncoder_Create(&config, NULL, work_size); + EXPECT_TRUE(encoder == NULL); + encoder = SRLAEncoder_Create(&config, work, 0); + EXPECT_TRUE(encoder == NULL); + + /* ワークサイズ不足 */ + encoder = SRLAEncoder_Create(&config, work, work_size - 1); + EXPECT_TRUE(encoder == NULL); + + /* コンフィグが不正 */ + SRLAEncoder_SetValidConfig(&config); + config.max_num_channels = 0; + encoder = SRLAEncoder_Create(&config, work, work_size); + EXPECT_TRUE(encoder == NULL); + + SRLAEncoder_SetValidConfig(&config); + config.max_num_samples_per_block = 0; + encoder = SRLAEncoder_Create(&config, work, work_size); + EXPECT_TRUE(encoder == NULL); + + SRLAEncoder_SetValidConfig(&config); + config.max_num_parameters = 0; + encoder = SRLAEncoder_Create(&config, work, work_size); + EXPECT_TRUE(encoder == NULL); + + free(work); + } + + /* 自前確保によるハンドル作成(失敗ケース) */ + { + struct SRLAEncoder *encoder; + struct SRLAEncoderConfig config; + + SRLAEncoder_SetValidConfig(&config); + + /* 引数が不正 */ + encoder = SRLAEncoder_Create(NULL, NULL, 0); + EXPECT_TRUE(encoder == NULL); + + /* コンフィグが不正 */ + SRLAEncoder_SetValidConfig(&config); + config.max_num_channels = 0; + encoder = SRLAEncoder_Create(&config, NULL, 0); + EXPECT_TRUE(encoder == NULL); + + SRLAEncoder_SetValidConfig(&config); + config.max_num_samples_per_block = 0; + encoder = SRLAEncoder_Create(&config, NULL, 0); + EXPECT_TRUE(encoder == NULL); + + SRLAEncoder_SetValidConfig(&config); + config.max_num_parameters = 0; + encoder = SRLAEncoder_Create(&config, NULL, 0); + EXPECT_TRUE(encoder == NULL); + } +} + +/* 1ブロックエンコードテスト */ +TEST(SRLAEncoderTest, EncodeBlockTest) +{ + /* 無効な引数 */ + { + struct SRLAEncoder *encoder; + struct SRLAEncoderConfig config; + struct SRLAEncodeParameter parameter; + int32_t *input[SRLA_MAX_NUM_CHANNELS]; + uint8_t *data; + uint32_t ch, sufficient_size, output_size, num_samples; + + SRLAEncoder_SetValidEncodeParameter(¶meter); + SRLAEncoder_SetValidConfig(&config); + + /* 十分なデータサイズ */ + sufficient_size = (2 * parameter.num_channels * parameter.num_samples_per_block * parameter.bits_per_sample) / 8; + + /* データ領域確保 */ + data = (uint8_t *)malloc(sufficient_size); + for (ch = 0; ch < parameter.num_channels; ch++) { + input[ch] = (int32_t *)malloc(sizeof(int32_t) * parameter.num_samples_per_block); + } + + /* エンコーダ作成 */ + encoder = SRLAEncoder_Create(&config, NULL, 0); + ASSERT_TRUE(encoder != NULL); + + /* 無効な引数を渡す */ + EXPECT_EQ( + SRLA_APIRESULT_INVALID_ARGUMENT, + SRLAEncoder_EncodeBlock(NULL, input, parameter.num_samples_per_block, + data, sufficient_size, &output_size)); + EXPECT_EQ( + SRLA_APIRESULT_INVALID_ARGUMENT, + SRLAEncoder_EncodeBlock(encoder, NULL, parameter.num_samples_per_block, + data, sufficient_size, &output_size)); + EXPECT_EQ( + SRLA_APIRESULT_INVALID_ARGUMENT, + SRLAEncoder_EncodeBlock(encoder, input, 0, + data, sufficient_size, &output_size)); + EXPECT_EQ( + SRLA_APIRESULT_INVALID_ARGUMENT, + SRLAEncoder_EncodeBlock(encoder, input, parameter.num_samples_per_block, + NULL, sufficient_size, &output_size)); + EXPECT_EQ( + SRLA_APIRESULT_INVALID_ARGUMENT, + SRLAEncoder_EncodeBlock(encoder, input, parameter.num_samples_per_block, + data, 0, &output_size)); + EXPECT_EQ( + SRLA_APIRESULT_INVALID_ARGUMENT, + SRLAEncoder_EncodeBlock(encoder, input, parameter.num_samples_per_block, + data, sufficient_size, NULL)); + + /* 領域の開放 */ + for (ch = 0; ch < parameter.num_channels; ch++) { + free(input[ch]); + } + free(data); + SRLAEncoder_Destroy(encoder); + } + + /* パラメータ未セットでエンコード */ + { + struct SRLAEncoder *encoder; + struct SRLAEncoderConfig config; + struct SRLAEncodeParameter parameter; + int32_t *input[SRLA_MAX_NUM_CHANNELS]; + uint8_t *data; + uint32_t ch, sufficient_size, output_size, num_samples; + + SRLAEncoder_SetValidEncodeParameter(¶meter); + SRLAEncoder_SetValidConfig(&config); + + /* 十分なデータサイズ */ + sufficient_size = (2 * parameter.num_channels * parameter.num_samples_per_block * parameter.bits_per_sample) / 8; + + /* データ領域確保 */ + data = (uint8_t *)malloc(sufficient_size); + for (ch = 0; ch < parameter.num_channels; ch++) { + input[ch] = (int32_t *)malloc(sizeof(int32_t) * parameter.num_samples_per_block); + /* 無音セット */ + memset(input[ch], 0, sizeof(int32_t) * parameter.num_samples_per_block); + } + + /* エンコーダ作成 */ + encoder = SRLAEncoder_Create(&config, NULL, 0); + ASSERT_TRUE(encoder != NULL); + + /* パラメータセット前にエンコード: エラー */ + EXPECT_EQ( + SRLA_APIRESULT_PARAMETER_NOT_SET, + SRLAEncoder_EncodeBlock(encoder, input, parameter.num_samples_per_block, + data, sufficient_size, &output_size)); + + /* パラメータ設定 */ + EXPECT_EQ( + SRLA_APIRESULT_OK, + SRLAEncoder_SetEncodeParameter(encoder, ¶meter)); + + /* 1ブロックエンコード */ + EXPECT_EQ( + SRLA_APIRESULT_OK, + SRLAEncoder_EncodeBlock(encoder, input, parameter.num_samples_per_block, + data, sufficient_size, &output_size)); + + /* 領域の開放 */ + for (ch = 0; ch < parameter.num_channels; ch++) { + free(input[ch]); + } + free(data); + SRLAEncoder_Destroy(encoder); + } + + /* 無音エンコード */ + { + struct SRLAEncoder *encoder; + struct SRLAEncoderConfig config; + struct SRLAEncodeParameter parameter; + struct BitStream stream; + int32_t *input[SRLA_MAX_NUM_CHANNELS]; + uint8_t *data; + uint32_t ch, sufficient_size, output_size, num_samples; + uint32_t bitbuf; + + SRLAEncoder_SetValidEncodeParameter(¶meter); + SRLAEncoder_SetValidConfig(&config); + + /* 十分なデータサイズ */ + sufficient_size = (2 * parameter.num_channels * parameter.num_samples_per_block * parameter.bits_per_sample) / 8; + + /* データ領域確保 */ + data = (uint8_t *)malloc(sufficient_size); + for (ch = 0; ch < parameter.num_channels; ch++) { + input[ch] = (int32_t *)malloc(sizeof(int32_t) * parameter.num_samples_per_block); + /* 無音セット */ + memset(input[ch], 0, sizeof(int32_t) * parameter.num_samples_per_block); + } + + /* エンコーダ作成 */ + encoder = SRLAEncoder_Create(&config, NULL, 0); + ASSERT_TRUE(encoder != NULL); + + /* パラメータ設定 */ + EXPECT_EQ( + SRLA_APIRESULT_OK, + SRLAEncoder_SetEncodeParameter(encoder, ¶meter)); + + /* 1ブロックエンコード */ + EXPECT_EQ( + SRLA_APIRESULT_OK, + SRLAEncoder_EncodeBlock(encoder, input, parameter.num_samples_per_block, + data, sufficient_size, &output_size)); + + /* ブロック先頭の同期コードがあるので2バイトよりは大きいはず */ + EXPECT_TRUE(output_size > 2); + + /* 内容の確認 */ + BitReader_Open(&stream, data, output_size); + /* 同期コード */ + BitReader_GetBits(&stream, &bitbuf, 16); + EXPECT_EQ(SRLA_BLOCK_SYNC_CODE, bitbuf); + /* ブロックデータタイプ */ + BitReader_GetBits(&stream, &bitbuf, 2); + EXPECT_TRUE((bitbuf == SRLA_BLOCK_DATA_TYPE_COMPRESSDATA) + || (bitbuf == SRLA_BLOCK_DATA_TYPE_SILENT) + || (bitbuf == SRLA_BLOCK_DATA_TYPE_RAWDATA)); + /* この後データがエンコードされているので、まだ終端ではないはず */ + BitStream_Tell(&stream, (int32_t *)&bitbuf); + EXPECT_TRUE(bitbuf < output_size); + BitStream_Close(&stream); + + /* 領域の開放 */ + for (ch = 0; ch < parameter.num_channels; ch++) { + free(input[ch]); + } + free(data); + SRLAEncoder_Destroy(encoder); + } +} diff --git a/test/srla_encoder/srla_lpc_predict_test.cpp b/test/srla_encoder/srla_lpc_predict_test.cpp new file mode 100644 index 0000000..49d9ec8 --- /dev/null +++ b/test/srla_encoder/srla_lpc_predict_test.cpp @@ -0,0 +1,4 @@ +/* テスト対象のモジュール */ +extern "C" { +#include "../../libs/srla_encoder/src/srla_lpc_predict.c" +} diff --git a/test/srla_internal/CMakeLists.txt b/test/srla_internal/CMakeLists.txt new file mode 100644 index 0000000..4ae09b8 --- /dev/null +++ b/test/srla_internal/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# テスト名 +set(TEST_NAME srla_internal_test) + +# 実行形式ファイル +add_executable(${TEST_NAME} main.cpp) + +# インクルードディレクトリ +include_directories(${PROJECT_ROOT_PATH}/libs/srla_internal/include) + +# リンクするライブラリ +target_link_libraries(${TEST_NAME} gtest gtest_main) +if (NOT MSVC) +target_link_libraries(${TEST_NAME} pthread) +endif() + +# コンパイルオプション +set_target_properties(${TEST_NAME} + PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) + +# 実行パスをtmp以下に +add_test( + NAME srla_internal + WORKING_DIRECTORY $/tmp + COMMAND $ + ) + +# run with: ctest -L lib +set_property( + TEST srla_internal + PROPERTY LABELS lib srla_internal + ) + +# ビルド後にテストリソースを持ってくる +add_custom_command( + TARGET ${TEST_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory $/tmp + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/PriChanIcon.png $/tmp + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/a.wav $/tmp + ) diff --git a/test/srla_internal/PriChanIcon.png b/test/srla_internal/PriChanIcon.png new file mode 100644 index 0000000..23786bc Binary files /dev/null and b/test/srla_internal/PriChanIcon.png differ diff --git a/test/srla_internal/a.wav b/test/srla_internal/a.wav new file mode 100644 index 0000000..02685f1 Binary files /dev/null and b/test/srla_internal/a.wav differ diff --git a/test/srla_internal/main.cpp b/test/srla_internal/main.cpp new file mode 100644 index 0000000..522eaf1 --- /dev/null +++ b/test/srla_internal/main.cpp @@ -0,0 +1,81 @@ +#include +#include + +#include + +/* テスト対象のモジュール */ +extern "C" { +#include "../../libs/srla_internal/src/srla_utility.c" +} + +/* Fletcher16の計算テスト */ +TEST(SRLAUtilityTest, CalculateFletcher16Test) +{ + /* リファレンス値と一致するか? */ + { + uint32_t i; + uint16_t ret; + + /* テストケース */ + struct Fletcher16TestCaseForString { + const char *string; + size_t string_len; + uint16_t answer; + }; + + static const struct Fletcher16TestCaseForString fletcher16_test_case[] = { + { "abcde", 5, 0xC8F0 }, + { "abcdef", 6, 0x2057 }, + { "abcdefgh", 8, 0x0627 }, + }; + const uint32_t fletcher16_num_test_cases = sizeof(fletcher16_test_case) / sizeof(fletcher16_test_case[0]); + + for (i = 0; i < fletcher16_num_test_cases; i++) { + ret = SRLAUtility_CalculateFletcher16CheckSum( + (const uint8_t *)fletcher16_test_case[i].string, fletcher16_test_case[i].string_len); + EXPECT_EQ(ret, fletcher16_test_case[i].answer); + } + } + + /* 実データでテスト */ + { + struct stat fstat; + uint32_t i, data_size; + uint16_t ret; + uint8_t* data; + FILE* fp; + + /* テストケース */ + struct Fletcher16TestCaseForFile { + const char* filename; + uint16_t answer; + }; + + static const struct Fletcher16TestCaseForFile fletcher16_test_case[] = { + { "a.wav", 0x4C08 }, + { "PriChanIcon.png", 0x6DA9 }, + }; + const uint32_t crc16ibm_num_test_cases + = sizeof(fletcher16_test_case) / sizeof(fletcher16_test_case[0]); + + for (i = 0; i < crc16ibm_num_test_cases; i++) { + stat(fletcher16_test_case[i].filename, &fstat); + data_size = fstat.st_size; + data = (uint8_t*)malloc(fstat.st_size * sizeof(uint8_t)); + + fp = fopen(fletcher16_test_case[i].filename, "rb"); + fread(data, sizeof(uint8_t), data_size, fp); + ret = SRLAUtility_CalculateFletcher16CheckSum(data, data_size); + EXPECT_EQ(ret, fletcher16_test_case[i].answer); + + free(data); + fclose(fp); + } + } +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/wav/16bit.wav b/test/wav/16bit.wav new file mode 100644 index 0000000..dd4ce45 Binary files /dev/null and b/test/wav/16bit.wav differ diff --git a/test/wav/16bit_2ch.wav b/test/wav/16bit_2ch.wav new file mode 100644 index 0000000..f8464ea Binary files /dev/null and b/test/wav/16bit_2ch.wav differ diff --git a/test/wav/24bit.wav b/test/wav/24bit.wav new file mode 100644 index 0000000..f84369d Binary files /dev/null and b/test/wav/24bit.wav differ diff --git a/test/wav/24bit_2ch.wav b/test/wav/24bit_2ch.wav new file mode 100644 index 0000000..2aea8df Binary files /dev/null and b/test/wav/24bit_2ch.wav differ diff --git a/test/wav/32bit.wav b/test/wav/32bit.wav new file mode 100644 index 0000000..1637703 Binary files /dev/null and b/test/wav/32bit.wav differ diff --git a/test/wav/32bit_2ch.wav b/test/wav/32bit_2ch.wav new file mode 100644 index 0000000..36b40f1 Binary files /dev/null and b/test/wav/32bit_2ch.wav differ diff --git a/test/wav/8bit.wav b/test/wav/8bit.wav new file mode 100644 index 0000000..dc096fe Binary files /dev/null and b/test/wav/8bit.wav differ diff --git a/test/wav/8bit_2ch.wav b/test/wav/8bit_2ch.wav new file mode 100644 index 0000000..f38cc65 Binary files /dev/null and b/test/wav/8bit_2ch.wav differ diff --git a/test/wav/CMakeLists.txt b/test/wav/CMakeLists.txt new file mode 100644 index 0000000..fda3016 --- /dev/null +++ b/test/wav/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# テスト名 +set(TEST_NAME wav_test) + +# 実行形式ファイル +add_executable(${TEST_NAME} main.cpp) + +# インクルードディレクトリ +include_directories(${PROJECT_ROOT_PATH}/libs/wav/include) + +# リンクするライブラリ +target_link_libraries(${TEST_NAME} gtest gtest_main) +if (NOT MSVC) +target_link_libraries(${TEST_NAME} pthread) +endif() + +# コンパイルオプション +set_target_properties(${TEST_NAME} + PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) + +# 実行パスをtmp以下に +add_test( + NAME wav + WORKING_DIRECTORY $/tmp + COMMAND $ + ) + +# run with: ctest -L lib +set_property( + TEST wav + PROPERTY LABELS lib wav + ) + +# ビルド後にテストリソースを持ってくる +file(GLOB TEST_WAVE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.wav) +add_custom_command( + TARGET ${TEST_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory $/tmp + COMMAND ${CMAKE_COMMAND} -E copy ${TEST_WAVE_FILES} $/tmp + ) diff --git a/test/wav/a.wav b/test/wav/a.wav new file mode 100644 index 0000000..02685f1 Binary files /dev/null and b/test/wav/a.wav differ diff --git a/test/wav/main.cpp b/test/wav/main.cpp new file mode 100644 index 0000000..c1a432a --- /dev/null +++ b/test/wav/main.cpp @@ -0,0 +1,246 @@ +#include +#include + +#include + +/* テスト対象のモジュール */ +extern "C" { +#include "../../libs/wav/src/wav.c" +} + +/* WAVファイルフォーマット取得テスト */ +TEST(WAVTest, GetWAVFormatTest) +{ + + /* 失敗テスト */ + { + struct WAVFileFormat format; + + EXPECT_EQ( + WAV_APIRESULT_NG, + WAV_GetWAVFormatFromFile(NULL, &format)); + EXPECT_EQ( + WAV_APIRESULT_NG, + WAV_GetWAVFormatFromFile("a.wav", NULL)); + EXPECT_EQ( + WAV_APIRESULT_NG, + WAV_GetWAVFormatFromFile(NULL, NULL)); + + /* 存在しないファイルを開こうとして失敗するか? */ + EXPECT_EQ( + WAV_APIRESULT_NG, + WAV_GetWAVFormatFromFile("dummy.a.wav.wav", &format)); + } + + /* 実wavファイルからの取得テスト */ + { + struct WAVFileFormat format; + EXPECT_EQ( + WAV_APIRESULT_OK, + WAV_GetWAVFormatFromFile("a.wav", &format)); + } + +} + +/* WAVファイルデータ取得テスト */ +TEST(WAVTest, CreateDestroyTest) +{ + + /* 失敗テスト */ + { + struct WAVFileFormat format; + format.data_format = (WAVDataFormat)255; /* 不正なフォーマット */ + format.bits_per_sample = 16; + format.num_channels = 8; + format.sampling_rate = 48000; + format.num_samples = 48000 * 5; + EXPECT_TRUE(WAV_Create(NULL) == NULL); + EXPECT_TRUE(WAV_Create(&format) == NULL); + EXPECT_TRUE(WAV_CreateFromFile(NULL) == NULL); + EXPECT_TRUE(WAV_CreateFromFile("dummy.a.wav.wav") == NULL); + } + + /* ハンドル作成 / 破棄テスト */ + { + uint32_t ch, is_ok; + struct WAVFile* wavfile; + struct WAVFileFormat format; + format.data_format = WAV_DATA_FORMAT_PCM; + format.bits_per_sample = 16; + format.num_channels = 8; + format.sampling_rate = 48000; + format.num_samples = 48000 * 5; + + wavfile = WAV_Create(&format); + EXPECT_TRUE(wavfile != NULL); + EXPECT_TRUE(wavfile->data != NULL); + EXPECT_EQ( + 0, + memcmp(&wavfile->format, &format, sizeof(struct WAVFileFormat))); + is_ok = 1; + for (ch = 0; ch < wavfile->format.num_channels; ch++) { + if (wavfile->data[ch] == NULL) { + is_ok = 0; + break; + } + } + EXPECT_EQ(1, is_ok); + + WAV_Destroy(wavfile); + } + + + /* 実wavファイルからの取得テスト */ + { + struct WAVFile* wavfile; + + wavfile = WAV_CreateFromFile("a.wav"); + EXPECT_TRUE(wavfile != NULL); + WAV_Destroy(wavfile); + } +} + +/* WAVファイルデータ書き込みテスト */ +TEST(WAVTest, WriteTest) +{ + /* 失敗テスト */ + { + const char test_filename[] = "test.wav"; + struct WAVWriter writer; + struct WAVFileFormat format; + FILE *fp; + + format.data_format = (WAVDataFormat)0xFF; /* 不正 */ + format.num_samples = 0; /* dummy */ + format.num_channels = 0; /* dummy */ + format.bits_per_sample = 0; /* dummy */ + format.sampling_rate = 0; /* dummy */ + + fp = fopen(test_filename, "wb"); + WAVWriter_Initialize(&writer, fp); + + EXPECT_NE( + WAV_ERROR_OK, + WAVWriter_PutWAVHeader(&writer, NULL)); + EXPECT_NE( + WAV_ERROR_OK, + WAVWriter_PutWAVHeader(NULL, &format)); + EXPECT_NE( + WAV_ERROR_OK, + WAVWriter_PutWAVHeader(NULL, NULL)); + EXPECT_NE( + WAV_ERROR_OK, + WAVWriter_PutWAVHeader(&writer, &format)); + + WAVWriter_Finalize(&writer); + fclose(fp); + } + + /* PCMデータ書き出しテスト */ + { + const char test_filename[] = "test.wav"; + struct WAVWriter writer; + struct WAVFileFormat format; + FILE *fp; + struct WAVFile* wavfile; + uint32_t ch, sample; + + /* 適宜フォーマットを用意 */ + format.data_format = WAV_DATA_FORMAT_PCM; + format.num_samples = 16; + format.num_channels = 1; + format.sampling_rate = 48000; + format.bits_per_sample = 8; + + /* ハンドル作成 */ + wavfile = WAV_Create(&format); + EXPECT_TRUE(wavfile != NULL); + + /* データを書いてみる */ + for (ch = 0; ch < format.num_channels; ch++) { + for (sample = 0; sample < format.num_samples; sample++) { + WAVFile_PCM(wavfile, sample, ch) = sample - (int32_t)format.num_samples / 2; + } + } + + fp = fopen(test_filename, "wb"); + WAVWriter_Initialize(&writer, fp); + + /* 不正なビット深度に書き換えて書き出し */ + /* -> 失敗を期待 */ + wavfile->format.bits_per_sample = 3; + EXPECT_NE( + WAV_ERROR_OK, + WAVWriter_PutWAVPcmData(&writer, wavfile)); + + /* 書き出し */ + wavfile->format.bits_per_sample = 8; + EXPECT_EQ( + WAV_ERROR_OK, + WAVWriter_PutWAVPcmData(&writer, wavfile)); + + WAVWriter_Finalize(&writer); + fclose(fp); + } + + /* 実ファイルを読み出してそのまま書き出してみる */ + { + uint32_t ch, is_ok, i_test; + const char* test_sourcefile_list[] = { + "a.wav", + "8bit.wav", + "16bit.wav", + "24bit.wav", + "32bit.wav", + "8bit_2ch.wav", + "16bit_2ch.wav", + "24bit_2ch.wav", + "32bit_2ch.wav", + }; + const char test_filename[] = "tmp.wav"; + struct WAVFile *src_wavfile, *test_wavfile; + WAVApiResult ret; + + for (i_test = 0; + i_test < sizeof(test_sourcefile_list) / sizeof(test_sourcefile_list[0]); + i_test++) { + /* 元になるファイルを読み込み */ + src_wavfile = WAV_CreateFromFile(test_sourcefile_list[i_test]); + + /* 読み込んだデータをそのままファイルへ書き出し */ + ret = WAV_WriteToFile(test_filename, src_wavfile); + ASSERT_EQ(WAV_APIRESULT_OK, ret); + + /* 一度書き出したファイルを読み込んでみる */ + test_wavfile = WAV_CreateFromFile(test_filename); + ASSERT_TRUE(test_wavfile != NULL); + + /* 最初に読み込んだファイルと一致するか? */ + /* フォーマットの一致確認 */ + EXPECT_EQ( + 0, memcmp(&src_wavfile->format, &test_wavfile->format, sizeof(struct WAVFileFormat))); + + /* PCMの一致確認 */ + is_ok = 1; + for (ch = 0; ch < src_wavfile->format.num_channels; ch++) { + if (memcmp(src_wavfile->data[ch], test_wavfile->data[ch], + sizeof(WAVPcmData) * src_wavfile->format.num_samples) != 0) { + is_ok = 0; + break; + } + } + EXPECT_EQ(1, is_ok); + + /* ハンドル破棄 */ + WAV_Destroy(src_wavfile); + WAV_Destroy(test_wavfile); + } + } + +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tools/sfla_codec/CMakeLists.txt b/tools/sfla_codec/CMakeLists.txt new file mode 100644 index 0000000..06a0360 --- /dev/null +++ b/tools/sfla_codec/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# プロジェクト名 +project(SRLACodec C) + +# アプリケーション名 +set(APP_NAME srla) + +# ライブラリのテストはしない +set(without-test 1) + +# 実行形式ファイル +add_executable(${APP_NAME} srla_codec.c) + +# 依存するサブディレクトリを追加 +add_subdirectory(${PROJECT_ROOT_PATH} ${CMAKE_CURRENT_BINARY_DIR}/libsrlacodec) + +# インクルードパス +target_include_directories(${APP_NAME} + PRIVATE + ${PROJECT_ROOT_PATH}/include + ) + +# リンクするライブラリ +target_link_libraries(${APP_NAME} command_line_parser) +target_link_libraries(${APP_NAME} wav) +target_link_libraries(${APP_NAME} srlacodec) +if (UNIX AND NOT APPLE) + target_link_libraries(${APP_NAME} m) +endif() + +# コンパイルオプション +if(MSVC) + target_compile_options(${APP_NAME} PRIVATE /W4) +else() + target_compile_options(${APP_NAME} PRIVATE -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=2 -Wconversion -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition) + set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +endif() +set_target_properties(${APP_NAME} + PROPERTIES + C_STANDARD 90 C_EXTENSIONS OFF + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) diff --git a/tools/sfla_codec/sfla_codec.c b/tools/sfla_codec/sfla_codec.c new file mode 100644 index 0000000..49287ee --- /dev/null +++ b/tools/sfla_codec/sfla_codec.c @@ -0,0 +1,359 @@ +#include +#include +#include "wav.h" +#include "command_line_parser.h" + +#include +#include +#include +#include + +/* a, bのうち小さい方を選択 */ +#define SRLACODEC_MIN(a, b) (((a) < (b)) ? (a) : (b)) + +/* コマンドライン仕様 */ +static struct CommandLineParserSpecification command_line_spec[] = { + { 'e', "encode", "Encode mode", + COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'd', "decode", "Decode mode", + COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'm', "mode", "Specify compress mode: 0(fast), ..., 3(high compression) (default:0)", + COMMAND_LINE_PARSER_TRUE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'c', "no-checksum-check", "Whether to NOT check checksum at decoding (default:no)", + COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'h', "help", "Show command help message", + COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 'v', "version", "Show version information", + COMMAND_LINE_PARSER_FALSE, NULL, COMMAND_LINE_PARSER_FALSE }, + { 0, NULL, } +}; + +/* エンコード 成功時は0、失敗時は0以外を返す */ +static int do_encode(const char *in_filename, const char *out_filename, uint32_t encode_preset_no) +{ +#define NUM_BLOCK_SAMPLES 4096 + FILE *out_fp; + struct WAVFile *in_wav; + struct SRLAEncoder *encoder; + struct SRLAEncoderConfig config; + struct SRLAEncodeParameter parameter; + struct stat fstat; + int32_t *input[SRLA_MAX_NUM_CHANNELS]; + uint8_t *buffer; + uint32_t buffer_size, encoded_data_size; + SRLAApiResult ret; + uint32_t ch, smpl, num_channels, num_samples; + + /* エンコーダ作成 */ + config.max_num_channels = SRLA_MAX_NUM_CHANNELS; + config.max_num_samples_per_block = NUM_BLOCK_SAMPLES; + config.max_num_parameters = SRLA_MAX_COEFFICIENT_ORDER; + if ((encoder = SRLAEncoder_Create(&config, NULL, 0)) == NULL) { + fprintf(stderr, "Failed to create encoder handle. \n"); + return 1; + } + + /* WAVファイルオープン */ + if ((in_wav = WAV_CreateFromFile(in_filename)) == NULL) { + fprintf(stderr, "Failed to open %s. \n", in_filename); + return 1; + } + num_channels = in_wav->format.num_channels; + num_samples = in_wav->format.num_samples; + + /* エンコードパラメータセット */ + parameter.num_channels = (uint16_t)num_channels; + parameter.bits_per_sample = (uint16_t)in_wav->format.bits_per_sample; + parameter.sampling_rate = in_wav->format.sampling_rate; + parameter.num_samples_per_block = NUM_BLOCK_SAMPLES; + /* プリセットの反映 */ + parameter.preset = (uint8_t)encode_preset_no; + if ((ret = SRLAEncoder_SetEncodeParameter(encoder, ¶meter)) != SRLA_APIRESULT_OK) { + fprintf(stderr, "Failed to set encode parameter: %d \n", ret); + return 1; + } + + /* 入力ファイルのサイズを拾っておく */ + stat(in_filename, &fstat); + /* 入力wavの2倍よりは大きくならないだろうという想定 */ + buffer_size = (uint32_t)(2 * fstat.st_size); + + /* エンコードデータ/入力データ領域を作成 */ + buffer = (uint8_t *)malloc(buffer_size); + for (ch = 0; ch < num_channels; ch++) { + input[ch] = (int32_t *)malloc(sizeof(int32_t) * num_samples); + } + + /* 情報が失われない程度に右シフト */ + for (ch = 0; ch < num_channels; ch++) { + for (smpl = 0; smpl < num_samples; smpl++) { + input[ch][smpl] = (int32_t)(WAVFile_PCM(in_wav, smpl, ch) >> (32 - in_wav->format.bits_per_sample)); + } + } + + /* エンコード実行 */ + { + uint8_t *data_pos = buffer; + uint32_t write_offset, progress; + struct SRLAHeader header; + + write_offset = 0; + + /* ヘッダエンコード */ + header.num_channels = (uint16_t)num_channels; + header.num_samples = num_samples; + header.sampling_rate = parameter.sampling_rate; + header.bits_per_sample = parameter.bits_per_sample; + header.preset = parameter.preset; + header.num_samples_per_block = parameter.num_samples_per_block; + if ((ret = SRLAEncoder_EncodeHeader(&header, data_pos, buffer_size)) + != SRLA_APIRESULT_OK) { + fprintf(stderr, "Failed to encode header! ret:%d \n", ret); + return 1; + } + data_pos += SRLA_HEADER_SIZE; + write_offset += SRLA_HEADER_SIZE; + + /* ブロックを時系列順にエンコード */ + progress = 0; + while (progress < num_samples) { + uint32_t ch, write_size; + const int32_t *input_ptr[SRLA_MAX_NUM_CHANNELS]; + /* エンコードサンプル数の確定 */ + const uint32_t num_encode_samples = SRLACODEC_MIN(NUM_BLOCK_SAMPLES, num_samples - progress); + + /* サンプル参照位置のセット */ + for (ch = 0; ch < (uint32_t)num_channels; ch++) { + input_ptr[ch] = &input[ch][progress]; + } + + /* ブロックエンコード */ + if ((ret = SRLAEncoder_EncodeBlock(encoder, + input_ptr, num_encode_samples, + data_pos, buffer_size - write_offset, &write_size)) != SRLA_APIRESULT_OK) { + fprintf(stderr, "Failed to encode! ret:%d \n", ret); + return 1; + } + + /* 進捗更新 */ + data_pos += write_size; + write_offset += write_size; + progress += num_encode_samples; + + /* 進捗表示 */ + printf("progress... %5.2f%% \r", (progress * 100.0f) / num_samples); + fflush(stdout); + } + + /* 書き出しサイズ取得 */ + encoded_data_size = write_offset; + } + + /* ファイル書き出し */ + out_fp = fopen(out_filename, "wb"); + if (fwrite(buffer, sizeof(uint8_t), encoded_data_size, out_fp) < encoded_data_size) { + fprintf(stderr, "File output error! %d \n", ret); + return 1; + } + + /* 圧縮結果サマリの表示 */ + printf("finished: %d -> %d (%6.2f %%) \n", + (uint32_t)fstat.st_size, encoded_data_size, 100.f * (double)encoded_data_size / fstat.st_size); + + /* リソース破棄 */ + fclose(out_fp); + free(buffer); + for (ch = 0; ch < num_channels; ch++) { + free(input[ch]); + } + WAV_Destroy(in_wav); + SRLAEncoder_Destroy(encoder); + + return 0; +} + +/* デコード 成功時は0、失敗時は0以外を返す */ +static int do_decode(const char *in_filename, const char *out_filename, uint8_t check_checksum) +{ + FILE* in_fp; + struct WAVFile* out_wav; + struct WAVFileFormat wav_format; + struct stat fstat; + struct SRLADecoder* decoder; + struct SRLADecoderConfig config; + struct SRLAHeader header; + uint8_t* buffer; + uint32_t ch, smpl, buffer_size; + SRLAApiResult ret; + + /* デコーダハンドルの作成 */ + config.max_num_channels = SRLA_MAX_NUM_CHANNELS; + config.max_num_parameters = SRLA_MAX_COEFFICIENT_ORDER; + config.check_checksum = check_checksum; + if ((decoder = SRLADecoder_Create(&config, NULL, 0)) == NULL) { + fprintf(stderr, "Failed to create decoder handle. \n"); + return 1; + } + + /* 入力ファイルオープン */ + in_fp = fopen(in_filename, "rb"); + /* 入力ファイルのサイズ取得 / バッファ領域割り当て */ + stat(in_filename, &fstat); + buffer_size = (uint32_t)fstat.st_size; + buffer = (uint8_t *)malloc(buffer_size); + /* バッファ領域にデータをロード */ + fread(buffer, sizeof(uint8_t), buffer_size, in_fp); + fclose(in_fp); + + /* ヘッダデコード */ + if ((ret = SRLADecoder_DecodeHeader(buffer, buffer_size, &header)) + != SRLA_APIRESULT_OK) { + fprintf(stderr, "Failed to get header information: %d \n", ret); + return 1; + } + + /* 出力wavハンドルの生成 */ + wav_format.data_format = WAV_DATA_FORMAT_PCM; + wav_format.num_channels = header.num_channels; + wav_format.sampling_rate = header.sampling_rate; + wav_format.bits_per_sample = header.bits_per_sample; + wav_format.num_samples = header.num_samples; + if ((out_wav = WAV_Create(&wav_format)) == NULL) { + fprintf(stderr, "Failed to create wav handle. \n"); + return 1; + } + + /* 一括デコード */ + if ((ret = SRLADecoder_DecodeWhole(decoder, + buffer, buffer_size, + (int32_t **)out_wav->data, out_wav->format.num_channels, out_wav->format.num_samples)) + != SRLA_APIRESULT_OK) { + fprintf(stderr, "Decoding error! %d \n", ret); + return 1; + } + + /* エンコード時に右シフトした分を戻し、32bit化 */ + for (ch = 0; ch < out_wav->format.num_channels; ch++) { + for (smpl = 0; smpl < out_wav->format.num_samples; smpl++) { + WAVFile_PCM(out_wav, smpl, ch) <<= (32 - out_wav->format.bits_per_sample); + } + } + + /* WAVファイル書き出し */ + if (WAV_WriteToFile(out_filename, out_wav) != WAV_APIRESULT_OK) { + fprintf(stderr, "Failed to write wav file. \n"); + return 1; + } + + free(buffer); + WAV_Destroy(out_wav); + SRLADecoder_Destroy(decoder); + + return 0; +} + +/* 使用法の表示 */ +static void print_usage(char** argv) +{ + printf("Usage: %s [options] INPUT_FILE_NAME OUTPUT_FILE_NAME \n", argv[0]); +} + +/* バージョン情報の表示 */ +static void print_version_info(void) +{ + printf("SRLA -- SVR-FIR Lossless Audio codec Version.%d \n", SRLA_CODEC_VERSION); +} + +/* メインエントリ */ +int main(int argc, char** argv) +{ + const char* filename_ptr[2] = { NULL, NULL }; + const char* input_file; + const char* output_file; + + /* 引数が足らない */ + if (argc == 1) { + print_usage(argv); + /* 初めて使った人が詰まらないようにヘルプの表示を促す */ + printf("Type `%s -h` to display command helps. \n", argv[0]); + return 1; + } + + /* コマンドライン解析 */ + if (CommandLineParser_ParseArguments(command_line_spec, + argc, (const char* const*)argv, filename_ptr, sizeof(filename_ptr) / sizeof(filename_ptr[0])) + != COMMAND_LINE_PARSER_RESULT_OK) { + return 1; + } + + /* ヘルプやバージョン情報の表示判定 */ + if (CommandLineParser_GetOptionAcquired(command_line_spec, "help") == COMMAND_LINE_PARSER_TRUE) { + print_usage(argv); + printf("options: \n"); + CommandLineParser_PrintDescription(command_line_spec); + return 0; + } else if (CommandLineParser_GetOptionAcquired(command_line_spec, "version") == COMMAND_LINE_PARSER_TRUE) { + print_version_info(); + return 0; + } + + /* 入力ファイル名の取得 */ + if ((input_file = filename_ptr[0]) == NULL) { + fprintf(stderr, "%s: input file must be specified. \n", argv[0]); + return 1; + } + + /* 出力ファイル名の取得 */ + if ((output_file = filename_ptr[1]) == NULL) { + fprintf(stderr, "%s: output file must be specified. \n", argv[0]); + return 1; + } + + /* エンコードとデコードは同時に指定できない */ + if ((CommandLineParser_GetOptionAcquired(command_line_spec, "decode") == COMMAND_LINE_PARSER_TRUE) + && (CommandLineParser_GetOptionAcquired(command_line_spec, "encode") == COMMAND_LINE_PARSER_TRUE)) { + fprintf(stderr, "%s: encode and decode mode cannot specify simultaneously. \n", argv[0]); + return 1; + } + + if (CommandLineParser_GetOptionAcquired(command_line_spec, "decode") == COMMAND_LINE_PARSER_TRUE) { + /* デコード */ + uint8_t crc_check = 1; + /* CRC無効フラグを取得 */ + if (CommandLineParser_GetOptionAcquired(command_line_spec, "no-crc-check") == COMMAND_LINE_PARSER_TRUE) { + crc_check = 0; + } + /* 一括デコード実行 */ + if (do_decode(input_file, output_file, crc_check) != 0) { + fprintf(stderr, "%s: failed to decode %s. \n", argv[0], input_file); + return 1; + } + } else if (CommandLineParser_GetOptionAcquired(command_line_spec, "encode") == COMMAND_LINE_PARSER_TRUE) { + /* エンコード */ + uint32_t encode_preset_no = 0; + /* エンコードプリセット番号取得 */ + if (CommandLineParser_GetOptionAcquired(command_line_spec, "mode") == COMMAND_LINE_PARSER_TRUE) { + char *e; + const char *lstr = CommandLineParser_GetArgumentString(command_line_spec, "mode"); + encode_preset_no = (uint32_t)strtol(lstr, &e, 10); + if (*e != '\0') { + fprintf(stderr, "%s: invalid encode preset number. (irregular character found in %s at %s)\n", argv[0], lstr, e); + return 1; + } + if (encode_preset_no >= SRLA_NUM_PARAMETER_PRESETS) { + fprintf(stderr, "%s: encode preset number is out of range. \n", argv[0]); + return 1; + } + } + /* 一括エンコード実行 */ + if (do_encode(input_file, output_file, encode_preset_no) != 0) { + fprintf(stderr, "%s: failed to encode %s. \n", argv[0], input_file); + return 1; + } + } else { + fprintf(stderr, "%s: decode(-d) or encode(-e) option must be specified. \n", argv[0]); + return 1; + } + + return 0; +} diff --git a/tools/sfla_player/CMakeLists.txt b/tools/sfla_player/CMakeLists.txt new file mode 100644 index 0000000..909d761 --- /dev/null +++ b/tools/sfla_player/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.15) + +set(PROJECT_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# プロジェクト名 +project(SRLAPlayer C) + +# アプリケーション名 +set(APP_NAME srlaplayer) + +# ライブラリのテストはしない +set(without-test 1) + +# 実行形式ファイル +add_executable(${APP_NAME} srla_player.c) + +# 依存するサブディレクトリを追加 +add_subdirectory(${PROJECT_ROOT_PATH} ${CMAKE_CURRENT_BINARY_DIR}/libsrladec) + +# 機種依存のソース追加 +if (APPLE) + set(CMAKE_C_FLAGS "-framework Audiotoolbox -framework CoreAudio -framework CoreServices") + target_sources(${APP_NAME} PRIVATE srla_player_coreaudio.c) +elseif (MSVC) + target_sources(${APP_NAME} PRIVATE srla_player_wasapi.c) +elseif (UNIX) + target_sources(${APP_NAME} PRIVATE srla_player_pulseaudio.c) +endif() + +# インクルードパス +target_include_directories(${APP_NAME} + PRIVATE + ${PROJECT_ROOT_PATH}/include + ) + +# リンクするライブラリ +target_link_libraries(${APP_NAME} srladec) +if (UNIX AND NOT APPLE) + target_link_libraries(${APP_NAME} pulse-simple pulse m) +endif() + +# コンパイルオプション +if(MSVC) + target_compile_options(${APP_NAME} PRIVATE /W4) +else() + target_compile_options(${APP_NAME} PRIVATE -Wall -Wextra -Wpedantic -Wformat=2 -Wstrict-aliasing=2 -Wconversion -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition) + set(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -DDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +endif() +set_target_properties(${APP_NAME} + PROPERTIES + C_STANDARD 90 C_EXTENSIONS OFF + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) diff --git a/tools/sfla_player/sfla_player.c b/tools/sfla_player/sfla_player.c new file mode 100644 index 0000000..94e278b --- /dev/null +++ b/tools/sfla_player/sfla_player.c @@ -0,0 +1,161 @@ +#include "srla_player.h" +#include + +#include +#include +#include +#include + +/* 出力要求コールバック */ +static void SRLAPlayer_SampleRequestCallback(int32_t **buffer, uint32_t num_channels, uint32_t num_samples); +/* 終了処理 */ +static void exit_srla_player(void); + +/* 再生制御のためのグローバル変数 */ +static struct SRLAHeader header = { 0, }; +static uint32_t output_samples = 0; +static int32_t *decode_buffer[SRLA_MAX_NUM_CHANNELS] = { NULL, }; +static uint32_t num_buffered_samples = 0; +static uint32_t buffer_pos = 0; +static uint32_t data_size = 0; +static uint8_t *data = NULL; +static uint32_t decode_offset = 0; +static struct SRLADecoder* decoder = NULL; + +/* メインエントリ */ +int main(int argc, char **argv) +{ + uint32_t i; + SRLAApiResult ret; + struct SRLADecoderConfig decoder_config; + struct SRLAPlayerConfig player_config; + + /* 引数チェック 間違えたら使用方法を提示 */ + if (argc != 2) { + printf("Usage: %s SRLAFILE \n", argv[0]); + return 1; + } + + /* lnnファイルのロード */ + { + struct stat fstat; + FILE* fp; + const char *filename = argv[1]; + + /* ファイルオープン */ + if ((fp = fopen(filename, "rb")) == NULL) { + fprintf(stderr, "Failed to open %s \n", filename); + return 1; + } + + /* 入力ファイルのサイズ取得 / バッファ領域割り当て */ + stat(filename, &fstat); + data_size = (uint32_t)fstat.st_size; + data = (uint8_t *)malloc(data_size); + + /* バッファ領域にデータをロード */ + if (fread(data, sizeof(uint8_t), data_size, fp) < data_size) { + fprintf(stderr, "Failed to load %s data \n", filename); + return 1; + } + + fclose(fp); + } + + /* ヘッダデコード */ + if ((ret = SRLADecoder_DecodeHeader(data, data_size, &header)) != SRLA_APIRESULT_OK) { + fprintf(stderr, "Failed to get header information: %d \n", ret); + return 1; + } + + /* デコーダハンドルの作成 */ + decoder_config.max_num_channels = header.num_channels; + decoder_config.max_num_parameters = 32; + decoder_config.check_crc = 1; + if ((decoder = SRLADecoder_Create(&decoder_config, NULL, 0)) == NULL) { + fprintf(stderr, "Failed to create decoder handle. \n"); + return 1; + } + + /* デコーダにヘッダをセット */ + if ((ret = SRLADecoder_SetHeader(decoder, &header)) != SRLA_APIRESULT_OK) { + fprintf(stderr, "Failed to set header to decoder. \n"); + return 1; + } + + /* デコード出力領域割当 */ + for (i = 0; i < header.num_channels; i++) { + decode_buffer[i] = (int32_t *)malloc(sizeof(int32_t) * header.num_samples_per_block); + memset(decode_buffer[i], 0, sizeof(int32_t) * header.num_samples_per_block); + } + + /* デコード位置をヘッダ分進める */ + decode_offset = SRLA_HEADER_SIZE; + + /* プレイヤー初期化 */ + player_config.sampling_rate = header.sampling_rate; + player_config.num_channels = header.num_channels; + player_config.bits_per_sample = header.bits_per_sample; + player_config.sample_request_callback = SRLAPlayer_SampleRequestCallback; + SRLAPlayer_Initialize(&player_config); + + /* この後はコールバック要求により進む */ + while (1) { ; } + + return 0; +} + +/* 出力要求コールバック */ +static void SRLAPlayer_SampleRequestCallback(int32_t **buffer, uint32_t num_channels, uint32_t num_samples) +{ + uint32_t ch, smpl; + + for (smpl = 0; smpl < num_samples; smpl++) { + /* バッファを使い切ったら即時にデコード */ + if (buffer_pos >= num_buffered_samples) { + uint32_t decode_size; + if (SRLADecoder_DecodeBlock(decoder, + &data[decode_offset], data_size - decode_offset, + decode_buffer, header.num_channels, header.num_samples_per_block, + &decode_size, &num_buffered_samples) != SRLA_APIRESULT_OK) { + fprintf(stderr, "decoding error! \n"); + exit(1); + } + buffer_pos = 0; + decode_offset += decode_size; + } + + /* 出力用バッファ領域にコピー */ + for (ch = 0; ch < num_channels; ch++) { + buffer[ch][smpl] = decode_buffer[ch][buffer_pos]; + } + buffer_pos++; + output_samples++; + + /* 再生終了次第終了処理へ */ + if (output_samples >= header.num_samples) { + exit_srla_player(); + } + } + + /* 進捗表示 */ + printf("playing... %7.3f / %7.3f \r", + (double)output_samples / header.sampling_rate, (double)header.num_samples / header.sampling_rate); + fflush(stdout); +} + +/* 終了処理 */ +static void exit_srla_player(void) +{ + uint32_t i; + + SRLAPlayer_Finalize(); + + for (i = 0; i < header.num_channels; i++) { + free(decode_buffer[i]); + } + SRLADecoder_Destroy(decoder); + free(data); + + exit(0); +} diff --git a/tools/sfla_player/sfla_player.h b/tools/sfla_player/sfla_player.h new file mode 100644 index 0000000..7d6a460 --- /dev/null +++ b/tools/sfla_player/sfla_player.h @@ -0,0 +1,32 @@ +#ifndef SRLAPLAYER_H_INCLUDED +#define SRLAPLAYER_H_INCLUDED + +#include + +/* 出力要求コールバック */ +typedef void (*SRLASampleRequestCallback)( + int32_t **buffer, uint32_t num_channels, uint32_t num_samples); + +/* プレイヤー初期化コンフィグ */ +struct SRLAPlayerConfig { + uint32_t sampling_rate; + uint16_t num_channels; + uint16_t bits_per_sample; + SRLASampleRequestCallback sample_request_callback; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* 初期化 この関数内でデバイスドライバの初期化を行い、再生開始 */ +void SRLAPlayer_Initialize(const struct SRLAPlayerConfig *config); + +/* 終了 初期化したときのリソースの開放はここで */ +void SRLAPlayer_Finalize(void); + +#ifdef __cplusplus +} +#endif + +#endif /* SRLAPLAYER_H_INCLUDED */ diff --git a/tools/sfla_player/sfla_player_coreaudio.c b/tools/sfla_player/sfla_player_coreaudio.c new file mode 100644 index 0000000..b056158 --- /dev/null +++ b/tools/sfla_player/sfla_player_coreaudio.c @@ -0,0 +1,127 @@ +#include "srla_player.h" +#include + +#include +#include +#include + +#define NUM_BUFFERS 3 +#define BUFFER_SIZE (8 * 1024) +#define DECODE_BUFFER_NUM_SAMPLES (1024) + +/* CoreAudioの出力コールバック関数 */ +static void SRLAPlayer_CoreAudioCallback(void *custom_data, AudioQueueRef queue, AudioQueueBufferRef buffer); + +/* 初期化カウント */ +static int32_t st_initialize_count = 0; +/* 初期化時のプレイヤーコンフィグ */ +static struct SRLAPlayerConfig st_config = { 0, }; +/* デコードしたデータのバッファ領域 */ +static int32_t **st_decode_buffer = NULL; +/* バッファ参照位置 */ +static uint32_t st_buffer_pos = DECODE_BUFFER_NUM_SAMPLES; /* 空の状態 */ +/* 出力キューの参照 */ +static AudioQueueRef queue = NULL; + +/* 初期化 この関数内でデバイスドライバの初期化を行い、再生開始 */ +void SRLAPlayer_Initialize(const struct SRLAPlayerConfig *config) +{ + uint32_t i; + AudioStreamBasicDescription format; + AudioQueueBufferRef buffers[NUM_BUFFERS]; + + assert(config != NULL); + + /* 多重初期化は不可 */ + if (st_initialize_count > 0) { + return; + } + + /* コンフィグ取得 */ + st_config = (*config); + + /* フォーマットに属性を詰める */ + format.mSampleRate = st_config.sampling_rate; /* サンプリングレート */ + format.mFormatID = kAudioFormatLinearPCM; /* フォーマット: PCM */ + format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; /* フォーマットフラグの指定. */ + format.mBitsPerChannel = st_config.bits_per_sample; /* チャンネル当たりのビット数 */ + format.mChannelsPerFrame = st_config.num_channels; /* チャンネル数 */ + format.mBytesPerFrame = (st_config.bits_per_sample * st_config.num_channels) / 8; /* 1フレーム(全てのフレームの1サンプル)のバイト数 */ + format.mFramesPerPacket = 1; /* パケットあたりのフレーム数 */ + format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket; /* パケット当たりのバイト数 */ + format.mReserved = 0; /* (予約領域) */ + + /* デコード領域のバッファ確保 */ + st_decode_buffer = (int32_t **)malloc(sizeof(int32_t *) * st_config.num_channels); + for (i = 0; i < st_config.num_channels; i++) { + st_decode_buffer[i] = (int32_t *)malloc(sizeof(int32_t) * DECODE_BUFFER_NUM_SAMPLES); + memset(st_decode_buffer[i], 0, sizeof(int32_t) * DECODE_BUFFER_NUM_SAMPLES); + } + + /* 新しい出力キューを生成 */ + AudioQueueNewOutput(&format, + SRLAPlayer_CoreAudioCallback, NULL, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &queue); + + for (i = 0; i < NUM_BUFFERS; i++) { + /* 指定したキューのバッファの領域を割り当てる */ + AudioQueueAllocateBuffer(queue, BUFFER_SIZE, &buffers[i]); + /* サイズをセット */ + buffers[i]->mAudioDataByteSize = BUFFER_SIZE; + /* 一発目のデータを出力 */ + SRLAPlayer_CoreAudioCallback(NULL, queue, buffers[i]); + } + + /* キューの再生開始 */ + AudioQueueStart(queue, NULL); + + /* スレッドのループ処理開始 + * この関数が終わってもスレッド処理が回る(監視ループというらしい) */ + CFRunLoopRun(); + + st_initialize_count++; +} + +/* 終了 初期化したときのリソースの開放はここで */ +void SRLAPlayer_Finalize(void) +{ + if (st_initialize_count == 1) { + uint32_t i; + /* キューの停止・破棄 */ + AudioQueueStop(queue, false); + AudioQueueDispose(queue, false); + CFRunLoopStop(CFRunLoopGetCurrent()); + + /* デコード領域のバッファ開放 */ + for (i = 0; i < st_config.num_channels; i++) { + free(st_decode_buffer[i]); + } + free(st_decode_buffer); + } + + st_initialize_count--; +} + +/* CoreAudioの出力コールバック関数 */ +static void SRLAPlayer_CoreAudioCallback(void *custom_data, AudioQueueRef queue, AudioQueueBufferRef buffer) +{ + uint32_t i, ch; + int16_t *ch_interleaved_buffer = (int16_t *)buffer->mAudioData; + const uint32_t num_buffer_samples = BUFFER_SIZE / sizeof(int16_t); + + for (i = 0; i < num_buffer_samples; i += st_config.num_channels) { + /* バッファを使い切っていたらその場で次のデータを要求 */ + if (st_buffer_pos >= DECODE_BUFFER_NUM_SAMPLES) { + st_config.sample_request_callback(st_decode_buffer, st_config.num_channels, DECODE_BUFFER_NUM_SAMPLES); + st_buffer_pos = 0; + } + + /* インターリーブしたバッファにデータを詰める */ + for (ch = 0; ch < st_config.num_channels; ch++) { + ch_interleaved_buffer[i + ch] = (int16_t)st_decode_buffer[ch][st_buffer_pos]; + } + st_buffer_pos++; + } + + /* バッファをエンキュー */ + AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); +} diff --git a/tools/sfla_player/sfla_player_pulseaudio.c b/tools/sfla_player/sfla_player_pulseaudio.c new file mode 100644 index 0000000..3747e5c --- /dev/null +++ b/tools/sfla_player/sfla_player_pulseaudio.c @@ -0,0 +1,108 @@ +#include "srla_player.h" +#include +#include +#include +#include + +/* C89環境でビルドするためinlineキーワードを無効にする */ +#define inline +#include +#include +#include + +#define BUFFER_SIZE (1 * 1024) +#define DECODE_BUFFER_NUM_SAMPLES (1024) + +/* 初期化カウント */ +static int32_t st_initialize_count = 0; +/* 初期化時のプレイヤーコンフィグ */ +static struct SRLAPlayerConfig st_config = { 0, }; +/* デコードしたデータのバッファ領域 */ +static int32_t **st_decode_buffer = NULL; +/* バッファ参照位置 */ +static uint32_t st_buffer_pos = DECODE_BUFFER_NUM_SAMPLES; /* 空の状態 */ +/* simple pulseaudio ハンドル */ +static pa_simple *pa_simple_hn = NULL; +/* バッファ領域 */ +static uint8_t buffer[BUFFER_SIZE]; + +/* 初期化 この関数内でデバイスドライバの初期化を行い、再生開始 */ +void SRLAPlayer_Initialize(const struct SRLAPlayerConfig *config) +{ + uint32_t i; + int error; + pa_sample_spec sample_spec; + + assert(config != NULL); + + /* 多重初期化は不可 */ + if (st_initialize_count > 0) { + return; + } + + /* コンフィグ取得 */ + st_config = (*config); + + /* フォーマットに属性を詰める */ + sample_spec.format = PA_SAMPLE_S16LE; + sample_spec.rate = st_config.sampling_rate; + sample_spec.channels = (uint8_t)st_config.num_channels; + + /* デコード領域のバッファ確保 */ + st_decode_buffer = (int32_t **)malloc(sizeof(int32_t *) * st_config.num_channels); + for (i = 0; i < st_config.num_channels; i++) { + st_decode_buffer[i] = (int32_t *)malloc(sizeof(int32_t) * DECODE_BUFFER_NUM_SAMPLES); + memset(st_decode_buffer[i], 0, sizeof(int32_t) * DECODE_BUFFER_NUM_SAMPLES); + } + + /* playbackハンドル作成 */ + if ((pa_simple_hn = pa_simple_new(NULL, "SRLAPlayer", PA_STREAM_PLAYBACK, NULL, "playback", + &sample_spec, NULL, NULL, &error)) == NULL) { + fprintf(stderr, "failed to create pulseaudio playback: %s \n", pa_strerror(error)); + exit(1); + } + + st_initialize_count++; + + while (1) { + uint32_t i, ch; + int16_t *pbuffer = (int16_t *)&buffer[0]; + const uint32_t num_writable_samples_per_channel = (uint32_t)(BUFFER_SIZE / (st_config.num_channels * sizeof(int16_t))); + + for (i = 0; i < num_writable_samples_per_channel; i++) { + /* バッファを使い切っていたらその場で次のデータを要求 */ + if (st_buffer_pos >= DECODE_BUFFER_NUM_SAMPLES) { + st_config.sample_request_callback(st_decode_buffer, st_config.num_channels, DECODE_BUFFER_NUM_SAMPLES); + st_buffer_pos = 0; + } + /* インターリーブしたバッファにデータを詰める */ + for (ch = 0; ch < st_config.num_channels; ch++) { + *pbuffer++ = (int16_t)st_decode_buffer[ch][st_buffer_pos]; + } + st_buffer_pos++; + } + + if (pa_simple_write(pa_simple_hn, buffer, BUFFER_SIZE, &error) < 0) { + fprintf(stderr, "pa_simple_write() failed: %s\n", pa_strerror(error)); + exit(1); + } + } +} + +/* 終了 初期化したときのリソースの開放はここで */ +void SRLAPlayer_Finalize(void) +{ + if (st_initialize_count == 1) { + uint32_t i; + + pa_simple_free(pa_simple_hn); + + /* デコード領域のバッファ開放 */ + for (i = 0; i < st_config.num_channels; i++) { + free(st_decode_buffer[i]); + } + free(st_decode_buffer); + } + + st_initialize_count--; +} diff --git a/tools/sfla_player/sfla_player_wasapi.c b/tools/sfla_player/sfla_player_wasapi.c new file mode 100644 index 0000000..cfd943d --- /dev/null +++ b/tools/sfla_player/sfla_player_wasapi.c @@ -0,0 +1,179 @@ +#include "srla_player.h" +#include + +#include + +/* マクロ呼び出しを使うためCOBJMACROSを定義 */ +#define COBJMACROS +#include +#include +#undef COBJMACROS + +#define DECODE_BUFFER_NUM_SAMPLES (1024) +#define REQUESTED_SOUND_BUFFER_DURATION (2 * 10000000LL) /* 内部に要求するバッファサイズ[100ナノ秒] */ + +/* 初期化カウント */ +static int32_t st_initialize_count = 0; +/* 初期化時のプレイヤーコンフィグ */ +static struct SRLAPlayerConfig st_config = { 0, }; +/* デコードしたデータのバッファ領域 */ +static int32_t** st_decode_buffer = NULL; +/* バッファ参照位置 */ +static uint32_t st_buffer_pos = DECODE_BUFFER_NUM_SAMPLES; /* 空の状態 */ +/* WASAPI制御用のハンドル */ +static IAudioClient* audio_client = NULL; +static IAudioRenderClient* audio_render_client = NULL; + +/* CLSID,IIDを自前定義 */ +/* 補足)C++ソースにしないと__uuidが使えない。C++にするならクラスを使う。しかしwindowsの事情だけで全てをC++プロジェクトにしたくない */ +static const CLSID st_CLSID_MMDeviceEnumerator = { 0xBCDE0395, 0xE52F, 0x467C, {0x8E,0x3D,0xC4,0x57,0x92,0x91,0x69,0x2E} }; +static const IID st_IID_IMMDeviceEnumerator = { 0xA95664D2, 0x9614, 0x4F35, {0xA7,0x46,0xDE,0x8D,0xB6,0x36,0x17,0xE6} }; +static const IID st_IID_IAudioClient = { 0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1,0x78,0xC2,0xF5,0x68,0xA7,0x03,0xB2} }; +static const IID st_IID_IAudioClockAdjustment = { 0xF6E4C0A0, 0x46D9, 0x4FB8, {0xBE,0x21,0x57,0xA3,0xEF,0x2B,0x62,0x6C} }; +static const IID st_IID_IAudioRenderClient = { 0xF294ACFC, 0x3146, 0x4483, {0xA7,0xBF,0xAD,0xDC,0xA7,0xC2,0x60,0xE2} }; + +/* 初期化 この関数内でデバイスドライバの初期化を行い、再生開始 */ +void SRLAPlayer_Initialize(const struct SRLAPlayerConfig* config) +{ + uint32_t i, buffer_frame_size; + HRESULT hr; + IMMDeviceEnumerator* device_enumerator; + IMMDevice* audio_device; + WAVEFORMATEX format; + + assert(config != NULL); + + /* 多重初期化は不可 */ + if (st_initialize_count > 0) { + return; + } + + /* コンフィグ取得 */ + st_config = (*config); + + /* デコード領域のバッファ確保 */ + st_decode_buffer = (int32_t**)malloc(sizeof(int32_t*) * st_config.num_channels); + for (i = 0; i < st_config.num_channels; i++) { + st_decode_buffer[i] = (int32_t*)malloc(sizeof(int32_t) * DECODE_BUFFER_NUM_SAMPLES); + memset(st_decode_buffer[i], 0, sizeof(int32_t) * DECODE_BUFFER_NUM_SAMPLES); + } + + /* COMの初期化 */ + hr = CoInitializeEx(NULL, COINIT_SPEED_OVER_MEMORY); + assert(SUCCEEDED(hr)); + + /* マルチメディアデバイス列挙子取得 */ + hr = CoCreateInstance(&st_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &st_IID_IMMDeviceEnumerator, &device_enumerator); + assert(SUCCEEDED(hr)); + + /* デフォルトのデバイス取得 */ + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(device_enumerator, eRender, eConsole, &audio_device); + assert(SUCCEEDED(hr)); + IMMDeviceEnumerator_Release(device_enumerator); + + /* クライアント取得 */ + hr = IMMDevice_Activate(audio_device, &st_IID_IAudioClient, CLSCTX_ALL, NULL, &audio_client); + assert(SUCCEEDED(hr)); + IMMDevice_Release(audio_device); + + /* 出力フォーマット指定 */ + ZeroMemory(&format, sizeof(WAVEFORMATEX)); + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = st_config.num_channels; + format.nSamplesPerSec = st_config.sampling_rate; + format.wBitsPerSample = st_config.bits_per_sample; + format.nBlockAlign = (format.nChannels * format.wBitsPerSample) / 8; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + + /* 出力フォーマットが対応しているかチェック */ + { + WAVEFORMATEX closest_format, * pformat; + pformat = &closest_format; + hr = IAudioClient_IsFormatSupported(audio_client, AUDCLNT_SHAREMODE_SHARED, &format, &pformat); + assert(SUCCEEDED(hr)); + } + + /* クライアント初期化 */ + hr = IAudioClient_Initialize(audio_client, + AUDCLNT_SHAREMODE_SHARED, /* 共有モード */ + AUDCLNT_STREAMFLAGS_RATEADJUST /* レート変換を使う(入力波形に合わせたレートで再生する) */ + | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM /* レート変換の自動挿入を有効にする */ + | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, /* 良い品質のレート変換を使う */ + REQUESTED_SOUND_BUFFER_DURATION, 0, &format, NULL); + assert(SUCCEEDED(hr)); + + /* サンプリングレート変換設定 */ + { + IAudioClockAdjustment* clock_adj; + hr = IAudioClient_GetService(audio_client, &st_IID_IAudioClockAdjustment, &clock_adj); + assert(SUCCEEDED(hr)); + + hr = IAudioClockAdjustment_SetSampleRate(clock_adj, st_config.sampling_rate); + assert(SUCCEEDED(hr)); + IAudioClockAdjustment_Release(clock_adj); + } + + /* バッファを書き込む為のレンダラーを取得 */ + hr = IAudioClient_GetService(audio_client, &st_IID_IAudioRenderClient, &audio_render_client); + assert(SUCCEEDED(hr)); + + /* 書き込み用のバッファサイズ取得 */ + hr = IAudioClient_GetBufferSize(audio_client, &buffer_frame_size); + assert(SUCCEEDED(hr)); + + /* 再生開始 */ + hr = IAudioClient_Start(audio_client); + assert(SUCCEEDED(hr)); + + st_initialize_count++; + + while (1) { + int16_t* buffer; + /* レイテンシ: 小さすぎると途切れる, 大きすぎると遅延が大きくなる */ + const uint32_t buffer_latency = buffer_frame_size / 50; + uint32_t padding_size, available_buffer_frame_size; + + /* パディングフレームサイズ(サウンドバッファ内に入っていてまだ出力されてないデータ量)の取得 */ + hr = IAudioClient_GetCurrentPadding(audio_client, &padding_size); + assert(SUCCEEDED(hr)); + + /* 書き込み可能なフレームサイズの取得 */ + available_buffer_frame_size = buffer_latency - padding_size; + + /* 書き込み用バッファ取得 */ + hr = IAudioRenderClient_GetBuffer(audio_render_client, available_buffer_frame_size, &buffer); + assert(SUCCEEDED(hr)); + + /* インターリーブしつつ書き込み チャンネル数分のサンプルのまとまりが1フレーム */ + for (i = 0; i < available_buffer_frame_size; i++) { + uint32_t ch; + /* バッファを使い切っていたらその場で次のデータを要求 */ + if (st_buffer_pos >= DECODE_BUFFER_NUM_SAMPLES) { + st_config.sample_request_callback(st_decode_buffer, st_config.num_channels, DECODE_BUFFER_NUM_SAMPLES); + st_buffer_pos = 0; + } + + /* インターリーブしたバッファにデータを詰める */ + for (ch = 0; ch < st_config.num_channels; ch++) { + *buffer++ = (int16_t)st_decode_buffer[ch][st_buffer_pos]; + } + st_buffer_pos++; + } + + /* バッファの解放 */ + hr = IAudioRenderClient_ReleaseBuffer(audio_render_client, available_buffer_frame_size, 0); + assert(SUCCEEDED(hr)); + } +} + +/* 終了 初期化したときのリソースの開放はここで */ +void SRLAPlayer_Finalize(void) +{ + if (st_initialize_count == 1) { + IAudioClient_Stop(audio_client); + IAudioClient_Release(audio_client); + IAudioRenderClient_Release(audio_render_client); + } + + st_initialize_count--; +}