未验证 提交 8e1b4cd5 编写于 作者: Y YangZhou 提交者: GitHub

[engine] rename speechx (#2892)

* rename speechx

* fix wfst decode error

* replace reset with make_unique
上级 21183d48
...@@ -8,7 +8,7 @@ repos: ...@@ -8,7 +8,7 @@ repos:
entry: yapf entry: yapf
args: [-i, -vv] args: [-i, -vv]
types: [python] types: [python]
exclude: (?=speechx/speechx/kaldi|audio/paddleaudio/src|third_party).*(\.cpp|\.cc|\.h\.hpp|\.py)$ exclude: (?=runtime/engine/kaldi|audio/paddleaudio/src|third_party).*(\.cpp|\.cc|\.h\.hpp|\.py)$
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: a11d9314b22d8f8c7556443875b731ef05965464 rev: a11d9314b22d8f8c7556443875b731ef05965464
...@@ -35,7 +35,7 @@ repos: ...@@ -35,7 +35,7 @@ repos:
- --ignore=E501,E228,E226,E261,E266,E128,E402,W503 - --ignore=E501,E228,E226,E261,E266,E128,E402,W503
- --builtins=G,request - --builtins=G,request
- --jobs=1 - --jobs=1
exclude: (?=speechx/speechx/kaldi|audio/paddleaudio/src|third_party).*(\.cpp|\.cc|\.h\.hpp|\.py)$ exclude: (?=runtime/engine/kaldi|audio/paddleaudio/src|third_party).*(\.cpp|\.cc|\.h\.hpp|\.py)$
- repo : https://github.com/Lucas-C/pre-commit-hooks - repo : https://github.com/Lucas-C/pre-commit-hooks
rev: v1.0.1 rev: v1.0.1
...@@ -57,16 +57,16 @@ repos: ...@@ -57,16 +57,16 @@ repos:
entry: bash .pre-commit-hooks/clang-format.hook -i entry: bash .pre-commit-hooks/clang-format.hook -i
language: system language: system
files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$ files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$
exclude: (?=speechx/speechx/kaldi|audio/paddleaudio/src|speechx/patch|speechx/tools/fstbin|speechx/tools/lmbin|third_party/ctc_decoders|speechx/speechx/common/utils).*(\.cpp|\.cc|\.h|\.hpp|\.py)$ exclude: (?=runtime/engine/kaldi|audio/paddleaudio/src|runtime/patch|runtime/tools/fstbin|runtime/tools/lmbin|third_party/ctc_decoders|runtime/engine/common/utils).*(\.cpp|\.cc|\.h|\.hpp|\.py)$
- id: cpplint - id: cpplint
name: cpplint name: cpplint
description: Static code analysis of C/C++ files description: Static code analysis of C/C++ files
language: python language: python
files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$ files: \.(h\+\+|h|hh|hxx|hpp|cuh|c|cc|cpp|cu|c\+\+|cxx|tpp|txx)$
exclude: (?=speechx/speechx/kaldi|audio/paddleaudio/src|speechx/patch|speechx/tools/fstbin|speechx/tools/lmbin|third_party/ctc_decoders|speechx/speechx/common/utils).*(\.cpp|\.cc|\.h|\.hpp|\.py)$ exclude: (?=runtime/engine/kaldi|runtime/engine/common/matrix|audio/paddleaudio/src|runtime/patch|runtime/tools/fstbin|runtime/tools/lmbin|third_party/ctc_decoders|runtime/engine/common/utils).*(\.cpp|\.cc|\.h|\.hpp|\.py)$
entry: cpplint --filter=-build,-whitespace,+whitespace/comma,-whitespace/indent entry: cpplint --filter=-build,-whitespace,+whitespace/comma,-whitespace/indent
- repo: https://github.com/asottile/reorder_python_imports - repo: https://github.com/asottile/reorder_python_imports
rev: v2.4.0 rev: v2.4.0
hooks: hooks:
- id: reorder-python-imports - id: reorder-python-imports
exclude: (?=speechx/speechx/kaldi|audio/paddleaudio/src|speechx/patch|speechx/tools/fstbin|speechx/tools/lmbin|third_party/ctc_decoders).*(\.cpp|\.cc|\.h\.hpp|\.py)$ exclude: (?=runtime/engine/kaldi|audio/paddleaudio/src|runtime/patch|runtime/tools/fstbin|runtime/tools/lmbin|third_party/ctc_decoders).*(\.cpp|\.cc|\.h\.hpp|\.py)$
...@@ -164,7 +164,7 @@ Via the easy-to-use, efficient, flexible and scalable implementation, our vision ...@@ -164,7 +164,7 @@ Via the easy-to-use, efficient, flexible and scalable implementation, our vision
- 👑 2022.11.18: Add [Whisper CLI and Demos](https://github.com/PaddlePaddle/PaddleSpeech/pull/2640), support multi language recognition and translation. - 👑 2022.11.18: Add [Whisper CLI and Demos](https://github.com/PaddlePaddle/PaddleSpeech/pull/2640), support multi language recognition and translation.
- 🔥 2022.11.18: Add [Wav2vec2 CLI and Demos](https://github.com/PaddlePaddle/PaddleSpeech/blob/develop/demos/speech_ssl), Support ASR and Feature Extraction. - 🔥 2022.11.18: Add [Wav2vec2 CLI and Demos](https://github.com/PaddlePaddle/PaddleSpeech/blob/develop/demos/speech_ssl), Support ASR and Feature Extraction.
- 🎉 2022.11.17: Add [male voice for TTS](https://github.com/PaddlePaddle/PaddleSpeech/pull/2660). - 🎉 2022.11.17: Add [male voice for TTS](https://github.com/PaddlePaddle/PaddleSpeech/pull/2660).
- 🔥 2022.11.07: Add [U2/U2++ C++ High Performance Streaming ASR Deployment](https://github.com/PaddlePaddle/PaddleSpeech/blob/develop/speechx/examples/u2pp_ol/wenetspeech). - 🔥 2022.11.07: Add [U2/U2++ C++ High Performance Streaming ASR Deployment](https://github.com/PaddlePaddle/PaddleSpeech/blob/develop/runtime/examples/u2pp_ol/wenetspeech).
- 👑 2022.11.01: Add [Adversarial Loss](https://arxiv.org/pdf/1907.04448.pdf) for [Chinese English mixed TTS](./examples/zh_en_tts/tts3). - 👑 2022.11.01: Add [Adversarial Loss](https://arxiv.org/pdf/1907.04448.pdf) for [Chinese English mixed TTS](./examples/zh_en_tts/tts3).
- 🔥 2022.10.26: Add [Prosody Prediction](https://github.com/PaddlePaddle/PaddleSpeech/tree/develop/examples/other/rhy) for TTS. - 🔥 2022.10.26: Add [Prosody Prediction](https://github.com/PaddlePaddle/PaddleSpeech/tree/develop/examples/other/rhy) for TTS.
- 🎉 2022.10.21: Add [SSML](https://github.com/PaddlePaddle/PaddleSpeech/discussions/2538) for TTS Chinese Text Frontend. - 🎉 2022.10.21: Add [SSML](https://github.com/PaddlePaddle/PaddleSpeech/discussions/2538) for TTS Chinese Text Frontend.
......
tools/valgrind* tools/valgrind*
*log *log
fc_patch/*
...@@ -93,7 +93,7 @@ endif() ...@@ -93,7 +93,7 @@ endif()
# paddle libpaddle.so # paddle libpaddle.so
# paddle include and link option # paddle include and link option
# -L/workspace/DeepSpeech-2.x/speechx/venv/lib/python3.7/site-packages/paddle/libs -L/workspace/DeepSpeech-2.x/speechx/venv/lib/python3.7/site-packages/paddle/fluid -l:libpaddle.so -l:libdnnl.so.2 -l:libiomp5.so # -L/workspace/DeepSpeech-2.x/engine/venv/lib/python3.7/site-packages/paddle/libs -L/workspace/DeepSpeech-2.x/speechx/venv/lib/python3.7/site-packages/paddle/fluid -l:libpaddle.so -l:libdnnl.so.2 -l:libiomp5.so
execute_process( execute_process(
COMMAND python -c "\ COMMAND python -c "\
import os;\ import os;\
...@@ -112,7 +112,7 @@ message(STATUS PADDLE_LINK_FLAGS= ${PADDLE_LINK_FLAGS}) ...@@ -112,7 +112,7 @@ message(STATUS PADDLE_LINK_FLAGS= ${PADDLE_LINK_FLAGS})
string(STRIP ${PADDLE_LINK_FLAGS} PADDLE_LINK_FLAGS) string(STRIP ${PADDLE_LINK_FLAGS} PADDLE_LINK_FLAGS)
# paddle compile option # paddle compile option
# -I/workspace/DeepSpeech-2.x/speechx/venv/lib/python3.7/site-packages/paddle/include # -I/workspace/DeepSpeech-2.x/engine/venv/lib/python3.7/site-packages/paddle/include
execute_process( execute_process(
COMMAND python -c "\ COMMAND python -c "\
import paddle; \ import paddle; \
...@@ -143,6 +143,6 @@ message(STATUS PADDLE_LIB_DIRS= ${PADDLE_LIB_DIRS}) ...@@ -143,6 +143,6 @@ message(STATUS PADDLE_LIB_DIRS= ${PADDLE_LIB_DIRS})
############################################################################### ###############################################################################
# Add local library # Add local library
############################################################################### ###############################################################################
set(SPEECHX_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/speechx) set(ENGINE_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/engine)
add_subdirectory(speechx) add_subdirectory(engine)
# SpeechX -- All in One Speech Task Inference
## Environment ## Environment
...@@ -9,7 +8,7 @@ We develop under: ...@@ -9,7 +8,7 @@ We develop under:
* gcc/g++/gfortran - 8.2.0 * gcc/g++/gfortran - 8.2.0
* cmake - 3.16.0 * cmake - 3.16.0
> Please use `tools/env.sh` to create python `venv`, then `source venv/bin/activate` to build speechx. > Please use `tools/env.sh` to create python `venv`, then `source venv/bin/activate` to build engine.
> We make sure all things work fun under docker, and recommend using it to develop and deploy. > We make sure all things work fun under docker, and recommend using it to develop and deploy.
...@@ -33,7 +32,7 @@ docker run --privileged --net=host --ipc=host -it --rm -v /path/to/paddlespeech ...@@ -33,7 +32,7 @@ docker run --privileged --net=host --ipc=host -it --rm -v /path/to/paddlespeech
bash tools/venv.sh bash tools/venv.sh
``` ```
2. Build `speechx` and `examples`. 2. Build `engine` and `examples`.
For now we are using feature under `develop` branch of paddle, so we need to install `paddlepaddle` nightly build version. For now we are using feature under `develop` branch of paddle, so we need to install `paddlepaddle` nightly build version.
For example: For example:
......
...@@ -2,10 +2,10 @@ include(FetchContent) ...@@ -2,10 +2,10 @@ include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
gflags gflags
URL https://github.com/gflags/gflags/archive/v2.2.2.zip URL https://paddleaudio.bj.bcebos.com/build/gflag-2.2.2.zip
URL_HASH SHA256=19713a36c9f32b33df59d1c79b4958434cb005b5b47dc5400a7a4b078111d9b5 URL_HASH SHA256=19713a36c9f32b33df59d1c79b4958434cb005b5b47dc5400a7a4b078111d9b5
) )
FetchContent_MakeAvailable(gflags) FetchContent_MakeAvailable(gflags)
# openfst need # openfst need
include_directories(${gflags_BINARY_DIR}/include) include_directories(${gflags_BINARY_DIR}/include)
\ No newline at end of file
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
glog glog
URL https://github.com/google/glog/archive/v0.4.0.zip URL https://paddleaudio.bj.bcebos.com/build/glog-0.4.0.zip
URL_HASH SHA256=9e1b54eb2782f53cd8af107ecf08d2ab64b8d0dc2b7f5594472f3bd63ca85cdc URL_HASH SHA256=9e1b54eb2782f53cd8af107ecf08d2ab64b8d0dc2b7f5594472f3bd63ca85cdc
) )
FetchContent_MakeAvailable(glog) FetchContent_MakeAvailable(glog)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
gtest gtest
URL https://github.com/google/googletest/archive/release-1.11.0.zip URL https://paddleaudio.bj.bcebos.com/build/gtest-release-1.11.0.zip
URL_HASH SHA256=353571c2440176ded91c2de6d6cd88ddd41401d14692ec1f99e35d013feda55a URL_HASH SHA256=353571c2440176ded91c2de6d6cd88ddd41401d14692ec1f99e35d013feda55a
) )
FetchContent_MakeAvailable(gtest) FetchContent_MakeAvailable(gtest)
...@@ -12,4 +12,4 @@ include_directories(${gtest_BINARY_DIR} ${gtest_SOURCE_DIR}/src) ...@@ -12,4 +12,4 @@ include_directories(${gtest_BINARY_DIR} ${gtest_SOURCE_DIR}/src)
if(WITH_TESTING) if(WITH_TESTING)
enable_testing() enable_testing()
endif() endif()
\ No newline at end of file
include(FetchContent)
set(openfst_PREFIX_DIR ${fc_patch}/openfst) set(openfst_PREFIX_DIR ${fc_patch}/openfst)
set(openfst_SOURCE_DIR ${fc_patch}/openfst-src) set(openfst_SOURCE_DIR ${fc_patch}/openfst-src)
set(openfst_BINARY_DIR ${fc_patch}/openfst-build) set(openfst_BINARY_DIR ${fc_patch}/openfst-build)
include(FetchContent)
# openfst Acknowledgments: # openfst Acknowledgments:
#Cyril Allauzen, Michael Riley, Johan Schalkwyk, Wojciech Skut and Mehryar Mohri, #Cyril Allauzen, Michael Riley, Johan Schalkwyk, Wojciech Skut and Mehryar Mohri,
#"OpenFst: A General and Efficient Weighted Finite-State Transducer Library", #"OpenFst: A General and Efficient Weighted Finite-State Transducer Library",
...@@ -25,5 +25,7 @@ ExternalProject_Add(openfst ...@@ -25,5 +25,7 @@ ExternalProject_Add(openfst
) )
link_directories(${openfst_PREFIX_DIR}/lib) link_directories(${openfst_PREFIX_DIR}/lib)
include_directories(${openfst_PREFIX_DIR}/include) include_directories(${openfst_PREFIX_DIR}/include)
message(STATUS "OpenFST inc dir: ${openfst_PREFIX_DIR}/include") message(STATUS "OpenFST inc dir: ${openfst_PREFIX_DIR}/include")
message(STATUS "OpenFST lib dir: ${openfst_PREFIX_DIR}/lib") message(STATUS "OpenFST lib dir: ${openfst_PREFIX_DIR}/lib")
\ No newline at end of file
...@@ -63,9 +63,7 @@ void CTCPrefixBeamSearch::Reset() { ...@@ -63,9 +63,7 @@ void CTCPrefixBeamSearch::Reset() {
times_.emplace_back(empty); times_.emplace_back(empty);
} }
void CTCPrefixBeamSearch::InitDecoder() { void CTCPrefixBeamSearch::InitDecoder() { Reset(); }
Reset();
}
void CTCPrefixBeamSearch::AdvanceDecode( void CTCPrefixBeamSearch::AdvanceDecode(
const std::shared_ptr<kaldi::DecodableInterface>& decodable) { const std::shared_ptr<kaldi::DecodableInterface>& decodable) {
......
...@@ -29,6 +29,11 @@ TLGDecoder::TLGDecoder(TLGDecoderOptions opts) : opts_(opts) { ...@@ -29,6 +29,11 @@ TLGDecoder::TLGDecoder(TLGDecoderOptions opts) : opts_(opts) {
void TLGDecoder::Reset() { void TLGDecoder::Reset() {
decoder_->InitDecoding(); decoder_->InitDecoding();
hypotheses_.clear();
likelihood_.clear();
olabels_.clear();
times_.clear();
num_frame_decoded_ = 0; num_frame_decoded_ = 0;
return; return;
} }
...@@ -103,7 +108,7 @@ void TLGDecoder::FinalizeSearch() { ...@@ -103,7 +108,7 @@ void TLGDecoder::FinalizeSearch() {
time.push_back(idx); // fake time, todo later time.push_back(idx); // fake time, todo later
hypotheses_.push_back(hypothese); hypotheses_.push_back(hypothese);
times_.push_back(time); times_.push_back(time);
olabels.push_back(words_id); olabels_.push_back(words_id);
likelihood_.push_back(-(weight.Value2() + weight.Value1())); likelihood_.push_back(-(weight.Value2() + weight.Value1()));
} }
} }
......
...@@ -24,6 +24,7 @@ DECLARE_string(graph_path); ...@@ -24,6 +24,7 @@ DECLARE_string(graph_path);
DECLARE_int32(max_active); DECLARE_int32(max_active);
DECLARE_double(beam); DECLARE_double(beam);
DECLARE_double(lattice_beam); DECLARE_double(lattice_beam);
DECLARE_int32(nbest);
namespace ppspeech { namespace ppspeech {
...@@ -46,7 +47,7 @@ struct TLGDecoderOptions { ...@@ -46,7 +47,7 @@ struct TLGDecoderOptions {
decoder_opts.opts.max_active = FLAGS_max_active; decoder_opts.opts.max_active = FLAGS_max_active;
decoder_opts.opts.beam = FLAGS_beam; decoder_opts.opts.beam = FLAGS_beam;
decoder_opts.opts.lattice_beam = FLAGS_lattice_beam; decoder_opts.opts.lattice_beam = FLAGS_lattice_beam;
// decoder_opts.nbest = FLAGS_lattice_nbest; decoder_opts.nbest = FLAGS_nbest;
LOG(INFO) << "LatticeFasterDecoder max active: " LOG(INFO) << "LatticeFasterDecoder max active: "
<< decoder_opts.opts.max_active; << decoder_opts.opts.max_active;
LOG(INFO) << "LatticeFasterDecoder beam: " << decoder_opts.opts.beam; LOG(INFO) << "LatticeFasterDecoder beam: " << decoder_opts.opts.beam;
...@@ -85,7 +86,7 @@ class TLGDecoder : public DecoderBase { ...@@ -85,7 +86,7 @@ class TLGDecoder : public DecoderBase {
return hypotheses_; return hypotheses_;
} }
const std::vector<std::vector<int>>& Outputs() const override { const std::vector<std::vector<int>>& Outputs() const override {
return olabels; return olabels_;
} // outputs_; } } // outputs_; }
const std::vector<float>& Likelihood() const override { const std::vector<float>& Likelihood() const override {
return likelihood_; return likelihood_;
...@@ -111,8 +112,9 @@ class TLGDecoder : public DecoderBase { ...@@ -111,8 +112,9 @@ class TLGDecoder : public DecoderBase {
private: private:
void AdvanceDecoding(kaldi::DecodableInterface* decodable); void AdvanceDecoding(kaldi::DecodableInterface* decodable);
int num_frame_decoded_;
std::vector<std::vector<int>> hypotheses_; std::vector<std::vector<int>> hypotheses_;
std::vector<std::vector<int>> olabels; std::vector<std::vector<int>> olabels_;
std::vector<float> likelihood_; std::vector<float> likelihood_;
std::vector<std::vector<int>> times_; std::vector<std::vector<int>> times_;
...@@ -123,4 +125,4 @@ class TLGDecoder : public DecoderBase { ...@@ -123,4 +125,4 @@ class TLGDecoder : public DecoderBase {
}; };
} // namespace ppspeech } // namespace ppspeech
\ No newline at end of file
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#pragma once #pragma once
#include "base/common.h" #include "base/common.h"
//#include "decoder/ctc_tlg_decoder.h"
// feature // feature
DEFINE_bool(use_fbank, false, "False for fbank; or linear feature"); DEFINE_bool(use_fbank, false, "False for fbank; or linear feature");
......
...@@ -13,13 +13,13 @@ ...@@ -13,13 +13,13 @@
// limitations under the License. // limitations under the License.
#include "nnet/u2_nnet.h"
#include "base/common.h" #include "base/common.h"
#include "decoder/param.h" #include "decoder/param.h"
#include "frontend/assembler.h" #include "frontend/assembler.h"
#include "frontend/data_cache.h" #include "frontend/data_cache.h"
#include "kaldi/util/table-types.h" #include "kaldi/util/table-types.h"
#include "nnet/decodable.h" #include "nnet/decodable.h"
#include "nnet/u2_nnet.h"
DEFINE_string(feature_rspecifier, "", "test feature rspecifier"); DEFINE_string(feature_rspecifier, "", "test feature rspecifier");
...@@ -93,9 +93,9 @@ int main(int argc, char* argv[]) { ...@@ -93,9 +93,9 @@ int main(int argc, char* argv[]) {
ori_feature_len - chunk_idx * chunk_stride, chunk_size); ori_feature_len - chunk_idx * chunk_stride, chunk_size);
} }
if (this_chunk_size < receptive_field_length) { if (this_chunk_size < receptive_field_length) {
LOG(WARNING) LOG(WARNING) << "utt: " << utt << " skip last "
<< "utt: " << utt << " skip last " << this_chunk_size << this_chunk_size << " frames, expect is "
<< " frames, expect is " << receptive_field_length; << receptive_field_length;
break; break;
} }
......
...@@ -13,13 +13,13 @@ ...@@ -13,13 +13,13 @@
// limitations under the License. // limitations under the License.
#include "nnet/u2_nnet.h"
#include "base/common.h" #include "base/common.h"
#include "decoder/param.h" #include "decoder/param.h"
#include "frontend/wave-reader.h"
#include "frontend/feature_pipeline.h" #include "frontend/feature_pipeline.h"
#include "frontend/wave-reader.h"
#include "kaldi/util/table-types.h" #include "kaldi/util/table-types.h"
#include "nnet/decodable.h" #include "nnet/decodable.h"
#include "nnet/u2_nnet.h"
#include "nnet/nnet_producer.h" #include "nnet/nnet_producer.h"
DEFINE_string(wav_rspecifier, "", "test wav rspecifier"); DEFINE_string(wav_rspecifier, "", "test wav rspecifier");
...@@ -104,7 +104,7 @@ int main(int argc, char* argv[]) { ...@@ -104,7 +104,7 @@ int main(int argc, char* argv[]) {
CHECK(sample_offset == tot_samples); CHECK(sample_offset == tot_samples);
std::vector<std::vector<kaldi::BaseFloat>> prob_vec; std::vector<std::vector<kaldi::BaseFloat>> prob_vec;
while(1) { while (1) {
std::vector<kaldi::BaseFloat> logprobs; std::vector<kaldi::BaseFloat> logprobs;
bool isok = nnet_producer->Read(&logprobs); bool isok = nnet_producer->Read(&logprobs);
if (nnet_producer->IsFinished()) break; if (nnet_producer->IsFinished()) break;
......
...@@ -33,12 +33,12 @@ U2Recognizer::U2Recognizer(const U2RecognizerResource& resource) ...@@ -33,12 +33,12 @@ U2Recognizer::U2Recognizer(const U2RecognizerResource& resource)
decodable_.reset(new Decodable(nnet_producer_, am_scale)); decodable_.reset(new Decodable(nnet_producer_, am_scale));
CHECK_NE(resource.vocab_path, ""); CHECK_NE(resource.vocab_path, "");
if (resource.decoder_opts.tlg_decoder_opts.fst_path == "") { if (resource.decoder_opts.tlg_decoder_opts.fst_path.empty()) {
LOG(INFO) << resource.decoder_opts.tlg_decoder_opts.fst_path; LOG(INFO) << resource.decoder_opts.tlg_decoder_opts.fst_path;
decoder_.reset(new CTCPrefixBeamSearch( decoder_ = std::make_unique<CTCPrefixBeamSearch>(
resource.vocab_path, resource.decoder_opts.ctc_prefix_search_opts)); resource.vocab_path, resource.decoder_opts.ctc_prefix_search_opts);
} else { } else {
decoder_.reset(new TLGDecoder(resource.decoder_opts.tlg_decoder_opts)); decoder_ = std::make_unique<TLGDecoder>(resource.decoder_opts.tlg_decoder_opts);
} }
symbol_table_ = decoder_->WordSymbolTable(); symbol_table_ = decoder_->WordSymbolTable();
...@@ -268,4 +268,4 @@ void U2Recognizer::SetInputFinished() { ...@@ -268,4 +268,4 @@ void U2Recognizer::SetInputFinished() {
} }
} // namespace ppspeech } // namespace ppspeech
\ No newline at end of file
...@@ -31,11 +31,9 @@ DECLARE_double(rescoring_weight); ...@@ -31,11 +31,9 @@ DECLARE_double(rescoring_weight);
DECLARE_double(reverse_weight); DECLARE_double(reverse_weight);
DECLARE_int32(nbest); DECLARE_int32(nbest);
DECLARE_int32(blank); DECLARE_int32(blank);
DECLARE_double(acoustic_scale); DECLARE_double(acoustic_scale);
DECLARE_string(vocab_path); DECLARE_string(vocab_path);
DECLARE_string(word_symbol_table); DECLARE_string(word_symbol_table);
// DECLARE_string(fst_path);
namespace ppspeech { namespace ppspeech {
...@@ -74,10 +72,6 @@ struct DecodeOptions { ...@@ -74,10 +72,6 @@ struct DecodeOptions {
decoder_opts.ctc_prefix_search_opts.blank = FLAGS_blank; decoder_opts.ctc_prefix_search_opts.blank = FLAGS_blank;
decoder_opts.ctc_prefix_search_opts.first_beam_size = FLAGS_nbest; decoder_opts.ctc_prefix_search_opts.first_beam_size = FLAGS_nbest;
decoder_opts.ctc_prefix_search_opts.second_beam_size = FLAGS_nbest; decoder_opts.ctc_prefix_search_opts.second_beam_size = FLAGS_nbest;
// decoder_opts.tlg_decoder_opts.fst_path = "";//FLAGS_fst_path;
// decoder_opts.tlg_decoder_opts.word_symbol_table =
// FLAGS_word_symbol_table;
// decoder_opts.tlg_decoder_opts.nbest = FLAGS_nbest;
decoder_opts.tlg_decoder_opts = decoder_opts.tlg_decoder_opts =
ppspeech::TLGDecoderOptions::InitFromFlags(); ppspeech::TLGDecoderOptions::InitFromFlags();
...@@ -183,4 +177,4 @@ class U2Recognizer { ...@@ -183,4 +177,4 @@ class U2Recognizer {
std::thread thread_; std::thread thread_;
}; };
} // namespace ppspeech } // namespace ppspeech
\ No newline at end of file
...@@ -12,10 +12,10 @@ ...@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "recognizer/u2_recognizer.h"
#include "decoder/param.h" #include "decoder/param.h"
#include "frontend/wave-reader.h" #include "frontend/wave-reader.h"
#include "kaldi/util/table-types.h" #include "kaldi/util/table-types.h"
#include "recognizer/u2_recognizer.h"
DEFINE_string(wav_rspecifier, "", "test feature rspecifier"); DEFINE_string(wav_rspecifier, "", "test feature rspecifier");
DEFINE_string(result_wspecifier, "", "test result wspecifier"); DEFINE_string(result_wspecifier, "", "test result wspecifier");
......
...@@ -100,7 +100,7 @@ int main(int argc, char* argv[]) { ...@@ -100,7 +100,7 @@ int main(int argc, char* argv[]) {
continue; continue;
} }
tot_decode_time += local_timer.Elapsed(); tot_decode_time += local_timer.Elapsed();
LOG(INFO) << utt << " " << result; LOG(INFO) << utt << " " << result;
LOG(INFO) << " RTF: " << local_timer.Elapsed() / dur << " dur: " << dur LOG(INFO) << " RTF: " << local_timer.Elapsed() / dur << " dur: " << dur
<< " cost: " << local_timer.Elapsed(); << " cost: " << local_timer.Elapsed();
......
...@@ -12,10 +12,10 @@ ...@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "websocket/websocket_client.h"
#include "kaldi/feat/wave-reader.h" #include "kaldi/feat/wave-reader.h"
#include "kaldi/util/kaldi-io.h" #include "kaldi/util/kaldi-io.h"
#include "kaldi/util/table-types.h" #include "kaldi/util/table-types.h"
#include "websocket/websocket_client.h"
DEFINE_string(host, "127.0.0.1", "host of websocket server"); DEFINE_string(host, "127.0.0.1", "host of websocket server");
DEFINE_int32(port, 8082, "port of websocket server"); DEFINE_int32(port, 8082, "port of websocket server");
......
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "decoder/param.h"
#include "websocket/websocket_server.h" #include "websocket/websocket_server.h"
#include "decoder/param.h"
DEFINE_int32(port, 8082, "websocket listening port"); DEFINE_int32(port, 8082, "websocket listening port");
......
...@@ -28,7 +28,7 @@ typedef int int32; // NOLINT ...@@ -28,7 +28,7 @@ typedef int int32; // NOLINT
#if defined(__LP64__) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) #if defined(__LP64__) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
typedef long int64; // NOLINT typedef long int64; // NOLINT
#else #else
typedef long long int64; // NOLINT typedef long long int64; // NOLINT
#endif #endif
typedef unsigned char uint8; // NOLINT typedef unsigned char uint8; // NOLINT
......
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#include <cstring> #include <cstring>
#include <deque> #include <deque>
#include <fstream> #include <fstream>
#include <functional>
#include <future>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <istream> #include <istream>
...@@ -42,8 +44,6 @@ ...@@ -42,8 +44,6 @@
#include <unordered_set> #include <unordered_set>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <future>
#include <functional>
#include "base/basic_types.h" #include "base/basic_types.h"
#include "base/flags.h" #include "base/flags.h"
......
...@@ -97,8 +97,8 @@ bool Assembler::Compute(vector<BaseFloat>* feats) { ...@@ -97,8 +97,8 @@ bool Assembler::Compute(vector<BaseFloat>* feats) {
CHECK(val.size() == dim_) << val.size(); CHECK(val.size() == dim_) << val.size();
int32 start = counter * dim_; int32 start = counter * dim_;
std::memcpy(feats->data() + start, std::memcpy(
val.data(), val.size() * sizeof(BaseFloat)); feats->data() + start, val.data(), val.size() * sizeof(BaseFloat));
if (this_chunk_size - counter <= cache_size_) { if (this_chunk_size - counter <= cache_size_) {
feature_cache_.push(val); feature_cache_.push(val);
......
...@@ -84,11 +84,12 @@ void CMVN::Compute(vector<BaseFloat>* feats) const { ...@@ -84,11 +84,12 @@ void CMVN::Compute(vector<BaseFloat>* feats) const {
KALDI_ASSERT(feats != NULL); KALDI_ASSERT(feats != NULL);
if (feats->size() % dim_ != 0) { if (feats->size() % dim_ != 0) {
LOG(ERROR)<< "Dim mismatch: cmvn " << mean_stats_.size() << ',' LOG(ERROR) << "Dim mismatch: cmvn " << mean_stats_.size() << ','
<< var_stats_.size() - 1 << ", feats " << feats->size() << 'x'; << var_stats_.size() - 1 << ", feats " << feats->size()
<< 'x';
} }
if (var_stats_.size() == 0 && var_norm_) { if (var_stats_.size() == 0 && var_norm_) {
LOG(ERROR) LOG(ERROR)
<< "You requested variance normalization but no variance stats_ " << "You requested variance normalization but no variance stats_ "
<< "are supplied."; << "are supplied.";
} }
...@@ -98,8 +99,8 @@ void CMVN::Compute(vector<BaseFloat>* feats) const { ...@@ -98,8 +99,8 @@ void CMVN::Compute(vector<BaseFloat>* feats) const {
// computing an offset and representing it as stats_, we use a count of one. // computing an offset and representing it as stats_, we use a count of one.
if (count < 1.0) if (count < 1.0)
LOG(ERROR) << "Insufficient stats_ for cepstral mean and variance " LOG(ERROR) << "Insufficient stats_ for cepstral mean and variance "
"normalization: " "normalization: "
<< "count = " << count; << "count = " << count;
if (!var_norm_) { if (!var_norm_) {
vector<BaseFloat> offset(feats->size()); vector<BaseFloat> offset(feats->size());
...@@ -112,11 +113,12 @@ void CMVN::Compute(vector<BaseFloat>* feats) const { ...@@ -112,11 +113,12 @@ void CMVN::Compute(vector<BaseFloat>* feats) const {
// with the dim_ of feature. // with the dim_ of feature.
// the dim_ of feats = dim_ * num_frames; // the dim_ of feats = dim_ * num_frames;
for (int32 idx = 0; idx < feats->size() / dim_; ++idx) { for (int32 idx = 0; idx < feats->size() / dim_; ++idx) {
std::memcpy(mean_stats_apply.data() + dim_ * idx, std::memcpy(mean_stats_apply.data() + dim_ * idx,
mean_stats.data(), dim_* sizeof(double)); mean_stats.data(),
dim_ * sizeof(double));
} }
for (size_t idx = 0; idx < feats->size(); ++idx) { for (size_t idx = 0; idx < feats->size(); ++idx) {
feats->at(idx) += offset[idx]; feats->at(idx) += offset[idx];
} }
return; return;
} }
...@@ -130,7 +132,7 @@ void CMVN::Compute(vector<BaseFloat>* feats) const { ...@@ -130,7 +132,7 @@ void CMVN::Compute(vector<BaseFloat>* feats) const {
double var = (var_stats_[d] / count) - mean * mean, floor = 1.0e-20; double var = (var_stats_[d] / count) - mean * mean, floor = 1.0e-20;
if (var < floor) { if (var < floor) {
LOG(WARNING) << "Flooring cepstral variance from " << var << " to " LOG(WARNING) << "Flooring cepstral variance from " << var << " to "
<< floor; << floor;
var = floor; var = floor;
} }
scale = 1.0 / sqrt(var); scale = 1.0 / sqrt(var);
...@@ -146,7 +148,7 @@ void CMVN::Compute(vector<BaseFloat>* feats) const { ...@@ -146,7 +148,7 @@ void CMVN::Compute(vector<BaseFloat>* feats) const {
} }
// Apply the normalization. // Apply the normalization.
for (size_t idx = 0; idx < feats->size(); ++idx) { for (size_t idx = 0; idx < feats->size(); ++idx) {
feats->at(idx) *= norm1[idx]; feats->at(idx) *= norm1[idx];
} }
for (size_t idx = 0; idx < feats->size(); ++idx) { for (size_t idx = 0; idx < feats->size(); ++idx) {
......
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
#pragma once #pragma once
#include "base/common.h" #include "base/common.h"
#include "frontend/feature_common.h"
#include "frontend/feature-fbank.h" #include "frontend/feature-fbank.h"
#include "frontend/feature_common.h"
namespace ppspeech { namespace ppspeech {
......
...@@ -67,7 +67,7 @@ bool FeatureCache::Compute() { ...@@ -67,7 +67,7 @@ bool FeatureCache::Compute() {
for (int chunk_idx = 0; chunk_idx < num_chunk; ++chunk_idx) { for (int chunk_idx = 0; chunk_idx < num_chunk; ++chunk_idx) {
int32 start = chunk_idx * dim_; int32 start = chunk_idx * dim_;
vector<BaseFloat> feature_chunk(feature.data() + start, vector<BaseFloat> feature_chunk(feature.data() + start,
feature.data() + start + dim_); feature.data() + start + dim_);
// feed cache // feed cache
cache_.push(feature_chunk); cache_.push(feature_chunk);
......
...@@ -57,7 +57,7 @@ class FeatureCache : public FrontendInterface { ...@@ -57,7 +57,7 @@ class FeatureCache : public FrontendInterface {
bool Compute(); bool Compute();
int32 dim_; int32 dim_;
size_t max_size_; // cache capacity size_t max_size_; // cache capacity
std::unique_ptr<FrontendInterface> base_extractor_; std::unique_ptr<FrontendInterface> base_extractor_;
std::queue<std::vector<BaseFloat>> cache_; // feature cache std::queue<std::vector<BaseFloat>> cache_; // feature cache
......
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
#pragma once #pragma once
#include "frontend_itf.h"
#include "frontend/feature-window.h" #include "frontend/feature-window.h"
#include "frontend_itf.h"
namespace ppspeech { namespace ppspeech {
......
...@@ -18,11 +18,11 @@ ...@@ -18,11 +18,11 @@
#include "frontend/assembler.h" #include "frontend/assembler.h"
#include "frontend/audio_cache.h" #include "frontend/audio_cache.h"
#include "frontend/cmvn.h"
#include "frontend/data_cache.h" #include "frontend/data_cache.h"
#include "frontend/fbank.h" #include "frontend/fbank.h"
#include "frontend/feature_cache.h" #include "frontend/feature_cache.h"
#include "frontend/frontend_itf.h" #include "frontend/frontend_itf.h"
#include "frontend/cmvn.h"
// feature // feature
DECLARE_bool(fill_zero); DECLARE_bool(fill_zero);
......
// feat/wave-reader.cc
// Copyright 2009-2011 Karel Vesely; Petr Motlicek
// 2013 Florent Masson
// 2013 Johns Hopkins University (author: Daniel Povey)
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <cstdio>
#include <limits>
#include <sstream>
#include <vector>
#include "base/kaldi-error.h"
#include "base/kaldi-utils.h"
#include "frontend/wave-reader.h"
namespace kaldi {
// A utility class for reading wave header.
struct WaveHeaderReadGofer {
std::istream &is;
bool swap;
char tag[5];
WaveHeaderReadGofer(std::istream &is) : is(is), swap(false) {
memset(tag, '\0', sizeof tag);
}
void Expect4ByteTag(const char *expected) {
is.read(tag, 4);
if (is.fail())
KALDI_ERR << "WaveData: expected " << expected
<< ", failed to read anything";
if (strcmp(tag, expected))
KALDI_ERR << "WaveData: expected " << expected << ", got " << tag;
}
void Read4ByteTag() {
is.read(tag, 4);
if (is.fail())
KALDI_ERR << "WaveData: expected 4-byte chunk-name, got read error";
}
uint32 ReadUint32() {
union {
char result[4];
uint32 ans;
} u;
is.read(u.result, 4);
if (swap) KALDI_SWAP4(u.result);
if (is.fail())
KALDI_ERR << "WaveData: unexpected end of file or read error";
return u.ans;
}
uint16 ReadUint16() {
union {
char result[2];
int16 ans;
} u;
is.read(u.result, 2);
if (swap) KALDI_SWAP2(u.result);
if (is.fail())
KALDI_ERR << "WaveData: unexpected end of file or read error";
return u.ans;
}
};
static void WriteUint32(std::ostream &os, int32 i) {
union {
char buf[4];
int i;
} u;
u.i = i;
#ifdef __BIG_ENDIAN__
KALDI_SWAP4(u.buf);
#endif
os.write(u.buf, 4);
if (os.fail()) KALDI_ERR << "WaveData: error writing to stream.";
}
static void WriteUint16(std::ostream &os, int16 i) {
union {
char buf[2];
int16 i;
} u;
u.i = i;
#ifdef __BIG_ENDIAN__
KALDI_SWAP2(u.buf);
#endif
os.write(u.buf, 2);
if (os.fail()) KALDI_ERR << "WaveData: error writing to stream.";
}
void WaveInfo::Read(std::istream &is) {
WaveHeaderReadGofer reader(is);
reader.Read4ByteTag();
if (strcmp(reader.tag, "RIFF") == 0)
reverse_bytes_ = false;
else if (strcmp(reader.tag, "RIFX") == 0)
reverse_bytes_ = true;
else
KALDI_ERR << "WaveData: expected RIFF or RIFX, got " << reader.tag;
#ifdef __BIG_ENDIAN__
reverse_bytes_ = !reverse_bytes_;
#endif
reader.swap = reverse_bytes_;
uint32 riff_chunk_size = reader.ReadUint32();
reader.Expect4ByteTag("WAVE");
uint32 riff_chunk_read = 0;
riff_chunk_read += 4; // WAVE included in riff_chunk_size.
// Possibly skip any RIFF tags between 'WAVE' and 'fmt '.
// Apple devices produce a filler tag 'JUNK' for memory alignment.
reader.Read4ByteTag();
riff_chunk_read += 4;
while (strcmp(reader.tag, "fmt ") != 0) {
uint32 filler_size = reader.ReadUint32();
riff_chunk_read += 4;
for (uint32 i = 0; i < filler_size; i++) {
is.get(); // read 1 byte,
}
riff_chunk_read += filler_size;
// get next RIFF tag,
reader.Read4ByteTag();
riff_chunk_read += 4;
}
KALDI_ASSERT(strcmp(reader.tag, "fmt ") == 0);
uint32 subchunk1_size = reader.ReadUint32();
uint16 audio_format = reader.ReadUint16();
num_channels_ = reader.ReadUint16();
uint32 sample_rate = reader.ReadUint32(), byte_rate = reader.ReadUint32(),
block_align = reader.ReadUint16(),
bits_per_sample = reader.ReadUint16();
samp_freq_ = static_cast<BaseFloat>(sample_rate);
uint32 fmt_chunk_read = 16;
if (audio_format == 1) {
if (subchunk1_size < 16) {
KALDI_ERR << "WaveData: expect PCM format data to have fmt chunk "
<< "of at least size 16.";
}
} else if (audio_format == 0xFFFE) { // WAVE_FORMAT_EXTENSIBLE
uint16 extra_size = reader.ReadUint16();
if (subchunk1_size < 40 || extra_size < 22) {
KALDI_ERR
<< "WaveData: malformed WAVE_FORMAT_EXTENSIBLE format data.";
}
reader.ReadUint16(); // Unused for PCM.
reader.ReadUint32(); // Channel map: we do not care.
uint32 guid1 = reader.ReadUint32(), guid2 = reader.ReadUint32(),
guid3 = reader.ReadUint32(), guid4 = reader.ReadUint32();
fmt_chunk_read = 40;
// Support only KSDATAFORMAT_SUBTYPE_PCM for now. Interesting formats:
// ("00000001-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_PCM)
// ("00000003-0000-0010-8000-00aa00389b71",
// KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
// ("00000006-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_ALAW)
// ("00000007-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_MULAW)
if (guid1 != 0x00000001 || guid2 != 0x00100000 || guid3 != 0xAA000080 ||
guid4 != 0x719B3800) {
KALDI_ERR << "WaveData: unsupported WAVE_FORMAT_EXTENSIBLE format.";
}
} else {
KALDI_ERR << "WaveData: can read only PCM data, format id in file is: "
<< audio_format;
}
for (uint32 i = fmt_chunk_read; i < subchunk1_size; ++i)
is.get(); // use up extra data.
if (num_channels_ == 0) KALDI_ERR << "WaveData: no channels present";
if (bits_per_sample != 16)
KALDI_ERR << "WaveData: unsupported bits_per_sample = "
<< bits_per_sample;
if (byte_rate != sample_rate * bits_per_sample / 8 * num_channels_)
KALDI_ERR << "Unexpected byte rate " << byte_rate << " vs. "
<< sample_rate << " * " << (bits_per_sample / 8) << " * "
<< num_channels_;
if (block_align != num_channels_ * bits_per_sample / 8)
KALDI_ERR << "Unexpected block_align: " << block_align << " vs. "
<< num_channels_ << " * " << (bits_per_sample / 8);
riff_chunk_read += 4 + subchunk1_size;
// size of what we just read, 4 for subchunk1_size + subchunk1_size itself.
// We support an optional "fact" chunk (which is useless but which
// we encountered), and then a single "data" chunk.
reader.Read4ByteTag();
riff_chunk_read += 4;
// Skip any subchunks between "fmt" and "data". Usually there will
// be a single "fact" subchunk, but on Windows there can also be a
// "list" subchunk.
while (strcmp(reader.tag, "data") != 0) {
// We will just ignore the data in these chunks.
uint32 chunk_sz = reader.ReadUint32();
if (chunk_sz != 4 && strcmp(reader.tag, "fact") == 0)
KALDI_WARN << "Expected fact chunk to be 4 bytes long.";
for (uint32 i = 0; i < chunk_sz; i++) is.get();
riff_chunk_read +=
4 + chunk_sz; // for chunk_sz (4) + chunk contents (chunk-sz)
// Now read the next chunk name.
reader.Read4ByteTag();
riff_chunk_read += 4;
}
KALDI_ASSERT(strcmp(reader.tag, "data") == 0);
uint32 data_chunk_size = reader.ReadUint32();
riff_chunk_read += 4;
// Figure out if the file is going to be read to the end. Values as
// observed in the wild:
bool is_stream_mode =
riff_chunk_size == 0 || riff_chunk_size == 0xFFFFFFFF ||
data_chunk_size == 0 || data_chunk_size == 0xFFFFFFFF ||
data_chunk_size == 0x7FFFF000; // This value is used by SoX.
if (is_stream_mode)
KALDI_VLOG(1) << "Read in RIFF chunk size: " << riff_chunk_size
<< ", data chunk size: " << data_chunk_size
<< ". Assume 'stream mode' (reading data to EOF).";
if (!is_stream_mode &&
std::abs(static_cast<int64>(riff_chunk_read) +
static_cast<int64>(data_chunk_size) -
static_cast<int64>(riff_chunk_size)) > 1) {
// We allow the size to be off by one without warning, because there is
// a
// weirdness in the format of RIFF files that means that the input may
// sometimes be padded with 1 unused byte to make the total size even.
KALDI_WARN << "Expected " << riff_chunk_size
<< " bytes in RIFF chunk, but "
<< "after first data block there will be " << riff_chunk_read
<< " + " << data_chunk_size << " bytes "
<< "(we do not support reading multiple data chunks).";
}
if (is_stream_mode)
samp_count_ = -1;
else
samp_count_ = data_chunk_size / block_align;
}
void WaveData::Read(std::istream &is) {
const uint32 kBlockSize = 1024 * 1024;
WaveInfo header;
header.Read(is);
data_.Resize(0, 0); // clear the data.
samp_freq_ = header.SampFreq();
std::vector<char> buffer;
uint32 bytes_to_go = header.IsStreamed() ? kBlockSize : header.DataBytes();
// Once in a while header.DataBytes() will report an insane value;
// read the file to the end
while (is && bytes_to_go > 0) {
uint32 block_bytes = std::min(bytes_to_go, kBlockSize);
uint32 offset = buffer.size();
buffer.resize(offset + block_bytes);
is.read(&buffer[offset], block_bytes);
uint32 bytes_read = is.gcount();
buffer.resize(offset + bytes_read);
if (!header.IsStreamed()) bytes_to_go -= bytes_read;
}
if (is.bad()) KALDI_ERR << "WaveData: file read error";
if (buffer.size() == 0) KALDI_ERR << "WaveData: empty file (no data)";
if (!header.IsStreamed() && buffer.size() < header.DataBytes()) {
KALDI_WARN << "Expected " << header.DataBytes()
<< " bytes of wave data, "
<< "but read only " << buffer.size() << " bytes. "
<< "Truncated file?";
}
uint16 *data_ptr = reinterpret_cast<uint16 *>(&buffer[0]);
// The matrix is arranged row per channel, column per sample.
data_.Resize(header.NumChannels(), buffer.size() / header.BlockAlign());
for (uint32 i = 0; i < data_.NumCols(); ++i) {
for (uint32 j = 0; j < data_.NumRows(); ++j) {
int16 k = *data_ptr++;
if (header.ReverseBytes()) KALDI_SWAP2(k);
data_(j, i) = k;
}
}
}
// Write 16-bit PCM.
// note: the WAVE chunk contains 2 subchunks.
//
// subchunk2size = data.NumRows() * data.NumCols() * 2.
void WaveData::Write(std::ostream &os) const {
os << "RIFF";
if (data_.NumRows() == 0)
KALDI_ERR << "Error: attempting to write empty WAVE file";
int32 num_chan = data_.NumRows(), num_samp = data_.NumCols(),
bytes_per_samp = 2;
int32 subchunk2size = (num_chan * num_samp * bytes_per_samp);
int32 chunk_size = 36 + subchunk2size;
WriteUint32(os, chunk_size);
os << "WAVE";
os << "fmt ";
WriteUint32(os, 16);
WriteUint16(os, 1);
WriteUint16(os, num_chan);
KALDI_ASSERT(samp_freq_ > 0);
WriteUint32(os, static_cast<int32>(samp_freq_));
WriteUint32(os, static_cast<int32>(samp_freq_) * num_chan * bytes_per_samp);
WriteUint16(os, num_chan * bytes_per_samp);
WriteUint16(os, 8 * bytes_per_samp);
os << "data";
WriteUint32(os, subchunk2size);
const BaseFloat *data_ptr = data_.Data();
int32 stride = data_.Stride();
int num_clipped = 0;
for (int32 i = 0; i < num_samp; i++) {
for (int32 j = 0; j < num_chan; j++) {
int32 elem = static_cast<int32>(trunc(data_ptr[j * stride + i]));
int16 elem_16 = static_cast<int16>(elem);
if (elem < std::numeric_limits<int16>::min()) {
elem_16 = std::numeric_limits<int16>::min();
++num_clipped;
} else if (elem > std::numeric_limits<int16>::max()) {
elem_16 = std::numeric_limits<int16>::max();
++num_clipped;
}
#ifdef __BIG_ENDIAN__
KALDI_SWAP2(elem_16);
#endif
os.write(reinterpret_cast<char *>(&elem_16), 2);
}
}
if (os.fail()) KALDI_ERR << "Error writing wave data to stream.";
if (num_clipped > 0)
KALDI_WARN << "WARNING: clipped " << num_clipped
<< " samples out of total " << num_chan * num_samp
<< ". Reduce volume?";
}
} // end namespace kaldi
...@@ -51,8 +51,8 @@ ...@@ -51,8 +51,8 @@
#include <cstring> #include <cstring>
#include "base/kaldi-types.h" #include "base/kaldi-types.h"
#include "matrix/kaldi-vector.h"
#include "matrix/kaldi-matrix.h" #include "matrix/kaldi-matrix.h"
#include "matrix/kaldi-vector.h"
namespace kaldi { namespace kaldi {
...@@ -63,90 +63,90 @@ const BaseFloat kWaveSampleMax = 32768.0; ...@@ -63,90 +63,90 @@ const BaseFloat kWaveSampleMax = 32768.0;
/// This class reads and hold wave file header information. /// This class reads and hold wave file header information.
class WaveInfo { class WaveInfo {
public: public:
WaveInfo() : samp_freq_(0), samp_count_(0), WaveInfo()
num_channels_(0), reverse_bytes_(0) {} : samp_freq_(0), samp_count_(0), num_channels_(0), reverse_bytes_(0) {}
/// Is stream size unknown? Duration and SampleCount not valid if true. /// Is stream size unknown? Duration and SampleCount not valid if true.
bool IsStreamed() const { return samp_count_ < 0; } bool IsStreamed() const { return samp_count_ < 0; }
/// Sample frequency, Hz. /// Sample frequency, Hz.
BaseFloat SampFreq() const { return samp_freq_; } BaseFloat SampFreq() const { return samp_freq_; }
/// Number of samples in stream. Invalid if IsStreamed() is true. /// Number of samples in stream. Invalid if IsStreamed() is true.
uint32 SampleCount() const { return samp_count_; } uint32 SampleCount() const { return samp_count_; }
/// Approximate duration, seconds. Invalid if IsStreamed() is true. /// Approximate duration, seconds. Invalid if IsStreamed() is true.
BaseFloat Duration() const { return samp_count_ / samp_freq_; } BaseFloat Duration() const { return samp_count_ / samp_freq_; }
/// Number of channels, 1 to 16. /// Number of channels, 1 to 16.
int32 NumChannels() const { return num_channels_; } int32 NumChannels() const { return num_channels_; }
/// Bytes per sample. /// Bytes per sample.
size_t BlockAlign() const { return 2 * num_channels_; } size_t BlockAlign() const { return 2 * num_channels_; }
/// Wave data bytes. Invalid if IsStreamed() is true. /// Wave data bytes. Invalid if IsStreamed() is true.
size_t DataBytes() const { return samp_count_ * BlockAlign(); } size_t DataBytes() const { return samp_count_ * BlockAlign(); }
/// Is data file byte order different from machine byte order? /// Is data file byte order different from machine byte order?
bool ReverseBytes() const { return reverse_bytes_; } bool ReverseBytes() const { return reverse_bytes_; }
/// 'is' should be opened in binary mode. Read() will throw on error. /// 'is' should be opened in binary mode. Read() will throw on error.
/// On success 'is' will be positioned at the beginning of wave data. /// On success 'is' will be positioned at the beginning of wave data.
void Read(std::istream &is); void Read(std::istream &is);
private: private:
BaseFloat samp_freq_; BaseFloat samp_freq_;
int32 samp_count_; // 0 if empty, -1 if undefined length. int32 samp_count_; // 0 if empty, -1 if undefined length.
uint8 num_channels_; uint8 num_channels_;
bool reverse_bytes_; // File endianness differs from host. bool reverse_bytes_; // File endianness differs from host.
}; };
/// This class's purpose is to read in Wave files. /// This class's purpose is to read in Wave files.
class WaveData { class WaveData {
public: public:
WaveData(BaseFloat samp_freq, const MatrixBase<BaseFloat> &data) WaveData(BaseFloat samp_freq, const MatrixBase<BaseFloat> &data)
: data_(data), samp_freq_(samp_freq) {} : data_(data), samp_freq_(samp_freq) {}
WaveData() : samp_freq_(0.0) {} WaveData() : samp_freq_(0.0) {}
/// Read() will throw on error. It's valid to call Read() more than once-- /// Read() will throw on error. It's valid to call Read() more than once--
/// in this case it will destroy what was there before. /// in this case it will destroy what was there before.
/// "is" should be opened in binary mode. /// "is" should be opened in binary mode.
void Read(std::istream &is); void Read(std::istream &is);
/// Write() will throw on error. os should be opened in binary mode. /// Write() will throw on error. os should be opened in binary mode.
void Write(std::ostream &os) const; void Write(std::ostream &os) const;
// This function returns the wave data-- it's in a matrix // This function returns the wave data-- it's in a matrix
// because there may be multiple channels. In the normal case // because there may be multiple channels. In the normal case
// there's just one channel so Data() will have one row. // there's just one channel so Data() will have one row.
const Matrix<BaseFloat> &Data() const { return data_; } const Matrix<BaseFloat> &Data() const { return data_; }
BaseFloat SampFreq() const { return samp_freq_; } BaseFloat SampFreq() const { return samp_freq_; }
// Returns the duration in seconds // Returns the duration in seconds
BaseFloat Duration() const { return data_.NumCols() / samp_freq_; } BaseFloat Duration() const { return data_.NumCols() / samp_freq_; }
void CopyFrom(const WaveData &other) { void CopyFrom(const WaveData &other) {
samp_freq_ = other.samp_freq_; samp_freq_ = other.samp_freq_;
data_.CopyFromMat(other.data_); data_.CopyFromMat(other.data_);
} }
void Clear() { void Clear() {
data_.Resize(0, 0); data_.Resize(0, 0);
samp_freq_ = 0.0; samp_freq_ = 0.0;
} }
void Swap(WaveData *other) { void Swap(WaveData *other) {
data_.Swap(&(other->data_)); data_.Swap(&(other->data_));
std::swap(samp_freq_, other->samp_freq_); std::swap(samp_freq_, other->samp_freq_);
} }
private: private:
static const uint32 kBlockSize = 1024 * 1024; // Use 1M bytes. static const uint32 kBlockSize = 1024 * 1024; // Use 1M bytes.
Matrix<BaseFloat> data_; Matrix<BaseFloat> data_;
BaseFloat samp_freq_; BaseFloat samp_freq_;
}; };
...@@ -156,90 +156,90 @@ class WaveData { ...@@ -156,90 +156,90 @@ class WaveData {
// it by pretending to read in the wave data in text mode after failing to find // it by pretending to read in the wave data in text mode after failing to find
// the \0B header, but that would have been a little ugly. // the \0B header, but that would have been a little ugly.
class WaveHolder { class WaveHolder {
public: public:
typedef WaveData T; typedef WaveData T;
static bool Write(std::ostream &os, bool binary, const T &t) { static bool Write(std::ostream &os, bool binary, const T &t) {
// We don't write the binary-mode header here [always binary]. // We don't write the binary-mode header here [always binary].
if (!binary) if (!binary)
KALDI_ERR << "Wave data can only be written in binary mode."; KALDI_ERR << "Wave data can only be written in binary mode.";
try { try {
t.Write(os); // throws exception on failure. t.Write(os); // throws exception on failure.
return true; return true;
} catch (const std::exception &e) { } catch (const std::exception &e) {
KALDI_WARN << "Exception caught in WaveHolder object (writing). " KALDI_WARN << "Exception caught in WaveHolder object (writing). "
<< e.what(); << e.what();
return false; // write failure. return false; // write failure.
}
} }
} void Copy(const T &t) { t_.CopyFrom(t); }
void Copy(const T &t) { t_.CopyFrom(t); }
static bool IsReadInBinary() { return true; } static bool IsReadInBinary() { return true; }
void Clear() { t_.Clear(); } void Clear() { t_.Clear(); }
T &Value() { return t_; } T &Value() { return t_; }
WaveHolder &operator = (const WaveHolder &other) { WaveHolder &operator=(const WaveHolder &other) {
t_.CopyFrom(other.t_); t_.CopyFrom(other.t_);
return *this; return *this;
} }
WaveHolder(const WaveHolder &other): t_(other.t_) {} WaveHolder(const WaveHolder &other) : t_(other.t_) {}
WaveHolder() {} WaveHolder() {}
bool Read(std::istream &is) { bool Read(std::istream &is) {
// We don't look for the binary-mode header here [always binary] // We don't look for the binary-mode header here [always binary]
try { try {
t_.Read(is); // Throws exception on failure. t_.Read(is); // Throws exception on failure.
return true; return true;
} catch (const std::exception &e) { } catch (const std::exception &e) {
KALDI_WARN << "Exception caught in WaveHolder::Read(). " << e.what(); KALDI_WARN << "Exception caught in WaveHolder::Read(). "
return false; << e.what();
return false;
}
} }
}
void Swap(WaveHolder *other) { void Swap(WaveHolder *other) { t_.Swap(&(other->t_)); }
t_.Swap(&(other->t_));
}
bool ExtractRange(const WaveHolder &other, const std::string &range) { bool ExtractRange(const WaveHolder &other, const std::string &range) {
KALDI_ERR << "ExtractRange is not defined for this type of holder."; KALDI_ERR << "ExtractRange is not defined for this type of holder.";
return false; return false;
} }
private: private:
T t_; T t_;
}; };
// This is like WaveHolder but when you just want the metadata- // This is like WaveHolder but when you just want the metadata-
// it leaves the actual data undefined, it doesn't read it. // it leaves the actual data undefined, it doesn't read it.
class WaveInfoHolder { class WaveInfoHolder {
public: public:
typedef WaveInfo T; typedef WaveInfo T;
void Clear() { info_ = WaveInfo(); } void Clear() { info_ = WaveInfo(); }
void Swap(WaveInfoHolder *other) { std::swap(info_, other->info_); } void Swap(WaveInfoHolder *other) { std::swap(info_, other->info_); }
T &Value() { return info_; } T &Value() { return info_; }
static bool IsReadInBinary() { return true; } static bool IsReadInBinary() { return true; }
bool Read(std::istream &is) { bool Read(std::istream &is) {
try { try {
info_.Read(is); // Throws exception on failure. info_.Read(is); // Throws exception on failure.
return true; return true;
} catch (const std::exception &e) { } catch (const std::exception &e) {
KALDI_WARN << "Exception caught in WaveInfoHolder::Read(). " << e.what(); KALDI_WARN << "Exception caught in WaveInfoHolder::Read(). "
return false; << e.what();
return false;
}
} }
}
bool ExtractRange(const WaveInfoHolder &other, const std::string &range) { bool ExtractRange(const WaveInfoHolder &other, const std::string &range) {
KALDI_ERR << "ExtractRange is not defined for this type of holder."; KALDI_ERR << "ExtractRange is not defined for this type of holder.";
return false; return false;
} }
private: private:
WaveInfo info_; WaveInfo info_;
}; };
......
...@@ -20,8 +20,8 @@ ...@@ -20,8 +20,8 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <queue> #include <queue>
#include <utility>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
......
/*
* Copyright 2009-2010 Cybozu Labs, Inc.
* Copyright 2011-2014 Kazuho Oku
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef picojson_h
#define picojson_h
#include <algorithm>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#define PICOJSON_USE_INT64 1
// for isnan/isinf
#if __cplusplus >= 201103L
#include <cmath>
#else
extern "C" {
#ifdef _MSC_VER
#include <float.h>
#elif defined(__INTEL_COMPILER)
#include <mathimf.h>
#else
#include <math.h>
#endif
}
#endif
#ifndef PICOJSON_USE_RVALUE_REFERENCE
#if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || \
(defined(_MSC_VER) && _MSC_VER >= 1600)
#define PICOJSON_USE_RVALUE_REFERENCE 1
#else
#define PICOJSON_USE_RVALUE_REFERENCE 0
#endif
#endif // PICOJSON_USE_RVALUE_REFERENCE
#ifndef PICOJSON_NOEXCEPT
#if PICOJSON_USE_RVALUE_REFERENCE
#define PICOJSON_NOEXCEPT noexcept
#else
#define PICOJSON_NOEXCEPT throw()
#endif
#endif
// experimental support for int64_t (see README.mkdn for detail)
#ifdef PICOJSON_USE_INT64
#define __STDC_FORMAT_MACROS
#include <cerrno>
#if __cplusplus >= 201103L
#include <cinttypes>
#else
extern "C" {
#include <inttypes.h>
}
#endif
#endif
// to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0
#ifndef PICOJSON_USE_LOCALE
#define PICOJSON_USE_LOCALE 1
#endif
#if PICOJSON_USE_LOCALE
extern "C" {
#include <locale.h>
}
#endif
#ifndef PICOJSON_ASSERT
#define PICOJSON_ASSERT(e) \
do { \
if (!(e)) throw std::runtime_error(#e); \
} while (0)
#endif
#ifdef _MSC_VER
#define SNPRINTF _snprintf_s
#pragma warning(push)
#pragma warning(disable : 4244) // conversion from int to char
#pragma warning(disable : 4127) // conditional expression is constant
#pragma warning(disable : 4702) // unreachable code
#pragma warning(disable : 4706) // assignment within conditional expression
#else
#define SNPRINTF snprintf
#endif
namespace picojson {
enum {
null_type,
boolean_type,
number_type,
string_type,
array_type,
object_type
#ifdef PICOJSON_USE_INT64
,
int64_type
#endif
};
enum { INDENT_WIDTH = 2, DEFAULT_MAX_DEPTHS = 100 };
struct null {};
class value {
public:
typedef std::vector<value> array;
typedef std::map<std::string, value> object;
union _storage {
bool boolean_;
double number_;
#ifdef PICOJSON_USE_INT64
int64_t int64_;
#endif
std::string *string_;
array *array_;
object *object_;
};
protected:
int type_;
_storage u_;
public:
value();
value(int type, bool);
explicit value(bool b);
#ifdef PICOJSON_USE_INT64
explicit value(int64_t i);
#endif
explicit value(double n);
explicit value(const std::string &s);
explicit value(const array &a);
explicit value(const object &o);
#if PICOJSON_USE_RVALUE_REFERENCE
explicit value(std::string &&s);
explicit value(array &&a);
explicit value(object &&o);
#endif
explicit value(const char *s);
value(const char *s, size_t len);
~value();
value(const value &x);
value &operator=(const value &x);
#if PICOJSON_USE_RVALUE_REFERENCE
value(value &&x) PICOJSON_NOEXCEPT;
value &operator=(value &&x) PICOJSON_NOEXCEPT;
#endif
void swap(value &x) PICOJSON_NOEXCEPT;
template <typename T>
bool is() const;
template <typename T>
const T &get() const;
template <typename T>
T &get();
template <typename T>
void set(const T &);
#if PICOJSON_USE_RVALUE_REFERENCE
template <typename T>
void set(T &&);
#endif
bool evaluate_as_boolean() const;
const value &get(const size_t idx) const;
const value &get(const std::string &key) const;
value &get(const size_t idx);
value &get(const std::string &key);
bool contains(const size_t idx) const;
bool contains(const std::string &key) const;
std::string to_str() const;
template <typename Iter>
void serialize(Iter os, bool prettify = false) const;
std::string serialize(bool prettify = false) const;
private:
template <typename T>
value(const T *); // intentionally defined to block implicit conversion of
// pointer to bool
template <typename Iter>
static void _indent(Iter os, int indent);
template <typename Iter>
void _serialize(Iter os, int indent) const;
std::string _serialize(int indent) const;
void clear();
};
typedef value::array array;
typedef value::object object;
inline value::value() : type_(null_type), u_() {}
inline value::value(int type, bool) : type_(type), u_() {
switch (type) {
#define INIT(p, v) \
case p##type: \
u_.p = v; \
break
INIT(boolean_, false);
INIT(number_, 0.0);
#ifdef PICOJSON_USE_INT64
INIT(int64_, 0);
#endif
INIT(string_, new std::string());
INIT(array_, new array());
INIT(object_, new object());
#undef INIT
default:
break;
}
}
inline value::value(bool b) : type_(boolean_type), u_() { u_.boolean_ = b; }
#ifdef PICOJSON_USE_INT64
inline value::value(int64_t i) : type_(int64_type), u_() { u_.int64_ = i; }
#endif
inline value::value(double n) : type_(number_type), u_() {
if (
#ifdef _MSC_VER
!_finite(n)
#elif __cplusplus >= 201103L
std::isnan(n) || std::isinf(n)
#else
isnan(n) || isinf(n)
#endif
) {
throw std::overflow_error("");
}
u_.number_ = n;
}
inline value::value(const std::string &s) : type_(string_type), u_() {
u_.string_ = new std::string(s);
}
inline value::value(const array &a) : type_(array_type), u_() {
u_.array_ = new array(a);
}
inline value::value(const object &o) : type_(object_type), u_() {
u_.object_ = new object(o);
}
#if PICOJSON_USE_RVALUE_REFERENCE
inline value::value(std::string &&s) : type_(string_type), u_() {
u_.string_ = new std::string(std::move(s));
}
inline value::value(array &&a) : type_(array_type), u_() {
u_.array_ = new array(std::move(a));
}
inline value::value(object &&o) : type_(object_type), u_() {
u_.object_ = new object(std::move(o));
}
#endif
inline value::value(const char *s) : type_(string_type), u_() {
u_.string_ = new std::string(s);
}
inline value::value(const char *s, size_t len) : type_(string_type), u_() {
u_.string_ = new std::string(s, len);
}
inline void value::clear() {
switch (type_) {
#define DEINIT(p) \
case p##type: \
delete u_.p; \
break
DEINIT(string_);
DEINIT(array_);
DEINIT(object_);
#undef DEINIT
default:
break;
}
}
inline value::~value() { clear(); }
inline value::value(const value &x) : type_(x.type_), u_() {
switch (type_) {
#define INIT(p, v) \
case p##type: \
u_.p = v; \
break
INIT(string_, new std::string(*x.u_.string_));
INIT(array_, new array(*x.u_.array_));
INIT(object_, new object(*x.u_.object_));
#undef INIT
default:
u_ = x.u_;
break;
}
}
inline value &value::operator=(const value &x) {
if (this != &x) {
value t(x);
swap(t);
}
return *this;
}
#if PICOJSON_USE_RVALUE_REFERENCE
inline value::value(value &&x) PICOJSON_NOEXCEPT : type_(null_type), u_() {
swap(x);
}
inline value &value::operator=(value &&x) PICOJSON_NOEXCEPT {
swap(x);
return *this;
}
#endif
inline void value::swap(value &x) PICOJSON_NOEXCEPT {
std::swap(type_, x.type_);
std::swap(u_, x.u_);
}
#define IS(ctype, jtype) \
template <> \
inline bool value::is<ctype>() const { \
return type_ == jtype##_type; \
}
IS(null, null)
IS(bool, boolean)
#ifdef PICOJSON_USE_INT64
IS(int64_t, int64)
#endif
IS(std::string, string)
IS(array, array)
IS(object, object)
#undef IS
template <>
inline bool value::is<double>() const {
return type_ == number_type
#ifdef PICOJSON_USE_INT64
|| type_ == int64_type
#endif
;
}
#define GET(ctype, var) \
template <> \
inline const ctype &value::get<ctype>() const { \
PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" && \
is<ctype>()); \
return var; \
} \
template <> \
inline ctype &value::get<ctype>() { \
PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" && \
is<ctype>()); \
return var; \
}
GET(bool, u_.boolean_)
GET(std::string, *u_.string_)
GET(array, *u_.array_)
GET(object, *u_.object_)
#ifdef PICOJSON_USE_INT64
GET(double,
(type_ == int64_type &&
(const_cast<value *>(this)->type_ = number_type,
(const_cast<value *>(this)->u_.number_ = u_.int64_)),
u_.number_))
GET(int64_t, u_.int64_)
#else
GET(double, u_.number_)
#endif
#undef GET
#define SET(ctype, jtype, setter) \
template <> \
inline void value::set<ctype>(const ctype &_val) { \
clear(); \
type_ = jtype##_type; \
setter \
}
SET(bool, boolean, u_.boolean_ = _val;)
SET(std::string, string, u_.string_ = new std::string(_val);)
SET(array, array, u_.array_ = new array(_val);)
SET(object, object, u_.object_ = new object(_val);)
SET(double, number, u_.number_ = _val;)
#ifdef PICOJSON_USE_INT64
SET(int64_t, int64, u_.int64_ = _val;)
#endif
#undef SET
#if PICOJSON_USE_RVALUE_REFERENCE
#define MOVESET(ctype, jtype, setter) \
template <> \
inline void value::set<ctype>(ctype && _val) { \
clear(); \
type_ = jtype##_type; \
setter \
}
MOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));)
MOVESET(array, array, u_.array_ = new array(std::move(_val));)
MOVESET(object, object, u_.object_ = new object(std::move(_val));)
#undef MOVESET
#endif
inline bool value::evaluate_as_boolean() const {
switch (type_) {
case null_type:
return false;
case boolean_type:
return u_.boolean_;
case number_type:
return u_.number_ != 0;
#ifdef PICOJSON_USE_INT64
case int64_type:
return u_.int64_ != 0;
#endif
case string_type:
return !u_.string_->empty();
default:
return true;
}
}
inline const value &value::get(const size_t idx) const {
static value s_null;
PICOJSON_ASSERT(is<array>());
return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
}
inline value &value::get(const size_t idx) {
static value s_null;
PICOJSON_ASSERT(is<array>());
return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
}
inline const value &value::get(const std::string &key) const {
static value s_null;
PICOJSON_ASSERT(is<object>());
object::const_iterator i = u_.object_->find(key);
return i != u_.object_->end() ? i->second : s_null;
}
inline value &value::get(const std::string &key) {
static value s_null;
PICOJSON_ASSERT(is<object>());
object::iterator i = u_.object_->find(key);
return i != u_.object_->end() ? i->second : s_null;
}
inline bool value::contains(const size_t idx) const {
PICOJSON_ASSERT(is<array>());
return idx < u_.array_->size();
}
inline bool value::contains(const std::string &key) const {
PICOJSON_ASSERT(is<object>());
object::const_iterator i = u_.object_->find(key);
return i != u_.object_->end();
}
inline std::string value::to_str() const {
switch (type_) {
case null_type:
return "null";
case boolean_type:
return u_.boolean_ ? "true" : "false";
#ifdef PICOJSON_USE_INT64
case int64_type: {
char buf[sizeof("-9223372036854775808")];
SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_);
return buf;
}
#endif
case number_type: {
char buf[256];
double tmp;
SNPRINTF(
buf,
sizeof(buf),
fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0
? "%.f"
: "%.17g",
u_.number_);
#if PICOJSON_USE_LOCALE
char *decimal_point = localeconv()->decimal_point;
if (strcmp(decimal_point, ".") != 0) {
size_t decimal_point_len = strlen(decimal_point);
for (char *p = buf; *p != '\0'; ++p) {
if (strncmp(p, decimal_point, decimal_point_len) == 0) {
return std::string(buf, p) + "." +
(p + decimal_point_len);
}
}
}
#endif
return buf;
}
case string_type:
return *u_.string_;
case array_type:
return "array";
case object_type:
return "object";
default:
PICOJSON_ASSERT(0);
#ifdef _MSC_VER
__assume(0);
#endif
}
return std::string();
}
template <typename Iter>
void copy(const std::string &s, Iter oi) {
std::copy(s.begin(), s.end(), oi);
}
template <typename Iter>
struct serialize_str_char {
Iter oi;
void operator()(char c) {
switch (c) {
#define MAP(val, sym) \
case val: \
copy(sym, oi); \
break
MAP('"', "\\\"");
MAP('\\', "\\\\");
MAP('/', "\\/");
MAP('\b', "\\b");
MAP('\f', "\\f");
MAP('\n', "\\n");
MAP('\r', "\\r");
MAP('\t', "\\t");
#undef MAP
default:
if (static_cast<unsigned char>(c) < 0x20 || c == 0x7f) {
char buf[7];
SNPRINTF(buf, sizeof(buf), "\\u%04x", c & 0xff);
copy(buf, buf + 6, oi);
} else {
*oi++ = c;
}
break;
}
}
};
template <typename Iter>
void serialize_str(const std::string &s, Iter oi) {
*oi++ = '"';
serialize_str_char<Iter> process_char = {oi};
std::for_each(s.begin(), s.end(), process_char);
*oi++ = '"';
}
template <typename Iter>
void value::serialize(Iter oi, bool prettify) const {
return _serialize(oi, prettify ? 0 : -1);
}
inline std::string value::serialize(bool prettify) const {
return _serialize(prettify ? 0 : -1);
}
template <typename Iter>
void value::_indent(Iter oi, int indent) {
*oi++ = '\n';
for (int i = 0; i < indent * INDENT_WIDTH; ++i) {
*oi++ = ' ';
}
}
template <typename Iter>
void value::_serialize(Iter oi, int indent) const {
switch (type_) {
case string_type:
serialize_str(*u_.string_, oi);
break;
case array_type: {
*oi++ = '[';
if (indent != -1) {
++indent;
}
for (array::const_iterator i = u_.array_->begin();
i != u_.array_->end();
++i) {
if (i != u_.array_->begin()) {
*oi++ = ',';
}
if (indent != -1) {
_indent(oi, indent);
}
i->_serialize(oi, indent);
}
if (indent != -1) {
--indent;
if (!u_.array_->empty()) {
_indent(oi, indent);
}
}
*oi++ = ']';
break;
}
case object_type: {
*oi++ = '{';
if (indent != -1) {
++indent;
}
for (object::const_iterator i = u_.object_->begin();
i != u_.object_->end();
++i) {
if (i != u_.object_->begin()) {
*oi++ = ',';
}
if (indent != -1) {
_indent(oi, indent);
}
serialize_str(i->first, oi);
*oi++ = ':';
if (indent != -1) {
*oi++ = ' ';
}
i->second._serialize(oi, indent);
}
if (indent != -1) {
--indent;
if (!u_.object_->empty()) {
_indent(oi, indent);
}
}
*oi++ = '}';
break;
}
default:
copy(to_str(), oi);
break;
}
if (indent == 0) {
*oi++ = '\n';
}
}
inline std::string value::_serialize(int indent) const {
std::string s;
_serialize(std::back_inserter(s), indent);
return s;
}
template <typename Iter>
class input {
protected:
Iter cur_, end_;
bool consumed_;
int line_;
public:
input(const Iter &first, const Iter &last)
: cur_(first), end_(last), consumed_(false), line_(1) {}
int getc() {
if (consumed_) {
if (*cur_ == '\n') {
++line_;
}
++cur_;
}
if (cur_ == end_) {
consumed_ = false;
return -1;
}
consumed_ = true;
return *cur_ & 0xff;
}
void ungetc() { consumed_ = false; }
Iter cur() const {
if (consumed_) {
input<Iter> *self = const_cast<input<Iter> *>(this);
self->consumed_ = false;
++self->cur_;
}
return cur_;
}
int line() const { return line_; }
void skip_ws() {
while (1) {
int ch = getc();
if (!(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) {
ungetc();
break;
}
}
}
bool expect(const int expected) {
skip_ws();
if (getc() != expected) {
ungetc();
return false;
}
return true;
}
bool match(const std::string &pattern) {
for (std::string::const_iterator pi(pattern.begin());
pi != pattern.end();
++pi) {
if (getc() != *pi) {
ungetc();
return false;
}
}
return true;
}
};
template <typename Iter>
inline int _parse_quadhex(input<Iter> &in) {
int uni_ch = 0, hex;
for (int i = 0; i < 4; i++) {
if ((hex = in.getc()) == -1) {
return -1;
}
if ('0' <= hex && hex <= '9') {
hex -= '0';
} else if ('A' <= hex && hex <= 'F') {
hex -= 'A' - 0xa;
} else if ('a' <= hex && hex <= 'f') {
hex -= 'a' - 0xa;
} else {
in.ungetc();
return -1;
}
uni_ch = uni_ch * 16 + hex;
}
return uni_ch;
}
template <typename String, typename Iter>
inline bool _parse_codepoint(String &out, input<Iter> &in) {
int uni_ch;
if ((uni_ch = _parse_quadhex(in)) == -1) {
return false;
}
if (0xd800 <= uni_ch && uni_ch <= 0xdfff) {
if (0xdc00 <= uni_ch) {
// a second 16-bit of a surrogate pair appeared
return false;
}
// first 16-bit of surrogate pair, get the next one
if (in.getc() != '\\' || in.getc() != 'u') {
in.ungetc();
return false;
}
int second = _parse_quadhex(in);
if (!(0xdc00 <= second && second <= 0xdfff)) {
return false;
}
uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff);
uni_ch += 0x10000;
}
if (uni_ch < 0x80) {
out.push_back(static_cast<char>(uni_ch));
} else {
if (uni_ch < 0x800) {
out.push_back(static_cast<char>(0xc0 | (uni_ch >> 6)));
} else {
if (uni_ch < 0x10000) {
out.push_back(static_cast<char>(0xe0 | (uni_ch >> 12)));
} else {
out.push_back(static_cast<char>(0xf0 | (uni_ch >> 18)));
out.push_back(
static_cast<char>(0x80 | ((uni_ch >> 12) & 0x3f)));
}
out.push_back(static_cast<char>(0x80 | ((uni_ch >> 6) & 0x3f)));
}
out.push_back(static_cast<char>(0x80 | (uni_ch & 0x3f)));
}
return true;
}
template <typename String, typename Iter>
inline bool _parse_string(String &out, input<Iter> &in) {
while (1) {
int ch = in.getc();
if (ch < ' ') {
in.ungetc();
return false;
} else if (ch == '"') {
return true;
} else if (ch == '\\') {
if ((ch = in.getc()) == -1) {
return false;
}
switch (ch) {
#define MAP(sym, val) \
case sym: \
out.push_back(val); \
break
MAP('"', '\"');
MAP('\\', '\\');
MAP('/', '/');
MAP('b', '\b');
MAP('f', '\f');
MAP('n', '\n');
MAP('r', '\r');
MAP('t', '\t');
#undef MAP
case 'u':
if (!_parse_codepoint(out, in)) {
return false;
}
break;
default:
return false;
}
} else {
out.push_back(static_cast<char>(ch));
}
}
return false;
}
template <typename Context, typename Iter>
inline bool _parse_array(Context &ctx, input<Iter> &in) {
if (!ctx.parse_array_start()) {
return false;
}
size_t idx = 0;
if (in.expect(']')) {
return ctx.parse_array_stop(idx);
}
do {
if (!ctx.parse_array_item(in, idx)) {
return false;
}
idx++;
} while (in.expect(','));
return in.expect(']') && ctx.parse_array_stop(idx);
}
template <typename Context, typename Iter>
inline bool _parse_object(Context &ctx, input<Iter> &in) {
if (!ctx.parse_object_start()) {
return false;
}
if (in.expect('}')) {
return ctx.parse_object_stop();
}
do {
std::string key;
if (!in.expect('"') || !_parse_string(key, in) || !in.expect(':')) {
return false;
}
if (!ctx.parse_object_item(in, key)) {
return false;
}
} while (in.expect(','));
return in.expect('}') && ctx.parse_object_stop();
}
template <typename Iter>
inline std::string _parse_number(input<Iter> &in) {
std::string num_str;
while (1) {
int ch = in.getc();
if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' ||
ch == 'E') {
num_str.push_back(static_cast<char>(ch));
} else if (ch == '.') {
#if PICOJSON_USE_LOCALE
num_str += localeconv()->decimal_point;
#else
num_str.push_back('.');
#endif
} else {
in.ungetc();
break;
}
}
return num_str;
}
template <typename Context, typename Iter>
inline bool _parse(Context &ctx, input<Iter> &in) {
in.skip_ws();
int ch = in.getc();
switch (ch) {
#define IS(ch, text, op) \
case ch: \
if (in.match(text) && op) { \
return true; \
} else { \
return false; \
}
IS('n', "ull", ctx.set_null());
IS('f', "alse", ctx.set_bool(false));
IS('t', "rue", ctx.set_bool(true));
#undef IS
case '"':
return ctx.parse_string(in);
case '[':
return _parse_array(ctx, in);
case '{':
return _parse_object(ctx, in);
default:
if (('0' <= ch && ch <= '9') || ch == '-') {
double f;
char *endp;
in.ungetc();
std::string num_str(_parse_number(in));
if (num_str.empty()) {
return false;
}
#ifdef PICOJSON_USE_INT64
{
errno = 0;
intmax_t ival = strtoimax(num_str.c_str(), &endp, 10);
if (errno == 0 &&
std::numeric_limits<int64_t>::min() <= ival &&
ival <= std::numeric_limits<int64_t>::max() &&
endp == num_str.c_str() + num_str.size()) {
ctx.set_int64(ival);
return true;
}
}
#endif
f = strtod(num_str.c_str(), &endp);
if (endp == num_str.c_str() + num_str.size()) {
ctx.set_number(f);
return true;
}
return false;
}
break;
}
in.ungetc();
return false;
}
class deny_parse_context {
public:
bool set_null() { return false; }
bool set_bool(bool) { return false; }
#ifdef PICOJSON_USE_INT64
bool set_int64(int64_t) { return false; }
#endif
bool set_number(double) { return false; }
template <typename Iter>
bool parse_string(input<Iter> &) {
return false;
}
bool parse_array_start() { return false; }
template <typename Iter>
bool parse_array_item(input<Iter> &, size_t) {
return false;
}
bool parse_array_stop(size_t) { return false; }
bool parse_object_start() { return false; }
template <typename Iter>
bool parse_object_item(input<Iter> &, const std::string &) {
return false;
}
};
class default_parse_context {
protected:
value *out_;
size_t depths_;
public:
default_parse_context(value *out, size_t depths = DEFAULT_MAX_DEPTHS)
: out_(out), depths_(depths) {}
bool set_null() {
*out_ = value();
return true;
}
bool set_bool(bool b) {
*out_ = value(b);
return true;
}
#ifdef PICOJSON_USE_INT64
bool set_int64(int64_t i) {
*out_ = value(i);
return true;
}
#endif
bool set_number(double f) {
*out_ = value(f);
return true;
}
template <typename Iter>
bool parse_string(input<Iter> &in) {
*out_ = value(string_type, false);
return _parse_string(out_->get<std::string>(), in);
}
bool parse_array_start() {
if (depths_ == 0) return false;
--depths_;
*out_ = value(array_type, false);
return true;
}
template <typename Iter>
bool parse_array_item(input<Iter> &in, size_t) {
array &a = out_->get<array>();
a.push_back(value());
default_parse_context ctx(&a.back(), depths_);
return _parse(ctx, in);
}
bool parse_array_stop(size_t) {
++depths_;
return true;
}
bool parse_object_start() {
if (depths_ == 0) return false;
*out_ = value(object_type, false);
return true;
}
template <typename Iter>
bool parse_object_item(input<Iter> &in, const std::string &key) {
object &o = out_->get<object>();
default_parse_context ctx(&o[key], depths_);
return _parse(ctx, in);
}
bool parse_object_stop() {
++depths_;
return true;
}
private:
default_parse_context(const default_parse_context &);
default_parse_context &operator=(const default_parse_context &);
};
class null_parse_context {
protected:
size_t depths_;
public:
struct dummy_str {
void push_back(int) {}
};
public:
null_parse_context(size_t depths = DEFAULT_MAX_DEPTHS) : depths_(depths) {}
bool set_null() { return true; }
bool set_bool(bool) { return true; }
#ifdef PICOJSON_USE_INT64
bool set_int64(int64_t) { return true; }
#endif
bool set_number(double) { return true; }
template <typename Iter>
bool parse_string(input<Iter> &in) {
dummy_str s;
return _parse_string(s, in);
}
bool parse_array_start() {
if (depths_ == 0) return false;
--depths_;
return true;
}
template <typename Iter>
bool parse_array_item(input<Iter> &in, size_t) {
return _parse(*this, in);
}
bool parse_array_stop(size_t) {
++depths_;
return true;
}
bool parse_object_start() {
if (depths_ == 0) return false;
--depths_;
return true;
}
template <typename Iter>
bool parse_object_item(input<Iter> &in, const std::string &) {
++depths_;
return _parse(*this, in);
}
bool parse_object_stop() { return true; }
private:
null_parse_context(const null_parse_context &);
null_parse_context &operator=(const null_parse_context &);
};
// obsolete, use the version below
template <typename Iter>
inline std::string parse(value &out, Iter &pos, const Iter &last) {
std::string err;
pos = parse(out, pos, last, &err);
return err;
}
template <typename Context, typename Iter>
inline Iter _parse(Context &ctx,
const Iter &first,
const Iter &last,
std::string *err) {
input<Iter> in(first, last);
if (!_parse(ctx, in) && err != NULL) {
char buf[64];
SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line());
*err = buf;
while (1) {
int ch = in.getc();
if (ch == -1 || ch == '\n') {
break;
} else if (ch >= ' ') {
err->push_back(static_cast<char>(ch));
}
}
}
return in.cur();
}
template <typename Iter>
inline Iter parse(value &out,
const Iter &first,
const Iter &last,
std::string *err) {
default_parse_context ctx(&out);
return _parse(ctx, first, last, err);
}
inline std::string parse(value &out, const std::string &s) {
std::string err;
parse(out, s.begin(), s.end(), &err);
return err;
}
inline std::string parse(value &out, std::istream &is) {
std::string err;
parse(out,
std::istreambuf_iterator<char>(is.rdbuf()),
std::istreambuf_iterator<char>(),
&err);
return err;
}
template <typename T>
struct last_error_t {
static std::string s;
};
template <typename T>
std::string last_error_t<T>::s;
inline void set_last_error(const std::string &s) { last_error_t<bool>::s = s; }
inline const std::string &get_last_error() { return last_error_t<bool>::s; }
inline bool operator==(const value &x, const value &y) {
if (x.is<null>()) return y.is<null>();
#define PICOJSON_CMP(type) \
if (x.is<type>()) return y.is<type>() && x.get<type>() == y.get<type>()
PICOJSON_CMP(bool);
PICOJSON_CMP(double);
PICOJSON_CMP(std::string);
PICOJSON_CMP(array);
PICOJSON_CMP(object);
#undef PICOJSON_CMP
PICOJSON_ASSERT(0);
#ifdef _MSC_VER
__assume(0);
#endif
return false;
}
inline bool operator!=(const value &x, const value &y) { return !(x == y); }
}
#if !PICOJSON_USE_RVALUE_REFERENCE
namespace std {
template <>
inline void swap(picojson::value &x, picojson::value &y) {
x.swap(y);
}
}
#endif
inline std::istream &operator>>(std::istream &is, picojson::value &x) {
picojson::set_last_error(std::string());
const std::string err(picojson::parse(x, is));
if (!err.empty()) {
picojson::set_last_error(err);
is.setstate(std::ios::failbit);
}
return is;
}
inline std::ostream &operator<<(std::ostream &os, const picojson::value &x) {
x.serialize(std::ostream_iterator<char>(os));
return os;
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif
\ No newline at end of file
...@@ -18,15 +18,17 @@ ...@@ -18,15 +18,17 @@
namespace ppspeech { namespace ppspeech {
std::vector<std::string> StrSplit(const std::string& str, const char *delim, bool omit_empty_string){ std::vector<std::string> StrSplit(const std::string& str,
const char* delim,
bool omit_empty_string) {
std::vector<std::string> outs; std::vector<std::string> outs;
int start = 0; int start = 0;
int end = str.size(); int end = str.size();
int found = 0; int found = 0;
while(found != std::string::npos){ while (found != std::string::npos) {
found = str.find_first_of(delim, start); found = str.find_first_of(delim, start);
// start != end condition is for when the delimiter is at the end // start != end condition is for when the delimiter is at the end
if (!omit_empty_string || (found != start && start != end)){ if (!omit_empty_string || (found != start && start != end)) {
outs.push_back(str.substr(start, found - start)); outs.push_back(str.substr(start, found - start));
} }
start = found + 1; start = found + 1;
...@@ -38,13 +40,13 @@ std::vector<std::string> StrSplit(const std::string& str, const char *delim, boo ...@@ -38,13 +40,13 @@ std::vector<std::string> StrSplit(const std::string& str, const char *delim, boo
std::string StrJoin(const std::vector<std::string>& strs, const char* delim) { std::string StrJoin(const std::vector<std::string>& strs, const char* delim) {
std::stringstream ss; std::stringstream ss;
for (ssize_t i = 0; i < strs.size(); ++i){ for (ssize_t i = 0; i < strs.size(); ++i) {
ss << strs[i]; ss << strs[i];
if ( i < strs.size() -1){ if (i < strs.size() - 1) {
ss << std::string(delim); ss << std::string(delim);
} }
} }
return ss.str(); return ss.str();
} }
} // namespace ppspeech } // namespace ppspeech
\ No newline at end of file \ No newline at end of file
...@@ -14,13 +14,15 @@ ...@@ -14,13 +14,15 @@
#pragma once #pragma once
#include <vector>
#include <string> #include <string>
#include <vector>
namespace ppspeech { namespace ppspeech {
std::vector<std::string> StrSplit(const std::string& str, const char *delim, bool omit_empty_string=true); std::vector<std::string> StrSplit(const std::string& str,
const char* delim,
bool omit_empty_string = true);
std::string StrJoin(const std::vector<std::string>& strs, const char* delim); std::string StrJoin(const std::vector<std::string>& strs, const char* delim);
} // namespace ppspeech } // namespace ppspeech
\ No newline at end of file \ No newline at end of file
...@@ -15,16 +15,16 @@ ...@@ -15,16 +15,16 @@
#include "utils/strings.h" #include "utils/strings.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h>
TEST(StringTest, StrSplitTest) { TEST(StringTest, StrSplitTest) {
using ::testing::ElementsAre; using ::testing::ElementsAre;
std::string test_str = "hello world"; std::string test_str = "hello world";
std::vector<std::string> outs = ppspeech::StrSplit(test_str, " \t"); std::vector<std::string> outs = ppspeech::StrSplit(test_str, " \t");
EXPECT_THAT(outs, ElementsAre("hello", "world")); EXPECT_THAT(outs, ElementsAre("hello", "world"));
} }
......
# This contains the locations of binarys build required for running the examples. # This contains the locations of binarys build required for running the examples.
MAIN_ROOT=`realpath $PWD/../../../` MAIN_ROOT=`realpath $PWD/../../../`
SPEECHX_ROOT=`realpath $MAIN_ROOT/speechx` RUNTIME_ROOT=`realpath $MAIN_ROOT/runtime`
SPEECHX_EXAMPLES=$SPEECHX_ROOT/build/examples RUNTIME_EXAMPLES=$RUNTIME_ROOT/build/examples
export LC_AL=C export LC_AL=C
...@@ -12,6 +12,6 @@ export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}:${LIBLBFGS}/lib/.libs ...@@ -12,6 +12,6 @@ export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}:${LIBLBFGS}/lib/.libs
export SRILM=${MAIN_ROOT}/tools/srilm export SRILM=${MAIN_ROOT}/tools/srilm
# kaldi lm # kaldi lm
KALDI_DIR=$SPEECHX_ROOT/build/speechx/kaldi/ KALDI_DIR=$RUNTIME_ROOT/build/engine/kaldi/
OPENFST_DIR=$SPEECHX_ROOT/fc_patch/openfst-build/src OPENFST_DIR=$RUNTIME_ROOT/fc_patch/openfst-build/src
export PATH=${PATH}:${SRILM}/bin:${SRILM}/bin/i686-m64:$KALDI_DIR/lmbin:$KALDI_DIR/fstbin:$OPENFST_DIR/bin:$SPEECHX_EXAMPLES/ds2_ol/decoder export PATH=${PATH}:${SRILM}/bin:${SRILM}/bin/i686-m64:$KALDI_DIR/lmbin:$KALDI_DIR/fstbin:$OPENFST_DIR/bin:$SPEECHX_EXAMPLES/ds2_ol/decoder
MAIN_ROOT=`realpath $PWD/../../../`
ENGINE_ROOT=`realpath $MAIN_ROOT/runtime`
export LC_AL=C
...@@ -50,10 +50,10 @@ This stage using `u2_recognizer_main` to recognize wav file. ...@@ -50,10 +50,10 @@ This stage using `u2_recognizer_main` to recognize wav file.
The input is `scp` file which look like this: The input is `scp` file which look like this:
```text ```text
# head data/split1/1/aishell_test.scp # head data/split1/1/aishell_test.scp
BAC009S0764W0121 /workspace/PaddleSpeech/speechx/examples/u2pp_ol/wenetspeech/data/test/S0764/BAC009S0764W0121.wav BAC009S0764W0121 /workspace/PaddleSpeech/runtime/examples/u2pp_ol/wenetspeech/data/test/S0764/BAC009S0764W0121.wav
BAC009S0764W0122 /workspace/PaddleSpeech/speechx/examples/u2pp_ol/wenetspeech/data/test/S0764/BAC009S0764W0122.wav BAC009S0764W0122 /workspace/PaddleSpeech/runtime/examples/u2pp_ol/wenetspeech/data/test/S0764/BAC009S0764W0122.wav
... ...
BAC009S0764W0125 /workspace/PaddleSpeech/speechx/examples/u2pp_ol/wenetspeech/data/test/S0764/BAC009S0764W0125.wav BAC009S0764W0125 /workspace/PaddleSpeech/runtime/examples/u2pp_ol/wenetspeech/data/test/S0764/BAC009S0764W0125.wav
``` ```
If you want to recognize one wav, you can make `scp` file like this: If you want to recognize one wav, you can make `scp` file like this:
......
#!/bin/bash
set -eo pipefail
#. path.sh
# attention, please replace the vocab is only for this script.
# different acustic model has different vocab
ckpt_dir=data/model/asr1_chunk_conformer_u2pp_wenetspeech_static_1.3.0.model
unit=$ckpt_dir/vocab.txt # vocab file, line: char/spm_pice
model_dir=$ckpt_dir/exp/deepspeech2_online/checkpoints/
stage=2
stop_stage=100
corpus=aishell
lexicon=data/lexicon.txt # line: word ph0 ... phn, aishell/resource_aishell/lexicon.txt
text=data/text # line: utt text, aishell/data_aishell/transcript/aishell_transcript_v0.8.txt
. utils/parse_options.sh
data=$PWD/data
mkdir -p $data
if [ $stage -le -1 ] && [ $stop_stage -ge -1 ]; then
if [ ! -f $data/speech.ngram.zh.tar.gz ];then
# download ngram
pushd $data
wget -c http://paddlespeech.bj.bcebos.com/speechx/examples/ngram/zh/speech.ngram.zh.tar.gz
tar xvzf speech.ngram.zh.tar.gz
popd
fi
fi
if [ ! -f $unit ]; then
echo "$0: No such file $unit"
exit 1;
fi
if ! which ngram-count; then
# need srilm install
pushd $MAIN_ROOT/tools
make srilm.done
popd
fi
echo "done."
mkdir -p data/local/dict
if [ ${stage} -le 0 ] && [ ${stop_stage} -ge 0 ]; then
# Prepare dict
# line: char/spm_pices
cp $unit data/local/dict/units.txt
if [ ! -f $lexicon ];then
utils/text_to_lexicon.py --has_key true --text $text --lexicon $lexicon
echo "Generate $lexicon from $text"
fi
# filter by vocab
# line: word ph0 ... phn -> line: word char0 ... charn
utils/fst/prepare_dict.py \
--unit_file $unit \
--in_lexicon ${lexicon} \
--out_lexicon data/local/dict/lexicon.txt
fi
lm=data/local/lm
mkdir -p $lm
if [ ${stage} -le 1 ] && [ ${stop_stage} -ge 1 ]; then
# Train ngram lm
cp $text $lm/text
local/aishell_train_lms.sh
echo "build LM done."
fi
# build TLG
if [ ${stage} -le 2 ] && [ ${stop_stage} -ge 2 ]; then
# build T & L
utils/fst/compile_lexicon_token_fst.sh \
data/local/dict data/local/tmp data/local/lang
# build G & TLG
utils/fst/make_tlg.sh data/local/lm data/local/lang data/lang_test || exit 1;
fi
# This contains the locations of binarys build required for running the examples.
unset GREP_OPTIONS
ENGINE_ROOT=$PWD/../../../
ENGINE_BUILD=$ENGINE_ROOT/build/engine/asr
ENGINE_TOOLS=$ENGINE_ROOT/tools
TOOLS_BIN=$ENGINE_TOOLS/valgrind/install/bin
[ -d $ENGINE_BUILD ] || { echo "Error: 'build/runtime' directory not found. please ensure that the project build successfully"; }
export LC_AL=C
export PATH=$PATH:$TOOLS_BIN:$ENGINE_BUILD/nnet:$ENGINE_BUILD/decoder:$ENGINE_BUILD/../common/frontend/audio:$ENGINE_BUILD/recognizer
#PADDLE_LIB_PATH=$(python -c "import os; import paddle; include_dir=paddle.sysconfig.get_include(); paddle_dir=os.path.split(include_dir)[0]; libs_dir=os.path.join(paddle_dir, 'libs'); fluid_dir=os.path.join(paddle_dir, 'fluid'); out=':'.join([libs_dir, fluid_dir]); print(out);")
export LD_LIBRARY_PATH=$PADDLE_LIB_PATH:$LD_LIBRARY_PATH
MAIN_ROOT=`realpath $PWD/../../../../`
SPEECHX_ROOT=`realpath $MAIN_ROOT/speechx`
export LC_AL=C
# This contains the locations of binarys build required for running the examples.
unset GREP_OPTIONS
SPEECHX_ROOT=$PWD/../../../
SPEECHX_BUILD=$SPEECHX_ROOT/build/speechx/asr
SPEECHX_TOOLS=$SPEECHX_ROOT/tools
TOOLS_BIN=$SPEECHX_TOOLS/valgrind/install/bin
[ -d $SPEECHX_BUILD ] || { echo "Error: 'build/speechx' directory not found. please ensure that the project build successfully"; }
export LC_AL=C
export PATH=$PATH:$TOOLS_BIN:$SPEECHX_BUILD/nnet:$SPEECHX_BUILD/decoder:$SPEECHX_BUILD/../common/frontend/audio:$SPEECHX_BUILD/recognizer
PADDLE_LIB_PATH=$(python -c "import os; import paddle; include_dir=paddle.sysconfig.get_include(); paddle_dir=os.path.split(include_dir)[0]; libs_dir=os.path.join(paddle_dir, 'libs'); fluid_dir=os.path.join(paddle_dir, 'fluid'); out=':'.join([libs_dir, fluid_dir]); print(out);")
export LD_LIBRARY_PATH=$PADDLE_LIB_PATH:$LD_LIBRARY_PATH
// feat/wave-reader.cc
// Copyright 2009-2011 Karel Vesely; Petr Motlicek
// 2013 Florent Masson
// 2013 Johns Hopkins University (author: Daniel Povey)
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <cstdio>
#include <limits>
#include <sstream>
#include <vector>
#include "frontend/wave-reader.h"
#include "base/kaldi-error.h"
#include "base/kaldi-utils.h"
namespace kaldi {
// A utility class for reading wave header.
struct WaveHeaderReadGofer {
std::istream &is;
bool swap;
char tag[5];
WaveHeaderReadGofer(std::istream &is) : is(is), swap(false) {
memset(tag, '\0', sizeof tag);
}
void Expect4ByteTag(const char *expected) {
is.read(tag, 4);
if (is.fail())
KALDI_ERR << "WaveData: expected " << expected
<< ", failed to read anything";
if (strcmp(tag, expected))
KALDI_ERR << "WaveData: expected " << expected << ", got " << tag;
}
void Read4ByteTag() {
is.read(tag, 4);
if (is.fail())
KALDI_ERR << "WaveData: expected 4-byte chunk-name, got read error";
}
uint32 ReadUint32() {
union {
char result[4];
uint32 ans;
} u;
is.read(u.result, 4);
if (swap)
KALDI_SWAP4(u.result);
if (is.fail())
KALDI_ERR << "WaveData: unexpected end of file or read error";
return u.ans;
}
uint16 ReadUint16() {
union {
char result[2];
int16 ans;
} u;
is.read(u.result, 2);
if (swap)
KALDI_SWAP2(u.result);
if (is.fail())
KALDI_ERR << "WaveData: unexpected end of file or read error";
return u.ans;
}
};
static void WriteUint32(std::ostream &os, int32 i) {
union {
char buf[4];
int i;
} u;
u.i = i;
#ifdef __BIG_ENDIAN__
KALDI_SWAP4(u.buf);
#endif
os.write(u.buf, 4);
if (os.fail())
KALDI_ERR << "WaveData: error writing to stream.";
}
static void WriteUint16(std::ostream &os, int16 i) {
union {
char buf[2];
int16 i;
} u;
u.i = i;
#ifdef __BIG_ENDIAN__
KALDI_SWAP2(u.buf);
#endif
os.write(u.buf, 2);
if (os.fail())
KALDI_ERR << "WaveData: error writing to stream.";
}
void WaveInfo::Read(std::istream &is) {
WaveHeaderReadGofer reader(is);
reader.Read4ByteTag();
if (strcmp(reader.tag, "RIFF") == 0)
reverse_bytes_ = false;
else if (strcmp(reader.tag, "RIFX") == 0)
reverse_bytes_ = true;
else
KALDI_ERR << "WaveData: expected RIFF or RIFX, got " << reader.tag;
#ifdef __BIG_ENDIAN__
reverse_bytes_ = !reverse_bytes_;
#endif
reader.swap = reverse_bytes_;
uint32 riff_chunk_size = reader.ReadUint32();
reader.Expect4ByteTag("WAVE");
uint32 riff_chunk_read = 0;
riff_chunk_read += 4; // WAVE included in riff_chunk_size.
// Possibly skip any RIFF tags between 'WAVE' and 'fmt '.
// Apple devices produce a filler tag 'JUNK' for memory alignment.
reader.Read4ByteTag();
riff_chunk_read += 4;
while (strcmp(reader.tag,"fmt ") != 0) {
uint32 filler_size = reader.ReadUint32();
riff_chunk_read += 4;
for (uint32 i = 0; i < filler_size; i++) {
is.get(); // read 1 byte,
}
riff_chunk_read += filler_size;
// get next RIFF tag,
reader.Read4ByteTag();
riff_chunk_read += 4;
}
KALDI_ASSERT(strcmp(reader.tag,"fmt ") == 0);
uint32 subchunk1_size = reader.ReadUint32();
uint16 audio_format = reader.ReadUint16();
num_channels_ = reader.ReadUint16();
uint32 sample_rate = reader.ReadUint32(),
byte_rate = reader.ReadUint32(),
block_align = reader.ReadUint16(),
bits_per_sample = reader.ReadUint16();
samp_freq_ = static_cast<BaseFloat>(sample_rate);
uint32 fmt_chunk_read = 16;
if (audio_format == 1) {
if (subchunk1_size < 16) {
KALDI_ERR << "WaveData: expect PCM format data to have fmt chunk "
<< "of at least size 16.";
}
} else if (audio_format == 0xFFFE) { // WAVE_FORMAT_EXTENSIBLE
uint16 extra_size = reader.ReadUint16();
if (subchunk1_size < 40 || extra_size < 22) {
KALDI_ERR << "WaveData: malformed WAVE_FORMAT_EXTENSIBLE format data.";
}
reader.ReadUint16(); // Unused for PCM.
reader.ReadUint32(); // Channel map: we do not care.
uint32 guid1 = reader.ReadUint32(),
guid2 = reader.ReadUint32(),
guid3 = reader.ReadUint32(),
guid4 = reader.ReadUint32();
fmt_chunk_read = 40;
// Support only KSDATAFORMAT_SUBTYPE_PCM for now. Interesting formats:
// ("00000001-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_PCM)
// ("00000003-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
// ("00000006-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_ALAW)
// ("00000007-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_MULAW)
if (guid1 != 0x00000001 || guid2 != 0x00100000 ||
guid3 != 0xAA000080 || guid4 != 0x719B3800) {
KALDI_ERR << "WaveData: unsupported WAVE_FORMAT_EXTENSIBLE format.";
}
} else {
KALDI_ERR << "WaveData: can read only PCM data, format id in file is: "
<< audio_format;
}
for (uint32 i = fmt_chunk_read; i < subchunk1_size; ++i)
is.get(); // use up extra data.
if (num_channels_ == 0)
KALDI_ERR << "WaveData: no channels present";
if (bits_per_sample != 16)
KALDI_ERR << "WaveData: unsupported bits_per_sample = " << bits_per_sample;
if (byte_rate != sample_rate * bits_per_sample/8 * num_channels_)
KALDI_ERR << "Unexpected byte rate " << byte_rate << " vs. "
<< sample_rate << " * " << (bits_per_sample/8)
<< " * " << num_channels_;
if (block_align != num_channels_ * bits_per_sample/8)
KALDI_ERR << "Unexpected block_align: " << block_align << " vs. "
<< num_channels_ << " * " << (bits_per_sample/8);
riff_chunk_read += 4 + subchunk1_size;
// size of what we just read, 4 for subchunk1_size + subchunk1_size itself.
// We support an optional "fact" chunk (which is useless but which
// we encountered), and then a single "data" chunk.
reader.Read4ByteTag();
riff_chunk_read += 4;
// Skip any subchunks between "fmt" and "data". Usually there will
// be a single "fact" subchunk, but on Windows there can also be a
// "list" subchunk.
while (strcmp(reader.tag, "data") != 0) {
// We will just ignore the data in these chunks.
uint32 chunk_sz = reader.ReadUint32();
if (chunk_sz != 4 && strcmp(reader.tag, "fact") == 0)
KALDI_WARN << "Expected fact chunk to be 4 bytes long.";
for (uint32 i = 0; i < chunk_sz; i++)
is.get();
riff_chunk_read += 4 + chunk_sz; // for chunk_sz (4) + chunk contents (chunk-sz)
// Now read the next chunk name.
reader.Read4ByteTag();
riff_chunk_read += 4;
}
KALDI_ASSERT(strcmp(reader.tag, "data") == 0);
uint32 data_chunk_size = reader.ReadUint32();
riff_chunk_read += 4;
// Figure out if the file is going to be read to the end. Values as
// observed in the wild:
bool is_stream_mode =
riff_chunk_size == 0
|| riff_chunk_size == 0xFFFFFFFF
|| data_chunk_size == 0
|| data_chunk_size == 0xFFFFFFFF
|| data_chunk_size == 0x7FFFF000; // This value is used by SoX.
if (is_stream_mode)
KALDI_VLOG(1) << "Read in RIFF chunk size: " << riff_chunk_size
<< ", data chunk size: " << data_chunk_size
<< ". Assume 'stream mode' (reading data to EOF).";
if (!is_stream_mode
&& std::abs(static_cast<int64>(riff_chunk_read) +
static_cast<int64>(data_chunk_size) -
static_cast<int64>(riff_chunk_size)) > 1) {
// We allow the size to be off by one without warning, because there is a
// weirdness in the format of RIFF files that means that the input may
// sometimes be padded with 1 unused byte to make the total size even.
KALDI_WARN << "Expected " << riff_chunk_size << " bytes in RIFF chunk, but "
<< "after first data block there will be " << riff_chunk_read
<< " + " << data_chunk_size << " bytes "
<< "(we do not support reading multiple data chunks).";
}
if (is_stream_mode)
samp_count_ = -1;
else
samp_count_ = data_chunk_size / block_align;
}
void WaveData::Read(std::istream &is) {
const uint32 kBlockSize = 1024 * 1024;
WaveInfo header;
header.Read(is);
data_.Resize(0, 0); // clear the data.
samp_freq_ = header.SampFreq();
std::vector<char> buffer;
uint32 bytes_to_go = header.IsStreamed() ? kBlockSize : header.DataBytes();
// Once in a while header.DataBytes() will report an insane value;
// read the file to the end
while (is && bytes_to_go > 0) {
uint32 block_bytes = std::min(bytes_to_go, kBlockSize);
uint32 offset = buffer.size();
buffer.resize(offset + block_bytes);
is.read(&buffer[offset], block_bytes);
uint32 bytes_read = is.gcount();
buffer.resize(offset + bytes_read);
if (!header.IsStreamed())
bytes_to_go -= bytes_read;
}
if (is.bad())
KALDI_ERR << "WaveData: file read error";
if (buffer.size() == 0)
KALDI_ERR << "WaveData: empty file (no data)";
if (!header.IsStreamed() && buffer.size() < header.DataBytes()) {
KALDI_WARN << "Expected " << header.DataBytes() << " bytes of wave data, "
<< "but read only " << buffer.size() << " bytes. "
<< "Truncated file?";
}
uint16 *data_ptr = reinterpret_cast<uint16*>(&buffer[0]);
// The matrix is arranged row per channel, column per sample.
data_.Resize(header.NumChannels(),
buffer.size() / header.BlockAlign());
for (uint32 i = 0; i < data_.NumCols(); ++i) {
for (uint32 j = 0; j < data_.NumRows(); ++j) {
int16 k = *data_ptr++;
if (header.ReverseBytes())
KALDI_SWAP2(k);
data_(j, i) = k;
}
}
}
// Write 16-bit PCM.
// note: the WAVE chunk contains 2 subchunks.
//
// subchunk2size = data.NumRows() * data.NumCols() * 2.
void WaveData::Write(std::ostream &os) const {
os << "RIFF";
if (data_.NumRows() == 0)
KALDI_ERR << "Error: attempting to write empty WAVE file";
int32 num_chan = data_.NumRows(),
num_samp = data_.NumCols(),
bytes_per_samp = 2;
int32 subchunk2size = (num_chan * num_samp * bytes_per_samp);
int32 chunk_size = 36 + subchunk2size;
WriteUint32(os, chunk_size);
os << "WAVE";
os << "fmt ";
WriteUint32(os, 16);
WriteUint16(os, 1);
WriteUint16(os, num_chan);
KALDI_ASSERT(samp_freq_ > 0);
WriteUint32(os, static_cast<int32>(samp_freq_));
WriteUint32(os, static_cast<int32>(samp_freq_) * num_chan * bytes_per_samp);
WriteUint16(os, num_chan * bytes_per_samp);
WriteUint16(os, 8 * bytes_per_samp);
os << "data";
WriteUint32(os, subchunk2size);
const BaseFloat *data_ptr = data_.Data();
int32 stride = data_.Stride();
int num_clipped = 0;
for (int32 i = 0; i < num_samp; i++) {
for (int32 j = 0; j < num_chan; j++) {
int32 elem = static_cast<int32>(trunc(data_ptr[j * stride + i]));
int16 elem_16 = static_cast<int16>(elem);
if (elem < std::numeric_limits<int16>::min()) {
elem_16 = std::numeric_limits<int16>::min();
++num_clipped;
} else if (elem > std::numeric_limits<int16>::max()) {
elem_16 = std::numeric_limits<int16>::max();
++num_clipped;
}
#ifdef __BIG_ENDIAN__
KALDI_SWAP2(elem_16);
#endif
os.write(reinterpret_cast<char*>(&elem_16), 2);
}
}
if (os.fail())
KALDI_ERR << "Error writing wave data to stream.";
if (num_clipped > 0)
KALDI_WARN << "WARNING: clipped " << num_clipped
<< " samples out of total " << num_chan * num_samp
<< ". Reduce volume?";
}
} // end namespace kaldi
/*
* Copyright 2009-2010 Cybozu Labs, Inc.
* Copyright 2011-2014 Kazuho Oku
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef picojson_h
#define picojson_h
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstddef>
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <stdexcept>
#include <string>
#include <vector>
#include <utility>
#define PICOJSON_USE_INT64 1
// for isnan/isinf
#if __cplusplus >= 201103L
#include <cmath>
#else
extern "C" {
#ifdef _MSC_VER
#include <float.h>
#elif defined(__INTEL_COMPILER)
#include <mathimf.h>
#else
#include <math.h>
#endif
}
#endif
#ifndef PICOJSON_USE_RVALUE_REFERENCE
#if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || (defined(_MSC_VER) && _MSC_VER >= 1600)
#define PICOJSON_USE_RVALUE_REFERENCE 1
#else
#define PICOJSON_USE_RVALUE_REFERENCE 0
#endif
#endif // PICOJSON_USE_RVALUE_REFERENCE
#ifndef PICOJSON_NOEXCEPT
#if PICOJSON_USE_RVALUE_REFERENCE
#define PICOJSON_NOEXCEPT noexcept
#else
#define PICOJSON_NOEXCEPT throw()
#endif
#endif
// experimental support for int64_t (see README.mkdn for detail)
#ifdef PICOJSON_USE_INT64
#define __STDC_FORMAT_MACROS
#include <cerrno>
#if __cplusplus >= 201103L
#include <cinttypes>
#else
extern "C" {
#include <inttypes.h>
}
#endif
#endif
// to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0
#ifndef PICOJSON_USE_LOCALE
#define PICOJSON_USE_LOCALE 1
#endif
#if PICOJSON_USE_LOCALE
extern "C" {
#include <locale.h>
}
#endif
#ifndef PICOJSON_ASSERT
#define PICOJSON_ASSERT(e) \
do { \
if (!(e)) \
throw std::runtime_error(#e); \
} while (0)
#endif
#ifdef _MSC_VER
#define SNPRINTF _snprintf_s
#pragma warning(push)
#pragma warning(disable : 4244) // conversion from int to char
#pragma warning(disable : 4127) // conditional expression is constant
#pragma warning(disable : 4702) // unreachable code
#pragma warning(disable : 4706) // assignment within conditional expression
#else
#define SNPRINTF snprintf
#endif
namespace picojson {
enum {
null_type,
boolean_type,
number_type,
string_type,
array_type,
object_type
#ifdef PICOJSON_USE_INT64
,
int64_type
#endif
};
enum { INDENT_WIDTH = 2, DEFAULT_MAX_DEPTHS = 100 };
struct null {};
class value {
public:
typedef std::vector<value> array;
typedef std::map<std::string, value> object;
union _storage {
bool boolean_;
double number_;
#ifdef PICOJSON_USE_INT64
int64_t int64_;
#endif
std::string *string_;
array *array_;
object *object_;
};
protected:
int type_;
_storage u_;
public:
value();
value(int type, bool);
explicit value(bool b);
#ifdef PICOJSON_USE_INT64
explicit value(int64_t i);
#endif
explicit value(double n);
explicit value(const std::string &s);
explicit value(const array &a);
explicit value(const object &o);
#if PICOJSON_USE_RVALUE_REFERENCE
explicit value(std::string &&s);
explicit value(array &&a);
explicit value(object &&o);
#endif
explicit value(const char *s);
value(const char *s, size_t len);
~value();
value(const value &x);
value &operator=(const value &x);
#if PICOJSON_USE_RVALUE_REFERENCE
value(value &&x) PICOJSON_NOEXCEPT;
value &operator=(value &&x) PICOJSON_NOEXCEPT;
#endif
void swap(value &x) PICOJSON_NOEXCEPT;
template <typename T> bool is() const;
template <typename T> const T &get() const;
template <typename T> T &get();
template <typename T> void set(const T &);
#if PICOJSON_USE_RVALUE_REFERENCE
template <typename T> void set(T &&);
#endif
bool evaluate_as_boolean() const;
const value &get(const size_t idx) const;
const value &get(const std::string &key) const;
value &get(const size_t idx);
value &get(const std::string &key);
bool contains(const size_t idx) const;
bool contains(const std::string &key) const;
std::string to_str() const;
template <typename Iter> void serialize(Iter os, bool prettify = false) const;
std::string serialize(bool prettify = false) const;
private:
template <typename T> value(const T *); // intentionally defined to block implicit conversion of pointer to bool
template <typename Iter> static void _indent(Iter os, int indent);
template <typename Iter> void _serialize(Iter os, int indent) const;
std::string _serialize(int indent) const;
void clear();
};
typedef value::array array;
typedef value::object object;
inline value::value() : type_(null_type), u_() {
}
inline value::value(int type, bool) : type_(type), u_() {
switch (type) {
#define INIT(p, v) \
case p##type: \
u_.p = v; \
break
INIT(boolean_, false);
INIT(number_, 0.0);
#ifdef PICOJSON_USE_INT64
INIT(int64_, 0);
#endif
INIT(string_, new std::string());
INIT(array_, new array());
INIT(object_, new object());
#undef INIT
default:
break;
}
}
inline value::value(bool b) : type_(boolean_type), u_() {
u_.boolean_ = b;
}
#ifdef PICOJSON_USE_INT64
inline value::value(int64_t i) : type_(int64_type), u_() {
u_.int64_ = i;
}
#endif
inline value::value(double n) : type_(number_type), u_() {
if (
#ifdef _MSC_VER
!_finite(n)
#elif __cplusplus >= 201103L
std::isnan(n) || std::isinf(n)
#else
isnan(n) || isinf(n)
#endif
) {
throw std::overflow_error("");
}
u_.number_ = n;
}
inline value::value(const std::string &s) : type_(string_type), u_() {
u_.string_ = new std::string(s);
}
inline value::value(const array &a) : type_(array_type), u_() {
u_.array_ = new array(a);
}
inline value::value(const object &o) : type_(object_type), u_() {
u_.object_ = new object(o);
}
#if PICOJSON_USE_RVALUE_REFERENCE
inline value::value(std::string &&s) : type_(string_type), u_() {
u_.string_ = new std::string(std::move(s));
}
inline value::value(array &&a) : type_(array_type), u_() {
u_.array_ = new array(std::move(a));
}
inline value::value(object &&o) : type_(object_type), u_() {
u_.object_ = new object(std::move(o));
}
#endif
inline value::value(const char *s) : type_(string_type), u_() {
u_.string_ = new std::string(s);
}
inline value::value(const char *s, size_t len) : type_(string_type), u_() {
u_.string_ = new std::string(s, len);
}
inline void value::clear() {
switch (type_) {
#define DEINIT(p) \
case p##type: \
delete u_.p; \
break
DEINIT(string_);
DEINIT(array_);
DEINIT(object_);
#undef DEINIT
default:
break;
}
}
inline value::~value() {
clear();
}
inline value::value(const value &x) : type_(x.type_), u_() {
switch (type_) {
#define INIT(p, v) \
case p##type: \
u_.p = v; \
break
INIT(string_, new std::string(*x.u_.string_));
INIT(array_, new array(*x.u_.array_));
INIT(object_, new object(*x.u_.object_));
#undef INIT
default:
u_ = x.u_;
break;
}
}
inline value &value::operator=(const value &x) {
if (this != &x) {
value t(x);
swap(t);
}
return *this;
}
#if PICOJSON_USE_RVALUE_REFERENCE
inline value::value(value &&x) PICOJSON_NOEXCEPT : type_(null_type), u_() {
swap(x);
}
inline value &value::operator=(value &&x) PICOJSON_NOEXCEPT {
swap(x);
return *this;
}
#endif
inline void value::swap(value &x) PICOJSON_NOEXCEPT {
std::swap(type_, x.type_);
std::swap(u_, x.u_);
}
#define IS(ctype, jtype) \
template <> inline bool value::is<ctype>() const { \
return type_ == jtype##_type; \
}
IS(null, null)
IS(bool, boolean)
#ifdef PICOJSON_USE_INT64
IS(int64_t, int64)
#endif
IS(std::string, string)
IS(array, array)
IS(object, object)
#undef IS
template <> inline bool value::is<double>() const {
return type_ == number_type
#ifdef PICOJSON_USE_INT64
|| type_ == int64_type
#endif
;
}
#define GET(ctype, var) \
template <> inline const ctype &value::get<ctype>() const { \
PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" && is<ctype>()); \
return var; \
} \
template <> inline ctype &value::get<ctype>() { \
PICOJSON_ASSERT("type mismatch! call is<type>() before get<type>()" && is<ctype>()); \
return var; \
}
GET(bool, u_.boolean_)
GET(std::string, *u_.string_)
GET(array, *u_.array_)
GET(object, *u_.object_)
#ifdef PICOJSON_USE_INT64
GET(double,
(type_ == int64_type && (const_cast<value *>(this)->type_ = number_type, (const_cast<value *>(this)->u_.number_ = u_.int64_)),
u_.number_))
GET(int64_t, u_.int64_)
#else
GET(double, u_.number_)
#endif
#undef GET
#define SET(ctype, jtype, setter) \
template <> inline void value::set<ctype>(const ctype &_val) { \
clear(); \
type_ = jtype##_type; \
setter \
}
SET(bool, boolean, u_.boolean_ = _val;)
SET(std::string, string, u_.string_ = new std::string(_val);)
SET(array, array, u_.array_ = new array(_val);)
SET(object, object, u_.object_ = new object(_val);)
SET(double, number, u_.number_ = _val;)
#ifdef PICOJSON_USE_INT64
SET(int64_t, int64, u_.int64_ = _val;)
#endif
#undef SET
#if PICOJSON_USE_RVALUE_REFERENCE
#define MOVESET(ctype, jtype, setter) \
template <> inline void value::set<ctype>(ctype && _val) { \
clear(); \
type_ = jtype##_type; \
setter \
}
MOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));)
MOVESET(array, array, u_.array_ = new array(std::move(_val));)
MOVESET(object, object, u_.object_ = new object(std::move(_val));)
#undef MOVESET
#endif
inline bool value::evaluate_as_boolean() const {
switch (type_) {
case null_type:
return false;
case boolean_type:
return u_.boolean_;
case number_type:
return u_.number_ != 0;
#ifdef PICOJSON_USE_INT64
case int64_type:
return u_.int64_ != 0;
#endif
case string_type:
return !u_.string_->empty();
default:
return true;
}
}
inline const value &value::get(const size_t idx) const {
static value s_null;
PICOJSON_ASSERT(is<array>());
return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
}
inline value &value::get(const size_t idx) {
static value s_null;
PICOJSON_ASSERT(is<array>());
return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
}
inline const value &value::get(const std::string &key) const {
static value s_null;
PICOJSON_ASSERT(is<object>());
object::const_iterator i = u_.object_->find(key);
return i != u_.object_->end() ? i->second : s_null;
}
inline value &value::get(const std::string &key) {
static value s_null;
PICOJSON_ASSERT(is<object>());
object::iterator i = u_.object_->find(key);
return i != u_.object_->end() ? i->second : s_null;
}
inline bool value::contains(const size_t idx) const {
PICOJSON_ASSERT(is<array>());
return idx < u_.array_->size();
}
inline bool value::contains(const std::string &key) const {
PICOJSON_ASSERT(is<object>());
object::const_iterator i = u_.object_->find(key);
return i != u_.object_->end();
}
inline std::string value::to_str() const {
switch (type_) {
case null_type:
return "null";
case boolean_type:
return u_.boolean_ ? "true" : "false";
#ifdef PICOJSON_USE_INT64
case int64_type: {
char buf[sizeof("-9223372036854775808")];
SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_);
return buf;
}
#endif
case number_type: {
char buf[256];
double tmp;
SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_);
#if PICOJSON_USE_LOCALE
char *decimal_point = localeconv()->decimal_point;
if (strcmp(decimal_point, ".") != 0) {
size_t decimal_point_len = strlen(decimal_point);
for (char *p = buf; *p != '\0'; ++p) {
if (strncmp(p, decimal_point, decimal_point_len) == 0) {
return std::string(buf, p) + "." + (p + decimal_point_len);
}
}
}
#endif
return buf;
}
case string_type:
return *u_.string_;
case array_type:
return "array";
case object_type:
return "object";
default:
PICOJSON_ASSERT(0);
#ifdef _MSC_VER
__assume(0);
#endif
}
return std::string();
}
template <typename Iter> void copy(const std::string &s, Iter oi) {
std::copy(s.begin(), s.end(), oi);
}
template <typename Iter> struct serialize_str_char {
Iter oi;
void operator()(char c) {
switch (c) {
#define MAP(val, sym) \
case val: \
copy(sym, oi); \
break
MAP('"', "\\\"");
MAP('\\', "\\\\");
MAP('/', "\\/");
MAP('\b', "\\b");
MAP('\f', "\\f");
MAP('\n', "\\n");
MAP('\r', "\\r");
MAP('\t', "\\t");
#undef MAP
default:
if (static_cast<unsigned char>(c) < 0x20 || c == 0x7f) {
char buf[7];
SNPRINTF(buf, sizeof(buf), "\\u%04x", c & 0xff);
copy(buf, buf + 6, oi);
} else {
*oi++ = c;
}
break;
}
}
};
template <typename Iter> void serialize_str(const std::string &s, Iter oi) {
*oi++ = '"';
serialize_str_char<Iter> process_char = {oi};
std::for_each(s.begin(), s.end(), process_char);
*oi++ = '"';
}
template <typename Iter> void value::serialize(Iter oi, bool prettify) const {
return _serialize(oi, prettify ? 0 : -1);
}
inline std::string value::serialize(bool prettify) const {
return _serialize(prettify ? 0 : -1);
}
template <typename Iter> void value::_indent(Iter oi, int indent) {
*oi++ = '\n';
for (int i = 0; i < indent * INDENT_WIDTH; ++i) {
*oi++ = ' ';
}
}
template <typename Iter> void value::_serialize(Iter oi, int indent) const {
switch (type_) {
case string_type:
serialize_str(*u_.string_, oi);
break;
case array_type: {
*oi++ = '[';
if (indent != -1) {
++indent;
}
for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) {
if (i != u_.array_->begin()) {
*oi++ = ',';
}
if (indent != -1) {
_indent(oi, indent);
}
i->_serialize(oi, indent);
}
if (indent != -1) {
--indent;
if (!u_.array_->empty()) {
_indent(oi, indent);
}
}
*oi++ = ']';
break;
}
case object_type: {
*oi++ = '{';
if (indent != -1) {
++indent;
}
for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) {
if (i != u_.object_->begin()) {
*oi++ = ',';
}
if (indent != -1) {
_indent(oi, indent);
}
serialize_str(i->first, oi);
*oi++ = ':';
if (indent != -1) {
*oi++ = ' ';
}
i->second._serialize(oi, indent);
}
if (indent != -1) {
--indent;
if (!u_.object_->empty()) {
_indent(oi, indent);
}
}
*oi++ = '}';
break;
}
default:
copy(to_str(), oi);
break;
}
if (indent == 0) {
*oi++ = '\n';
}
}
inline std::string value::_serialize(int indent) const {
std::string s;
_serialize(std::back_inserter(s), indent);
return s;
}
template <typename Iter> class input {
protected:
Iter cur_, end_;
bool consumed_;
int line_;
public:
input(const Iter &first, const Iter &last) : cur_(first), end_(last), consumed_(false), line_(1) {
}
int getc() {
if (consumed_) {
if (*cur_ == '\n') {
++line_;
}
++cur_;
}
if (cur_ == end_) {
consumed_ = false;
return -1;
}
consumed_ = true;
return *cur_ & 0xff;
}
void ungetc() {
consumed_ = false;
}
Iter cur() const {
if (consumed_) {
input<Iter> *self = const_cast<input<Iter> *>(this);
self->consumed_ = false;
++self->cur_;
}
return cur_;
}
int line() const {
return line_;
}
void skip_ws() {
while (1) {
int ch = getc();
if (!(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) {
ungetc();
break;
}
}
}
bool expect(const int expected) {
skip_ws();
if (getc() != expected) {
ungetc();
return false;
}
return true;
}
bool match(const std::string &pattern) {
for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) {
if (getc() != *pi) {
ungetc();
return false;
}
}
return true;
}
};
template <typename Iter> inline int _parse_quadhex(input<Iter> &in) {
int uni_ch = 0, hex;
for (int i = 0; i < 4; i++) {
if ((hex = in.getc()) == -1) {
return -1;
}
if ('0' <= hex && hex <= '9') {
hex -= '0';
} else if ('A' <= hex && hex <= 'F') {
hex -= 'A' - 0xa;
} else if ('a' <= hex && hex <= 'f') {
hex -= 'a' - 0xa;
} else {
in.ungetc();
return -1;
}
uni_ch = uni_ch * 16 + hex;
}
return uni_ch;
}
template <typename String, typename Iter> inline bool _parse_codepoint(String &out, input<Iter> &in) {
int uni_ch;
if ((uni_ch = _parse_quadhex(in)) == -1) {
return false;
}
if (0xd800 <= uni_ch && uni_ch <= 0xdfff) {
if (0xdc00 <= uni_ch) {
// a second 16-bit of a surrogate pair appeared
return false;
}
// first 16-bit of surrogate pair, get the next one
if (in.getc() != '\\' || in.getc() != 'u') {
in.ungetc();
return false;
}
int second = _parse_quadhex(in);
if (!(0xdc00 <= second && second <= 0xdfff)) {
return false;
}
uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff);
uni_ch += 0x10000;
}
if (uni_ch < 0x80) {
out.push_back(static_cast<char>(uni_ch));
} else {
if (uni_ch < 0x800) {
out.push_back(static_cast<char>(0xc0 | (uni_ch >> 6)));
} else {
if (uni_ch < 0x10000) {
out.push_back(static_cast<char>(0xe0 | (uni_ch >> 12)));
} else {
out.push_back(static_cast<char>(0xf0 | (uni_ch >> 18)));
out.push_back(static_cast<char>(0x80 | ((uni_ch >> 12) & 0x3f)));
}
out.push_back(static_cast<char>(0x80 | ((uni_ch >> 6) & 0x3f)));
}
out.push_back(static_cast<char>(0x80 | (uni_ch & 0x3f)));
}
return true;
}
template <typename String, typename Iter> inline bool _parse_string(String &out, input<Iter> &in) {
while (1) {
int ch = in.getc();
if (ch < ' ') {
in.ungetc();
return false;
} else if (ch == '"') {
return true;
} else if (ch == '\\') {
if ((ch = in.getc()) == -1) {
return false;
}
switch (ch) {
#define MAP(sym, val) \
case sym: \
out.push_back(val); \
break
MAP('"', '\"');
MAP('\\', '\\');
MAP('/', '/');
MAP('b', '\b');
MAP('f', '\f');
MAP('n', '\n');
MAP('r', '\r');
MAP('t', '\t');
#undef MAP
case 'u':
if (!_parse_codepoint(out, in)) {
return false;
}
break;
default:
return false;
}
} else {
out.push_back(static_cast<char>(ch));
}
}
return false;
}
template <typename Context, typename Iter> inline bool _parse_array(Context &ctx, input<Iter> &in) {
if (!ctx.parse_array_start()) {
return false;
}
size_t idx = 0;
if (in.expect(']')) {
return ctx.parse_array_stop(idx);
}
do {
if (!ctx.parse_array_item(in, idx)) {
return false;
}
idx++;
} while (in.expect(','));
return in.expect(']') && ctx.parse_array_stop(idx);
}
template <typename Context, typename Iter> inline bool _parse_object(Context &ctx, input<Iter> &in) {
if (!ctx.parse_object_start()) {
return false;
}
if (in.expect('}')) {
return ctx.parse_object_stop();
}
do {
std::string key;
if (!in.expect('"') || !_parse_string(key, in) || !in.expect(':')) {
return false;
}
if (!ctx.parse_object_item(in, key)) {
return false;
}
} while (in.expect(','));
return in.expect('}') && ctx.parse_object_stop();
}
template <typename Iter> inline std::string _parse_number(input<Iter> &in) {
std::string num_str;
while (1) {
int ch = in.getc();
if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') {
num_str.push_back(static_cast<char>(ch));
} else if (ch == '.') {
#if PICOJSON_USE_LOCALE
num_str += localeconv()->decimal_point;
#else
num_str.push_back('.');
#endif
} else {
in.ungetc();
break;
}
}
return num_str;
}
template <typename Context, typename Iter> inline bool _parse(Context &ctx, input<Iter> &in) {
in.skip_ws();
int ch = in.getc();
switch (ch) {
#define IS(ch, text, op) \
case ch: \
if (in.match(text) && op) { \
return true; \
} else { \
return false; \
}
IS('n', "ull", ctx.set_null());
IS('f', "alse", ctx.set_bool(false));
IS('t', "rue", ctx.set_bool(true));
#undef IS
case '"':
return ctx.parse_string(in);
case '[':
return _parse_array(ctx, in);
case '{':
return _parse_object(ctx, in);
default:
if (('0' <= ch && ch <= '9') || ch == '-') {
double f;
char *endp;
in.ungetc();
std::string num_str(_parse_number(in));
if (num_str.empty()) {
return false;
}
#ifdef PICOJSON_USE_INT64
{
errno = 0;
intmax_t ival = strtoimax(num_str.c_str(), &endp, 10);
if (errno == 0 && std::numeric_limits<int64_t>::min() <= ival && ival <= std::numeric_limits<int64_t>::max() &&
endp == num_str.c_str() + num_str.size()) {
ctx.set_int64(ival);
return true;
}
}
#endif
f = strtod(num_str.c_str(), &endp);
if (endp == num_str.c_str() + num_str.size()) {
ctx.set_number(f);
return true;
}
return false;
}
break;
}
in.ungetc();
return false;
}
class deny_parse_context {
public:
bool set_null() {
return false;
}
bool set_bool(bool) {
return false;
}
#ifdef PICOJSON_USE_INT64
bool set_int64(int64_t) {
return false;
}
#endif
bool set_number(double) {
return false;
}
template <typename Iter> bool parse_string(input<Iter> &) {
return false;
}
bool parse_array_start() {
return false;
}
template <typename Iter> bool parse_array_item(input<Iter> &, size_t) {
return false;
}
bool parse_array_stop(size_t) {
return false;
}
bool parse_object_start() {
return false;
}
template <typename Iter> bool parse_object_item(input<Iter> &, const std::string &) {
return false;
}
};
class default_parse_context {
protected:
value *out_;
size_t depths_;
public:
default_parse_context(value *out, size_t depths = DEFAULT_MAX_DEPTHS) : out_(out), depths_(depths) {
}
bool set_null() {
*out_ = value();
return true;
}
bool set_bool(bool b) {
*out_ = value(b);
return true;
}
#ifdef PICOJSON_USE_INT64
bool set_int64(int64_t i) {
*out_ = value(i);
return true;
}
#endif
bool set_number(double f) {
*out_ = value(f);
return true;
}
template <typename Iter> bool parse_string(input<Iter> &in) {
*out_ = value(string_type, false);
return _parse_string(out_->get<std::string>(), in);
}
bool parse_array_start() {
if (depths_ == 0)
return false;
--depths_;
*out_ = value(array_type, false);
return true;
}
template <typename Iter> bool parse_array_item(input<Iter> &in, size_t) {
array &a = out_->get<array>();
a.push_back(value());
default_parse_context ctx(&a.back(), depths_);
return _parse(ctx, in);
}
bool parse_array_stop(size_t) {
++depths_;
return true;
}
bool parse_object_start() {
if (depths_ == 0)
return false;
*out_ = value(object_type, false);
return true;
}
template <typename Iter> bool parse_object_item(input<Iter> &in, const std::string &key) {
object &o = out_->get<object>();
default_parse_context ctx(&o[key], depths_);
return _parse(ctx, in);
}
bool parse_object_stop() {
++depths_;
return true;
}
private:
default_parse_context(const default_parse_context &);
default_parse_context &operator=(const default_parse_context &);
};
class null_parse_context {
protected:
size_t depths_;
public:
struct dummy_str {
void push_back(int) {
}
};
public:
null_parse_context(size_t depths = DEFAULT_MAX_DEPTHS) : depths_(depths) {
}
bool set_null() {
return true;
}
bool set_bool(bool) {
return true;
}
#ifdef PICOJSON_USE_INT64
bool set_int64(int64_t) {
return true;
}
#endif
bool set_number(double) {
return true;
}
template <typename Iter> bool parse_string(input<Iter> &in) {
dummy_str s;
return _parse_string(s, in);
}
bool parse_array_start() {
if (depths_ == 0)
return false;
--depths_;
return true;
}
template <typename Iter> bool parse_array_item(input<Iter> &in, size_t) {
return _parse(*this, in);
}
bool parse_array_stop(size_t) {
++depths_;
return true;
}
bool parse_object_start() {
if (depths_ == 0)
return false;
--depths_;
return true;
}
template <typename Iter> bool parse_object_item(input<Iter> &in, const std::string &) {
++depths_;
return _parse(*this, in);
}
bool parse_object_stop() {
return true;
}
private:
null_parse_context(const null_parse_context &);
null_parse_context &operator=(const null_parse_context &);
};
// obsolete, use the version below
template <typename Iter> inline std::string parse(value &out, Iter &pos, const Iter &last) {
std::string err;
pos = parse(out, pos, last, &err);
return err;
}
template <typename Context, typename Iter> inline Iter _parse(Context &ctx, const Iter &first, const Iter &last, std::string *err) {
input<Iter> in(first, last);
if (!_parse(ctx, in) && err != NULL) {
char buf[64];
SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line());
*err = buf;
while (1) {
int ch = in.getc();
if (ch == -1 || ch == '\n') {
break;
} else if (ch >= ' ') {
err->push_back(static_cast<char>(ch));
}
}
}
return in.cur();
}
template <typename Iter> inline Iter parse(value &out, const Iter &first, const Iter &last, std::string *err) {
default_parse_context ctx(&out);
return _parse(ctx, first, last, err);
}
inline std::string parse(value &out, const std::string &s) {
std::string err;
parse(out, s.begin(), s.end(), &err);
return err;
}
inline std::string parse(value &out, std::istream &is) {
std::string err;
parse(out, std::istreambuf_iterator<char>(is.rdbuf()), std::istreambuf_iterator<char>(), &err);
return err;
}
template <typename T> struct last_error_t { static std::string s; };
template <typename T> std::string last_error_t<T>::s;
inline void set_last_error(const std::string &s) {
last_error_t<bool>::s = s;
}
inline const std::string &get_last_error() {
return last_error_t<bool>::s;
}
inline bool operator==(const value &x, const value &y) {
if (x.is<null>())
return y.is<null>();
#define PICOJSON_CMP(type) \
if (x.is<type>()) \
return y.is<type>() && x.get<type>() == y.get<type>()
PICOJSON_CMP(bool);
PICOJSON_CMP(double);
PICOJSON_CMP(std::string);
PICOJSON_CMP(array);
PICOJSON_CMP(object);
#undef PICOJSON_CMP
PICOJSON_ASSERT(0);
#ifdef _MSC_VER
__assume(0);
#endif
return false;
}
inline bool operator!=(const value &x, const value &y) {
return !(x == y);
}
}
#if !PICOJSON_USE_RVALUE_REFERENCE
namespace std {
template <> inline void swap(picojson::value &x, picojson::value &y) {
x.swap(y);
}
}
#endif
inline std::istream &operator>>(std::istream &is, picojson::value &x) {
picojson::set_last_error(std::string());
const std::string err(picojson::parse(x, is));
if (!err.empty()) {
picojson::set_last_error(err);
is.setstate(std::ios::failbit);
}
return is;
}
inline std::ostream &operator<<(std::ostream &os, const picojson::value &x) {
x.serialize(std::ostream_iterator<char>(os));
return os;
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册