dspx 0.1.1-alpha.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 (172) hide show
  1. package/.github/workflows/ci.yml +185 -0
  2. package/.vscode/c_cpp_properties.json +17 -0
  3. package/.vscode/settings.json +68 -0
  4. package/.vscode/tasks.json +28 -0
  5. package/DISCLAIMER.md +32 -0
  6. package/LICENSE +21 -0
  7. package/README.md +1803 -0
  8. package/ROADMAP.md +192 -0
  9. package/TECHNICAL_DEBT.md +165 -0
  10. package/binding.gyp +65 -0
  11. package/docs/ADVANCED_LOGGER_FEATURES.md +598 -0
  12. package/docs/AUTHENTICATION_SECURITY.md +396 -0
  13. package/docs/BACKEND_IMPROVEMENTS.md +399 -0
  14. package/docs/CHEBYSHEV_BIQUAD_EQ_IMPLEMENTATION.md +405 -0
  15. package/docs/FFT_IMPLEMENTATION.md +490 -0
  16. package/docs/FFT_IMPROVEMENTS_SUMMARY.md +387 -0
  17. package/docs/FFT_USER_GUIDE.md +494 -0
  18. package/docs/FILTERS_IMPLEMENTATION.md +260 -0
  19. package/docs/FILTER_API_GUIDE.md +418 -0
  20. package/docs/FIR_SIMD_OPTIMIZATION.md +175 -0
  21. package/docs/LOGGER_API_REFERENCE.md +350 -0
  22. package/docs/NOTCH_FILTER_QUICK_REF.md +121 -0
  23. package/docs/PHASE2_TESTS_AND_NOTCH_FILTER.md +341 -0
  24. package/docs/PHASES_5_7_SUMMARY.md +403 -0
  25. package/docs/PIPELINE_FILTER_INTEGRATION.md +446 -0
  26. package/docs/SIMD_OPTIMIZATIONS.md +211 -0
  27. package/docs/TEST_MIGRATION_SUMMARY.md +173 -0
  28. package/docs/TIMESERIES_IMPLEMENTATION_SUMMARY.md +322 -0
  29. package/docs/TIMESERIES_QUICK_REF.md +85 -0
  30. package/docs/advanced.md +559 -0
  31. package/docs/time-series-guide.md +617 -0
  32. package/docs/time-series-migration.md +376 -0
  33. package/jest.config.js +37 -0
  34. package/package.json +42 -0
  35. package/prebuilds/linux-x64/dsp-ts-redis.node +0 -0
  36. package/prebuilds/win32-x64/dsp-ts-redis.node +0 -0
  37. package/scripts/test.js +24 -0
  38. package/src/build/dsp-ts-redis.node +0 -0
  39. package/src/native/DspPipeline.cc +675 -0
  40. package/src/native/DspPipeline.h +44 -0
  41. package/src/native/FftBindings.cc +817 -0
  42. package/src/native/FilterBindings.cc +1001 -0
  43. package/src/native/IDspStage.h +53 -0
  44. package/src/native/adapters/InterpolatorStage.h +201 -0
  45. package/src/native/adapters/MeanAbsoluteValueStage.h +289 -0
  46. package/src/native/adapters/MovingAverageStage.h +306 -0
  47. package/src/native/adapters/RectifyStage.h +88 -0
  48. package/src/native/adapters/ResamplerStage.h +238 -0
  49. package/src/native/adapters/RmsStage.h +299 -0
  50. package/src/native/adapters/SscStage.h +121 -0
  51. package/src/native/adapters/VarianceStage.h +307 -0
  52. package/src/native/adapters/WampStage.h +114 -0
  53. package/src/native/adapters/WaveformLengthStage.h +115 -0
  54. package/src/native/adapters/ZScoreNormalizeStage.h +326 -0
  55. package/src/native/core/FftEngine.cc +441 -0
  56. package/src/native/core/FftEngine.h +224 -0
  57. package/src/native/core/FirFilter.cc +324 -0
  58. package/src/native/core/FirFilter.h +149 -0
  59. package/src/native/core/IirFilter.cc +576 -0
  60. package/src/native/core/IirFilter.h +210 -0
  61. package/src/native/core/MovingAbsoluteValueFilter.cc +17 -0
  62. package/src/native/core/MovingAbsoluteValueFilter.h +135 -0
  63. package/src/native/core/MovingAverageFilter.cc +18 -0
  64. package/src/native/core/MovingAverageFilter.h +135 -0
  65. package/src/native/core/MovingFftFilter.cc +291 -0
  66. package/src/native/core/MovingFftFilter.h +203 -0
  67. package/src/native/core/MovingVarianceFilter.cc +194 -0
  68. package/src/native/core/MovingVarianceFilter.h +114 -0
  69. package/src/native/core/MovingZScoreFilter.cc +215 -0
  70. package/src/native/core/MovingZScoreFilter.h +113 -0
  71. package/src/native/core/Policies.h +352 -0
  72. package/src/native/core/RmsFilter.cc +18 -0
  73. package/src/native/core/RmsFilter.h +131 -0
  74. package/src/native/core/SscFilter.cc +16 -0
  75. package/src/native/core/SscFilter.h +137 -0
  76. package/src/native/core/WampFilter.cc +16 -0
  77. package/src/native/core/WampFilter.h +101 -0
  78. package/src/native/core/WaveformLengthFilter.cc +17 -0
  79. package/src/native/core/WaveformLengthFilter.h +98 -0
  80. package/src/native/utils/CircularBufferArray.cc +336 -0
  81. package/src/native/utils/CircularBufferArray.h +62 -0
  82. package/src/native/utils/CircularBufferVector.cc +145 -0
  83. package/src/native/utils/CircularBufferVector.h +45 -0
  84. package/src/native/utils/NapiUtils.cc +53 -0
  85. package/src/native/utils/NapiUtils.h +21 -0
  86. package/src/native/utils/SimdOps.h +870 -0
  87. package/src/native/utils/SlidingWindowFilter.cc +239 -0
  88. package/src/native/utils/SlidingWindowFilter.h +159 -0
  89. package/src/native/utils/TimeSeriesBuffer.cc +205 -0
  90. package/src/native/utils/TimeSeriesBuffer.h +140 -0
  91. package/src/ts/CircularLogBuffer.ts +87 -0
  92. package/src/ts/DriftDetector.ts +331 -0
  93. package/src/ts/TopicRouter.ts +428 -0
  94. package/src/ts/__tests__/AdvancedDsp.test.ts +585 -0
  95. package/src/ts/__tests__/AuthAndEdgeCases.test.ts +241 -0
  96. package/src/ts/__tests__/Chaining.test.ts +387 -0
  97. package/src/ts/__tests__/ChebyshevBiquad.test.ts +229 -0
  98. package/src/ts/__tests__/CircularLogBuffer.test.ts +158 -0
  99. package/src/ts/__tests__/DriftDetector.test.ts +389 -0
  100. package/src/ts/__tests__/Fft.test.ts +484 -0
  101. package/src/ts/__tests__/ListState.test.ts +153 -0
  102. package/src/ts/__tests__/Logger.test.ts +208 -0
  103. package/src/ts/__tests__/LoggerAdvanced.test.ts +319 -0
  104. package/src/ts/__tests__/LoggerMinor.test.ts +247 -0
  105. package/src/ts/__tests__/MeanAbsoluteValue.test.ts +398 -0
  106. package/src/ts/__tests__/MovingAverage.test.ts +322 -0
  107. package/src/ts/__tests__/RMS.test.ts +315 -0
  108. package/src/ts/__tests__/Rectify.test.ts +272 -0
  109. package/src/ts/__tests__/Redis.test.ts +456 -0
  110. package/src/ts/__tests__/SlopeSignChange.test.ts +166 -0
  111. package/src/ts/__tests__/Tap.test.ts +164 -0
  112. package/src/ts/__tests__/TimeBasedExpiration.test.ts +124 -0
  113. package/src/ts/__tests__/TimeBasedRmsAndMav.test.ts +231 -0
  114. package/src/ts/__tests__/TimeBasedVarianceAndZScore.test.ts +284 -0
  115. package/src/ts/__tests__/TimeSeries.test.ts +254 -0
  116. package/src/ts/__tests__/TopicRouter.test.ts +332 -0
  117. package/src/ts/__tests__/TopicRouterAdvanced.test.ts +483 -0
  118. package/src/ts/__tests__/TopicRouterPriority.test.ts +487 -0
  119. package/src/ts/__tests__/Variance.test.ts +509 -0
  120. package/src/ts/__tests__/WaveformLength.test.ts +147 -0
  121. package/src/ts/__tests__/WillisonAmplitude.test.ts +197 -0
  122. package/src/ts/__tests__/ZScoreNormalize.test.ts +459 -0
  123. package/src/ts/advanced-dsp.ts +566 -0
  124. package/src/ts/backends.ts +1137 -0
  125. package/src/ts/bindings.ts +1225 -0
  126. package/src/ts/easter-egg.ts +42 -0
  127. package/src/ts/examples/MeanAbsoluteValue/test-state.ts +99 -0
  128. package/src/ts/examples/MeanAbsoluteValue/test-streaming.ts +269 -0
  129. package/src/ts/examples/MovingAverage/test-state.ts +85 -0
  130. package/src/ts/examples/MovingAverage/test-streaming.ts +188 -0
  131. package/src/ts/examples/RMS/test-state.ts +97 -0
  132. package/src/ts/examples/RMS/test-streaming.ts +253 -0
  133. package/src/ts/examples/Rectify/test-state.ts +107 -0
  134. package/src/ts/examples/Rectify/test-streaming.ts +242 -0
  135. package/src/ts/examples/Variance/test-state.ts +195 -0
  136. package/src/ts/examples/Variance/test-streaming.ts +260 -0
  137. package/src/ts/examples/ZScoreNormalize/test-state.ts +277 -0
  138. package/src/ts/examples/ZScoreNormalize/test-streaming.ts +306 -0
  139. package/src/ts/examples/advanced-dsp-examples.ts +397 -0
  140. package/src/ts/examples/callbacks/advanced-router-features.ts +326 -0
  141. package/src/ts/examples/callbacks/benchmark-circular-buffer.ts +109 -0
  142. package/src/ts/examples/callbacks/monitoring-example.ts +265 -0
  143. package/src/ts/examples/callbacks/pipeline-callbacks-example.ts +137 -0
  144. package/src/ts/examples/callbacks/pooled-callbacks-example.ts +274 -0
  145. package/src/ts/examples/callbacks/priority-routing-example.ts +277 -0
  146. package/src/ts/examples/callbacks/production-topic-router.ts +214 -0
  147. package/src/ts/examples/callbacks/topic-based-logging.ts +161 -0
  148. package/src/ts/examples/chaining/test-chaining-redis.ts +113 -0
  149. package/src/ts/examples/chaining/test-chaining.ts +52 -0
  150. package/src/ts/examples/emg-features-example.ts +284 -0
  151. package/src/ts/examples/fft-example.ts +309 -0
  152. package/src/ts/examples/fft-examples.ts +349 -0
  153. package/src/ts/examples/filter-examples.ts +320 -0
  154. package/src/ts/examples/list-state-example.ts +131 -0
  155. package/src/ts/examples/logger-example.ts +91 -0
  156. package/src/ts/examples/notch-filter-examples.ts +243 -0
  157. package/src/ts/examples/phase5/drift-detection-example.ts +290 -0
  158. package/src/ts/examples/phase6-7/production-observability.ts +476 -0
  159. package/src/ts/examples/phase6-7/redis-timeseries-integration.ts +446 -0
  160. package/src/ts/examples/redis/redis-example.ts +202 -0
  161. package/src/ts/examples/redis-example.ts +202 -0
  162. package/src/ts/examples/simd-benchmark.ts +126 -0
  163. package/src/ts/examples/tap-debugging.ts +230 -0
  164. package/src/ts/examples/timeseries/comparison-example.ts +290 -0
  165. package/src/ts/examples/timeseries/iot-sensor-example.ts +143 -0
  166. package/src/ts/examples/timeseries/redis-streaming-example.ts +233 -0
  167. package/src/ts/examples/waveform-length-example.ts +139 -0
  168. package/src/ts/fft.ts +722 -0
  169. package/src/ts/filters.ts +1078 -0
  170. package/src/ts/index.ts +120 -0
  171. package/src/ts/types.ts +589 -0
  172. package/tsconfig.json +15 -0
@@ -0,0 +1,326 @@
1
+ #pragma once
2
+
3
+ #include "../IDspStage.h"
4
+ #include "../core/MovingZScoreFilter.h"
5
+ #include <vector>
6
+ #include <string>
7
+ #include <stdexcept>
8
+ #include <cmath>
9
+ #include <numeric> // For std::accumulate
10
+ #include <algorithm> // For std::max
11
+
12
+ namespace dsp::adapters
13
+ {
14
+ enum class ZScoreNormalizeMode
15
+ {
16
+ Batch,
17
+ Moving
18
+ };
19
+
20
+ class ZScoreNormalizeStage : public IDspStage
21
+ {
22
+ public:
23
+ /**
24
+ * @brief Constructs a new Z-Score Normalize Stage.
25
+ * @param mode The variance mode (Batch or Moving).
26
+ * @param window_size The window size in samples (0 if using duration-based).
27
+ * @param window_duration_ms The window duration in milliseconds (0 if using size-based).
28
+ * @param epsilon A small value to prevent division by zero (default 1e-6).
29
+ */
30
+ explicit ZScoreNormalizeStage(ZScoreNormalizeMode mode, size_t window_size = 0, double window_duration_ms = 0.0, float epsilon = 1e-6f)
31
+ : m_mode(mode),
32
+ m_window_size(window_size),
33
+ m_window_duration_ms(window_duration_ms),
34
+ m_epsilon(epsilon),
35
+ m_is_initialized(window_size > 0)
36
+ {
37
+ if (m_mode == ZScoreNormalizeMode::Moving && window_size == 0 && window_duration_ms == 0.0)
38
+ {
39
+ throw std::invalid_argument("ZScoreNormalize: either window size or window duration must be greater than 0 for 'moving' mode");
40
+ }
41
+ }
42
+
43
+ // Return the type identifier for this stage
44
+ const char *getType() const override
45
+ {
46
+ return "zScoreNormalize";
47
+ }
48
+
49
+ // Implementation of the interface method
50
+ void process(float *buffer, size_t numSamples, int numChannels, const float *timestamps = nullptr) override
51
+ {
52
+ if (m_mode == ZScoreNormalizeMode::Batch)
53
+ {
54
+ processBatch(buffer, numSamples, numChannels);
55
+ }
56
+ else // ZScoreNormalizeMode::Moving
57
+ {
58
+ processMoving(buffer, numSamples, numChannels, timestamps);
59
+ }
60
+ }
61
+
62
+ // Serialize the stage's state
63
+ Napi::Object serializeState(Napi::Env env) const override
64
+ {
65
+ Napi::Object state = Napi::Object::New(env);
66
+ std::string modeStr = (m_mode == ZScoreNormalizeMode::Moving) ? "moving" : "batch";
67
+ state.Set("mode", modeStr);
68
+ state.Set("epsilon", Napi::Number::New(env, m_epsilon));
69
+
70
+ if (m_mode == ZScoreNormalizeMode::Moving)
71
+ {
72
+ state.Set("windowSize", static_cast<uint32_t>(m_window_size));
73
+ state.Set("numChannels", static_cast<uint32_t>(m_filters.size()));
74
+
75
+ // Serialize each channel's filter state
76
+ Napi::Array channelsArray = Napi::Array::New(env, m_filters.size());
77
+ for (size_t i = 0; i < m_filters.size(); ++i)
78
+ {
79
+ Napi::Object channelState = Napi::Object::New(env);
80
+
81
+ // Get the filter's internal state
82
+ auto [bufferData, sums] = m_filters[i].getState();
83
+ auto [runningSum, runningSumOfSquares] = sums;
84
+
85
+ // Convert buffer data to JavaScript array
86
+ Napi::Array bufferArray = Napi::Array::New(env, bufferData.size());
87
+ for (size_t j = 0; j < bufferData.size(); ++j)
88
+ {
89
+ bufferArray.Set(j, Napi::Number::New(env, bufferData[j]));
90
+ }
91
+
92
+ channelState.Set("buffer", bufferArray);
93
+ channelState.Set("runningSum", Napi::Number::New(env, runningSum));
94
+ channelState.Set("runningSumOfSquares", Napi::Number::New(env, runningSumOfSquares));
95
+
96
+ channelsArray.Set(static_cast<uint32_t>(i), channelState);
97
+ }
98
+ state.Set("channels", channelsArray);
99
+ }
100
+
101
+ return state;
102
+ }
103
+
104
+ // Deserialize and restore the stage's state
105
+ void deserializeState(const Napi::Object &state) override
106
+ {
107
+ std::string modeStr = state.Get("mode").As<Napi::String>().Utf8Value();
108
+ ZScoreNormalizeMode newMode = (modeStr == "moving") ? ZScoreNormalizeMode::Moving : ZScoreNormalizeMode::Batch;
109
+
110
+ if (newMode != m_mode)
111
+ {
112
+ throw std::runtime_error("ZScoreNormalize mode mismatch during deserialization");
113
+ }
114
+
115
+ m_epsilon = state.Get("epsilon").As<Napi::Number>().FloatValue();
116
+
117
+ if (m_mode == ZScoreNormalizeMode::Moving)
118
+ {
119
+ // Get window size and validate
120
+ size_t windowSize = state.Get("windowSize").As<Napi::Number>().Uint32Value();
121
+ if (windowSize != m_window_size)
122
+ {
123
+ throw std::runtime_error("Window size mismatch during deserialization");
124
+ }
125
+
126
+ // Get number of channels
127
+ uint32_t numChannels = state.Get("channels").As<Napi::Array>().Length();
128
+
129
+ // Recreate filters
130
+ m_filters.clear();
131
+ for (uint32_t i = 0; i < numChannels; ++i)
132
+ {
133
+ m_filters.emplace_back(m_window_size, m_epsilon);
134
+ }
135
+
136
+ // Restore each channel's state (identical logic to VarianceStage)
137
+ Napi::Array channelsArray = state.Get("channels").As<Napi::Array>();
138
+ for (uint32_t i = 0; i < numChannels; ++i)
139
+ {
140
+ Napi::Object channelState = channelsArray.Get(i).As<Napi::Object>();
141
+
142
+ // Get buffer data
143
+ Napi::Array bufferArray = channelState.Get("buffer").As<Napi::Array>();
144
+ std::vector<float> bufferData;
145
+ bufferData.reserve(bufferArray.Length());
146
+ for (uint32_t j = 0; j < bufferArray.Length(); ++j)
147
+ {
148
+ bufferData.push_back(bufferArray.Get(j).As<Napi::Number>().FloatValue());
149
+ }
150
+
151
+ // Get running sums
152
+ float runningSum = channelState.Get("runningSum").As<Napi::Number>().FloatValue();
153
+ float runningSumOfSquares = channelState.Get("runningSumOfSquares").As<Napi::Number>().FloatValue();
154
+
155
+ // --- Validation (identical to VarianceStage) ---
156
+ float actualSum = 0.0f;
157
+ float actualSumOfSquares = 0.0f;
158
+ for (const auto &val : bufferData)
159
+ {
160
+ actualSum += val;
161
+ actualSumOfSquares += val * val;
162
+ }
163
+
164
+ const float toleranceSum = 0.0001f * std::max(1.0f, std::abs(actualSum));
165
+ if (std::abs(runningSum - actualSum) > toleranceSum)
166
+ {
167
+ throw std::runtime_error(
168
+ "Running sum validation failed: expected " +
169
+ std::to_string(actualSum) + " but got " +
170
+ std::to_string(runningSum));
171
+ }
172
+
173
+ const float toleranceSq = 0.0001f * std::max(1.0f, std::abs(actualSumOfSquares));
174
+ if (std::abs(runningSumOfSquares - actualSumOfSquares) > toleranceSq)
175
+ {
176
+ throw std::runtime_error(
177
+ "Running sum of squares validation failed: expected " +
178
+ std::to_string(actualSumOfSquares) + " but got " +
179
+ std::to_string(runningSumOfSquares));
180
+ }
181
+ // --- End Validation ---
182
+
183
+ // Restore the filter's state
184
+ m_filters[i].setState(bufferData, runningSum, runningSumOfSquares);
185
+ }
186
+ }
187
+ }
188
+
189
+ // Reset all filters to initial state
190
+ void reset() override
191
+ {
192
+ for (auto &filter : m_filters)
193
+ {
194
+ filter.clear();
195
+ }
196
+ }
197
+
198
+ private:
199
+ /**
200
+ * @brief Statelessly calculates the Z-Score for each sample
201
+ * based on the entire buffer's stats for that channel.
202
+ */
203
+ void processBatch(float *buffer, size_t numSamples, int numChannels)
204
+ {
205
+ for (int c = 0; c < numChannels; ++c)
206
+ {
207
+ size_t numSamplesPerChannel = numSamples / numChannels;
208
+ if (numSamplesPerChannel == 0)
209
+ continue;
210
+
211
+ double sum = 0.0;
212
+ double sum_sq = 0.0;
213
+
214
+ // First pass: Calculate sums
215
+ for (size_t i = c; i < numSamples; i += numChannels)
216
+ {
217
+ double val = static_cast<double>(buffer[i]);
218
+ sum += val;
219
+ sum_sq += val * val;
220
+ }
221
+
222
+ // Calculate mean and variance
223
+ double mean = sum / numSamplesPerChannel;
224
+ double mean_sq = sum_sq / numSamplesPerChannel;
225
+ double variance = std::max(0.0, mean_sq - (mean * mean));
226
+ float stddev = static_cast<float>(std::sqrt(variance));
227
+
228
+ // Second pass: Apply Z-Score normalization in-place
229
+ if (stddev < m_epsilon)
230
+ {
231
+ // StdDev is zero, all values are the mean, Z-Score is 0
232
+ for (size_t i = c; i < numSamples; i += numChannels)
233
+ {
234
+ buffer[i] = 0.0f;
235
+ }
236
+ }
237
+ else
238
+ {
239
+ float mean_f = static_cast<float>(mean);
240
+ for (size_t i = c; i < numSamples; i += numChannels)
241
+ {
242
+ buffer[i] = (buffer[i] - mean_f) / stddev;
243
+ }
244
+ }
245
+ }
246
+ }
247
+
248
+ /**
249
+ * @brief Statefully processes samples using the moving z-score filters.
250
+ */
251
+ void processMoving(float *buffer, size_t numSamples, int numChannels, const float *timestamps)
252
+ {
253
+ // Determine if we're in time-aware mode
254
+ bool useTimeAware = (m_window_duration_ms > 0.0) && timestamps != nullptr;
255
+
256
+ // Lazy initialization: convert windowDuration to windowSize if needed
257
+ if (!m_is_initialized && m_window_duration_ms > 0.0)
258
+ {
259
+ if (timestamps != nullptr && numSamples > 1)
260
+ {
261
+ // Estimate sample rate from timestamps
262
+ size_t samples_to_check = std::min(numSamples, size_t(10));
263
+ double total_time_ms = timestamps[samples_to_check - 1] - timestamps[0];
264
+ double avg_sample_period_ms = total_time_ms / (samples_to_check - 1);
265
+ double estimated_sample_rate = 1000.0 / avg_sample_period_ms;
266
+
267
+ // Use 3x the estimated size for time-aware mode
268
+ size_t estimated_size = static_cast<size_t>((m_window_duration_ms / 1000.0) * estimated_sample_rate);
269
+ m_window_size = std::max(size_t(1), estimated_size * 3);
270
+
271
+ m_is_initialized = true;
272
+ }
273
+ else
274
+ {
275
+ throw std::runtime_error("ZScoreNormalize: windowDuration was set, but timestamps are not available to derive sample rate");
276
+ }
277
+ }
278
+
279
+ // Lazily initialize our filters, one for each channel
280
+ if (m_filters.size() != static_cast<size_t>(numChannels))
281
+ {
282
+ m_filters.clear();
283
+ for (int i = 0; i < numChannels; ++i)
284
+ {
285
+ if (useTimeAware)
286
+ {
287
+ // Create time-aware filter
288
+ m_filters.emplace_back(m_window_size, m_window_duration_ms, m_epsilon);
289
+ }
290
+ else
291
+ {
292
+ // Create regular filter
293
+ m_filters.emplace_back(m_window_size, m_epsilon);
294
+ }
295
+ }
296
+ }
297
+
298
+ // Process the buffer sample by sample, de-interleaving
299
+ for (size_t i = 0; i < numSamples; ++i)
300
+ {
301
+ int channel = i % numChannels;
302
+ size_t sample_index = i / numChannels;
303
+
304
+ if (useTimeAware)
305
+ {
306
+ // Use time-aware processing
307
+ buffer[i] = m_filters[channel].addSampleWithTimestamp(buffer[i], timestamps[sample_index]);
308
+ }
309
+ else
310
+ {
311
+ // Use sample-count processing
312
+ buffer[i] = m_filters[channel].addSample(buffer[i]);
313
+ }
314
+ }
315
+ }
316
+
317
+ ZScoreNormalizeMode m_mode;
318
+ size_t m_window_size;
319
+ double m_window_duration_ms;
320
+ float m_epsilon;
321
+ bool m_is_initialized;
322
+ // We need a separate filter instance for each channel's state
323
+ std::vector<dsp::core::MovingZScoreFilter<float>> m_filters;
324
+ };
325
+
326
+ } // namespace dsp::adapters