EchoMap 2026-07-25 6d3977c
An experimental cross-platform digital signal processing application for sound-source localisation.
Loading...
Searching...
No Matches
JSONFullDeserialiser.cpp
1//
2// Created by owd on 07/07/2026.
3
4#if !defined(__EMSCRIPTEN__) || defined(__DOXYGEN__)
5
7
10#include "../../Project.hpp"
11#include "../../Signal.hpp"
14
18namespace
19{
20
21echomap::Worker* parent_worker = nullptr;
22
23auto get_signals(
24 simdjson::ondemand::object& root,
25 const echomap::Project& project,
28 echomap::id_type>& loaded
29)
30{
31 // Step 1. Create our factories which detain only the signal metadata (sample rate, etc.) and source information.
33 if (const auto error = root["signals"].get(factories))
34 return error;
35
36 /*
37 * Step 2. Group the factories by the source filenames of the signals they're constructing.
38 *
39 * The key of the map is the source filename, and the value is a bijective correspondence between the channel in the
40 * wave file and the factory designated to load the channel at the index.
41 *
42 * That is, the first slot owns the factory for constructing the signal on Channel 1, the second for Channel 2, etc.
43 * If there is a gap, the entry is the empty unique_ptr<SignalFactory>, such that it equals nullptr.
44 */
46
47 for (auto factory : factories | std::views::as_rvalue) {
48 const auto& signal = factory->observe_signal();
49 if (!loaded.emplace(signal.get_name(), signal.get_id()).second)
50 throw std::runtime_error(std::format("Project contains duplicate signal {}.", signal.get_name()));
51
52 if (signal.observe_source().has_value()) {
53 /*
54 * If the Signal has a Source, it comes from the filesystem. Register the Signal as a destination for its
55 * source file for its channel number.
56 */
57 auto& slot_vector = slots[signal.observe_source()->path.c_str()];
58 const auto channel_num = signal.observe_source()->channel;
59
60 // Reminder: channel numbers are 1-based.
61 if (channel_num > slot_vector.size())
62 slot_vector.resize(channel_num);
63 else if (slot_vector[channel_num - 1] != nullptr)
66 "Both signals {} and {} have requested the same channel {} from {}.",
67 slot_vector[channel_num - 1]->observe_signal().get_name(),
68 signal.get_name(),
69 channel_num,
70 signal.observe_source()->path.c_str()
71 )
72 );
73
74 slot_vector[channel_num - 1] = std::move(factory);
75 }
76 }
77
78 /*
79 * Step 3. Once we have the factories grouped by source, and put into the bijective correspondence with the channel
80 * numbers, submit a task to the thread-safe Worker for each distinct file. The task takes ownership of the
81 * factories.
82 *
83 * The results will own the signal objects produced by the factories, which can be added to the project by the
84 * EchoMap controller (or whoever is the nominated consumer).
85 */
86 if (!slots.empty()) {
87 if (parent_worker == nullptr)
90 "Need to despatch extra work to load {}, but a suitable Worker is unavailable.",
91 project.get_name()
92 )
93 );
94
95 for (auto&& [file_path, slot_vector] : slots)
96 parent_worker->submit(
97 std::make_unique<echomap::LoadSignalFileTask>(project.get_id(), file_path, std::move(slot_vector))
98 );
99 }
100
101 return simdjson::SUCCESS;
102}
103
104} // namespace
105
106namespace simdjson
107{
108
109template <typename simdjson_value>
110auto tag_invoke(
111 deserialize_tag /*unused*/,
112 simdjson_value& value,
113 echomap::Project& project
114)
115{
116 ondemand::object root;
117 auto error = echomap::JSONDeserialiserHelpers::get_root(root, value);
118 if (error)
119 return error;
120
121 // Metadata
122 if ((error = echomap::JSONDeserialiserHelpers::get_metadata(root, project)))
123 return error;
124
125 // Signals
127 if ((error = get_signals(root, project, signal_ids)))
128 return error;
129
130 // Sensors
132 if ((error = echomap::JSONDeserialiserHelpers::get_sensors(root, project, sensor_ids)))
133 return error;
134
135 // Channel Map
136 if ((error = echomap::JSONDeserialiserHelpers::get_mappings(root, project, signal_ids, sensor_ids)))
137 return error;
138
139 return SUCCESS;
140}
141
142}
143
144namespace echomap
145{
146
147std::unique_ptr<Project> JSONFullDeserialiser::deserialise_project(
148 const std::filesystem::path& path,
149 Worker* const worker
150)
151{
152 parent_worker = worker;
153 const auto json = simdjson::padded_string::load(path.c_str());
154 auto doc = parser.iterate(json);
155 auto project = std::make_unique<Project>();
156
157 if (const auto error = doc.get(*project); error)
158 throw std::runtime_error(
160 "Could not load Project at {} due to error: {}",
161 path.c_str(),
162 simdjson::error_message(error)
163 )
164 );
165
166 return project;
167}
168
169} // namespace echomap
170
171#endif // __EMSCRIPTEN__
JSONDeserialiserHelpers specification.
JSONFullDeserialiser specification.
LoadSignalFileTask specification.
Wave file specification.
Audio signal class specification.
Worker specification.
A Worker provides an encapsulated thread-safe despatch model for submitting work and reviewing result...
Definition Worker.hpp:45
T empty(T... args)
T format(T... args)
T make_unique(T... args)
The main EchoMap outermost namespace for all non-exported symbols.