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.
Files changed (74) hide show
  1. package/README.md +40 -78
  2. package/binding.gyp +10 -0
  3. package/dist/FilterBankDesign.d.ts +233 -0
  4. package/dist/FilterBankDesign.d.ts.map +1 -0
  5. package/dist/FilterBankDesign.js +247 -0
  6. package/dist/FilterBankDesign.js.map +1 -0
  7. package/dist/advanced-dsp.d.ts +6 -6
  8. package/dist/advanced-dsp.d.ts.map +1 -1
  9. package/dist/advanced-dsp.js +35 -12
  10. package/dist/advanced-dsp.js.map +1 -1
  11. package/dist/backends.d.ts +0 -103
  12. package/dist/backends.d.ts.map +1 -1
  13. package/dist/backends.js +0 -217
  14. package/dist/backends.js.map +1 -1
  15. package/dist/bindings.d.ts +270 -17
  16. package/dist/bindings.d.ts.map +1 -1
  17. package/dist/bindings.js +566 -43
  18. package/dist/bindings.js.map +1 -1
  19. package/dist/index.d.ts +4 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/types.d.ts +67 -8
  24. package/dist/types.d.ts.map +1 -1
  25. package/dist/utils.d.ts +38 -8
  26. package/dist/utils.d.ts.map +1 -1
  27. package/dist/utils.js +84 -26
  28. package/dist/utils.js.map +1 -1
  29. package/package.json +1 -2
  30. package/prebuilds/win32-x64/dspx.node +0 -0
  31. package/scripts/add-dispose-to-tests.js +145 -0
  32. package/src/native/DspPipeline.cc +699 -126
  33. package/src/native/DspPipeline.h +13 -0
  34. package/src/native/FilterBankDesignBindings.cc +241 -0
  35. package/src/native/IDspStage.h +24 -0
  36. package/src/native/UtilityBindings.cc +130 -0
  37. package/src/native/adapters/AmplifyStage.h +148 -0
  38. package/src/native/adapters/ClipDetectionStage.h +15 -4
  39. package/src/native/adapters/ConvolutionStage.h +101 -0
  40. package/src/native/adapters/CumulativeMovingAverageStage.h +264 -0
  41. package/src/native/adapters/DecimatorStage.h +80 -0
  42. package/src/native/adapters/DifferentiatorStage.h +13 -0
  43. package/src/native/adapters/ExponentialMovingAverageStage.h +290 -0
  44. package/src/native/adapters/FilterBankStage.cc +336 -0
  45. package/src/native/adapters/FilterBankStage.h +170 -0
  46. package/src/native/adapters/FilterStage.cc +122 -0
  47. package/src/native/adapters/FilterStage.h +4 -0
  48. package/src/native/adapters/HilbertEnvelopeStage.h +55 -0
  49. package/src/native/adapters/IntegratorStage.h +15 -0
  50. package/src/native/adapters/InterpolatorStage.h +51 -0
  51. package/src/native/adapters/LinearRegressionStage.h +40 -0
  52. package/src/native/adapters/LmsStage.h +63 -0
  53. package/src/native/adapters/MeanAbsoluteValueStage.h +76 -0
  54. package/src/native/adapters/MovingAverageStage.h +119 -0
  55. package/src/native/adapters/PeakDetectionStage.h +53 -0
  56. package/src/native/adapters/RectifyStage.h +14 -0
  57. package/src/native/adapters/ResamplerStage.h +67 -0
  58. package/src/native/adapters/RlsStage.h +76 -0
  59. package/src/native/adapters/RmsStage.h +72 -0
  60. package/src/native/adapters/SnrStage.h +45 -0
  61. package/src/native/adapters/SquareStage.h +78 -0
  62. package/src/native/adapters/SscStage.h +65 -0
  63. package/src/native/adapters/StftStage.h +62 -0
  64. package/src/native/adapters/VarianceStage.h +59 -0
  65. package/src/native/adapters/WampStage.h +59 -0
  66. package/src/native/adapters/WaveformLengthStage.h +51 -0
  67. package/src/native/adapters/ZScoreNormalizeStage.h +64 -0
  68. package/src/native/core/CumulativeMovingAverageFilter.h +123 -0
  69. package/src/native/core/ExponentialMovingAverageFilter.h +129 -0
  70. package/src/native/core/FilterBankDesign.h +266 -0
  71. package/src/native/core/Policies.h +124 -0
  72. package/src/native/utils/CircularBufferArray.cc +2 -1
  73. package/src/native/utils/SimdOps.h +67 -0
  74. 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