EchoMap 2026-07-25 6d3977c
An experimental cross-platform digital signal processing application for sound-source localisation.
Loading...
Searching...
No Matches
JSONSerialiser.cpp
1//
2// Created by owd on 07/07/2026.
3//
4
5#include "JSONSerialiser.hpp"
6
7#include "../Project.hpp"
8#include "../Sensor.hpp"
9#include "../Signal.hpp"
10
11namespace simdjson
12{
13
14template <typename builder_type>
15void tag_invoke(
16 serialize_tag,
17 builder_type& builder,
18 const echomap::Project& project
19)
20{
21 static constexpr unsigned int schema_version = 1;
22 bool first_element = true;
23
24 builder.start_object();
25 builder.append_key_value("schema_version", schema_version);
26 builder.append_comma();
27
28 // Project Metadata
29 builder.escape_and_append_with_quotes("metadata");
30 builder.append_colon();
31 builder.start_object();
32 builder.append_key_value("name", project.get_name());
33 builder.end_object();
34 builder.append_comma();
35
36 // Signals
37 builder.escape_and_append_with_quotes("signals");
38 builder.append_colon();
39 builder.start_array();
40 for (const auto& signal : project.observe_signals()) {
41 if (!first_element)
42 builder.append_comma();
43
44 builder.append(signal);
45 first_element = false;
46 }
47 first_element = true;
48 builder.end_array();
49 builder.append_comma();
50
51 // Sensors
52 builder.escape_and_append_with_quotes("sensors");
53 builder.append_colon();
54 builder.start_array();
55 for (const auto& sensor : project.observe_sensors()) {
56 if (!first_element)
57 builder.append_comma();
58
59 builder.append(sensor);
60 first_element = false;
61 }
62 first_element = true;
63 builder.end_array();
64 builder.append_comma();
65
66 // Channel Mappings
67 builder.escape_and_append_with_quotes("mappings");
68 builder.append_colon();
69 builder.start_array();
70 for (const auto [signal, sensor] : project.observe_associations()) {
71 if (!first_element)
72 builder.append_comma();
73
74 builder.start_object();
75 builder.append_key_value("sensor", sensor.get_name());
76 builder.append_comma();
77 builder.append_key_value("signal", signal.get_name());
78 builder.end_object();
79
80 first_element = false;
81 }
82
83 builder.end_array();
84 builder.end_object();
85}
86
87template <typename builder_type>
88void tag_invoke(
89 serialize_tag,
90 builder_type& builder,
91 const echomap::Sensor& sensor
92)
93{
94 builder.start_object();
95
96 // Sensor Metadata
97 builder.append_key_value("name", sensor.get_name());
98 builder.append_comma();
99
100 // Position
101 builder.escape_and_append_with_quotes("position");
102 builder.append_colon();
103 builder.start_object();
104 builder.append_key_value("x", sensor.position.x);
105 builder.append_comma();
106 builder.append_key_value("y", sensor.position.y);
107 builder.append_comma();
108 builder.append_key_value("z", sensor.position.z);
109 builder.end_object();
110 builder.append_comma();
111
112 // Colour
113 builder.escape_and_append_with_quotes("colour");
114 builder.append_colon();
115 builder.start_object();
116 builder.append_key_value("r", sensor.colour.r);
117 builder.append_comma();
118 builder.append_key_value("g", sensor.colour.g);
119 builder.append_comma();
120 builder.append_key_value("b", sensor.colour.b);
121 builder.append_comma();
122 builder.append_key_value("a", sensor.colour.a);
123 builder.end_object();
124
125 builder.end_object();
126}
127
128template <typename builder_type>
129void tag_invoke(
130 serialize_tag,
131 builder_type& builder,
132 const echomap::Signal& signal
133)
134{
135 builder.start_object();
136
137 // Signal Metadata
138 builder.append_key_value("name", signal.get_name());
139 builder.append_comma();
140
141 // Source
142 builder.escape_and_append_with_quotes("source");
143 builder.append_colon();
144 builder.start_object();
145
146 // ... for clean external sources, forward file path and channel metadata.
147 if (signal.observe_source().has_value() && !signal.observe_source()->dirty) {
148 const auto& source = *signal.observe_source();
149 builder.append_key_value("kind", "filesystem");
150 builder.append_comma();
151 builder.append_key_value("path", std::filesystem::absolute(source.path).c_str());
152 builder.append_comma();
153 builder.append_key_value("channel", source.channel);
154 }
155
156 // ... for embedded or dirty sources, append samples in situ.
157 else {
158 builder.append_key_value("kind", signal.is_uniformly_sampled() ? "embeddedUniform" : "embeddedVariable");
159 builder.append_comma();
160
161 if (signal.is_uniformly_sampled() || signal.get_time_offset() != echomap::Signal::Sample::TimeT{0} ||
162 signal.get_sample_count() != 0) {
163 builder.append_key_value("time_offset", signal.get_time_offset());
164 builder.append_comma();
165 builder.append_key_value("sample_rate", signal.get_sample_rate());
166 builder.append_comma();
167 }
168
169 builder.append_key_value("sample_count", signal.get_sample_count());
170 builder.append_comma();
171
172 builder.escape_and_append_with_quotes("samples");
173 builder.append_colon();
174 builder.start_array();
175
176 bool first_element = true;
177
178 if (signal.is_uniformly_sampled())
179 for (const auto sample : signal) {
180 if (!first_element)
181 builder.append_comma();
182
183 builder.append(sample);
184 first_element = false;
185 }
186 else
187 for (const auto [time, amplitude] : signal.timed_samples()) {
188 if (!first_element)
189 builder.append_comma();
190
191 builder.start_object();
192 builder.append_key_value("time", time);
193 builder.append_comma();
194 builder.append_key_value("amplitude", amplitude);
195 builder.end_object();
196
197 first_element = false;
198 }
199
200 builder.end_array();
201 }
202
203 builder.end_object();
204 builder.end_object();
205}
206
207} // namespace simdjson
208
209namespace echomap
210{
211
212std::string_view JSONSerialiser::serialise_project(
213 const Project& project
214)
215{
216 sb.clear();
217 sb.append(project);
218 return sb.view(); // NOLINT(*-return-braced-init-list) - Warning comes from inlined library code.
219}
220
221std::string JSONSerialiser::pretty_print(
222 const std::string_view data
223)
224{
225 // ReSharper disable once CppLocalVariableMayBeConst
226 simdjson::fractured_json_options options{};
227
228 return simdjson::fractured_json_string(data, options);
229}
230
231} // namespace echomap
Audio signal class specification.
T absolute(T... args)
auto observe_signals() const noexcept
Provides a transformed view for stored Signal objects in the Project.
Definition Project.hpp:97
auto observe_associations() const
Provides a transformed view for defined channel mappings.
Definition Project.hpp:151
auto observe_sensors() const noexcept
Provides a transformed view for stored Sensor objects in the Project.
Definition Project.hpp:137
The main EchoMap outermost namespace for all non-exported symbols.
T signal(T... args)
float b
The blue component in the range [0, 1].
Definition Colour.hpp:23
float a
The alpha extent in the range [0, 1].
Definition Colour.hpp:24
float g
The green component in the range [0, 1].
Definition Colour.hpp:22
float r
The red component in the range [0, 1].
Definition Colour.hpp:21
float y
The Y co-ordinate.
Definition Position.hpp:22
float x
The X co-ordinate.
Definition Position.hpp:21
float z
The Z co-ordinate.
Definition Position.hpp:23
float TimeT
Type for sample times.
Definition Signal.hpp:47