dspx 1.2.4 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -78
- package/binding.gyp +10 -0
- package/dist/FilterBankDesign.d.ts +233 -0
- package/dist/FilterBankDesign.d.ts.map +1 -0
- package/dist/FilterBankDesign.js +247 -0
- package/dist/FilterBankDesign.js.map +1 -0
- package/dist/advanced-dsp.d.ts +6 -6
- package/dist/advanced-dsp.d.ts.map +1 -1
- package/dist/advanced-dsp.js +35 -12
- package/dist/advanced-dsp.js.map +1 -1
- package/dist/backends.d.ts +0 -103
- package/dist/backends.d.ts.map +1 -1
- package/dist/backends.js +0 -217
- package/dist/backends.js.map +1 -1
- package/dist/bindings.d.ts +270 -17
- package/dist/bindings.d.ts.map +1 -1
- package/dist/bindings.js +566 -43
- package/dist/bindings.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +67 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +38 -8
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +84 -26
- package/dist/utils.js.map +1 -1
- package/package.json +1 -2
- package/prebuilds/win32-x64/dspx.node +0 -0
- package/scripts/add-dispose-to-tests.js +145 -0
- package/src/native/DspPipeline.cc +699 -126
- package/src/native/DspPipeline.h +13 -0
- package/src/native/FilterBankDesignBindings.cc +241 -0
- package/src/native/IDspStage.h +24 -0
- package/src/native/UtilityBindings.cc +130 -0
- package/src/native/adapters/AmplifyStage.h +148 -0
- package/src/native/adapters/ClipDetectionStage.h +15 -4
- package/src/native/adapters/ConvolutionStage.h +101 -0
- package/src/native/adapters/CumulativeMovingAverageStage.h +264 -0
- package/src/native/adapters/DecimatorStage.h +80 -0
- package/src/native/adapters/DifferentiatorStage.h +13 -0
- package/src/native/adapters/ExponentialMovingAverageStage.h +290 -0
- package/src/native/adapters/FilterBankStage.cc +336 -0
- package/src/native/adapters/FilterBankStage.h +170 -0
- package/src/native/adapters/FilterStage.cc +122 -0
- package/src/native/adapters/FilterStage.h +4 -0
- package/src/native/adapters/HilbertEnvelopeStage.h +55 -0
- package/src/native/adapters/IntegratorStage.h +15 -0
- package/src/native/adapters/InterpolatorStage.h +51 -0
- package/src/native/adapters/LinearRegressionStage.h +40 -0
- package/src/native/adapters/LmsStage.h +63 -0
- package/src/native/adapters/MeanAbsoluteValueStage.h +76 -0
- package/src/native/adapters/MovingAverageStage.h +119 -0
- package/src/native/adapters/PeakDetectionStage.h +53 -0
- package/src/native/adapters/RectifyStage.h +14 -0
- package/src/native/adapters/ResamplerStage.h +67 -0
- package/src/native/adapters/RlsStage.h +76 -0
- package/src/native/adapters/RmsStage.h +72 -0
- package/src/native/adapters/SnrStage.h +45 -0
- package/src/native/adapters/SquareStage.h +78 -0
- package/src/native/adapters/SscStage.h +65 -0
- package/src/native/adapters/StftStage.h +62 -0
- package/src/native/adapters/VarianceStage.h +59 -0
- package/src/native/adapters/WampStage.h +59 -0
- package/src/native/adapters/WaveformLengthStage.h +51 -0
- package/src/native/adapters/ZScoreNormalizeStage.h +64 -0
- package/src/native/core/CumulativeMovingAverageFilter.h +123 -0
- package/src/native/core/ExponentialMovingAverageFilter.h +129 -0
- package/src/native/core/FilterBankDesign.h +266 -0
- package/src/native/core/Policies.h +124 -0
- package/src/native/utils/CircularBufferArray.cc +2 -1
- package/src/native/utils/SimdOps.h +67 -0
- package/src/native/utils/Toon.h +195 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include "../IDspStage.h"
|
|
4
4
|
#include "../core/MovingAbsoluteValueFilter.h" // Include the new core filter
|
|
5
|
+
#include "../utils/Toon.h"
|
|
5
6
|
#include <vector>
|
|
6
7
|
#include <stdexcept>
|
|
7
8
|
#include <cmath>
|
|
@@ -168,10 +169,85 @@ namespace dsp::adapters
|
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
|
|
172
|
+
inline void serializeToon(dsp::toon::Serializer &s) const override
|
|
173
|
+
{
|
|
174
|
+
// Write mode
|
|
175
|
+
s.writeInt32(static_cast<int32_t>(m_mode));
|
|
176
|
+
|
|
177
|
+
// Write configuration parameters
|
|
178
|
+
s.writeInt32(static_cast<int32_t>(m_window_size));
|
|
179
|
+
s.writeFloat(static_cast<float>(m_window_duration_ms));
|
|
180
|
+
s.writeBool(m_is_initialized);
|
|
181
|
+
|
|
182
|
+
// Write number of channels and filter states
|
|
183
|
+
s.writeInt32(static_cast<int32_t>(m_filters.size()));
|
|
184
|
+
|
|
185
|
+
// For Moving mode, serialize each filter's state
|
|
186
|
+
if (m_mode == MavMode::Moving)
|
|
187
|
+
{
|
|
188
|
+
for (const auto &filter : m_filters)
|
|
189
|
+
{
|
|
190
|
+
auto [bufferData, runningSum] = filter.getState();
|
|
191
|
+
s.writeFloatArray(bufferData);
|
|
192
|
+
s.writeFloat(runningSum);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
inline void deserializeToon(dsp::toon::Deserializer &d) override
|
|
198
|
+
{
|
|
199
|
+
// Read mode
|
|
200
|
+
int32_t mode_int = d.readInt32();
|
|
201
|
+
if (mode_int < 0 || mode_int > 1)
|
|
202
|
+
throw std::runtime_error("Invalid mode in MeanAbsoluteValueStage deserialization");
|
|
203
|
+
m_mode = static_cast<MavMode>(mode_int);
|
|
204
|
+
|
|
205
|
+
// Read configuration parameters
|
|
206
|
+
int32_t window_size = d.readInt32();
|
|
207
|
+
if (window_size < 0)
|
|
208
|
+
throw std::runtime_error("Invalid window_size in MeanAbsoluteValueStage deserialization");
|
|
209
|
+
m_window_size = static_cast<size_t>(window_size);
|
|
210
|
+
|
|
211
|
+
m_window_duration_ms = static_cast<double>(d.readFloat());
|
|
212
|
+
m_is_initialized = d.readBool();
|
|
213
|
+
|
|
214
|
+
// Read number of channels
|
|
215
|
+
int32_t num_channels = d.readInt32();
|
|
216
|
+
if (num_channels < 0)
|
|
217
|
+
throw std::runtime_error("Invalid num_channels in MeanAbsoluteValueStage deserialization");
|
|
218
|
+
|
|
219
|
+
// Reconstruct filters
|
|
220
|
+
m_filters.clear();
|
|
221
|
+
|
|
222
|
+
if (m_mode == MavMode::Moving)
|
|
223
|
+
{
|
|
224
|
+
for (int32_t i = 0; i < num_channels; ++i)
|
|
225
|
+
{
|
|
226
|
+
// Read buffer data
|
|
227
|
+
std::vector<float> bufferData = d.readFloatArray();
|
|
228
|
+
|
|
229
|
+
// Read running sum
|
|
230
|
+
float runningSum = d.readFloat();
|
|
231
|
+
|
|
232
|
+
// Create filter and restore state
|
|
233
|
+
if (m_window_duration_ms > 0.0)
|
|
234
|
+
{
|
|
235
|
+
m_filters.emplace_back(m_window_size, m_window_duration_ms);
|
|
236
|
+
}
|
|
237
|
+
else
|
|
238
|
+
{
|
|
239
|
+
m_filters.emplace_back(m_window_size);
|
|
240
|
+
}
|
|
241
|
+
m_filters.back().setState(bufferData, runningSum);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
171
246
|
// Reset all filters to initial state
|
|
172
247
|
void reset() override
|
|
173
248
|
{
|
|
174
249
|
for (auto &filter : m_filters)
|
|
250
|
+
|
|
175
251
|
{
|
|
176
252
|
filter.clear();
|
|
177
253
|
}
|
|
@@ -177,6 +177,125 @@ namespace dsp::adapters
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
// TOON Binary Serialization - CRITICAL OPTIMIZATION for large buffers
|
|
181
|
+
void serializeToon(dsp::toon::Serializer &serializer) const override
|
|
182
|
+
{
|
|
183
|
+
serializer.startObject();
|
|
184
|
+
|
|
185
|
+
// 1. Mode
|
|
186
|
+
serializer.writeString("mode");
|
|
187
|
+
serializer.writeString((m_mode == AverageMode::Moving) ? "moving" : "batch");
|
|
188
|
+
|
|
189
|
+
if (m_mode == AverageMode::Moving)
|
|
190
|
+
{
|
|
191
|
+
// 2. Config
|
|
192
|
+
serializer.writeString("windowSize");
|
|
193
|
+
serializer.writeInt32(static_cast<int32_t>(m_window_size));
|
|
194
|
+
|
|
195
|
+
serializer.writeString("windowDuration");
|
|
196
|
+
serializer.writeDouble(m_window_duration_ms);
|
|
197
|
+
|
|
198
|
+
serializer.writeString("initialized");
|
|
199
|
+
serializer.writeBool(m_is_initialized);
|
|
200
|
+
|
|
201
|
+
// 3. Channels - Zero-copy-like binary arrays
|
|
202
|
+
serializer.writeString("channels");
|
|
203
|
+
serializer.startArray();
|
|
204
|
+
|
|
205
|
+
for (const auto &filter : m_filters)
|
|
206
|
+
{
|
|
207
|
+
auto [bufferData, runningSum] = filter.getState();
|
|
208
|
+
|
|
209
|
+
serializer.startObject();
|
|
210
|
+
|
|
211
|
+
// CRITICAL: Direct binary copy of float buffer (not text!)
|
|
212
|
+
serializer.writeString("buffer");
|
|
213
|
+
serializer.writeFloatArray(bufferData);
|
|
214
|
+
|
|
215
|
+
serializer.writeString("sum");
|
|
216
|
+
serializer.writeFloat(runningSum);
|
|
217
|
+
|
|
218
|
+
serializer.endObject();
|
|
219
|
+
}
|
|
220
|
+
serializer.endArray();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
serializer.endObject();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// TOON Binary Deserialization - CRITICAL OPTIMIZATION for large buffers
|
|
227
|
+
void deserializeToon(dsp::toon::Deserializer &deserializer) override
|
|
228
|
+
{
|
|
229
|
+
deserializer.consumeToken(dsp::toon::T_OBJECT_START);
|
|
230
|
+
|
|
231
|
+
// 1. Mode
|
|
232
|
+
std::string key = deserializer.readString(); // "mode"
|
|
233
|
+
std::string modeStr = deserializer.readString();
|
|
234
|
+
|
|
235
|
+
AverageMode newMode = (modeStr == "moving") ? AverageMode::Moving : AverageMode::Batch;
|
|
236
|
+
if (newMode != m_mode)
|
|
237
|
+
{
|
|
238
|
+
throw std::runtime_error("MovingAverage mode mismatch during TOON deserialization");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (modeStr == "moving")
|
|
242
|
+
{
|
|
243
|
+
// 2. Config
|
|
244
|
+
key = deserializer.readString(); // "windowSize"
|
|
245
|
+
int32_t windowSize = deserializer.readInt32();
|
|
246
|
+
|
|
247
|
+
key = deserializer.readString(); // "windowDuration"
|
|
248
|
+
double windowDuration = deserializer.readDouble();
|
|
249
|
+
|
|
250
|
+
key = deserializer.readString(); // "initialized"
|
|
251
|
+
bool initialized = deserializer.readBool();
|
|
252
|
+
|
|
253
|
+
// Validate window size
|
|
254
|
+
if (windowSize != static_cast<int32_t>(m_window_size))
|
|
255
|
+
{
|
|
256
|
+
throw std::runtime_error("MovingAverage window size mismatch during TOON deserialization");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
m_window_duration_ms = windowDuration;
|
|
260
|
+
m_is_initialized = initialized;
|
|
261
|
+
|
|
262
|
+
// 3. Channels
|
|
263
|
+
key = deserializer.readString(); // "channels"
|
|
264
|
+
deserializer.consumeToken(dsp::toon::T_ARRAY_START);
|
|
265
|
+
|
|
266
|
+
m_filters.clear();
|
|
267
|
+
|
|
268
|
+
while (deserializer.peekToken() != dsp::toon::T_ARRAY_END)
|
|
269
|
+
{
|
|
270
|
+
deserializer.consumeToken(dsp::toon::T_OBJECT_START);
|
|
271
|
+
|
|
272
|
+
// Buffer - CRITICAL: Direct binary memcpy (not parsing text!)
|
|
273
|
+
deserializer.readString(); // "buffer"
|
|
274
|
+
std::vector<float> bufferData = deserializer.readFloatArray();
|
|
275
|
+
|
|
276
|
+
// Sum
|
|
277
|
+
deserializer.readString(); // "sum"
|
|
278
|
+
float runningSum = deserializer.readFloat();
|
|
279
|
+
|
|
280
|
+
// Reconstruct filter with time-aware support
|
|
281
|
+
if (m_window_duration_ms > 0.0)
|
|
282
|
+
{
|
|
283
|
+
m_filters.emplace_back(m_window_size, m_window_duration_ms);
|
|
284
|
+
}
|
|
285
|
+
else
|
|
286
|
+
{
|
|
287
|
+
m_filters.emplace_back(m_window_size);
|
|
288
|
+
}
|
|
289
|
+
m_filters.back().setState(bufferData, runningSum);
|
|
290
|
+
|
|
291
|
+
deserializer.consumeToken(dsp::toon::T_OBJECT_END);
|
|
292
|
+
}
|
|
293
|
+
deserializer.consumeToken(dsp::toon::T_ARRAY_END);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
deserializer.consumeToken(dsp::toon::T_OBJECT_END);
|
|
297
|
+
}
|
|
298
|
+
|
|
180
299
|
private:
|
|
181
300
|
/**
|
|
182
301
|
* @brief Statelessly calculates the average for each channel
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
#include "../IDspStage.h"
|
|
4
4
|
#include "../core/PeakDetection.h"
|
|
5
|
+
#include "../utils/Toon.h"
|
|
5
6
|
#include <cmath>
|
|
6
7
|
#include <stdexcept>
|
|
7
8
|
#include <string>
|
|
@@ -143,6 +144,58 @@ namespace dsp::adapters
|
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
|
|
147
|
+
inline void serializeToon(dsp::toon::Serializer &s) const override
|
|
148
|
+
{
|
|
149
|
+
// Write configuration
|
|
150
|
+
s.writeFloat(m_threshold);
|
|
151
|
+
s.writeInt32(m_num_channels);
|
|
152
|
+
s.writeString(m_mode);
|
|
153
|
+
s.writeString(m_domain);
|
|
154
|
+
s.writeInt32(m_windowSize);
|
|
155
|
+
s.writeInt32(m_minPeakDistance);
|
|
156
|
+
|
|
157
|
+
// Write state (only for moving mode)
|
|
158
|
+
if (m_mode == "moving" && !m_prev_sample.empty())
|
|
159
|
+
{
|
|
160
|
+
s.writeFloatArray(m_prev_sample);
|
|
161
|
+
s.writeFloatArray(m_prev_prev_sample);
|
|
162
|
+
|
|
163
|
+
// Write peakCooldown as int array (convert to float array)
|
|
164
|
+
std::vector<float> cooldown_float(m_peakCooldown.begin(), m_peakCooldown.end());
|
|
165
|
+
s.writeFloatArray(cooldown_float);
|
|
166
|
+
}
|
|
167
|
+
else
|
|
168
|
+
{
|
|
169
|
+
// Write empty arrays
|
|
170
|
+
s.writeFloatArray(std::vector<float>());
|
|
171
|
+
s.writeFloatArray(std::vector<float>());
|
|
172
|
+
s.writeFloatArray(std::vector<float>());
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
inline void deserializeToon(dsp::toon::Deserializer &d) override
|
|
177
|
+
{
|
|
178
|
+
// Read configuration
|
|
179
|
+
m_threshold = d.readFloat();
|
|
180
|
+
m_num_channels = d.readInt32();
|
|
181
|
+
m_mode = d.readString();
|
|
182
|
+
m_domain = d.readString();
|
|
183
|
+
m_windowSize = d.readInt32();
|
|
184
|
+
m_minPeakDistance = d.readInt32();
|
|
185
|
+
|
|
186
|
+
// Read state arrays
|
|
187
|
+
m_prev_sample = d.readFloatArray();
|
|
188
|
+
m_prev_prev_sample = d.readFloatArray();
|
|
189
|
+
|
|
190
|
+
// Read peakCooldown (convert from float array to int)
|
|
191
|
+
std::vector<float> cooldown_float = d.readFloatArray();
|
|
192
|
+
m_peakCooldown.clear();
|
|
193
|
+
for (float val : cooldown_float)
|
|
194
|
+
{
|
|
195
|
+
m_peakCooldown.push_back(static_cast<int>(val));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
146
199
|
void reset() override
|
|
147
200
|
{
|
|
148
201
|
std::fill(m_prev_sample.begin(), m_prev_sample.end(), 0.0f);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
#include "../IDspStage.h"
|
|
3
3
|
#include "../utils/SimdOps.h"
|
|
4
|
+
#include "../utils/Toon.h"
|
|
4
5
|
#include <cmath>
|
|
5
6
|
#include <stdexcept>
|
|
6
7
|
|
|
@@ -80,6 +81,19 @@ namespace dsp::adapters
|
|
|
80
81
|
throw std::runtime_error("Invalid rectify mode");
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
inline void serializeToon(dsp::toon::Serializer &s) const override
|
|
85
|
+
{
|
|
86
|
+
s.writeInt32(static_cast<int32_t>(m_mode));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
inline void deserializeToon(dsp::toon::Deserializer &d) override
|
|
90
|
+
{
|
|
91
|
+
int32_t mode_int = d.readInt32();
|
|
92
|
+
if (mode_int < 0 || mode_int > 1)
|
|
93
|
+
throw std::runtime_error("Invalid mode in RectifyStage deserialization");
|
|
94
|
+
m_mode = static_cast<RectifyMode>(mode_int);
|
|
95
|
+
}
|
|
96
|
+
|
|
83
97
|
void reset() override {} // No internal buffers
|
|
84
98
|
|
|
85
99
|
private:
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
10
|
#include "../IDspStage.h"
|
|
11
|
+
#include "../utils/Toon.h"
|
|
11
12
|
#include <vector>
|
|
12
13
|
#include <cmath>
|
|
13
14
|
#include <stdexcept>
|
|
@@ -187,6 +188,72 @@ namespace dsp
|
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
190
|
|
|
191
|
+
void serializeToon(dsp::toon::Serializer &s) const override
|
|
192
|
+
{
|
|
193
|
+
s.writeInt32(upFactor_);
|
|
194
|
+
s.writeInt32(downFactor_);
|
|
195
|
+
s.writeInt32(filterOrder_);
|
|
196
|
+
s.writeDouble(sampleRate_);
|
|
197
|
+
s.writeInt32(numChannels_);
|
|
198
|
+
s.writeInt32(static_cast<int32_t>(phaseAccumulator_));
|
|
199
|
+
|
|
200
|
+
// Serialize state buffers for each channel
|
|
201
|
+
for (const auto &buf : stateBuffers_)
|
|
202
|
+
{
|
|
203
|
+
s.writeFloatArray(buf);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Serialize state indices
|
|
207
|
+
for (size_t idx : stateIndices_)
|
|
208
|
+
{
|
|
209
|
+
s.writeInt32(static_cast<int32_t>(idx));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Serialize phase accumulators
|
|
213
|
+
for (size_t pa : phaseAccumulators_)
|
|
214
|
+
{
|
|
215
|
+
s.writeInt32(static_cast<int32_t>(pa));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
void deserializeToon(dsp::toon::Deserializer &d) override
|
|
220
|
+
{
|
|
221
|
+
int32_t upF = d.readInt32();
|
|
222
|
+
int32_t downF = d.readInt32();
|
|
223
|
+
int32_t order = d.readInt32();
|
|
224
|
+
double sr = d.readDouble();
|
|
225
|
+
int32_t nCh = d.readInt32();
|
|
226
|
+
phaseAccumulator_ = static_cast<size_t>(d.readInt32());
|
|
227
|
+
|
|
228
|
+
if (upF != upFactor_ || downF != downFactor_ || order != filterOrder_)
|
|
229
|
+
{
|
|
230
|
+
throw std::runtime_error("Resampler TOON: parameter mismatch");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (nCh != numChannels_)
|
|
234
|
+
{
|
|
235
|
+
initializeStateBuffers(nCh);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Deserialize state buffers
|
|
239
|
+
for (auto &buf : stateBuffers_)
|
|
240
|
+
{
|
|
241
|
+
buf = d.readFloatArray();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Deserialize state indices
|
|
245
|
+
for (size_t &idx : stateIndices_)
|
|
246
|
+
{
|
|
247
|
+
idx = static_cast<size_t>(d.readInt32());
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Deserialize phase accumulators
|
|
251
|
+
for (size_t &pa : phaseAccumulators_)
|
|
252
|
+
{
|
|
253
|
+
pa = static_cast<size_t>(d.readInt32());
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
190
257
|
private:
|
|
191
258
|
int upFactor_;
|
|
192
259
|
int downFactor_;
|
|
@@ -199,6 +199,82 @@ namespace dsp
|
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
void serializeToon(dsp::toon::Serializer &s) const override
|
|
203
|
+
{
|
|
204
|
+
s.startObject();
|
|
205
|
+
|
|
206
|
+
s.writeString("numTaps");
|
|
207
|
+
s.writeInt32(static_cast<int32_t>(m_numTaps));
|
|
208
|
+
|
|
209
|
+
s.writeString("lambda");
|
|
210
|
+
s.writeFloat(m_lambda);
|
|
211
|
+
|
|
212
|
+
s.writeString("delta");
|
|
213
|
+
s.writeFloat(m_delta);
|
|
214
|
+
|
|
215
|
+
s.writeString("initialized");
|
|
216
|
+
s.writeBool(m_initialized);
|
|
217
|
+
|
|
218
|
+
if (m_initialized && m_filter)
|
|
219
|
+
{
|
|
220
|
+
// Serialize weights
|
|
221
|
+
s.writeString("weights");
|
|
222
|
+
s.writeFloatArray(m_filter->getWeights());
|
|
223
|
+
|
|
224
|
+
// Serialize inverse covariance matrix
|
|
225
|
+
s.writeString("inverseCov");
|
|
226
|
+
s.writeFloatArray(m_filter->getInverseCov());
|
|
227
|
+
|
|
228
|
+
// Serialize buffer
|
|
229
|
+
const auto &buffer = m_filter->getBuffer();
|
|
230
|
+
std::vector<float> bufferData = buffer.toVector();
|
|
231
|
+
s.writeString("buffer");
|
|
232
|
+
s.writeFloatArray(bufferData);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
s.endObject();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
void deserializeToon(dsp::toon::Deserializer &d) override
|
|
239
|
+
{
|
|
240
|
+
d.consumeToken(dsp::toon::T_OBJECT_START);
|
|
241
|
+
|
|
242
|
+
std::string key = d.readString(); // "numTaps"
|
|
243
|
+
m_numTaps = d.readInt32();
|
|
244
|
+
|
|
245
|
+
key = d.readString(); // "lambda"
|
|
246
|
+
m_lambda = d.readFloat();
|
|
247
|
+
|
|
248
|
+
key = d.readString(); // "delta"
|
|
249
|
+
m_delta = d.readFloat();
|
|
250
|
+
|
|
251
|
+
key = d.readString(); // "initialized"
|
|
252
|
+
m_initialized = d.readBool();
|
|
253
|
+
|
|
254
|
+
if (m_initialized)
|
|
255
|
+
{
|
|
256
|
+
// Recreate filter
|
|
257
|
+
m_filter = std::make_unique<dsp::core::RlsFilter>(m_numTaps, m_lambda, m_delta);
|
|
258
|
+
|
|
259
|
+
// Restore weights
|
|
260
|
+
key = d.readString(); // "weights"
|
|
261
|
+
std::vector<float> weights = d.readFloatArray();
|
|
262
|
+
m_filter->setWeights(weights);
|
|
263
|
+
|
|
264
|
+
// Restore inverse covariance matrix
|
|
265
|
+
key = d.readString(); // "inverseCov"
|
|
266
|
+
std::vector<float> inverseCov = d.readFloatArray();
|
|
267
|
+
m_filter->setInverseCov(inverseCov);
|
|
268
|
+
|
|
269
|
+
// Restore buffer
|
|
270
|
+
key = d.readString(); // "buffer"
|
|
271
|
+
std::vector<float> bufferData = d.readFloatArray();
|
|
272
|
+
m_filter->setBuffer(bufferData);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
d.consumeToken(dsp::toon::T_OBJECT_END);
|
|
276
|
+
}
|
|
277
|
+
|
|
202
278
|
private:
|
|
203
279
|
void ensureScratchBuffers(size_t samplesPerChannel)
|
|
204
280
|
{
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include "../IDspStage.h"
|
|
4
4
|
#include "../core/RmsFilter.h"
|
|
5
5
|
#include "../utils/SimdOps.h"
|
|
6
|
+
#include "../utils/Toon.h"
|
|
6
7
|
#include <vector>
|
|
7
8
|
#include <stdexcept>
|
|
8
9
|
#include <cmath>
|
|
@@ -176,6 +177,77 @@ namespace dsp::adapters
|
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
179
|
|
|
180
|
+
void serializeToon(dsp::toon::Serializer &s) const override
|
|
181
|
+
{
|
|
182
|
+
// Serialize mode
|
|
183
|
+
s.writeString(m_mode == RmsMode::Moving ? "moving" : "batch");
|
|
184
|
+
|
|
185
|
+
if (m_mode == RmsMode::Moving)
|
|
186
|
+
{
|
|
187
|
+
// Serialize window configuration
|
|
188
|
+
s.writeInt32(static_cast<int32_t>(m_window_size));
|
|
189
|
+
s.writeDouble(m_window_duration_ms);
|
|
190
|
+
s.writeBool(m_is_initialized);
|
|
191
|
+
|
|
192
|
+
// Serialize filter states
|
|
193
|
+
s.writeInt32(static_cast<int32_t>(m_filters.size()));
|
|
194
|
+
for (const auto &filter : m_filters)
|
|
195
|
+
{
|
|
196
|
+
auto [bufferData, runningSumOfSquares] = filter.getState();
|
|
197
|
+
s.writeFloatArray(bufferData); // CRITICAL: Direct binary copy
|
|
198
|
+
s.writeFloat(runningSumOfSquares);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
void deserializeToon(dsp::toon::Deserializer &d) override
|
|
204
|
+
{
|
|
205
|
+
// Deserialize and validate mode
|
|
206
|
+
std::string modeStr = d.readString();
|
|
207
|
+
RmsMode newMode = (modeStr == "moving") ? RmsMode::Moving : RmsMode::Batch;
|
|
208
|
+
|
|
209
|
+
if (newMode != m_mode)
|
|
210
|
+
{
|
|
211
|
+
throw std::runtime_error("RMS TOON load: mode mismatch");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (m_mode == RmsMode::Moving)
|
|
215
|
+
{
|
|
216
|
+
// Deserialize window configuration
|
|
217
|
+
int32_t windowSize = d.readInt32();
|
|
218
|
+
if (windowSize != static_cast<int32_t>(m_window_size))
|
|
219
|
+
{
|
|
220
|
+
throw std::runtime_error("RMS TOON load: window size mismatch");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
m_window_duration_ms = d.readDouble(); // restore duration
|
|
224
|
+
m_is_initialized = d.readBool(); // restore init flag
|
|
225
|
+
|
|
226
|
+
// Deserialize filter states
|
|
227
|
+
int32_t numChannels = d.readInt32();
|
|
228
|
+
m_filters.clear();
|
|
229
|
+
for (int32_t i = 0; i < numChannels; ++i)
|
|
230
|
+
{
|
|
231
|
+
// Create filter with correct window size (time-aware detection happens at runtime)
|
|
232
|
+
// preserve time-aware vs size-based based on restored flags
|
|
233
|
+
if (m_window_duration_ms > 0.0)
|
|
234
|
+
{
|
|
235
|
+
m_filters.emplace_back(m_window_size, m_window_duration_ms);
|
|
236
|
+
}
|
|
237
|
+
else
|
|
238
|
+
{
|
|
239
|
+
m_filters.emplace_back(m_window_size);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Restore state
|
|
243
|
+
std::vector<float> bufferData = d.readFloatArray();
|
|
244
|
+
float runningSumOfSquares = d.readFloat();
|
|
245
|
+
|
|
246
|
+
m_filters[i].setState(bufferData, runningSumOfSquares);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
179
251
|
private:
|
|
180
252
|
/**
|
|
181
253
|
* @brief Statelessly calculates the RMS for each channel
|
|
@@ -185,6 +185,51 @@ namespace dsp::adapters
|
|
|
185
185
|
m_initialized = true;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
inline void serializeToon(dsp::toon::Serializer &s) const override
|
|
189
|
+
{
|
|
190
|
+
// Write window size
|
|
191
|
+
s.writeInt32(static_cast<int32_t>(m_window_size));
|
|
192
|
+
|
|
193
|
+
// Get and write signal RMS filter state
|
|
194
|
+
auto signal_state = m_signal_rms.getState();
|
|
195
|
+
const auto &signal_buffer = signal_state.first;
|
|
196
|
+
float signal_sum = signal_state.second;
|
|
197
|
+
|
|
198
|
+
s.writeFloatArray(signal_buffer);
|
|
199
|
+
s.writeFloat(signal_sum);
|
|
200
|
+
|
|
201
|
+
// Get and write noise RMS filter state
|
|
202
|
+
auto noise_state = m_noise_rms.getState();
|
|
203
|
+
const auto &noise_buffer = noise_state.first;
|
|
204
|
+
float noise_sum = noise_state.second;
|
|
205
|
+
|
|
206
|
+
s.writeFloatArray(noise_buffer);
|
|
207
|
+
s.writeFloat(noise_sum);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
inline void deserializeToon(dsp::toon::Deserializer &d) override
|
|
211
|
+
{
|
|
212
|
+
// Read and validate window size
|
|
213
|
+
int32_t window_size = d.readInt32();
|
|
214
|
+
if (window_size <= 0)
|
|
215
|
+
throw std::runtime_error("Invalid window_size in SnrStage deserialization");
|
|
216
|
+
m_window_size = static_cast<size_t>(window_size);
|
|
217
|
+
|
|
218
|
+
// Read signal RMS filter state
|
|
219
|
+
std::vector<float> signal_buffer = d.readFloatArray();
|
|
220
|
+
float signal_sum = d.readFloat();
|
|
221
|
+
|
|
222
|
+
// Read noise RMS filter state
|
|
223
|
+
std::vector<float> noise_buffer = d.readFloatArray();
|
|
224
|
+
float noise_sum = d.readFloat();
|
|
225
|
+
|
|
226
|
+
// Restore state to RMS filters
|
|
227
|
+
m_signal_rms.setState(signal_buffer, signal_sum);
|
|
228
|
+
m_noise_rms.setState(noise_buffer, noise_sum);
|
|
229
|
+
|
|
230
|
+
m_initialized = true;
|
|
231
|
+
}
|
|
232
|
+
|
|
188
233
|
void reset() override
|
|
189
234
|
{
|
|
190
235
|
m_signal_rms.clear();
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "../IDspStage.h"
|
|
4
|
+
#include "../utils/Toon.h"
|
|
5
|
+
#include "../utils/SimdOps.h"
|
|
6
|
+
#include <stdexcept>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <cmath>
|
|
9
|
+
|
|
10
|
+
namespace dsp::adapters
|
|
11
|
+
{
|
|
12
|
+
/**
|
|
13
|
+
* @brief Square Stage - Computes element-wise squaring of a signal.
|
|
14
|
+
*
|
|
15
|
+
* Implements: y[n] = x[n]^2
|
|
16
|
+
*
|
|
17
|
+
* **Use Cases:**
|
|
18
|
+
* - Energy calculation (signal power)
|
|
19
|
+
* - Non-linear signal transformation
|
|
20
|
+
* - Envelope detection
|
|
21
|
+
* - Part of Pan-Tompkins QRS detection algorithm
|
|
22
|
+
*
|
|
23
|
+
* **Note:** Squaring amplifies large values and suppresses small ones.
|
|
24
|
+
* This stage is stateless - no mode selection needed.
|
|
25
|
+
*/
|
|
26
|
+
class SquareStage : public IDspStage
|
|
27
|
+
{
|
|
28
|
+
public:
|
|
29
|
+
SquareStage()
|
|
30
|
+
{
|
|
31
|
+
// No parameters needed - stateless operation
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const char *getType() const override
|
|
35
|
+
{
|
|
36
|
+
return "square";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void process(float *buffer, size_t numSamples, int numChannels, const float *timestamps = nullptr) override
|
|
40
|
+
{
|
|
41
|
+
// Stateless squaring operation
|
|
42
|
+
dsp::simd::square_inplace(buffer, numSamples);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
Napi::Object serializeState(Napi::Env env) const override
|
|
46
|
+
{
|
|
47
|
+
Napi::Object state = Napi::Object::New(env);
|
|
48
|
+
// No state to serialize - squaring is stateless
|
|
49
|
+
return state;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void deserializeState(const Napi::Object &state) override
|
|
53
|
+
{
|
|
54
|
+
// No state to deserialize
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
void reset() override
|
|
58
|
+
{
|
|
59
|
+
// No state to reset
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
void serializeToon(dsp::toon::Serializer &s) const override
|
|
63
|
+
{
|
|
64
|
+
// No state to serialize
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void deserializeToon(dsp::toon::Deserializer &d) override
|
|
68
|
+
{
|
|
69
|
+
// No state to deserialize
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
bool isResizing() const override { return false; }
|
|
73
|
+
|
|
74
|
+
private:
|
|
75
|
+
// No member variables - stateless operation
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
} // namespace dsp::adapters
|