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
@@ -244,6 +244,107 @@ namespace dsp::adapters
244
244
  }
245
245
  }
246
246
 
247
+ void serializeToon(dsp::toon::Serializer &s) const override
248
+ {
249
+ s.startObject();
250
+
251
+ s.writeString("mode");
252
+ s.writeString((m_mode == ConvolutionMode::Moving) ? "moving" : "batch");
253
+
254
+ s.writeString("method");
255
+ std::string methodStr;
256
+ switch (m_method)
257
+ {
258
+ case ConvolutionMethod::Auto:
259
+ methodStr = "auto";
260
+ break;
261
+ case ConvolutionMethod::Direct:
262
+ methodStr = "direct";
263
+ break;
264
+ case ConvolutionMethod::FFT:
265
+ methodStr = "fft";
266
+ break;
267
+ }
268
+ s.writeString(methodStr);
269
+
270
+ s.writeString("kernel");
271
+ s.writeFloatArray(m_kernel);
272
+
273
+ if (m_mode == ConvolutionMode::Moving && m_actualMethod == ConvolutionMethod::Direct)
274
+ {
275
+ s.writeString("channels");
276
+ s.startArray();
277
+
278
+ for (const auto &filter : m_filters)
279
+ {
280
+ s.startObject();
281
+ auto filterState = filter.getState();
282
+ const auto &bufferData = filterState.first;
283
+
284
+ s.writeString("buffer");
285
+ s.writeFloatArray(bufferData);
286
+ s.endObject();
287
+ }
288
+ s.endArray();
289
+ }
290
+
291
+ s.endObject();
292
+ }
293
+
294
+ void deserializeToon(dsp::toon::Deserializer &d) override
295
+ {
296
+ d.consumeToken(dsp::toon::T_OBJECT_START);
297
+
298
+ std::string key = d.readString(); // "mode"
299
+ std::string modeStr = d.readString();
300
+ ConvolutionMode newMode = (modeStr == "moving") ? ConvolutionMode::Moving : ConvolutionMode::Batch;
301
+ if (newMode != m_mode)
302
+ {
303
+ throw std::runtime_error("Convolution mode mismatch during TOON deserialization");
304
+ }
305
+
306
+ key = d.readString(); // "method"
307
+ d.readString(); // Skip method validation
308
+
309
+ key = d.readString(); // "kernel"
310
+ std::vector<float> kernel = d.readFloatArray();
311
+ if (kernel.size() != m_kernel.size())
312
+ {
313
+ throw std::runtime_error("Convolution kernel size mismatch during TOON deserialization");
314
+ }
315
+
316
+ if (modeStr == "moving" && m_actualMethod == ConvolutionMethod::Direct)
317
+ {
318
+ key = d.readString(); // "channels"
319
+ d.consumeToken(dsp::toon::T_ARRAY_START);
320
+
321
+ m_filters.clear();
322
+
323
+ while (d.peekToken() != dsp::toon::T_ARRAY_END)
324
+ {
325
+ d.consumeToken(dsp::toon::T_OBJECT_START);
326
+
327
+ d.readString(); // "buffer"
328
+ std::vector<float> bufferData = d.readFloatArray();
329
+
330
+ // Create policy with kernel
331
+ utils::ConvolutionPolicy<float> policy(m_kernel);
332
+ m_filters.emplace_back(m_kernel.size(), std::move(policy));
333
+
334
+ // Restore state
335
+ typename utils::ConvolutionPolicy<float>::EmptyState policyState;
336
+ m_filters.back().setState(bufferData, policyState);
337
+
338
+ d.consumeToken(dsp::toon::T_OBJECT_END);
339
+ }
340
+ d.consumeToken(dsp::toon::T_ARRAY_END);
341
+
342
+ m_is_initialized = true;
343
+ }
344
+
345
+ d.consumeToken(dsp::toon::T_OBJECT_END);
346
+ }
347
+
247
348
  private:
248
349
  std::vector<float> m_kernel;
249
350
  ConvolutionMode m_mode;
@@ -0,0 +1,264 @@
1
+ #pragma once
2
+
3
+ #include "../IDspStage.h"
4
+ #include "../core/CumulativeMovingAverageFilter.h"
5
+ #include "../utils/SimdOps.h"
6
+ #include <vector>
7
+ #include <stdexcept>
8
+ #include <string>
9
+
10
+ namespace dsp::adapters
11
+ {
12
+ enum class CmaMode
13
+ {
14
+ Batch,
15
+ Moving
16
+ };
17
+
18
+ class CumulativeMovingAverageStage : public IDspStage
19
+ {
20
+ public:
21
+ /**
22
+ * @brief Constructs a new Cumulative Moving Average Stage.
23
+ * @param mode The averaging mode (Batch or Moving).
24
+ */
25
+ explicit CumulativeMovingAverageStage(CmaMode mode)
26
+ : m_mode(mode)
27
+ {
28
+ }
29
+
30
+ // Return the type identifier for this stage
31
+ const char *getType() const override
32
+ {
33
+ return "cumulativeMovingAverage";
34
+ }
35
+
36
+ // This is the implementation of the interface method
37
+ void process(float *buffer, size_t numSamples, int numChannels, const float *timestamps = nullptr) override
38
+ {
39
+ if (m_mode == CmaMode::Batch)
40
+ {
41
+ processBatch(buffer, numSamples, numChannels);
42
+ }
43
+ else // CmaMode::Moving
44
+ {
45
+ processMoving(buffer, numSamples, numChannels);
46
+ }
47
+ }
48
+
49
+ // Serialize the stage's state to a Napi::Object
50
+ Napi::Object serializeState(Napi::Env env) const override
51
+ {
52
+ Napi::Object state = Napi::Object::New(env);
53
+ std::string modeStr = (m_mode == CmaMode::Moving) ? "moving" : "batch";
54
+ state.Set("mode", modeStr);
55
+
56
+ if (m_mode == CmaMode::Moving)
57
+ {
58
+ state.Set("numChannels", static_cast<uint32_t>(m_filters.size()));
59
+
60
+ // Serialize each channel's filter state
61
+ Napi::Array channelsArray = Napi::Array::New(env, m_filters.size());
62
+ for (size_t i = 0; i < m_filters.size(); ++i)
63
+ {
64
+ Napi::Object channelState = Napi::Object::New(env);
65
+
66
+ // Get the filter's internal state
67
+ auto [sum, count] = m_filters[i].getState();
68
+
69
+ channelState.Set("sum", Napi::Number::New(env, sum));
70
+ channelState.Set("count", Napi::Number::New(env, static_cast<uint32_t>(count)));
71
+
72
+ channelsArray.Set(static_cast<uint32_t>(i), channelState);
73
+ }
74
+ state.Set("channels", channelsArray);
75
+ }
76
+
77
+ return state;
78
+ }
79
+
80
+ // Deserialize and restore the stage's state
81
+ void deserializeState(const Napi::Object &state) override
82
+ {
83
+ std::string modeStr = state.Get("mode").As<Napi::String>().Utf8Value();
84
+ CmaMode newMode = (modeStr == "moving") ? CmaMode::Moving : CmaMode::Batch;
85
+
86
+ if (newMode != m_mode)
87
+ {
88
+ throw std::runtime_error("CumulativeMovingAverage mode mismatch during deserialization");
89
+ }
90
+
91
+ if (m_mode == CmaMode::Moving)
92
+ {
93
+ // Get number of channels
94
+ uint32_t numChannels = state.Get("channels").As<Napi::Array>().Length();
95
+
96
+ // Recreate filters
97
+ m_filters.clear();
98
+ for (uint32_t i = 0; i < numChannels; ++i)
99
+ {
100
+ m_filters.emplace_back();
101
+ }
102
+
103
+ // Restore each channel's state
104
+ Napi::Array channelsArray = state.Get("channels").As<Napi::Array>();
105
+ for (uint32_t i = 0; i < numChannels; ++i)
106
+ {
107
+ Napi::Object channelState = channelsArray.Get(i).As<Napi::Object>();
108
+
109
+ float sum = channelState.Get("sum").As<Napi::Number>().FloatValue();
110
+ size_t count = channelState.Get("count").As<Napi::Number>().Uint32Value();
111
+
112
+ // Restore the filter's state
113
+ m_filters[i].setState(sum, count);
114
+ }
115
+ }
116
+ }
117
+
118
+ // Reset all filters to initial state
119
+ void reset() override
120
+ {
121
+ for (auto &filter : m_filters)
122
+ {
123
+ filter.clear();
124
+ }
125
+ }
126
+
127
+ // TOON Binary Serialization
128
+ void serializeToon(dsp::toon::Serializer &serializer) const override
129
+ {
130
+ serializer.startObject();
131
+
132
+ // 1. Mode
133
+ serializer.writeString("mode");
134
+ serializer.writeString((m_mode == CmaMode::Moving) ? "moving" : "batch");
135
+
136
+ if (m_mode == CmaMode::Moving)
137
+ {
138
+ // 2. Channels
139
+ serializer.writeString("channels");
140
+ serializer.startArray();
141
+
142
+ for (const auto &filter : m_filters)
143
+ {
144
+ auto [sum, count] = filter.getState();
145
+
146
+ serializer.startObject();
147
+
148
+ serializer.writeString("sum");
149
+ serializer.writeFloat(sum);
150
+
151
+ serializer.writeString("count");
152
+ serializer.writeInt32(static_cast<int32_t>(count));
153
+
154
+ serializer.endObject();
155
+ }
156
+ serializer.endArray();
157
+ }
158
+
159
+ serializer.endObject();
160
+ }
161
+
162
+ // TOON Binary Deserialization
163
+ void deserializeToon(dsp::toon::Deserializer &deserializer) override
164
+ {
165
+ deserializer.consumeToken(dsp::toon::T_OBJECT_START);
166
+
167
+ // 1. Mode
168
+ std::string key = deserializer.readString(); // "mode"
169
+ std::string modeStr = deserializer.readString();
170
+
171
+ CmaMode newMode = (modeStr == "moving") ? CmaMode::Moving : CmaMode::Batch;
172
+ if (newMode != m_mode)
173
+ {
174
+ throw std::runtime_error("CumulativeMovingAverage mode mismatch during TOON deserialization");
175
+ }
176
+
177
+ if (modeStr == "moving")
178
+ {
179
+ // 2. Channels
180
+ key = deserializer.readString(); // "channels"
181
+ deserializer.consumeToken(dsp::toon::T_ARRAY_START);
182
+
183
+ m_filters.clear();
184
+
185
+ while (deserializer.peekToken() != dsp::toon::T_ARRAY_END)
186
+ {
187
+ deserializer.consumeToken(dsp::toon::T_OBJECT_START);
188
+
189
+ // Sum
190
+ deserializer.readString(); // "sum"
191
+ float sum = deserializer.readFloat();
192
+
193
+ // Count
194
+ deserializer.readString(); // "count"
195
+ int32_t count = deserializer.readInt32();
196
+
197
+ // Reconstruct filter
198
+ m_filters.emplace_back();
199
+ m_filters.back().setState(sum, static_cast<size_t>(count));
200
+
201
+ deserializer.consumeToken(dsp::toon::T_OBJECT_END);
202
+ }
203
+ deserializer.consumeToken(dsp::toon::T_ARRAY_END);
204
+ }
205
+
206
+ deserializer.consumeToken(dsp::toon::T_OBJECT_END);
207
+ }
208
+
209
+ private:
210
+ /**
211
+ * @brief Batch mode: Compute cumulative moving average over entire buffer per channel.
212
+ * Each sample in the channel is replaced by the cumulative average up to that point.
213
+ */
214
+ void processBatch(float *buffer, size_t numSamples, int numChannels)
215
+ {
216
+ // Process each channel independently
217
+ for (int c = 0; c < numChannels; ++c)
218
+ {
219
+ float sum = 0.0f;
220
+ size_t count = 0;
221
+
222
+ // Process all samples for this channel
223
+ for (size_t i = c; i < numSamples; i += numChannels)
224
+ {
225
+ sum += buffer[i];
226
+ count++;
227
+ // CMA = sum / count
228
+ buffer[i] = sum / static_cast<float>(count);
229
+ }
230
+ }
231
+ }
232
+
233
+ /**
234
+ * @brief Moving mode: Statefully process samples using CMA filters with continuity.
235
+ * @param buffer The interleaved audio buffer.
236
+ * @param numSamples The total number of samples.
237
+ * @param numChannels The number of channels.
238
+ */
239
+ void processMoving(float *buffer, size_t numSamples, int numChannels)
240
+ {
241
+ // Lazily initialize our filters, one for each channel
242
+ if (m_filters.size() != static_cast<size_t>(numChannels))
243
+ {
244
+ m_filters.clear();
245
+ for (int i = 0; i < numChannels; ++i)
246
+ {
247
+ m_filters.emplace_back();
248
+ }
249
+ }
250
+
251
+ // Process the buffer sample by sample, de-interleaving
252
+ for (size_t i = 0; i < numSamples; ++i)
253
+ {
254
+ int channel = i % numChannels;
255
+ buffer[i] = m_filters[channel].addSample(buffer[i]);
256
+ }
257
+ }
258
+
259
+ CmaMode m_mode;
260
+ // Separate filter instance for each channel's state
261
+ std::vector<dsp::core::CumulativeMovingAverageFilter<float>> m_filters;
262
+ };
263
+
264
+ } // namespace dsp::adapters
@@ -215,6 +215,86 @@ namespace dsp
215
215
  }
216
216
  }
217
217
 
218
+ void serializeToon(dsp::toon::Serializer &s) const override
219
+ {
220
+ s.startObject();
221
+
222
+ s.writeString("factor");
223
+ s.writeInt32(decimationFactor_);
224
+
225
+ s.writeString("order");
226
+ s.writeInt32(filterOrder_);
227
+
228
+ s.writeString("sampleRate");
229
+ s.writeDouble(sampleRate_);
230
+
231
+ s.writeString("phaseIndex");
232
+ s.writeInt32(phaseIndex_);
233
+
234
+ s.writeString("numChannels");
235
+ s.writeInt32(numChannels_);
236
+
237
+ s.writeString("stateBuffer");
238
+ s.writeFloatArray(stateBuffer_);
239
+
240
+ s.writeString("stateIndices");
241
+ s.startArray();
242
+ for (size_t idx : stateIndices_)
243
+ {
244
+ s.writeInt32(static_cast<int32_t>(idx));
245
+ }
246
+ s.endArray();
247
+
248
+ s.endObject();
249
+ }
250
+
251
+ void deserializeToon(dsp::toon::Deserializer &d) override
252
+ {
253
+ d.consumeToken(dsp::toon::T_OBJECT_START);
254
+
255
+ std::string key = d.readString(); // "factor"
256
+ int factor = d.readInt32();
257
+ if (factor != decimationFactor_)
258
+ {
259
+ throw std::runtime_error("Decimator factor mismatch during TOON deserialization");
260
+ }
261
+
262
+ key = d.readString(); // "order"
263
+ int order = d.readInt32();
264
+ if (order != filterOrder_)
265
+ {
266
+ throw std::runtime_error("Decimator order mismatch during TOON deserialization");
267
+ }
268
+
269
+ key = d.readString(); // "sampleRate"
270
+ d.readDouble(); // Skip sampleRate
271
+
272
+ key = d.readString(); // "phaseIndex"
273
+ phaseIndex_ = d.readInt32();
274
+
275
+ key = d.readString(); // "numChannels"
276
+ int numCh = d.readInt32();
277
+
278
+ if (numCh != numChannels_)
279
+ {
280
+ initializeStateBuffers(numCh);
281
+ }
282
+
283
+ key = d.readString(); // "stateBuffer"
284
+ stateBuffer_ = d.readFloatArray();
285
+
286
+ key = d.readString(); // "stateIndices"
287
+ d.consumeToken(dsp::toon::T_ARRAY_START);
288
+ stateIndices_.clear();
289
+ while (d.peekToken() != dsp::toon::T_ARRAY_END)
290
+ {
291
+ stateIndices_.push_back(static_cast<size_t>(d.readInt32()));
292
+ }
293
+ d.consumeToken(dsp::toon::T_ARRAY_END);
294
+
295
+ d.consumeToken(dsp::toon::T_OBJECT_END);
296
+ }
297
+
218
298
  /**
219
299
  * Get decimation factor
220
300
  */
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include "../IDspStage.h"
4
+ #include "../utils/Toon.h"
4
5
  #include <stdexcept>
5
6
  #include <string>
6
7
  #include <vector>
@@ -92,6 +93,18 @@ namespace dsp::adapters
92
93
  std::fill(m_prev_sample.begin(), m_prev_sample.end(), 0.0f);
93
94
  }
94
95
 
96
+ void serializeToon(dsp::toon::Serializer &s) const override
97
+ {
98
+ s.writeInt32(m_num_channels);
99
+ s.writeFloatArray(m_prev_sample);
100
+ }
101
+
102
+ void deserializeToon(dsp::toon::Deserializer &d) override
103
+ {
104
+ m_num_channels = d.readInt32();
105
+ m_prev_sample = d.readFloatArray();
106
+ }
107
+
95
108
  bool isResizing() const override { return false; }
96
109
 
97
110
  private: