diff --git a/.gitmodules b/.gitmodules index 2e380a1..e806c9c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "visr_bear/submodules/rapidjson"] - path = visr_bear/submodules/rapidjson - url = https://github.com/Tencent/rapidjson.git [submodule "visr_bear/submodules/libear"] path = visr_bear/submodules/libear url = https://github.com/ebu/libear.git diff --git a/nix/visr_bear.nix b/nix/visr_bear.nix index 955e4f1..d36d5bb 100644 --- a/nix/visr_bear.nix +++ b/nix/visr_bear.nix @@ -1,4 +1,4 @@ -{ lib, stdenv, src, cmake, ninja, boost, python, visr ? null, visr_python ? null, data_files, eigen, libear, rapidjson, enable_python ? true }: +{ lib, stdenv, src, cmake, ninja, boost, python, visr ? null, visr_python ? null, data_files, eigen, libear, nlohmann_json, enable_python ? true }: stdenv.mkDerivation rec { name = "visr_bear"; inherit src; @@ -8,7 +8,7 @@ stdenv.mkDerivation rec { boost eigen libear - rapidjson + nlohmann_json ] ++ (if enable_python then [ python visr_python ] else [ visr ]); cmakeFlags = [ "-DBEAR_UNIT_TESTS=true" @@ -16,7 +16,6 @@ stdenv.mkDerivation rec { "-DBEAR_DATA_PATH_DEFAULT_SMALL=${data_files.default_small.file}" "-DBEAR_SYMLINK_DATA_FILES=true" "-DBEAR_USE_INTERNAL_LIBEAR=false" - "-DBEAR_USE_INTERNAL_RAPIDJSON=false" ] ++ lib.optional (!enable_python) "-DBUILD_PYTHON_BINDINGS=false"; preConfigure = ''cmakeFlags="$cmakeFlags -DBEAR_PYTHON_SITE_PACKAGES=$out/${python.sitePackages}"''; diff --git a/visr_bear/CMakeLists.txt b/visr_bear/CMakeLists.txt index 6533ee4..2a0141c 100644 --- a/visr_bear/CMakeLists.txt +++ b/visr_bear/CMakeLists.txt @@ -90,18 +90,7 @@ if(NOT BEAR_USE_INTERNAL_LIBEAR) find_package(Eigen3 REQUIRED) endif() -option( - BEAR_USE_INTERNAL_RAPIDJSON - "should we use our own version of RapidJSON, or find one with find_package?" - ON) -if(NOT BEAR_USE_INTERNAL_RAPIDJSON) - find_package(RapidJSON REQUIRED) - if(NOT TARGET RapidJSON) - # rapidjson cmake currently doesn't export a target - add_library(RapidJSON INTERFACE) - target_include_directories(RapidJSON INTERFACE ${RAPIDJSON_INCLUDE_DIRS}) - endif() -endif() +find_package(nlohmann_json 3.11.0 REQUIRED) find_package(Boost REQUIRED) diff --git a/visr_bear/pythonwrappers/internals.cpp b/visr_bear/pythonwrappers/internals.cpp index 9fd9299..a3be3df 100644 --- a/visr_bear/pythonwrappers/internals.cpp +++ b/visr_bear/pythonwrappers/internals.cpp @@ -20,7 +20,7 @@ namespace python { { init_parameters(); - // make Config convertable to ConfigImpl; this is what the internals use, + // make Config convertible to ConfigImpl; this is what the internals use, // and this means that there's only one config interface on the python side // where we don't need to care about ABI compatibility. py::class_(m, "_ConfigImpl").def(py::init([](const Config &config) { diff --git a/visr_bear/src/CMakeLists.txt b/visr_bear/src/CMakeLists.txt index 2c60bdc..0287a3b 100644 --- a/visr_bear/src/CMakeLists.txt +++ b/visr_bear/src/CMakeLists.txt @@ -2,7 +2,7 @@ # internals (test, add_library(bear-internals INTERFACE) target_link_libraries(bear-internals INTERFACE mio) -target_link_libraries(bear-internals INTERFACE RapidJSON) +target_link_libraries(bear-internals INTERFACE nlohmann_json::nlohmann_json) target_link_libraries(bear-internals INTERFACE Eigen3::Eigen) target_include_directories(bear-internals INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/visr_bear/src/api.cpp b/visr_bear/src/api.cpp index 17b83b0..74668b2 100644 --- a/visr_bear/src/api.cpp +++ b/visr_bear/src/api.cpp @@ -286,7 +286,7 @@ Renderer::~Renderer() = default; class DataFileMetadataImpl { public: - rapidjson::Value metadata; + nlohmann::json metadata; bool has_metadata = false; std::string label; @@ -295,34 +295,34 @@ class DataFileMetadataImpl { }; namespace { - std::string parse_label(rapidjson::Value &metadata) + std::string parse_label(const nlohmann::json &metadata) { - auto it = metadata.FindMember("label"); - if (it == metadata.MemberEnd()) throw std::runtime_error("expected label"); + auto it = metadata.find("label"); + if (it == metadata.end()) throw std::runtime_error("expected label"); - if (!it->value.IsString()) throw std::runtime_error("label should be a string"); + if (!it->is_string()) throw std::runtime_error("label should be a string"); - return it->value.GetString(); + return it->template get(); } - std::string parse_description(rapidjson::Value &metadata) + std::string parse_description(const nlohmann::json &metadata) { - auto it = metadata.FindMember("description"); - if (it == metadata.MemberEnd()) return ""; + auto it = metadata.find("description"); + if (it == metadata.end()) return ""; - if (!it->value.IsString()) throw std::runtime_error("description should be a string"); + if (!it->is_string()) throw std::runtime_error("description should be a string"); - return it->value.GetString(); + return it->template get(); } - bool parse_released(rapidjson::Value &metadata) + bool parse_released(const nlohmann::json &metadata) { - auto it = metadata.FindMember("released"); - if (it == metadata.MemberEnd()) throw std::runtime_error("expected released"); + auto it = metadata.find("released"); + if (it == metadata.end()) throw std::runtime_error("expected released"); - if (!it->value.IsBool()) throw std::runtime_error("released should be a bool"); + if (!it->is_boolean()) throw std::runtime_error("released should be a bool"); - return it->value.GetBool(); + return it->template get(); } } // namespace @@ -338,13 +338,13 @@ DataFileMetadata DataFileMetadata::read_from_file(const std::string &path) auto impl = std::make_unique(); - auto it = tf.metadata.FindMember("metadata"); - if (it != tf.metadata.MemberEnd()) { - if (!it->value.IsObject()) throw std::runtime_error("data file metadata should be object"); + auto it = tf.metadata.find("metadata"); + if (it != tf.metadata.end()) { + if (!it->is_object()) throw std::runtime_error("data file metadata should be object"); impl->has_metadata = true; - impl->metadata = it->value; + impl->metadata = *it; impl->label = parse_label(impl->metadata); impl->description = parse_description(impl->metadata); diff --git a/visr_bear/src/data_file.cpp b/visr_bear/src/data_file.cpp index 0535f4f..41935ef 100644 --- a/visr_bear/src/data_file.cpp +++ b/visr_bear/src/data_file.cpp @@ -5,12 +5,12 @@ namespace bear { int get_data_file_version(tensorfile::TensorFile &tf) { - auto it = tf.metadata.FindMember("bear_data_version"); - if (it == tf.metadata.MemberEnd()) return 0; + auto it = tf.metadata.find("bear_data_version"); + if (it == tf.metadata.end()) return 0; - if (!it->value.IsInt()) throw std::runtime_error("version must be an integer"); + if (!it->is_number_integer()) throw std::runtime_error("version must be an integer"); - return it->value.GetInt(); + return it->template get(); } void check_data_file_version(tensorfile::TensorFile &tf) diff --git a/visr_bear/src/panner.cpp b/visr_bear/src/panner.cpp index 2c9345a..a034a82 100644 --- a/visr_bear/src/panner.cpp +++ b/visr_bear/src/panner.cpp @@ -3,41 +3,43 @@ #include "data_file.hpp" #include "utils.hpp" +using json = nlohmann::json; + namespace { -ear::PolarPosition load_position(const rapidjson::Value &position_j) +ear::PolarPosition load_position(const json &position_j) { - return {position_j["azimuth"].GetDouble(), - position_j["elevation"].GetDouble(), - position_j["distance"].GetDouble()}; + return {position_j.at("azimuth").template get(), + position_j.at("elevation").template get(), + position_j.at("distance").template get()}; } -ear::Channel load_channel(const rapidjson::Value &channel_j) +ear::Channel load_channel(const json &channel_j) { ear::Channel channel; - channel.name(channel_j["name"].GetString()); + channel.name(channel_j.at("name").template get()); - channel.polarPosition(load_position(channel_j["polar_position"])); - channel.polarPositionNominal(load_position(channel_j["polar_nominal_position"])); + channel.polarPosition(load_position(channel_j.at("polar_position"))); + channel.polarPositionNominal(load_position(channel_j.at("polar_nominal_position"))); - const auto &az_range_j = channel_j["az_range"].GetArray(); - channel.azimuthRange({{az_range_j[0].GetDouble(), az_range_j[1].GetDouble()}}); + auto az_range = channel_j.at("az_range").template get>(); + channel.azimuthRange(az_range); - const auto &el_range_j = channel_j["el_range"].GetArray(); - channel.elevationRange({{el_range_j[0].GetDouble(), el_range_j[1].GetDouble()}}); + auto el_range = channel_j.at("el_range").template get>(); + channel.elevationRange(el_range); - channel.isLfe(channel_j["is_lfe"].GetBool()); + channel.isLfe(channel_j.at("is_lfe").get()); return channel; } -ear::Layout load_layout(const rapidjson::Value &layout_j) +ear::Layout load_layout(const json &layout_j) { + const json::array_t &channels_j = layout_j.at("channels").template get_ref(); + std::vector channels; - for (auto &channel_j : layout_j["channels"].GetArray()) { - channels.push_back(load_channel(channel_j)); - } + for (const auto &channel_j : channels_j) channels.push_back(load_channel(channel_j)); - return {layout_j["name"].GetString(), channels}; + return {layout_j.at("name").template get(), channels}; } void check(bool x, const std::string &message) @@ -55,13 +57,13 @@ Panner::Panner(const std::string &brir_file_name) check_data_file_version(tf); - views = tf.unpack(tf.metadata["views"]); - brirs = tf.unpack(tf.metadata["brirs"]); - delays = tf.unpack(tf.metadata["delays"]); - decorrelation_filters = tf.unpack(tf.metadata["decorrelation_filters"]); - fs = tf.metadata["fs"].GetDouble(); - decorrelation_delay_ = tf.metadata["decorrelation_delay"].GetDouble(); - front_loudspeaker_ = tf.metadata["front_loudspeaker"].GetUint(); + views = tf.unpack(tf.metadata.at("views")); + brirs = tf.unpack(tf.metadata.at("brirs")); + delays = tf.unpack(tf.metadata.at("delays")); + decorrelation_filters = tf.unpack(tf.metadata.at("decorrelation_filters")); + fs = tf.metadata.at("fs").template get(); + decorrelation_delay_ = tf.metadata.at("decorrelation_delay").template get(); + front_loudspeaker_ = tf.metadata.at("front_loudspeaker").template get(); // check and unpack dimensions check(views->ndim() == 2, "views must have 2 dimensions"); @@ -93,11 +95,11 @@ Panner::Panner(const std::string &brir_file_name) // load layout ear::Layout layout; - if (tf.metadata["layout"].IsString()) { - std::string layout_name = tf.metadata["layout"].GetString(); + if (tf.metadata.at("layout").is_string()) { + std::string layout_name = tf.metadata.at("layout").template get(); layout = ear::getLayout(layout_name).withoutLfe(); } else { - layout = load_layout(tf.metadata["layout"]); + layout = load_layout(tf.metadata.at("layout")); } // make gain calculators @@ -110,9 +112,9 @@ Panner::Panner(const std::string &brir_file_name) temp_direct_diffuse.resize(num_gains() * 2); temp_direct_speakers.resize(num_gains()); - if (tf.metadata.HasMember("gain_norm_quick")) { + if (tf.metadata.contains("gain_norm_quick")) { gain_comp_type = GainCompType::QUICK; - gain_comp_factors = tf.unpack(tf.metadata["gain_norm_quick"]["factors"]); + gain_comp_factors = tf.unpack(tf.metadata.at("gain_norm_quick").at("factors")); check(gain_comp_factors->ndim() == 5, "gain comp factors must have 5 dimensions"); // TODO: check max delays? check(gain_comp_factors->shape(1) == n_views_, "gain comp factors axis 1 is wrong size"); @@ -123,13 +125,14 @@ Panner::Panner(const std::string &brir_file_name) check(gain_comp_factors->shape(4) == 2, "gain comp factors axis 4 is wrong size"); } - check(tf.metadata.HasMember("hoa"), "HOA decoder not present"); - hoa_irs = tf.unpack(tf.metadata["hoa"]["irs"]); + check(tf.metadata.contains("hoa"), "HOA decoder not present"); + hoa_irs = tf.unpack(tf.metadata.at("hoa").at("irs")); check(hoa_irs->ndim() == 3, "HOA decoder must have 3 dimensions"); n_hoa_channels_ = hoa_irs->shape(0); check(hoa_irs->shape(1) == 2, "HOA decoder axis 1 is wrong size"); - if (tf.metadata["hoa"].HasMember("delay")) hoa_delay_ = tf.metadata["hoa"]["delay"].GetDouble(); + if (tf.metadata.at("hoa").contains("delay")) + hoa_delay_ = tf.metadata.at("hoa").at("delay").template get(); hoa_order_ = ((size_t)std::round(std::sqrt(n_hoa_channels_))) - 1; size_t nch_for_order = (hoa_order_ + 1) * (hoa_order_ + 1); diff --git a/visr_bear/src/tensorfile.cpp b/visr_bear/src/tensorfile.cpp index e2a2684..29e0dde 100644 --- a/visr_bear/src/tensorfile.cpp +++ b/visr_bear/src/tensorfile.cpp @@ -6,7 +6,6 @@ #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include "mio.hpp" -#include "rapidjson/error/en.h" namespace tensorfile { @@ -176,19 +175,17 @@ namespace detail { } // namespace detail -std::shared_ptr TensorFile::unpack(const rapidjson::Value &v) const +std::shared_ptr TensorFile::unpack(const nlohmann::json &v) const { - if (std::string(v["_tenf_type"].GetString()) == "array") { - std::vector shape; - for (auto &shape_ax : v["shape"].GetArray()) shape.push_back(shape_ax.GetUint64()); + if (std::string(v.at("_tenf_type").template get()) == "array") { + std::vector shape = v.at("shape").template get>(); - std::vector strides; - for (auto &stride : v["strides"].GetArray()) strides.push_back(stride.GetUint64()); + std::vector strides = v.at("strides").template get>(); if (strides.size() != shape.size()) throw format_error("mismatched strides and shape"); - std::string dtype(v["dtype"].GetString()); - size_t offset = v["base"].GetInt64(); + std::string dtype = v.at("dtype").template get(); + size_t offset = v.at("base").template get(); auto type_handler = detail::get_type_handler(dtype); return (*type_handler)(std::move(dtype), std::move(shape), std::move(strides), mmap, offset); @@ -215,13 +212,13 @@ TensorFile read(const std::string &path) if (mmap->length() < header_len + data_len + metadata_len) throw format_error("file not long enough"); if (metadata_len == 0) throw format_error("no JSON metadata found"); - rapidjson::Document metadata; - metadata.Parse((char *)data + header_len + data_len, metadata_len); - if (metadata.HasParseError()) { - std::stringstream err; - err << "could not parse JSON metadata at " << metadata.GetErrorOffset() << ": " - << GetParseError_En(metadata.GetParseError()); - throw format_error(err.str()); + const char *metadata_start = reinterpret_cast(data) + header_len + data_len; + const char *metadata_end = metadata_start + metadata_len; + nlohmann::json metadata; + try { + metadata = nlohmann::json::parse(metadata_start, metadata_end); + } catch (const nlohmann::json::parse_error &e) { + throw format_error(std::string{"could not parse JSON metadata: "} + e.what()); } return TensorFile(std::move(mmap), std::move(metadata)); diff --git a/visr_bear/src/tensorfile.hpp b/visr_bear/src/tensorfile.hpp index 149e230..3077a3b 100644 --- a/visr_bear/src/tensorfile.hpp +++ b/visr_bear/src/tensorfile.hpp @@ -5,7 +5,7 @@ #include #include -#include "rapidjson/document.h" +#include "nlohmann/json.hpp" namespace tensorfile { @@ -82,16 +82,16 @@ struct NDArrayT : public NDArray { class TensorFile { public: - TensorFile(std::shared_ptr mmap, rapidjson::Document metadata) + TensorFile(std::shared_ptr mmap, nlohmann::json metadata) : metadata(std::move(metadata)), mmap(std::move(mmap)) { } - rapidjson::Document metadata; + nlohmann::json metadata; - std::shared_ptr unpack(const rapidjson::Value &v) const; + std::shared_ptr unpack(const nlohmann::json &v) const; template - std::shared_ptr> unpack(const rapidjson::Value &v) const + std::shared_ptr> unpack(const nlohmann::json &v) const { return std::dynamic_pointer_cast>(unpack(v)); } diff --git a/visr_bear/submodules/CMakeLists.txt b/visr_bear/submodules/CMakeLists.txt index 5183324..c4abe58 100644 --- a/visr_bear/submodules/CMakeLists.txt +++ b/visr_bear/submodules/CMakeLists.txt @@ -7,9 +7,3 @@ if(BEAR_USE_INTERNAL_LIBEAR) CACHE BOOL "Package and install libear") add_subdirectory(libear) endif() - -if(BEAR_USE_INTERNAL_RAPIDJSON) - add_library(RapidJSON INTERFACE) - target_include_directories( - RapidJSON INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/rapidjson/include) -endif() diff --git a/visr_bear/submodules/rapidjson b/visr_bear/submodules/rapidjson deleted file mode 160000 index f54b0e4..0000000 --- a/visr_bear/submodules/rapidjson +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f54b0e47a08782a6131cc3d60f94d038fa6e0a51 diff --git a/visr_bear/test/benchmark.cpp b/visr_bear/test/benchmark.cpp index 4c83072..fcc2035 100644 --- a/visr_bear/test/benchmark.cpp +++ b/visr_bear/test/benchmark.cpp @@ -6,13 +6,9 @@ #include "bear/api.hpp" #include "ear_bits/hoa.hpp" +#include "nlohmann/json.hpp" #include "test_config.h" -#define RAPIDJSON_HAS_STDSTRING 1 -#include "rapidjson/document.h" -#include "rapidjson/ostreamwrapper.h" -#include "rapidjson/writer.h" - #if defined(__unix__) || defined(__unix) #define RT_SCHEDULING_POSIX #endif @@ -23,6 +19,7 @@ #endif using namespace bear; +using json = nlohmann::json; /// set high-priority RT scheduling; restore after class SetSchedRT { @@ -204,39 +201,29 @@ void run_benchmark_print(const BenchmarkConfig &c, size_t run) { std::vector times = run_benchmark(c); - rapidjson::Document d(rapidjson::kObjectType); - - rapidjson::Value config_json(rapidjson::kObjectType); - config_json.AddMember("update_every_time", c.update_every_time, d.GetAllocator()); - config_json.AddMember("head_track", c.head_track, d.GetAllocator()); - config_json.AddMember("extent", c.extent, d.GetAllocator()); - config_json.AddMember("hoa_channels_per_block", c.hoa_channels_per_block, d.GetAllocator()); + json data_json(json::value_t::object); - config_json.AddMember("sample_rate", c.config.get_sample_rate(), d.GetAllocator()); - config_json.AddMember( - "data_path", rapidjson::Value(c.config.get_data_path(), d.GetAllocator()), d.GetAllocator()); - config_json.AddMember("period_size", c.config.get_period_size(), d.GetAllocator()); - config_json.AddMember("fft_implementation", - rapidjson::Value(c.config.get_fft_implementation(), d.GetAllocator()), - d.GetAllocator()); - config_json.AddMember("num_objects_channels", c.config.get_num_objects_channels(), d.GetAllocator()); - config_json.AddMember( - "num_direct_speakers_channels", c.config.get_num_direct_speakers_channels(), d.GetAllocator()); - config_json.AddMember("num_hoa_channels", c.config.get_num_hoa_channels(), d.GetAllocator()); + json config_json(json::value_t::object); + config_json["update_every_time"] = c.update_every_time; + config_json["head_track"] = c.head_track; + config_json["extent"] = c.extent; + config_json["hoa_channels_per_block"] = c.hoa_channels_per_block; - d.AddMember("config", std::move(config_json), d.GetAllocator()); + config_json["sample_rate"] = c.config.get_sample_rate(); + config_json["data_path"] = c.config.get_data_path(); + config_json["period_size"] = c.config.get_period_size(); + config_json["fft_implementation"] = c.config.get_fft_implementation(); + config_json["num_objects_channels"] = c.config.get_num_objects_channels(); + config_json["num_direct_speakers_channels"] = c.config.get_num_direct_speakers_channels(); + config_json["num_hoa_channels"] = c.config.get_num_hoa_channels(); - d.AddMember("run", run, d.GetAllocator()); + data_json["config"] = std::move(config_json); - rapidjson::Value times_json(rapidjson::kArrayType); - for (double time : times) times_json.PushBack(time, d.GetAllocator()); + data_json["run"] = run; - d.AddMember("times", std::move(times_json), d.GetAllocator()); + data_json["times"] = times; - rapidjson::OStreamWrapper osw(std::cout); - rapidjson::Writer writer(osw); - d.Accept(writer); - std::cout << "\n"; + std::cout << data_json << "\n"; } int main(int, char **) diff --git a/visr_bear/test/test_tensorfile.cpp b/visr_bear/test/test_tensorfile.cpp index 4d531fd..078d4be 100644 --- a/visr_bear/test/test_tensorfile.cpp +++ b/visr_bear/test/test_tensorfile.cpp @@ -5,7 +5,7 @@ using namespace tensorfile; template -void check_one_multi_dim(const TensorFile &tenf, const rapidjson::Value &v) +void check_one_multi_dim(const TensorFile &tenf, const nlohmann::json &v) { auto ndarray = tenf.unpack(v); auto ndarray_f = std::dynamic_pointer_cast>(ndarray);