dspx 1.2.4 → 1.3.0

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 (71) 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 +216 -17
  16. package/dist/bindings.d.ts.map +1 -1
  17. package/dist/bindings.js +503 -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 +668 -118
  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/ClipDetectionStage.h +15 -4
  38. package/src/native/adapters/ConvolutionStage.h +101 -0
  39. package/src/native/adapters/CumulativeMovingAverageStage.h +264 -0
  40. package/src/native/adapters/DecimatorStage.h +80 -0
  41. package/src/native/adapters/DifferentiatorStage.h +13 -0
  42. package/src/native/adapters/ExponentialMovingAverageStage.h +290 -0
  43. package/src/native/adapters/FilterBankStage.cc +336 -0
  44. package/src/native/adapters/FilterBankStage.h +170 -0
  45. package/src/native/adapters/FilterStage.cc +122 -0
  46. package/src/native/adapters/FilterStage.h +4 -0
  47. package/src/native/adapters/HilbertEnvelopeStage.h +55 -0
  48. package/src/native/adapters/IntegratorStage.h +15 -0
  49. package/src/native/adapters/InterpolatorStage.h +51 -0
  50. package/src/native/adapters/LinearRegressionStage.h +40 -0
  51. package/src/native/adapters/LmsStage.h +63 -0
  52. package/src/native/adapters/MeanAbsoluteValueStage.h +76 -0
  53. package/src/native/adapters/MovingAverageStage.h +119 -0
  54. package/src/native/adapters/PeakDetectionStage.h +53 -0
  55. package/src/native/adapters/RectifyStage.h +14 -0
  56. package/src/native/adapters/ResamplerStage.h +67 -0
  57. package/src/native/adapters/RlsStage.h +76 -0
  58. package/src/native/adapters/RmsStage.h +72 -0
  59. package/src/native/adapters/SnrStage.h +45 -0
  60. package/src/native/adapters/SscStage.h +65 -0
  61. package/src/native/adapters/StftStage.h +62 -0
  62. package/src/native/adapters/VarianceStage.h +59 -0
  63. package/src/native/adapters/WampStage.h +59 -0
  64. package/src/native/adapters/WaveformLengthStage.h +51 -0
  65. package/src/native/adapters/ZScoreNormalizeStage.h +64 -0
  66. package/src/native/core/CumulativeMovingAverageFilter.h +123 -0
  67. package/src/native/core/ExponentialMovingAverageFilter.h +129 -0
  68. package/src/native/core/FilterBankDesign.h +266 -0
  69. package/src/native/core/Policies.h +124 -0
  70. package/src/native/utils/CircularBufferArray.cc +2 -1
  71. package/src/native/utils/Toon.h +195 -0
@@ -31,6 +31,7 @@
31
31
  #include "../core/FftEngine.h"
32
32
  #include "../utils/CircularBufferArray.h"
33
33
  #include "../utils/SimdOps.h"
34
+ #include "../utils/Toon.h"
34
35
  #include <vector>
35
36
  #include <complex>
36
37
  #include <memory>
@@ -199,6 +200,60 @@ namespace dsp::adapters
199
200
  }
200
201
  }
201
202
 
203
+ inline void serializeToon(dsp::toon::Serializer &s) const override
204
+ {
205
+ // Write configuration
206
+ s.writeInt32(static_cast<int32_t>(m_window_size));
207
+ s.writeInt32(static_cast<int32_t>(m_hop_size));
208
+ s.writeInt32(static_cast<int32_t>(m_channel_buffers.size()));
209
+
210
+ // Write per-channel state
211
+ for (size_t i = 0; i < m_channel_buffers.size(); ++i)
212
+ {
213
+ std::vector<float> buffer_data = m_channel_buffers[i].toVector();
214
+ s.writeFloatArray(buffer_data);
215
+ s.writeInt32(static_cast<int32_t>(m_samples_since_output[i]));
216
+ }
217
+ }
218
+
219
+ inline void deserializeToon(dsp::toon::Deserializer &d) override
220
+ {
221
+ // Read configuration
222
+ int32_t window_size = d.readInt32();
223
+ int32_t hop_size = d.readInt32();
224
+
225
+ if (window_size <= 0 || hop_size <= 0)
226
+ throw std::runtime_error("Invalid window/hop size in HilbertEnvelopeStage deserialization");
227
+
228
+ if (static_cast<size_t>(window_size) != m_window_size || static_cast<size_t>(hop_size) != m_hop_size)
229
+ throw std::runtime_error("Window/hop size mismatch in HilbertEnvelopeStage deserialization");
230
+
231
+ int32_t num_channels = d.readInt32();
232
+ if (num_channels < 0)
233
+ throw std::runtime_error("Invalid num_channels in HilbertEnvelopeStage deserialization");
234
+
235
+ // Recreate channel buffers
236
+ m_channel_buffers.clear();
237
+ m_samples_since_output.clear();
238
+
239
+ for (int32_t i = 0; i < num_channels; ++i)
240
+ {
241
+ m_channel_buffers.emplace_back(m_window_size);
242
+ m_samples_since_output.push_back(0);
243
+ }
244
+
245
+ // Restore per-channel state
246
+ for (int32_t i = 0; i < num_channels; ++i)
247
+ {
248
+ std::vector<float> buffer_data = d.readFloatArray();
249
+ for (float value : buffer_data)
250
+ {
251
+ m_channel_buffers[i].push(value);
252
+ }
253
+ m_samples_since_output[i] = static_cast<size_t>(d.readInt32());
254
+ }
255
+ }
256
+
202
257
  void reset() override
203
258
  {
204
259
  for (auto &buffer : m_channel_buffers)
@@ -2,6 +2,7 @@
2
2
  #define INTEGRATOR_STAGE_H
3
3
 
4
4
  #include "../IDspStage.h"
5
+ #include "../utils/Toon.h"
5
6
  #include <vector>
6
7
  #include <cmath>
7
8
  #include <stdexcept>
@@ -129,6 +130,20 @@ namespace dsp::adapters
129
130
  std::fill(m_prev_output.begin(), m_prev_output.end(), 0.0f);
130
131
  }
131
132
 
133
+ void serializeToon(dsp::toon::Serializer &s) const override
134
+ {
135
+ s.writeFloat(m_alpha);
136
+ s.writeInt32(m_num_channels);
137
+ s.writeFloatArray(m_prev_output);
138
+ }
139
+
140
+ void deserializeToon(dsp::toon::Deserializer &d) override
141
+ {
142
+ m_alpha = d.readFloat();
143
+ m_num_channels = d.readInt32();
144
+ m_prev_output = d.readFloatArray();
145
+ }
146
+
132
147
  bool isResizing() const override
133
148
  {
134
149
  return false;
@@ -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>
@@ -154,6 +155,56 @@ namespace dsp
154
155
  }
155
156
  }
156
157
 
158
+ void serializeToon(dsp::toon::Serializer &s) const override
159
+ {
160
+ s.writeInt32(interpolationFactor_);
161
+ s.writeInt32(filterOrder_);
162
+ s.writeDouble(sampleRate_);
163
+ s.writeInt32(numChannels_);
164
+
165
+ // Serialize state buffers for each channel
166
+ for (const auto &buf : stateBuffers_)
167
+ {
168
+ s.writeFloatArray(buf);
169
+ }
170
+
171
+ // Serialize state indices
172
+ for (size_t idx : stateIndices_)
173
+ {
174
+ s.writeInt32(static_cast<int32_t>(idx));
175
+ }
176
+ }
177
+
178
+ void deserializeToon(dsp::toon::Deserializer &d) override
179
+ {
180
+ int32_t factor = d.readInt32();
181
+ int32_t order = d.readInt32();
182
+ double sr = d.readDouble();
183
+ int32_t nCh = d.readInt32();
184
+
185
+ if (factor != interpolationFactor_ || order != filterOrder_)
186
+ {
187
+ throw std::runtime_error("Interpolator TOON: parameter mismatch");
188
+ }
189
+
190
+ if (nCh != numChannels_)
191
+ {
192
+ initializeStateBuffers(nCh);
193
+ }
194
+
195
+ // Deserialize state buffers
196
+ for (auto &buf : stateBuffers_)
197
+ {
198
+ buf = d.readFloatArray();
199
+ }
200
+
201
+ // Deserialize state indices
202
+ for (size_t &idx : stateIndices_)
203
+ {
204
+ idx = static_cast<size_t>(d.readInt32());
205
+ }
206
+ }
207
+
157
208
  private:
158
209
  int interpolationFactor_;
159
210
  int filterOrder_;
@@ -3,6 +3,7 @@
3
3
 
4
4
  #include "../IDspStage.h"
5
5
  #include "../utils/SimdOps.h"
6
+ #include "../utils/Toon.h"
6
7
  #include <vector>
7
8
  #include <cmath>
8
9
  #include <stdexcept>
@@ -321,6 +322,45 @@ namespace dsp
321
322
  }
322
323
  }
323
324
 
325
+ inline void serializeToon(dsp::toon::Serializer &s) const override
326
+ {
327
+ // Write configuration
328
+ s.writeInt32(static_cast<int32_t>(m_windowSize));
329
+ s.writeInt32(static_cast<int32_t>(m_numChannels));
330
+
331
+ // Write per-channel state
332
+ for (size_t ch = 0; ch < m_numChannels; ++ch)
333
+ {
334
+ s.writeFloatArray(m_buffers[ch]);
335
+ s.writeInt32(static_cast<int32_t>(m_writeIndices[ch]));
336
+ s.writeInt32(static_cast<int32_t>(m_sampleCounts[ch]));
337
+ }
338
+ }
339
+
340
+ inline void deserializeToon(dsp::toon::Deserializer &d) override
341
+ {
342
+ // Read configuration
343
+ int32_t windowSize = d.readInt32();
344
+ if (windowSize < 2)
345
+ throw std::runtime_error("Invalid windowSize in LinearRegressionStage deserialization");
346
+ if (static_cast<size_t>(windowSize) != m_windowSize)
347
+ throw std::runtime_error("Window size mismatch in LinearRegressionStage deserialization");
348
+
349
+ int32_t numChannels = d.readInt32();
350
+ if (numChannels < 0)
351
+ throw std::runtime_error("Invalid numChannels in LinearRegressionStage deserialization");
352
+
353
+ init(static_cast<size_t>(numChannels));
354
+
355
+ // Read per-channel state
356
+ for (size_t ch = 0; ch < m_numChannels; ++ch)
357
+ {
358
+ m_buffers[ch] = d.readFloatArray();
359
+ m_writeIndices[ch] = static_cast<size_t>(d.readInt32());
360
+ m_sampleCounts[ch] = static_cast<size_t>(d.readInt32());
361
+ }
362
+ }
363
+
324
364
  void reset() override
325
365
  {
326
366
  for (size_t ch = 0; ch < m_numChannels; ++ch)
@@ -176,6 +176,69 @@ namespace dsp
176
176
  }
177
177
  }
178
178
 
179
+ void serializeToon(dsp::toon::Serializer &s) const override
180
+ {
181
+ s.startObject();
182
+
183
+ s.writeString("numTaps");
184
+ s.writeInt32(static_cast<int32_t>(m_numTaps));
185
+
186
+ s.writeString("learningRate");
187
+ s.writeFloat(m_learningRate);
188
+
189
+ s.writeString("normalized");
190
+ s.writeBool(m_normalized);
191
+
192
+ s.writeString("lambda");
193
+ s.writeFloat(m_lambda);
194
+
195
+ s.writeString("initialized");
196
+ s.writeBool(m_initialized);
197
+
198
+ if (m_initialized)
199
+ {
200
+ auto weights = m_filter->getWeights(0);
201
+ s.writeString("weights");
202
+ s.writeFloatArray(weights);
203
+ }
204
+
205
+ s.endObject();
206
+ }
207
+
208
+ void deserializeToon(dsp::toon::Deserializer &d) override
209
+ {
210
+ d.consumeToken(dsp::toon::T_OBJECT_START);
211
+
212
+ std::string key = d.readString(); // "numTaps"
213
+ m_numTaps = d.readInt32();
214
+
215
+ key = d.readString(); // "learningRate"
216
+ m_learningRate = d.readFloat();
217
+
218
+ key = d.readString(); // "normalized"
219
+ m_normalized = d.readBool();
220
+
221
+ key = d.readString(); // "lambda"
222
+ m_lambda = d.readFloat();
223
+
224
+ key = d.readString(); // "initialized"
225
+ m_initialized = d.readBool();
226
+
227
+ // Recreate filter with restored parameters
228
+ m_filter = std::make_unique<dsp::core::DifferentiableFilter<float>>(m_numTaps, m_learningRate, m_lambda, m_normalized);
229
+
230
+ if (m_initialized)
231
+ {
232
+ m_filter->init(1); // Reinitialize filter
233
+
234
+ key = d.readString(); // "weights"
235
+ std::vector<float> weights = d.readFloatArray();
236
+ m_filter->setWeights(0, weights);
237
+ }
238
+
239
+ d.consumeToken(dsp::toon::T_OBJECT_END);
240
+ }
241
+
179
242
  private:
180
243
  void ensureScratchBuffers(size_t samplesPerChannel)
181
244
  {
@@ -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_;