Skip to content

Commit 6127e89

Browse files
authored
Merge pull request #21 from ebu/switch_to_nlohmann_json
Switch to nlohmann json
2 parents b3f17f2 + edd038c commit 6127e89

16 files changed

+107
-140
lines changed

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- run: nix build .#bear --no-link
2121

2222
- run: nix build .#bear_docker -o bear_docker.tar.gz
23-
- uses: actions/upload-artifact@v3
23+
- uses: actions/upload-artifact@v4
2424
with:
2525
name: bear_docker.tar.gz
2626
path: bear_docker.tar.gz

.gitmodules

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
[submodule "visr_bear/submodules/rapidjson"]
2-
path = visr_bear/submodules/rapidjson
3-
url = https://github.com/Tencent/rapidjson.git
41
[submodule "visr_bear/submodules/libear"]
52
path = visr_bear/submodules/libear
63
url = https://github.com/ebu/libear.git

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ This is in the `visr_bear` subdirectory:
155155

156156
cd visr_bear
157157

158-
The process is the same as for VISR; to configure:
158+
Most dependencies are provided through submodules (some via submodules of submodules). The exception to this is the [nlohmann JSON library](https://github.com/nlohmann/json) which should be installed seperately. Cmake will attempt to locate this using `find_package`.
159+
160+
The build process is the same as for VISR; to configure:
159161

160162
cmake -B build .
161163

nix/visr_bear.nix

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{ lib, stdenv, src, cmake, ninja, boost, python, visr ? null, visr_python ? null, data_files, eigen, libear, rapidjson, enable_python ? true }:
1+
{ lib, stdenv, src, cmake, ninja, boost, python, visr ? null, visr_python ? null, data_files, eigen, libear, nlohmann_json, enable_python ? true }:
22
stdenv.mkDerivation rec {
33
name = "visr_bear";
44
inherit src;
@@ -8,15 +8,14 @@ stdenv.mkDerivation rec {
88
boost
99
eigen
1010
libear
11-
rapidjson
11+
nlohmann_json
1212
] ++ (if enable_python then [ python visr_python ] else [ visr ]);
1313
cmakeFlags = [
1414
"-DBEAR_UNIT_TESTS=true"
1515
"-DBEAR_DATA_PATH_DEFAULT=${data_files.default.file}"
1616
"-DBEAR_DATA_PATH_DEFAULT_SMALL=${data_files.default_small.file}"
1717
"-DBEAR_SYMLINK_DATA_FILES=true"
1818
"-DBEAR_USE_INTERNAL_LIBEAR=false"
19-
"-DBEAR_USE_INTERNAL_RAPIDJSON=false"
2019
] ++ lib.optional (!enable_python) "-DBUILD_PYTHON_BINDINGS=false";
2120
preConfigure = ''cmakeFlags="$cmakeFlags -DBEAR_PYTHON_SITE_PACKAGES=$out/${python.sitePackages}"'';
2221

visr_bear/CMakeLists.txt

+1-12
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,7 @@ if(NOT BEAR_USE_INTERNAL_LIBEAR)
9090
find_package(Eigen3 REQUIRED)
9191
endif()
9292

93-
option(
94-
BEAR_USE_INTERNAL_RAPIDJSON
95-
"should we use our own version of RapidJSON, or find one with find_package?"
96-
ON)
97-
if(NOT BEAR_USE_INTERNAL_RAPIDJSON)
98-
find_package(RapidJSON REQUIRED)
99-
if(NOT TARGET RapidJSON)
100-
# rapidjson cmake currently doesn't export a target
101-
add_library(RapidJSON INTERFACE)
102-
target_include_directories(RapidJSON INTERFACE ${RAPIDJSON_INCLUDE_DIRS})
103-
endif()
104-
endif()
93+
find_package(nlohmann_json 3.11.0 REQUIRED)
10594

10695
find_package(Boost REQUIRED)
10796

visr_bear/pythonwrappers/internals.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace python {
2020
{
2121
init_parameters();
2222

23-
// make Config convertable to ConfigImpl; this is what the internals use,
23+
// make Config convertible to ConfigImpl; this is what the internals use,
2424
// and this means that there's only one config interface on the python side
2525
// where we don't need to care about ABI compatibility.
2626
py::class_<ConfigImpl>(m, "_ConfigImpl").def(py::init([](const Config &config) {

visr_bear/src/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# internals (test,
33
add_library(bear-internals INTERFACE)
44
target_link_libraries(bear-internals INTERFACE mio)
5-
target_link_libraries(bear-internals INTERFACE RapidJSON)
5+
target_link_libraries(bear-internals INTERFACE nlohmann_json::nlohmann_json)
66
target_link_libraries(bear-internals INTERFACE Eigen3::Eigen)
77
target_include_directories(bear-internals INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
88

visr_bear/src/api.cpp

+20-20
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ Renderer::~Renderer() = default;
286286

287287
class DataFileMetadataImpl {
288288
public:
289-
rapidjson::Value metadata;
289+
nlohmann::json metadata;
290290

291291
bool has_metadata = false;
292292
std::string label;
@@ -295,34 +295,34 @@ class DataFileMetadataImpl {
295295
};
296296

297297
namespace {
298-
std::string parse_label(rapidjson::Value &metadata)
298+
std::string parse_label(const nlohmann::json &metadata)
299299
{
300-
auto it = metadata.FindMember("label");
301-
if (it == metadata.MemberEnd()) throw std::runtime_error("expected label");
300+
auto it = metadata.find("label");
301+
if (it == metadata.end()) throw std::runtime_error("expected label");
302302

303-
if (!it->value.IsString()) throw std::runtime_error("label should be a string");
303+
if (!it->is_string()) throw std::runtime_error("label should be a string");
304304

305-
return it->value.GetString();
305+
return it->template get<std::string>();
306306
}
307307

308-
std::string parse_description(rapidjson::Value &metadata)
308+
std::string parse_description(const nlohmann::json &metadata)
309309
{
310-
auto it = metadata.FindMember("description");
311-
if (it == metadata.MemberEnd()) return "";
310+
auto it = metadata.find("description");
311+
if (it == metadata.end()) return "";
312312

313-
if (!it->value.IsString()) throw std::runtime_error("description should be a string");
313+
if (!it->is_string()) throw std::runtime_error("description should be a string");
314314

315-
return it->value.GetString();
315+
return it->template get<std::string>();
316316
}
317317

318-
bool parse_released(rapidjson::Value &metadata)
318+
bool parse_released(const nlohmann::json &metadata)
319319
{
320-
auto it = metadata.FindMember("released");
321-
if (it == metadata.MemberEnd()) throw std::runtime_error("expected released");
320+
auto it = metadata.find("released");
321+
if (it == metadata.end()) throw std::runtime_error("expected released");
322322

323-
if (!it->value.IsBool()) throw std::runtime_error("released should be a bool");
323+
if (!it->is_boolean()) throw std::runtime_error("released should be a bool");
324324

325-
return it->value.GetBool();
325+
return it->template get<bool>();
326326
}
327327
} // namespace
328328

@@ -338,13 +338,13 @@ DataFileMetadata DataFileMetadata::read_from_file(const std::string &path)
338338

339339
auto impl = std::make_unique<DataFileMetadataImpl>();
340340

341-
auto it = tf.metadata.FindMember("metadata");
342-
if (it != tf.metadata.MemberEnd()) {
343-
if (!it->value.IsObject()) throw std::runtime_error("data file metadata should be object");
341+
auto it = tf.metadata.find("metadata");
342+
if (it != tf.metadata.end()) {
343+
if (!it->is_object()) throw std::runtime_error("data file metadata should be object");
344344

345345
impl->has_metadata = true;
346346

347-
impl->metadata = it->value;
347+
impl->metadata = *it;
348348

349349
impl->label = parse_label(impl->metadata);
350350
impl->description = parse_description(impl->metadata);

visr_bear/src/data_file.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
namespace bear {
66
int get_data_file_version(tensorfile::TensorFile &tf)
77
{
8-
auto it = tf.metadata.FindMember("bear_data_version");
9-
if (it == tf.metadata.MemberEnd()) return 0;
8+
auto it = tf.metadata.find("bear_data_version");
9+
if (it == tf.metadata.end()) return 0;
1010

11-
if (!it->value.IsInt()) throw std::runtime_error("version must be an integer");
11+
if (!it->is_number_integer()) throw std::runtime_error("version must be an integer");
1212

13-
return it->value.GetInt();
13+
return it->template get<int>();
1414
}
1515

1616
void check_data_file_version(tensorfile::TensorFile &tf)

visr_bear/src/panner.cpp

+36-33
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,43 @@
33
#include "data_file.hpp"
44
#include "utils.hpp"
55

6+
using json = nlohmann::json;
7+
68
namespace {
7-
ear::PolarPosition load_position(const rapidjson::Value &position_j)
9+
ear::PolarPosition load_position(const json &position_j)
810
{
9-
return {position_j["azimuth"].GetDouble(),
10-
position_j["elevation"].GetDouble(),
11-
position_j["distance"].GetDouble()};
11+
return {position_j.at("azimuth").template get<double>(),
12+
position_j.at("elevation").template get<double>(),
13+
position_j.at("distance").template get<double>()};
1214
}
1315

14-
ear::Channel load_channel(const rapidjson::Value &channel_j)
16+
ear::Channel load_channel(const json &channel_j)
1517
{
1618
ear::Channel channel;
17-
channel.name(channel_j["name"].GetString());
19+
channel.name(channel_j.at("name").template get<std::string>());
1820

19-
channel.polarPosition(load_position(channel_j["polar_position"]));
20-
channel.polarPositionNominal(load_position(channel_j["polar_nominal_position"]));
21+
channel.polarPosition(load_position(channel_j.at("polar_position")));
22+
channel.polarPositionNominal(load_position(channel_j.at("polar_nominal_position")));
2123

22-
const auto &az_range_j = channel_j["az_range"].GetArray();
23-
channel.azimuthRange({{az_range_j[0].GetDouble(), az_range_j[1].GetDouble()}});
24+
auto az_range = channel_j.at("az_range").template get<std::pair<double, double>>();
25+
channel.azimuthRange(az_range);
2426

25-
const auto &el_range_j = channel_j["el_range"].GetArray();
26-
channel.elevationRange({{el_range_j[0].GetDouble(), el_range_j[1].GetDouble()}});
27+
auto el_range = channel_j.at("el_range").template get<std::pair<double, double>>();
28+
channel.elevationRange(el_range);
2729

28-
channel.isLfe(channel_j["is_lfe"].GetBool());
30+
channel.isLfe(channel_j.at("is_lfe").get<bool>());
2931

3032
return channel;
3133
}
3234

33-
ear::Layout load_layout(const rapidjson::Value &layout_j)
35+
ear::Layout load_layout(const json &layout_j)
3436
{
37+
const json::array_t &channels_j = layout_j.at("channels").template get_ref<const json::array_t &>();
38+
3539
std::vector<ear::Channel> channels;
36-
for (auto &channel_j : layout_j["channels"].GetArray()) {
37-
channels.push_back(load_channel(channel_j));
38-
}
40+
for (const auto &channel_j : channels_j) channels.push_back(load_channel(channel_j));
3941

40-
return {layout_j["name"].GetString(), channels};
42+
return {layout_j.at("name").template get<std::string>(), channels};
4143
}
4244

4345
void check(bool x, const std::string &message)
@@ -55,13 +57,13 @@ Panner::Panner(const std::string &brir_file_name)
5557

5658
check_data_file_version(tf);
5759

58-
views = tf.unpack<float>(tf.metadata["views"]);
59-
brirs = tf.unpack<float>(tf.metadata["brirs"]);
60-
delays = tf.unpack<float>(tf.metadata["delays"]);
61-
decorrelation_filters = tf.unpack<float>(tf.metadata["decorrelation_filters"]);
62-
fs = tf.metadata["fs"].GetDouble();
63-
decorrelation_delay_ = tf.metadata["decorrelation_delay"].GetDouble();
64-
front_loudspeaker_ = tf.metadata["front_loudspeaker"].GetUint();
60+
views = tf.unpack<float>(tf.metadata.at("views"));
61+
brirs = tf.unpack<float>(tf.metadata.at("brirs"));
62+
delays = tf.unpack<float>(tf.metadata.at("delays"));
63+
decorrelation_filters = tf.unpack<float>(tf.metadata.at("decorrelation_filters"));
64+
fs = tf.metadata.at("fs").template get<double>();
65+
decorrelation_delay_ = tf.metadata.at("decorrelation_delay").template get<double>();
66+
front_loudspeaker_ = tf.metadata.at("front_loudspeaker").template get<size_t>();
6567

6668
// check and unpack dimensions
6769
check(views->ndim() == 2, "views must have 2 dimensions");
@@ -93,11 +95,11 @@ Panner::Panner(const std::string &brir_file_name)
9395

9496
// load layout
9597
ear::Layout layout;
96-
if (tf.metadata["layout"].IsString()) {
97-
std::string layout_name = tf.metadata["layout"].GetString();
98+
if (tf.metadata.at("layout").is_string()) {
99+
std::string layout_name = tf.metadata.at("layout").template get<std::string>();
98100
layout = ear::getLayout(layout_name).withoutLfe();
99101
} else {
100-
layout = load_layout(tf.metadata["layout"]);
102+
layout = load_layout(tf.metadata.at("layout"));
101103
}
102104

103105
// make gain calculators
@@ -110,9 +112,9 @@ Panner::Panner(const std::string &brir_file_name)
110112
temp_direct_diffuse.resize(num_gains() * 2);
111113
temp_direct_speakers.resize(num_gains());
112114

113-
if (tf.metadata.HasMember("gain_norm_quick")) {
115+
if (tf.metadata.contains("gain_norm_quick")) {
114116
gain_comp_type = GainCompType::QUICK;
115-
gain_comp_factors = tf.unpack<float>(tf.metadata["gain_norm_quick"]["factors"]);
117+
gain_comp_factors = tf.unpack<float>(tf.metadata.at("gain_norm_quick").at("factors"));
116118
check(gain_comp_factors->ndim() == 5, "gain comp factors must have 5 dimensions");
117119
// TODO: check max delays?
118120
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)
123125
check(gain_comp_factors->shape(4) == 2, "gain comp factors axis 4 is wrong size");
124126
}
125127

126-
check(tf.metadata.HasMember("hoa"), "HOA decoder not present");
127-
hoa_irs = tf.unpack<float>(tf.metadata["hoa"]["irs"]);
128+
check(tf.metadata.contains("hoa"), "HOA decoder not present");
129+
hoa_irs = tf.unpack<float>(tf.metadata.at("hoa").at("irs"));
128130
check(hoa_irs->ndim() == 3, "HOA decoder must have 3 dimensions");
129131
n_hoa_channels_ = hoa_irs->shape(0);
130132
check(hoa_irs->shape(1) == 2, "HOA decoder axis 1 is wrong size");
131133

132-
if (tf.metadata["hoa"].HasMember("delay")) hoa_delay_ = tf.metadata["hoa"]["delay"].GetDouble();
134+
if (tf.metadata.at("hoa").contains("delay"))
135+
hoa_delay_ = tf.metadata.at("hoa").at("delay").template get<double>();
133136

134137
hoa_order_ = ((size_t)std::round(std::sqrt(n_hoa_channels_))) - 1;
135138
size_t nch_for_order = (hoa_order_ + 1) * (hoa_order_ + 1);

visr_bear/src/tensorfile.cpp

+13-16
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#define WIN32_LEAN_AND_MEAN
77
#define NOMINMAX
88
#include "mio.hpp"
9-
#include "rapidjson/error/en.h"
109

1110
namespace tensorfile {
1211

@@ -176,19 +175,17 @@ namespace detail {
176175

177176
} // namespace detail
178177

179-
std::shared_ptr<NDArray> TensorFile::unpack(const rapidjson::Value &v) const
178+
std::shared_ptr<NDArray> TensorFile::unpack(const nlohmann::json &v) const
180179
{
181-
if (std::string(v["_tenf_type"].GetString()) == "array") {
182-
std::vector<size_t> shape;
183-
for (auto &shape_ax : v["shape"].GetArray()) shape.push_back(shape_ax.GetUint64());
180+
if (std::string(v.at("_tenf_type").template get<std::string>()) == "array") {
181+
std::vector<size_t> shape = v.at("shape").template get<std::vector<size_t>>();
184182

185-
std::vector<size_t> strides;
186-
for (auto &stride : v["strides"].GetArray()) strides.push_back(stride.GetUint64());
183+
std::vector<size_t> strides = v.at("strides").template get<std::vector<size_t>>();
187184

188185
if (strides.size() != shape.size()) throw format_error("mismatched strides and shape");
189186

190-
std::string dtype(v["dtype"].GetString());
191-
size_t offset = v["base"].GetInt64();
187+
std::string dtype = v.at("dtype").template get<std::string>();
188+
size_t offset = v.at("base").template get<size_t>();
192189

193190
auto type_handler = detail::get_type_handler(dtype);
194191
return (*type_handler)(std::move(dtype), std::move(shape), std::move(strides), mmap, offset);
@@ -215,13 +212,13 @@ TensorFile read(const std::string &path)
215212
if (mmap->length() < header_len + data_len + metadata_len) throw format_error("file not long enough");
216213
if (metadata_len == 0) throw format_error("no JSON metadata found");
217214

218-
rapidjson::Document metadata;
219-
metadata.Parse((char *)data + header_len + data_len, metadata_len);
220-
if (metadata.HasParseError()) {
221-
std::stringstream err;
222-
err << "could not parse JSON metadata at " << metadata.GetErrorOffset() << ": "
223-
<< GetParseError_En(metadata.GetParseError());
224-
throw format_error(err.str());
215+
const char *metadata_start = reinterpret_cast<const char *>(data) + header_len + data_len;
216+
const char *metadata_end = metadata_start + metadata_len;
217+
nlohmann::json metadata;
218+
try {
219+
metadata = nlohmann::json::parse(metadata_start, metadata_end);
220+
} catch (const nlohmann::json::parse_error &e) {
221+
throw format_error(std::string{"could not parse JSON metadata: "} + e.what());
225222
}
226223

227224
return TensorFile(std::move(mmap), std::move(metadata));

visr_bear/src/tensorfile.hpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include <string>
66
#include <vector>
77

8-
#include "rapidjson/document.h"
8+
#include "nlohmann/json.hpp"
99

1010
namespace tensorfile {
1111

@@ -82,16 +82,16 @@ struct NDArrayT : public NDArray {
8282

8383
class TensorFile {
8484
public:
85-
TensorFile(std::shared_ptr<MMap> mmap, rapidjson::Document metadata)
85+
TensorFile(std::shared_ptr<MMap> mmap, nlohmann::json metadata)
8686
: metadata(std::move(metadata)), mmap(std::move(mmap))
8787
{
8888
}
89-
rapidjson::Document metadata;
89+
nlohmann::json metadata;
9090

91-
std::shared_ptr<NDArray> unpack(const rapidjson::Value &v) const;
91+
std::shared_ptr<NDArray> unpack(const nlohmann::json &v) const;
9292

9393
template <typename T>
94-
std::shared_ptr<NDArrayT<T>> unpack(const rapidjson::Value &v) const
94+
std::shared_ptr<NDArrayT<T>> unpack(const nlohmann::json &v) const
9595
{
9696
return std::dynamic_pointer_cast<NDArrayT<T>>(unpack(v));
9797
}

0 commit comments

Comments
 (0)