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,202 @@
1
+ /**
2
+ * Example showing how to use Redis for DSP state persistence
3
+ *
4
+ * This demonstrates:
5
+ * 1. Creating a pipeline with Redis configuration
6
+ * 2. Processing audio data
7
+ * 3. Saving/loading state (when Redis integration is fully implemented)
8
+ *
9
+ * Note: This example shows the intended API. Full C++ Redis integration
10
+ * requires implementing saveState/loadState methods in DspPipeline.cc
11
+ */
12
+
13
+ import { createClient } from "redis";
14
+ import { createDspPipeline } from "../bindings";
15
+
16
+ async function redisExample(startFresh = false) {
17
+ console.log("=== DSP Pipeline with Redis State Persistence ===\n");
18
+
19
+ // 1. Create Redis client
20
+ const redis = await createClient({ url: "redis://localhost:6379" }).connect();
21
+
22
+ // 2. Create DSP pipeline with Redis configuration
23
+ const stateKey = "dsp:pipeline:channel1";
24
+
25
+ // Optional: Clear previous state to start fresh
26
+ if (startFresh) {
27
+ console.log("Clearing previous state to start fresh...");
28
+ await redis.del(stateKey);
29
+ console.log("State cleared\n");
30
+ }
31
+
32
+ const pipeline = createDspPipeline({
33
+ redisHost: "localhost",
34
+ redisPort: 6379,
35
+ stateKey: stateKey,
36
+ });
37
+
38
+ // 3. Build the pipeline
39
+ pipeline.MovingAverage({ mode: "moving", windowSize: 3 });
40
+
41
+ console.log("Pipeline created with moving average filter (window=3)");
42
+
43
+ // 4. Try to restore previous state (if exists)
44
+ const previousState = await redis.get(stateKey);
45
+ if (previousState) {
46
+ console.log("Found previous state in Redis, restoring...");
47
+ await pipeline.loadState(previousState);
48
+ console.log("State restored successfully!");
49
+ } else {
50
+ console.log("No previous state found in Redis");
51
+ }
52
+
53
+ // 5. First batch of audio
54
+ console.log("\n--- Processing first batch ---");
55
+ const batch1 = new Float32Array([1, 2, 3, 4, 5]);
56
+ console.log("Input batch 1:", Array.from(batch1));
57
+
58
+ const output1 = await pipeline.processCopy(batch1, {
59
+ sampleRate: 1000,
60
+ channels: 1,
61
+ });
62
+ console.log("Output batch 1:", Array.from(output1));
63
+
64
+ // 6. Save state to Redis
65
+ console.log("\n--- Saving state to Redis ---");
66
+ const state = await pipeline.saveState();
67
+ await redis.set(stateKey, state);
68
+ console.log("State saved:", state);
69
+
70
+ // 7. Simulate stopping and restarting the process
71
+ console.log("\n--- Simulating restart ---");
72
+
73
+ // 8. Create new pipeline and restore state
74
+ const pipeline2 = createDspPipeline({
75
+ redisHost: "localhost",
76
+ redisPort: 6379,
77
+ stateKey: stateKey,
78
+ });
79
+
80
+ pipeline2.MovingAverage({ mode: "moving", windowSize: 3 });
81
+
82
+ const restoredState = await redis.get(stateKey);
83
+ if (restoredState) {
84
+ console.log("Restored state from Redis");
85
+ await pipeline2.loadState(restoredState);
86
+ console.log(
87
+ "State restoration complete - continuing from where we left off!"
88
+ );
89
+ }
90
+
91
+ // 9. Continue processing with restored state
92
+ console.log("\n--- Processing second batch (after 'restart') ---");
93
+ const batch2 = new Float32Array([6, 7, 8]);
94
+ console.log("Input batch 2:", Array.from(batch2));
95
+
96
+ const output2 = await pipeline2.processCopy(batch2, {
97
+ sampleRate: 1000,
98
+ channels: 1,
99
+ });
100
+ console.log("Output batch 2:", Array.from(output2));
101
+
102
+ console.log(
103
+ "\nNote: With state restoration, batch2 continues the moving average from where batch1 left off!"
104
+ );
105
+ console.log(
106
+ "Expected output without state: [6, 6.5, 7] - but we got continuous averaging!"
107
+ );
108
+
109
+ // 10. Optional: Clear state (commented out to persist between runs)
110
+ // console.log("\n--- Clearing state ---");
111
+ // await redis.del(stateKey);
112
+ // pipeline2.clearState();
113
+ // console.log("State cleared from Redis");
114
+
115
+ // Disconnect from Redis
116
+ await redis.disconnect();
117
+ } // Real-world use case example
118
+ async function streamingExample(startFresh = false) {
119
+ console.log("\n\n=== Streaming Audio Processing Example ===\n");
120
+
121
+ const redis = await createClient({ url: "redis://localhost:6379" }).connect();
122
+ const channelId = "audio-stream-ch1";
123
+ const stateKey = `dsp:stream:${channelId}`;
124
+
125
+ // Optional: Clear previous state to start fresh
126
+ if (startFresh) {
127
+ console.log("Clearing previous streaming state to start fresh...");
128
+ await redis.del(stateKey);
129
+ console.log("Streaming state cleared\n");
130
+ }
131
+
132
+ // Create pipeline
133
+ const pipeline = createDspPipeline({
134
+ redisHost: "localhost",
135
+ redisPort: 6379,
136
+ stateKey: stateKey,
137
+ });
138
+
139
+ pipeline.MovingAverage({ mode: "moving", windowSize: 5 });
140
+
141
+ // Restore state if processing was interrupted
142
+ const savedState = await redis.get(stateKey);
143
+ if (savedState) {
144
+ console.log("Resuming from saved state...");
145
+ await pipeline.loadState(savedState);
146
+ }
147
+
148
+ // Simulate streaming audio chunks
149
+ const chunks = [
150
+ new Float32Array([1, 2, 3, 4, 5]),
151
+ new Float32Array([6, 7, 8, 9, 10]),
152
+ new Float32Array([11, 12, 13, 14, 15]),
153
+ ];
154
+
155
+ console.log("Processing audio stream in chunks...\n");
156
+
157
+ for (let i = 0; i < chunks.length; i++) {
158
+ const chunk = chunks[i];
159
+ console.log(`Chunk ${i + 1} input:`, Array.from(chunk));
160
+
161
+ const processed = await pipeline.processCopy(chunk, {
162
+ sampleRate: 44100,
163
+ channels: 1,
164
+ });
165
+
166
+ console.log(`Chunk ${i + 1} output:`, Array.from(processed));
167
+
168
+ // Save state after each chunk (for crash recovery)
169
+ const state = await pipeline.saveState();
170
+ await redis.set(stateKey, state);
171
+ console.log(`State saved after chunk ${i + 1}\n`);
172
+ }
173
+
174
+ console.log("Stream processing complete. State saved for next session.");
175
+
176
+ // Disconnect from Redis
177
+ await redis.disconnect();
178
+ }
179
+
180
+ // Run examples
181
+ console.log("Running DSP + Redis examples...\n");
182
+
183
+ // Set to true to clear previous state and start fresh
184
+ // Set to false to demonstrate state persistence across runs
185
+ const START_FRESH = false;
186
+
187
+ redisExample(START_FRESH)
188
+ .then(() => streamingExample(START_FRESH))
189
+ .then(() => {
190
+ console.log("\nAll examples completed successfully!");
191
+ console.log(
192
+ "\nTip: Set START_FRESH = true to clear Redis state and start fresh"
193
+ );
194
+ console.log(
195
+ "Tip: Set START_FRESH = false to see state persistence across runs"
196
+ );
197
+ process.exit(0);
198
+ })
199
+ .catch((error) => {
200
+ console.error("\nError:", error);
201
+ process.exit(1);
202
+ });
@@ -0,0 +1,126 @@
1
+ /**
2
+ * SIMD Performance Benchmark
3
+ *
4
+ * This benchmark demonstrates the performance of SIMD-accelerated operations.
5
+ *
6
+ * Run with: npm test (to see it passes), then check operation timings
7
+ *
8
+ * Note: For accurate benchmarks, build in Release mode and run multiple times
9
+ * to account for V8 JIT compilation and CPU thermal throttling.
10
+ */
11
+
12
+ import { createDspPipeline } from "../bindings.js";
13
+
14
+ console.log("🚀 SIMD Performance Demonstration");
15
+ console.log("===================================\n");
16
+
17
+ async function demonstrateSIMD() {
18
+ const BUFFER_SIZE = 100000; // 100k samples
19
+ const ITERATIONS = 50;
20
+
21
+ // Create test signal with mixed positive/negative values
22
+ const signal = new Float32Array(BUFFER_SIZE);
23
+ for (let i = 0; i < BUFFER_SIZE; i++) {
24
+ signal[i] = Math.sin(i * 0.1) * 100 + Math.cos(i * 0.05) * 50;
25
+ }
26
+
27
+ console.log(`📊 Test Configuration:`);
28
+ console.log(` • Buffer size: ${BUFFER_SIZE.toLocaleString()} samples`);
29
+ console.log(` • Iterations: ${ITERATIONS}`);
30
+ console.log(
31
+ ` • Total samples: ${(BUFFER_SIZE * ITERATIONS).toLocaleString()}\n`
32
+ );
33
+
34
+ // Test 1: Rectify (SIMD-accelerated)
35
+ console.log("🔧 Test 1: Rectify (Full-Wave) - SIMD Accelerated");
36
+ const processor1 = createDspPipeline();
37
+ processor1.Rectify({ mode: "full" });
38
+
39
+ const start1 = performance.now();
40
+ for (let i = 0; i < ITERATIONS; i++) {
41
+ const buffer = signal.slice();
42
+ await processor1.process(buffer, { channels: 1 });
43
+ }
44
+ const end1 = performance.now();
45
+ const time1 = end1 - start1;
46
+ const throughput1 = ((BUFFER_SIZE * ITERATIONS) / time1) * 1000;
47
+
48
+ console.log(` ✅ Time: ${time1.toFixed(2)} ms`);
49
+ console.log(
50
+ ` ✅ Throughput: ${(throughput1 / 1_000_000).toFixed(2)} M samples/sec\n`
51
+ );
52
+
53
+ // Test 2: Batch Average (SIMD-accelerated for single channel)
54
+ console.log("🔧 Test 2: Batch Average - SIMD Accelerated");
55
+ const processor2 = createDspPipeline();
56
+ processor2.MovingAverage({ mode: "batch" });
57
+
58
+ const start2 = performance.now();
59
+ for (let i = 0; i < ITERATIONS; i++) {
60
+ const buffer = signal.slice();
61
+ await processor2.process(buffer, { channels: 1 });
62
+ }
63
+ const end2 = performance.now();
64
+ const time2 = end2 - start2;
65
+ const throughput2 = ((BUFFER_SIZE * ITERATIONS) / time2) * 1000;
66
+
67
+ console.log(` ✅ Time: ${time2.toFixed(2)} ms`);
68
+ console.log(
69
+ ` ✅ Throughput: ${(throughput2 / 1_000_000).toFixed(2)} M samples/sec\n`
70
+ );
71
+
72
+ // Test 3: Batch RMS (SIMD-accelerated for single channel)
73
+ console.log("🔧 Test 3: Batch RMS - SIMD Accelerated");
74
+ const processor3 = createDspPipeline();
75
+ processor3.Rms({ mode: "batch" });
76
+
77
+ const start3 = performance.now();
78
+ for (let i = 0; i < ITERATIONS; i++) {
79
+ const buffer = signal.slice();
80
+ await processor3.process(buffer, { channels: 1 });
81
+ }
82
+ const end3 = performance.now();
83
+ const time3 = end3 - start3;
84
+ const throughput3 = ((BUFFER_SIZE * ITERATIONS) / time3) * 1000;
85
+
86
+ console.log(` ✅ Time: ${time3.toFixed(2)} ms`);
87
+ console.log(
88
+ ` ✅ Throughput: ${(throughput3 / 1_000_000).toFixed(2)} M samples/sec\n`
89
+ );
90
+
91
+ console.log("─".repeat(60));
92
+ console.log("\n📈 SIMD Optimization Summary:\n");
93
+ console.log("✨ What makes these operations fast:");
94
+ console.log(" 1. Compiler optimizations (-O3, /O2, -ffast-math)");
95
+ console.log(" 2. SIMD intrinsics (AVX2/SSE2/NEON)");
96
+ console.log(" 3. Zero-copy processing (in-place modification)");
97
+ console.log(" 4. C++ Native code (no JavaScript overhead)\n");
98
+
99
+ console.log("🎯 Platform-specific SIMD support:");
100
+ console.log(" • x86/x64 + AVX2: 8 floats/cycle → 4-8x speedup");
101
+ console.log(" • x86/x64 + SSE2: 4 floats/cycle → 2-4x speedup");
102
+ console.log(" • ARM + NEON: 4 floats/cycle → 2-4x speedup");
103
+ console.log(
104
+ " • Scalar fallback: 1 float/cycle → compiler auto-vectorizes\n"
105
+ );
106
+
107
+ console.log("💡 Best performance tips:");
108
+ console.log(" • Use single-channel data when possible (contiguous memory)");
109
+ console.log(" • Prefer batch mode for one-time calculations");
110
+ console.log(" • Use moving mode only when state continuity is needed");
111
+ console.log(" • Process larger buffers to amortize overhead\n");
112
+
113
+ console.log("� For more details, see:");
114
+ console.log(" docs/SIMD_OPTIMIZATIONS.md\n");
115
+ }
116
+
117
+ // Run demonstration
118
+ demonstrateSIMD()
119
+ .then(() => {
120
+ console.log("✅ Demonstration complete\n");
121
+ process.exit(0);
122
+ })
123
+ .catch((error) => {
124
+ console.error("❌ Demonstration failed:", error);
125
+ process.exit(1);
126
+ });
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Example: Using .tap() for Pipeline Debugging
3
+ * Demonstrates how to inspect intermediate results at any point in the pipeline
4
+ */
5
+
6
+ import { createDspPipeline } from "../index.js";
7
+
8
+ console.log("🔍 Pipeline Debugging with .tap()\n");
9
+
10
+ // Example 1: Basic inspection
11
+ console.log("Example 1: Basic Inspection");
12
+ const pipeline1 = createDspPipeline()
13
+ .MovingAverage({ mode: "moving", windowSize: 3 })
14
+ .tap((samples, stage) => {
15
+ console.log(` After ${stage}:`);
16
+ console.log(
17
+ ` First 5 samples: [${Array.from(samples.slice(0, 5))
18
+ .map((v) => v.toFixed(2))
19
+ .join(", ")}]`
20
+ );
21
+ console.log(` Length: ${samples.length}`);
22
+ })
23
+ .Rectify({ mode: "full" })
24
+ .tap((samples, stage) => {
25
+ console.log(` After ${stage}:`);
26
+ console.log(
27
+ ` First 5 samples: [${Array.from(samples.slice(0, 5))
28
+ .map((v) => v.toFixed(2))
29
+ .join(", ")}]`
30
+ );
31
+ });
32
+
33
+ const input1 = Float32Array.from(
34
+ { length: 10 },
35
+ (_, i) => Math.sin(i * 0.5) - 0.5
36
+ );
37
+ await pipeline1.process(input1, { sampleRate: 1000 });
38
+
39
+ // Example 2: Conditional alerts based on thresholds
40
+ console.log("\n\nExample 2: Threshold Monitoring");
41
+ const THRESHOLD = 0.8;
42
+ let alertCount = 0;
43
+
44
+ const pipeline2 = createDspPipeline()
45
+ .MovingAverage({ mode: "moving", windowSize: 5 })
46
+ .Rectify({ mode: "full" })
47
+ .Rms({ mode: "moving", windowSize: 10 })
48
+ .tap((samples, stage) => {
49
+ const max = Math.max(...samples);
50
+ const avg = samples.reduce((a, b) => a + b, 0) / samples.length;
51
+
52
+ console.log(` Stats after ${stage}:`);
53
+ console.log(` Max: ${max.toFixed(4)}, Avg: ${avg.toFixed(4)}`);
54
+
55
+ if (max > THRESHOLD) {
56
+ alertCount++;
57
+ console.log(
58
+ ` 🚨 ALERT: Max value ${max.toFixed(
59
+ 4
60
+ )} exceeds threshold ${THRESHOLD}`
61
+ );
62
+ }
63
+ });
64
+
65
+ const input2 = Float32Array.from(
66
+ { length: 50 },
67
+ (_, i) => Math.sin(i * 0.3) * (1 + Math.random() * 0.5)
68
+ );
69
+ await pipeline2.process(input2, { sampleRate: 1000 });
70
+ console.log(`\n Total alerts: ${alertCount}`);
71
+
72
+ // Example 3: Multiple taps in complex pipeline
73
+ console.log("\n\n🔗 Example 3: Multi-Stage Inspection");
74
+ const stages: Array<{
75
+ name: string;
76
+ stats: { min: number; max: number; mean: number };
77
+ }> = [];
78
+
79
+ const pipeline3 = createDspPipeline()
80
+ .tap((samples) => {
81
+ const stats = {
82
+ min: Math.min(...samples),
83
+ max: Math.max(...samples),
84
+ mean: samples.reduce((a, b) => a + b, 0) / samples.length,
85
+ };
86
+ stages.push({ name: "raw input", stats });
87
+ })
88
+ .MovingAverage({ mode: "moving", windowSize: 5 })
89
+ .tap((samples, stage) => {
90
+ const stats = {
91
+ min: Math.min(...samples),
92
+ max: Math.max(...samples),
93
+ mean: samples.reduce((a, b) => a + b, 0) / samples.length,
94
+ };
95
+ stages.push({ name: stage, stats });
96
+ })
97
+ .Rectify({ mode: "full" })
98
+ .tap((samples, stage) => {
99
+ const stats = {
100
+ min: Math.min(...samples),
101
+ max: Math.max(...samples),
102
+ mean: samples.reduce((a, b) => a + b, 0) / samples.length,
103
+ };
104
+ stages.push({ name: stage, stats });
105
+ })
106
+ .Rms({ mode: "moving", windowSize: 3 })
107
+ .tap((samples, stage) => {
108
+ const stats = {
109
+ min: Math.min(...samples),
110
+ max: Math.max(...samples),
111
+ mean: samples.reduce((a, b) => a + b, 0) / samples.length,
112
+ };
113
+ stages.push({ name: stage, stats });
114
+ });
115
+
116
+ const input3 = Float32Array.from(
117
+ { length: 100 },
118
+ (_, i) => Math.sin(i * 0.1) * 2 - 1
119
+ );
120
+ await pipeline3.process(input3, { sampleRate: 1000 });
121
+
122
+ console.log("\n Pipeline Statistics Table:");
123
+ console.log(
124
+ " ┌─────────────────────────────────┬──────────┬──────────┬──────────┐"
125
+ );
126
+ console.log(
127
+ " │ Stage │ Min │ Max │ Mean │"
128
+ );
129
+ console.log(
130
+ " ├─────────────────────────────────┼──────────┼──────────┼──────────┤"
131
+ );
132
+ stages.forEach(({ name, stats }) => {
133
+ const paddedName = name.padEnd(31);
134
+ console.log(
135
+ ` │ ${paddedName} │ ${stats.min.toFixed(4).padStart(8)} │ ${stats.max
136
+ .toFixed(4)
137
+ .padStart(8)} │ ${stats.mean.toFixed(4).padStart(8)} │`
138
+ );
139
+ });
140
+ console.log(
141
+ " └─────────────────────────────────┴──────────┴──────────┴──────────┘"
142
+ );
143
+
144
+ // Example 4: Debugging with logger integration
145
+ console.log("\n\nExample 4: Logger Integration");
146
+ const logger = {
147
+ debug: (msg: string, data?: any) =>
148
+ console.log(` [DEBUG] ${msg}`, data || ""),
149
+ info: (msg: string, data?: any) =>
150
+ console.log(` [INFO] ${msg}`, data || ""),
151
+ warn: (msg: string, data?: any) =>
152
+ console.log(` [WARN] ${msg}`, data || ""),
153
+ };
154
+
155
+ const pipeline4 = createDspPipeline()
156
+ .MovingAverage({ mode: "moving", windowSize: 10 })
157
+ .tap((samples, stage) =>
158
+ logger.debug(`Processed ${stage}`, { sampleCount: samples.length })
159
+ )
160
+ .Rectify()
161
+ .tap((samples, stage) => {
162
+ const hasNegative = samples.some((v) => v < 0);
163
+ if (hasNegative) {
164
+ logger.warn(`Unexpected negative values after ${stage}`);
165
+ } else {
166
+ logger.info(`${stage} completed successfully`);
167
+ }
168
+ })
169
+ .Rms({ mode: "moving", windowSize: 5 })
170
+ .tap((samples, stage) => {
171
+ const max = Math.max(...samples);
172
+ logger.debug(`Max RMS value`, { value: max.toFixed(4) });
173
+ });
174
+
175
+ const input4 = Float32Array.from({ length: 20 }, (_, i) => Math.cos(i * 0.2));
176
+ await pipeline4.process(input4, { sampleRate: 1000 });
177
+
178
+ // Example 5: Performance impact measurement
179
+ console.log("\n\nExample 5: Performance Impact of .tap()");
180
+ const iterations = 1000;
181
+ const sampleSize = 1000;
182
+
183
+ // Without tap
184
+ const pipelineNoTap = createDspPipeline()
185
+ .MovingAverage({ mode: "moving", windowSize: 10 })
186
+ .Rectify()
187
+ .Rms({ mode: "moving", windowSize: 5 });
188
+
189
+ const startNoTap = performance.now();
190
+ for (let i = 0; i < iterations; i++) {
191
+ const input = Float32Array.from({ length: sampleSize }, () => Math.random());
192
+ await pipelineNoTap.process(input, { sampleRate: 1000 });
193
+ }
194
+ const durationNoTap = performance.now() - startNoTap;
195
+
196
+ // With tap (minimal work)
197
+ const pipelineWithTap = createDspPipeline()
198
+ .MovingAverage({ mode: "moving", windowSize: 10 })
199
+ .tap(() => {}) // Empty tap
200
+ .Rectify()
201
+ .tap(() => {}) // Empty tap
202
+ .Rms({ mode: "moving", windowSize: 5 })
203
+ .tap(() => {}); // Empty tap
204
+
205
+ const startWithTap = performance.now();
206
+ for (let i = 0; i < iterations; i++) {
207
+ const input = Float32Array.from({ length: sampleSize }, () => Math.random());
208
+ await pipelineWithTap.process(input, { sampleRate: 1000 });
209
+ }
210
+ const durationWithTap = performance.now() - startWithTap;
211
+
212
+ console.log(
213
+ ` Without .tap(): ${durationNoTap.toFixed(2)}ms for ${iterations} iterations`
214
+ );
215
+ console.log(
216
+ ` With .tap(): ${durationWithTap.toFixed(
217
+ 2
218
+ )}ms for ${iterations} iterations`
219
+ );
220
+ console.log(
221
+ ` Overhead: ${(durationWithTap - durationNoTap).toFixed(2)}ms (${(
222
+ (durationWithTap / durationNoTap - 1) *
223
+ 100
224
+ ).toFixed(2)}%)`
225
+ );
226
+ console.log(
227
+ `\n Tip: Remove .tap() calls in production or use conditional logic`
228
+ );
229
+
230
+ console.log("\nAll examples completed!");