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
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include "../IDspStage.h"
|
|
3
|
+
#include "../core/IirFilter.h"
|
|
4
|
+
#include "../utils/SimdOps.h"
|
|
5
|
+
#include <vector>
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <stdexcept>
|
|
8
|
+
|
|
9
|
+
namespace dsp::adapters
|
|
10
|
+
{
|
|
11
|
+
/**
|
|
12
|
+
* @brief Lightweight struct to pass filter coefficients from JavaScript
|
|
13
|
+
*/
|
|
14
|
+
struct FilterDefinition
|
|
15
|
+
{
|
|
16
|
+
std::vector<double> b; // Feedforward coefficients
|
|
17
|
+
std::vector<double> a; // Feedback coefficients
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @brief Filter Bank Stage - Splits N input channels into N × M sub-bands
|
|
22
|
+
*
|
|
23
|
+
* Architecture:
|
|
24
|
+
* - Input: N channels (interleaved)
|
|
25
|
+
* - Output: N × M channels (interleaved, channel-major layout)
|
|
26
|
+
* - Layout: [ (Ch1_Band1, Ch1_Band2, ...), (Ch2_Band1, Ch2_Band2, ...) ]
|
|
27
|
+
*
|
|
28
|
+
* Optimization Strategy (Planar-Core):
|
|
29
|
+
* 1. De-interleave input to planar scratch buffers (cache-efficient)
|
|
30
|
+
* 2. Process filters on contiguous planar data (SIMD-friendly)
|
|
31
|
+
* 3. Interleave output back to standard format
|
|
32
|
+
*
|
|
33
|
+
* Use Cases:
|
|
34
|
+
* - Speech recognition: Mel-scale filter banks (20-40 bands)
|
|
35
|
+
* - Audio compression: Bark-scale for psychoacoustic analysis
|
|
36
|
+
* - Musical analysis: Octave bands (log scale)
|
|
37
|
+
* - Research: Linear-scale frequency analysis
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // 2 input channels → 20 output channels (10 bands per channel)
|
|
41
|
+
* // Input: [L, R, L, R, ...]
|
|
42
|
+
* // Output: [L_B1, L_B2, ..., L_B10, R_B1, R_B2, ..., R_B10, ...]
|
|
43
|
+
*/
|
|
44
|
+
class FilterBankStage : public dsp::IDspStage
|
|
45
|
+
{
|
|
46
|
+
public:
|
|
47
|
+
/**
|
|
48
|
+
* @brief Construct a filter bank stage
|
|
49
|
+
* @param bandDefinitions Array of filter coefficient definitions (one per band)
|
|
50
|
+
* @param numInputChannels Number of input channels
|
|
51
|
+
*/
|
|
52
|
+
FilterBankStage(const std::vector<FilterDefinition> &bandDefinitions, int numInputChannels);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @brief Destructor - ensures proper cleanup of filters and scratch buffers
|
|
56
|
+
*/
|
|
57
|
+
virtual ~FilterBankStage();
|
|
58
|
+
|
|
59
|
+
const char *getType() const override { return "filterBank"; }
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @brief This stage expands the buffer size (N channels → N×M channels)
|
|
63
|
+
* @return Always true
|
|
64
|
+
*/
|
|
65
|
+
bool isResizing() const override { return true; }
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @brief Returns output channel count = inputChannels × numBands
|
|
69
|
+
* @return Output channel count
|
|
70
|
+
*/
|
|
71
|
+
int getOutputChannels() const override
|
|
72
|
+
{
|
|
73
|
+
return m_numInputChannels * static_cast<int>(m_definitions.size());
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @brief Calculate output buffer size for resizing
|
|
78
|
+
* @param inputSize Input buffer size in samples
|
|
79
|
+
* @return Output buffer size in samples
|
|
80
|
+
*/
|
|
81
|
+
size_t calculateOutputSize(size_t inputSize) const override
|
|
82
|
+
{
|
|
83
|
+
return (inputSize / m_numInputChannels) * getOutputChannels();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @brief Standard process() throws because this stage requires resizing
|
|
88
|
+
*/
|
|
89
|
+
void process(float *buffer, size_t numSamples, int numChannels, const float *timestamps) override
|
|
90
|
+
{
|
|
91
|
+
throw std::runtime_error("FilterBank requires processResizing");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @brief Process audio data with buffer resizing
|
|
96
|
+
*
|
|
97
|
+
* Algorithm:
|
|
98
|
+
* 1. De-interleave input to planar scratch buffers
|
|
99
|
+
* 2. Apply each band's filter to each input channel (planar processing)
|
|
100
|
+
* 3. Interleave results to output in channel-major order
|
|
101
|
+
*
|
|
102
|
+
* @param inputBuffer Source interleaved buffer
|
|
103
|
+
* @param inputSize Input size in samples
|
|
104
|
+
* @param outputBuffer Destination buffer (caller must allocate)
|
|
105
|
+
* @param outputSize Reference to store actual output size
|
|
106
|
+
* @param numChannels Number of input channels
|
|
107
|
+
* @param timestamps Optional timestamp array
|
|
108
|
+
*/
|
|
109
|
+
void processResizing(const float *inputBuffer, size_t inputSize,
|
|
110
|
+
float *outputBuffer, size_t &outputSize,
|
|
111
|
+
int numChannels, const float *timestamps) override;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @brief Reset all filter states to initial conditions
|
|
115
|
+
*/
|
|
116
|
+
void reset() override;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @brief Serialize filter bank state to JavaScript object
|
|
120
|
+
* @param env N-API environment
|
|
121
|
+
* @return Napi::Object containing all filter states
|
|
122
|
+
*/
|
|
123
|
+
Napi::Object serializeState(Napi::Env env) const override;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @brief Restore filter bank state from JavaScript object
|
|
127
|
+
* @param state Napi::Object containing serialized filter states
|
|
128
|
+
*/
|
|
129
|
+
void deserializeState(const Napi::Object &state) override;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @brief Serialize state to TOON binary format
|
|
133
|
+
* @param serializer TOON serializer instance
|
|
134
|
+
*/
|
|
135
|
+
void serializeToon(toon::Serializer &serializer) const override;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @brief Deserialize state from TOON binary format
|
|
139
|
+
* @param deserializer TOON deserializer instance
|
|
140
|
+
*/
|
|
141
|
+
void deserializeToon(toon::Deserializer &deserializer) override;
|
|
142
|
+
|
|
143
|
+
private:
|
|
144
|
+
/**
|
|
145
|
+
* @brief Initialize the 2D matrix of IIR filters [channel][band]
|
|
146
|
+
*/
|
|
147
|
+
void initializeFilters();
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @brief Ensure scratch buffers are allocated and sized correctly
|
|
151
|
+
* @param samplesPerChannel Number of samples per channel to process
|
|
152
|
+
*/
|
|
153
|
+
void ensureScratchSize(size_t samplesPerChannel);
|
|
154
|
+
|
|
155
|
+
// Configuration
|
|
156
|
+
int m_numInputChannels;
|
|
157
|
+
std::vector<FilterDefinition> m_definitions;
|
|
158
|
+
|
|
159
|
+
// 2D Matrix of filters: m_filters[channelIndex][bandIndex]
|
|
160
|
+
std::vector<std::vector<std::unique_ptr<dsp::core::IirFilter<float>>>> m_filters;
|
|
161
|
+
|
|
162
|
+
// Optimization: Persistent scratch buffers in planar layout
|
|
163
|
+
// m_planarInput[channel][sample] - de-interleaved input
|
|
164
|
+
std::vector<std::vector<float>> m_planarInput;
|
|
165
|
+
|
|
166
|
+
// m_planarOutput[flatOutputChannelIndex][sample] - filtered planar data
|
|
167
|
+
std::vector<std::vector<float>> m_planarOutput;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
} // namespace dsp::adapters
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#include "FilterStage.h"
|
|
2
2
|
#include <stdexcept>
|
|
3
|
+
#include <iostream> // For optional debug logging
|
|
3
4
|
|
|
4
5
|
namespace dsp::adapters
|
|
5
6
|
{
|
|
@@ -303,4 +304,125 @@ namespace dsp::adapters
|
|
|
303
304
|
}
|
|
304
305
|
}
|
|
305
306
|
|
|
307
|
+
void FilterStage::serializeToon(dsp::toon::Serializer &s) const
|
|
308
|
+
{
|
|
309
|
+
// Serialize filter type
|
|
310
|
+
s.writeString(m_isFir ? "fir" : "iir");
|
|
311
|
+
|
|
312
|
+
// Serialize coefficients
|
|
313
|
+
s.writeInt32(static_cast<int32_t>(m_bCoeffs.size()));
|
|
314
|
+
for (double coeff : m_bCoeffs)
|
|
315
|
+
{
|
|
316
|
+
s.writeDouble(coeff);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
s.writeInt32(static_cast<int32_t>(m_aCoeffs.size()));
|
|
320
|
+
for (double coeff : m_aCoeffs)
|
|
321
|
+
{
|
|
322
|
+
s.writeDouble(coeff);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Serialize filter states
|
|
326
|
+
if (m_isFir)
|
|
327
|
+
{
|
|
328
|
+
s.writeInt32(static_cast<int32_t>(m_firFilters.size()));
|
|
329
|
+
for (const auto &filter : m_firFilters)
|
|
330
|
+
{
|
|
331
|
+
if (filter)
|
|
332
|
+
{
|
|
333
|
+
auto [stateBuffer, stateIndex] = filter->getState();
|
|
334
|
+
s.writeFloatArray(stateBuffer);
|
|
335
|
+
s.writeInt32(static_cast<int32_t>(stateIndex));
|
|
336
|
+
}
|
|
337
|
+
else
|
|
338
|
+
{
|
|
339
|
+
s.writeInt32(0); // Empty state buffer size
|
|
340
|
+
s.writeInt32(0); // stateIndex
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
else // IIR
|
|
345
|
+
{
|
|
346
|
+
s.writeInt32(static_cast<int32_t>(m_iirFilters.size()));
|
|
347
|
+
for (const auto &filter : m_iirFilters)
|
|
348
|
+
{
|
|
349
|
+
if (filter)
|
|
350
|
+
{
|
|
351
|
+
auto [xState, yState] = filter->getState();
|
|
352
|
+
s.writeFloatArray(xState);
|
|
353
|
+
s.writeFloatArray(yState);
|
|
354
|
+
}
|
|
355
|
+
else
|
|
356
|
+
{
|
|
357
|
+
s.writeInt32(0); // Empty xState size
|
|
358
|
+
s.writeInt32(0); // Empty yState size
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
void FilterStage::deserializeToon(dsp::toon::Deserializer &d)
|
|
365
|
+
{
|
|
366
|
+
// Deserialize filter type
|
|
367
|
+
std::string filterType = d.readString();
|
|
368
|
+
bool isFir = (filterType == "fir");
|
|
369
|
+
|
|
370
|
+
if (isFir != m_isFir)
|
|
371
|
+
{
|
|
372
|
+
throw std::runtime_error("FilterStage TOON load: filter type mismatch");
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Deserialize and validate coefficients
|
|
376
|
+
int32_t bSize = d.readInt32();
|
|
377
|
+
if (bSize != static_cast<int32_t>(m_bCoeffs.size()))
|
|
378
|
+
{
|
|
379
|
+
throw std::runtime_error("FilterStage TOON load: bCoeffs size mismatch");
|
|
380
|
+
}
|
|
381
|
+
for (int32_t i = 0; i < bSize; ++i)
|
|
382
|
+
{
|
|
383
|
+
d.readDouble(); // Skip validation for now
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
int32_t aSize = d.readInt32();
|
|
387
|
+
if (aSize != static_cast<int32_t>(m_aCoeffs.size()))
|
|
388
|
+
{
|
|
389
|
+
throw std::runtime_error("FilterStage TOON load: aCoeffs size mismatch");
|
|
390
|
+
}
|
|
391
|
+
for (int32_t i = 0; i < aSize; ++i)
|
|
392
|
+
{
|
|
393
|
+
d.readDouble(); // Skip validation for now
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Deserialize filter states
|
|
397
|
+
int32_t numChannels = d.readInt32();
|
|
398
|
+
initializeFilters(numChannels);
|
|
399
|
+
|
|
400
|
+
if (m_isFir)
|
|
401
|
+
{
|
|
402
|
+
for (int32_t i = 0; i < numChannels && i < static_cast<int32_t>(m_firFilters.size()); ++i)
|
|
403
|
+
{
|
|
404
|
+
std::vector<float> stateBuffer = d.readFloatArray();
|
|
405
|
+
int32_t stateIndex = d.readInt32();
|
|
406
|
+
|
|
407
|
+
if (m_firFilters[i] && !stateBuffer.empty())
|
|
408
|
+
{
|
|
409
|
+
m_firFilters[i]->setState(stateBuffer, static_cast<size_t>(stateIndex));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
else // IIR
|
|
414
|
+
{
|
|
415
|
+
for (int32_t i = 0; i < numChannels && i < static_cast<int32_t>(m_iirFilters.size()); ++i)
|
|
416
|
+
{
|
|
417
|
+
std::vector<float> xState = d.readFloatArray();
|
|
418
|
+
std::vector<float> yState = d.readFloatArray();
|
|
419
|
+
|
|
420
|
+
if (m_iirFilters[i] && !xState.empty())
|
|
421
|
+
{
|
|
422
|
+
m_iirFilters[i]->setState(xState, yState);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
306
428
|
} // namespace dsp::adapters
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include "../IDspStage.h"
|
|
4
4
|
#include "../core/IirFilter.h"
|
|
5
5
|
#include "../core/FirFilter.h"
|
|
6
|
+
#include "../utils/Toon.h"
|
|
6
7
|
#include <memory>
|
|
7
8
|
#include <vector>
|
|
8
9
|
#include <string>
|
|
@@ -23,6 +24,9 @@ namespace dsp::adapters
|
|
|
23
24
|
Napi::Object serializeState(Napi::Env env) const override;
|
|
24
25
|
void deserializeState(const Napi::Object &state) override;
|
|
25
26
|
|
|
27
|
+
void serializeToon(dsp::toon::Serializer &serializer) const override;
|
|
28
|
+
void deserializeToon(dsp::toon::Deserializer &deserializer) override;
|
|
29
|
+
|
|
26
30
|
private:
|
|
27
31
|
void initializeFilters(int numChannels);
|
|
28
32
|
|
|
@@ -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
|
{
|