dspx 1.1.4 → 1.1.6

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 CHANGED
@@ -346,6 +346,37 @@ const output = await pipeline.process(input, {
346
346
 
347
347
  **Recommendation:** Use batched callbacks in production. Individual callbacks benchmark faster but block the Node.js event loop and can't integrate with real telemetry systems (Kafka, Datadog, Loki).
348
348
 
349
+ ### 🎯 FFT Performance Note
350
+
351
+ **Pipeline vs Direct Processing:**
352
+
353
+ The DSP pipeline adds ~0.3-0.4ms overhead per operation for:
354
+
355
+ - Channel management and stage chaining
356
+ - N-API boundary crossings
357
+ - Output buffer allocation
358
+
359
+ For **maximum FFT performance** (when you don't need multi-stage processing or Redis persistence), use the direct `FftProcessor`:
360
+
361
+ ```javascript
362
+ // ⚡ FASTER: Direct FFT (no pipeline overhead)
363
+ import { FftProcessor } from "dspx";
364
+ const processor = new FftProcessor(1024);
365
+ const output = processor.rfft(signal); // ~4-14x faster than fft.js
366
+
367
+ // 🔧 FLEXIBLE: Pipeline FFT (with overhead)
368
+ const pipeline = createDspPipeline();
369
+ pipeline.fft({ size: 1024, type: "rfft" });
370
+ const result = pipeline.process(signal); // Still faster than pure JS, but ~0.3ms overhead
371
+ ```
372
+
373
+ **When to use each:**
374
+
375
+ - **FftProcessor**: Single FFT operations, maximum speed, batch processing
376
+ - **Pipeline**: Multi-stage processing (filter→FFT→analysis), Redis state, complex workflows
377
+
378
+ With recent optimizations (loop unrolling, memcpy, single-channel fast paths), dspx FFT is now **4-14x faster than fft.js** across all sizes when measured correctly. See [FFT_BENCHMARK_FIX.md](docs/FFT_BENCHMARK_FIX.md) for benchmark methodology.
379
+
349
380
  ### ⚠️ ARM/Mobile Platform Notice
350
381
 
351
382
  **ARM NEON optimizations are experimental.** Mobile/ARM devices face unique challenges:
@@ -281,15 +281,37 @@ declare class DspProcessor {
281
281
  Resample(params: ResampleParams): this;
282
282
  /**
283
283
  * Add a convolution stage to the pipeline
284
- * Applies a custom 1D kernel to the signal using either direct or FFT-based convolution
284
+ * Applies a 1D kernel to the signal using either direct or FFT-based convolution
285
+ *
286
+ * **Two Processing Modes:**
287
+ * 1. **Batch mode** (stateless): Standard convolution, output length = N - M + 1
288
+ * 2. **Moving mode** (stateful): Streaming convolution, maintains buffer, output length = N
285
289
  *
286
290
  * @param params - Convolution parameters
291
+ * @param params.kernel - 1D filter kernel (time-domain)
292
+ * @param params.mode - 'batch' (stateless, valid convolution) or 'moving' (stateful, streaming) [default: 'moving']
293
+ * @param params.method - 'auto' (smart selection), 'direct' (time-domain), or 'fft' (frequency-domain) [default: 'auto']
287
294
  * @returns this instance for method chaining
288
295
  *
289
296
  * @example
297
+ * // BATCH MODE: Standard convolution (output length = N - M + 1)
298
+ * const input = new Float32Array([1, 2, 3, 4, 5]);
299
+ * const kernel = new Float32Array([1, 0, -1]);
300
+ * pipeline.convolution({ kernel, mode: "batch" });
301
+ * const output = await pipeline.process(input, { channels: 1 });
302
+ * // output: [-2, -2, -2] (length = 5 - 3 + 1 = 3)
303
+ *
304
+ * @example
305
+ * // MOVING MODE: Streaming convolution (output length = N)
306
+ * pipeline.convolution({ kernel: mySmoothingKernel, mode: "moving" });
307
+ * const chunk1 = await pipeline.process(data1, { channels: 1 });
308
+ * const chunk2 = await pipeline.process(data2, { channels: 1 });
309
+ * // State maintained between chunks, same length as input
310
+ *
311
+ * @example
290
312
  * // Simple smoothing kernel
291
313
  * const smoothKernel = new Float32Array([0.2, 0.6, 0.2]);
292
- * pipeline.convolution({ kernel: smoothKernel });
314
+ * pipeline.convolution({ kernel: smoothKernel, mode: "batch" });
293
315
  *
294
316
  * @example
295
317
  * // Gaussian kernel with explicit method
@@ -311,11 +333,11 @@ declare class DspProcessor {
311
333
  * @example
312
334
  * // Multi-channel EMG grid with custom kernel
313
335
  * const emgGrid = new Float32Array(80000); // 10x8 grid, 1000 samples
314
- * pipeline.convolution({ kernel: mySmoothingKernel });
336
+ * pipeline.convolution({ kernel: mySmoothingKernel, mode: "batch" });
315
337
  * const output = await pipeline.process(emgGrid, {
316
338
  * sampleRate: 2000,
317
339
  * channels: 80 // 10 * 8 sensors
318
- * });\
340
+ * });
319
341
  */
320
342
  convolution(params: ConvolutionParams): this;
321
343
  /**
@@ -1038,46 +1060,53 @@ declare class DspProcessor {
1038
1060
  * Transforms time-domain signal to frequency domain or vice versa.
1039
1061
  * Supports both fast (radix-2) and direct (any size) transforms.
1040
1062
  *
1063
+ * **Two Processing Modes:**
1064
+ * 1. **Batch mode** (default): Stateless FFT over entire input buffer
1065
+ * 2. **Moving mode**: Stateful sliding-window FFT (STFT) with overlap
1066
+ *
1041
1067
  * **Transform Types:**
1042
- * - `fft`: Complex FFT (O(N log N), requires power-of-2 size)
1068
+ * - `rfft`: Real FFT (O(N log N), requires power-of-2 size) [MOST COMMON]
1069
+ * - `fft`: Complex FFT (O(N log N), requires power-of-2)
1070
+ * - `rdft`: Real DFT (O(N²), works with any size)
1043
1071
  * - `dft`: Complex DFT (O(N²), works with any size)
1044
- * - `rfft`: Real FFT for real signals (O(N log N), outputs N/2+1 bins)
1045
- * - `rdft`: Real DFT for real signals (O(N²), outputs N/2+1 bins)
1046
1072
  *
1047
1073
  * **Output Formats:**
1048
- * - `complex`: Returns interleaved [real0, imag0, real1, imag1, ...]
1049
- * - `magnitude`: Returns |X[k]| = sqrt(real² + imag²)
1050
- * - `power`: Returns |X[k] = real² + imag²
1051
- * - `phase`: Returns ∠X[k] = atan2(imag, real)
1074
+ * - `magnitude`: |X[k]| = sqrt(real² + imag²) [DEFAULT]
1075
+ * - `power`: |X[k] = real² + imag²
1076
+ * - `phase`: X[k] = atan2(imag, real)
1077
+ * - `complex`: Interleaved [real0, imag0, real1, imag1, ...]
1078
+ *
1079
+ * **Moving Mode Details:**
1080
+ * - Internally delegates to `stft()` with Hann windowing
1081
+ * - Uses CircularBufferArray for efficient buffering
1082
+ * - Default hopSize = size/2 (50% overlap)
1083
+ * - Better for time-varying signals (audio, EEG)
1052
1084
  *
1053
1085
  * @param params - FFT configuration
1086
+ * @param params.mode - 'batch' (stateless) or 'moving' (sliding window) [default: 'batch']
1054
1087
  * @param params.size - FFT size (power of 2 for FFT/RFFT, any size for DFT/RDFT)
1055
- * @param params.type - Transform type (default: 'rfft' for real signals)
1056
- * @param params.forward - Forward (time→freq) or inverse (freq→time) (default: true)
1057
- * @param params.output - Output format (default: 'magnitude')
1088
+ * @param params.hopSize - Window stride for 'moving' mode [default: size/2]
1089
+ * @param params.type - Transform type [default: 'rfft']
1090
+ * @param params.forward - Forward (time→freq) or inverse (freq→time) [default: true]
1091
+ * @param params.output - Output format [default: 'magnitude']
1058
1092
  * @returns this instance for method chaining
1059
1093
  *
1060
1094
  * @example
1061
- * // Spectral analysis with magnitude output
1095
+ * // Batch mode: Single FFT of entire buffer
1062
1096
  * pipeline.fft({
1097
+ * mode: 'batch',
1063
1098
  * size: 1024,
1064
1099
  * type: 'rfft',
1065
1100
  * output: 'magnitude'
1066
1101
  * });
1067
1102
  *
1068
1103
  * @example
1069
- * // Power spectrum for energy analysis
1070
- * pipeline.fft({
1071
- * size: 2048,
1072
- * output: 'power'
1073
- * });
1074
- *
1075
- * @example
1076
- * // Complex FFT for phase information
1104
+ * // Moving mode: STFT with 50% overlap
1077
1105
  * pipeline.fft({
1106
+ * mode: 'moving',
1078
1107
  * size: 512,
1079
- * type: 'fft',
1080
- * output: 'complex'
1108
+ * hopSize: 256, // 50% overlap
1109
+ * output: 'power'
1081
1110
  * });
1082
1111
  *
1083
1112
  * @example
@@ -1087,6 +1116,14 @@ declare class DspProcessor {
1087
1116
  * type: 'rdft',
1088
1117
  * output: 'magnitude'
1089
1118
  * });
1119
+ *
1120
+ * @example
1121
+ * // Complex FFT for phase analysis
1122
+ * pipeline.fft({
1123
+ * size: 512,
1124
+ * type: 'fft',
1125
+ * output: 'complex'
1126
+ * });
1090
1127
  */
1091
1128
  fft(params: fftParams): this;
1092
1129
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../src/ts/bindings.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../src/ts/bindings.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,SAAS,EACT,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,EAGf,WAAW,EACX,oBAAoB,EACpB,uBAAuB,EACvB,SAAS,EACT,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,SAAS,EACT,UAAU,EACV,oBAAoB,EACpB,UAAU,EACX,MAAM,YAAY,CAAC;AAGpB,OAAO,EAGL,KAAK,aAAa,EAGnB,MAAM,cAAc,CAAC;AAiCtB;;;GAGG;AACH,cAAM,YAAY;IAQJ,OAAO,CAAC,cAAc;IAPlC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,SAAS,CAAC,CAAoB;IACtC,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,YAAY,CACf;IACL,OAAO,CAAC,aAAa,CAA8B;gBAE/B,cAAc,EAAE,GAAG;IAMvC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAyB1B;;OAEG;IACH;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAe1B;;OAEG;IACH,OAAO,CAAC,OAAO;IAyBf;;OAEG;IACH,OAAO,CAAC,SAAS;IAOjB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IA6BhD;;;;;;;;;;;;;;;;;;;OAmBG;IACH,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IA6B5B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,IAAI;IAMrC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IA6BtC;;;;;;;;;;;;;;;;;OAiBG;IACH,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI;IA6BpD;;;;;;;;;;;;;;;;;OAiBG;IACH,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI;IA6BxD;;;;;;;;;;;;;;;;;;;OAmBG;IACH,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;IAWlD;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI;IAgBpD;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI;IAgBxD;;;;;;;;;;;;;;;;;;OAkBG;IACH,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAwB5C;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAwBtC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IA6BtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2DG;IACH,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IA4D5C;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IAyCtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+EG;IACH,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IA0CxC;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAwCxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IACH,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IA8B9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4CG;IACH,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAuB9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmDG;IACH,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI;IAyB1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8DG;IACH,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI;IAiCpD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI;IA0BpD;;;;;;;;;;;;;;;;;OAiBG;IACH,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAmChD;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAqC9C;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAUhD;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAUhD;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,cAAc,IAAI,IAAI;IAMtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACH,UAAU,CAAC,MAAM,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,IAAI;IAcjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoDG;IACH,GAAG,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAYzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6EG;IACH,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IA8C9C;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;IA+BtD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI;IA6BpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsEG;IACH,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IA4F5B;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuEG;IACH,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAiG9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwDG;IACH,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,IAAI;IAyClD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6EG;IACH,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IA+C9B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,GAAG,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI;IAMhC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiDG;IACH,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAoGpC;;OAEG;IACH,OAAO,CAAC,eAAe;IAuCvB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAgC/B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAqD7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA2E1B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,QAAQ,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI;IAK5C;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,CACX,KAAK,EAAE,YAAY,EACnB,mBAAmB,EAAE,YAAY,GAAG,cAAc,EAClD,mBAAmB,CAAC,EAAE,cAAc,GACnC,OAAO,CAAC,YAAY,CAAC;IAgJxB;;;;;;;;;;;;;;;;OAgBG;IACG,WAAW,CACf,KAAK,EAAE,YAAY,EACnB,mBAAmB,EAAE,YAAY,GAAG,cAAc,EAClD,mBAAmB,CAAC,EAAE,cAAc,GACnC,OAAO,CAAC,YAAY,CAAC;IAexB;;;;;;;;;OASG;IACG,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAIlC;;;;;;;;;;;;OAYG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIpD;;;;;;OAMG;IACH,UAAU,IAAI,IAAI;IAIlB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,SAAS,IAAI,oBAAoB;CAGlC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,YAAY,CAGpE;AAED,OAAO,EAAE,YAAY,EAAE,CAAC;AAMxB,OAAO,KAAK,EACV,SAAS,EACT,SAAS,EACT,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACzB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,wBAAgB,YAAY,CAC1B,eAAe,EAAE,YAAY,EAC7B,WAAW,EAAE,MAAM,GAClB,SAAS,CASX;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,wBAAgB,kBAAkB,CAChC,eAAe,EAAE,YAAY,EAC7B,WAAW,EAAE,MAAM,EACnB,cAAc,GAAE,MAAa,GAC5B,eAAe,CAoBjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,wBAAgB,YAAY,CAC1B,eAAe,EAAE,YAAY,EAC7B,WAAW,EAAE,MAAM,EACnB,aAAa,GAAE,MAAY,EAC3B,SAAS,GAAE,MAAa,GACvB,SAAS,CAsBX;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,EAC/C,cAAc,EAAE,MAAM,EACtB,cAAc,GAAE,MAAY,GAC3B,uBAAuB,CA4BzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqGG;AACH,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,YAAY,EACxB,UAAU,EAAE,YAAY,EACxB,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,GAClB,SAAS,CAiCX"}
package/dist/bindings.js CHANGED
@@ -534,15 +534,37 @@ class DspProcessor {
534
534
  }
535
535
  /**
536
536
  * Add a convolution stage to the pipeline
537
- * Applies a custom 1D kernel to the signal using either direct or FFT-based convolution
537
+ * Applies a 1D kernel to the signal using either direct or FFT-based convolution
538
+ *
539
+ * **Two Processing Modes:**
540
+ * 1. **Batch mode** (stateless): Standard convolution, output length = N - M + 1
541
+ * 2. **Moving mode** (stateful): Streaming convolution, maintains buffer, output length = N
538
542
  *
539
543
  * @param params - Convolution parameters
544
+ * @param params.kernel - 1D filter kernel (time-domain)
545
+ * @param params.mode - 'batch' (stateless, valid convolution) or 'moving' (stateful, streaming) [default: 'moving']
546
+ * @param params.method - 'auto' (smart selection), 'direct' (time-domain), or 'fft' (frequency-domain) [default: 'auto']
540
547
  * @returns this instance for method chaining
541
548
  *
542
549
  * @example
550
+ * // BATCH MODE: Standard convolution (output length = N - M + 1)
551
+ * const input = new Float32Array([1, 2, 3, 4, 5]);
552
+ * const kernel = new Float32Array([1, 0, -1]);
553
+ * pipeline.convolution({ kernel, mode: "batch" });
554
+ * const output = await pipeline.process(input, { channels: 1 });
555
+ * // output: [-2, -2, -2] (length = 5 - 3 + 1 = 3)
556
+ *
557
+ * @example
558
+ * // MOVING MODE: Streaming convolution (output length = N)
559
+ * pipeline.convolution({ kernel: mySmoothingKernel, mode: "moving" });
560
+ * const chunk1 = await pipeline.process(data1, { channels: 1 });
561
+ * const chunk2 = await pipeline.process(data2, { channels: 1 });
562
+ * // State maintained between chunks, same length as input
563
+ *
564
+ * @example
543
565
  * // Simple smoothing kernel
544
566
  * const smoothKernel = new Float32Array([0.2, 0.6, 0.2]);
545
- * pipeline.convolution({ kernel: smoothKernel });
567
+ * pipeline.convolution({ kernel: smoothKernel, mode: "batch" });
546
568
  *
547
569
  * @example
548
570
  * // Gaussian kernel with explicit method
@@ -564,11 +586,11 @@ class DspProcessor {
564
586
  * @example
565
587
  * // Multi-channel EMG grid with custom kernel
566
588
  * const emgGrid = new Float32Array(80000); // 10x8 grid, 1000 samples
567
- * pipeline.convolution({ kernel: mySmoothingKernel });
589
+ * pipeline.convolution({ kernel: mySmoothingKernel, mode: "batch" });
568
590
  * const output = await pipeline.process(emgGrid, {
569
591
  * sampleRate: 2000,
570
592
  * channels: 80 // 10 * 8 sensors
571
- * });\
593
+ * });
572
594
  */
573
595
  convolution(params) {
574
596
  if (!params.kernel || params.kernel.length === 0) {
@@ -1643,46 +1665,53 @@ class DspProcessor {
1643
1665
  * Transforms time-domain signal to frequency domain or vice versa.
1644
1666
  * Supports both fast (radix-2) and direct (any size) transforms.
1645
1667
  *
1668
+ * **Two Processing Modes:**
1669
+ * 1. **Batch mode** (default): Stateless FFT over entire input buffer
1670
+ * 2. **Moving mode**: Stateful sliding-window FFT (STFT) with overlap
1671
+ *
1646
1672
  * **Transform Types:**
1647
- * - `fft`: Complex FFT (O(N log N), requires power-of-2 size)
1673
+ * - `rfft`: Real FFT (O(N log N), requires power-of-2 size) [MOST COMMON]
1674
+ * - `fft`: Complex FFT (O(N log N), requires power-of-2)
1675
+ * - `rdft`: Real DFT (O(N²), works with any size)
1648
1676
  * - `dft`: Complex DFT (O(N²), works with any size)
1649
- * - `rfft`: Real FFT for real signals (O(N log N), outputs N/2+1 bins)
1650
- * - `rdft`: Real DFT for real signals (O(N²), outputs N/2+1 bins)
1651
1677
  *
1652
1678
  * **Output Formats:**
1653
- * - `complex`: Returns interleaved [real0, imag0, real1, imag1, ...]
1654
- * - `magnitude`: Returns |X[k]| = sqrt(real² + imag²)
1655
- * - `power`: Returns |X[k] = real² + imag²
1656
- * - `phase`: Returns ∠X[k] = atan2(imag, real)
1679
+ * - `magnitude`: |X[k]| = sqrt(real² + imag²) [DEFAULT]
1680
+ * - `power`: |X[k] = real² + imag²
1681
+ * - `phase`: X[k] = atan2(imag, real)
1682
+ * - `complex`: Interleaved [real0, imag0, real1, imag1, ...]
1683
+ *
1684
+ * **Moving Mode Details:**
1685
+ * - Internally delegates to `stft()` with Hann windowing
1686
+ * - Uses CircularBufferArray for efficient buffering
1687
+ * - Default hopSize = size/2 (50% overlap)
1688
+ * - Better for time-varying signals (audio, EEG)
1657
1689
  *
1658
1690
  * @param params - FFT configuration
1691
+ * @param params.mode - 'batch' (stateless) or 'moving' (sliding window) [default: 'batch']
1659
1692
  * @param params.size - FFT size (power of 2 for FFT/RFFT, any size for DFT/RDFT)
1660
- * @param params.type - Transform type (default: 'rfft' for real signals)
1661
- * @param params.forward - Forward (time→freq) or inverse (freq→time) (default: true)
1662
- * @param params.output - Output format (default: 'magnitude')
1693
+ * @param params.hopSize - Window stride for 'moving' mode [default: size/2]
1694
+ * @param params.type - Transform type [default: 'rfft']
1695
+ * @param params.forward - Forward (time→freq) or inverse (freq→time) [default: true]
1696
+ * @param params.output - Output format [default: 'magnitude']
1663
1697
  * @returns this instance for method chaining
1664
1698
  *
1665
1699
  * @example
1666
- * // Spectral analysis with magnitude output
1700
+ * // Batch mode: Single FFT of entire buffer
1667
1701
  * pipeline.fft({
1702
+ * mode: 'batch',
1668
1703
  * size: 1024,
1669
1704
  * type: 'rfft',
1670
1705
  * output: 'magnitude'
1671
1706
  * });
1672
1707
  *
1673
1708
  * @example
1674
- * // Power spectrum for energy analysis
1675
- * pipeline.fft({
1676
- * size: 2048,
1677
- * output: 'power'
1678
- * });
1679
- *
1680
- * @example
1681
- * // Complex FFT for phase information
1709
+ * // Moving mode: STFT with 50% overlap
1682
1710
  * pipeline.fft({
1711
+ * mode: 'moving',
1683
1712
  * size: 512,
1684
- * type: 'fft',
1685
- * output: 'complex'
1713
+ * hopSize: 256, // 50% overlap
1714
+ * output: 'power'
1686
1715
  * });
1687
1716
  *
1688
1717
  * @example
@@ -1692,14 +1721,37 @@ class DspProcessor {
1692
1721
  * type: 'rdft',
1693
1722
  * output: 'magnitude'
1694
1723
  * });
1724
+ *
1725
+ * @example
1726
+ * // Complex FFT for phase analysis
1727
+ * pipeline.fft({
1728
+ * size: 512,
1729
+ * type: 'fft',
1730
+ * output: 'complex'
1731
+ * });
1695
1732
  */
1696
1733
  fft(params) {
1697
1734
  if (!params.size || params.size <= 0 || !Number.isInteger(params.size)) {
1698
1735
  throw new TypeError(`Fft: size must be a positive integer, got ${params.size}`);
1699
1736
  }
1737
+ const mode = params.mode || "batch";
1700
1738
  const type = params.type || "rfft";
1701
1739
  const forward = params.forward ?? true;
1702
1740
  const output = params.output || "magnitude";
1741
+ const hopSize = params.hopSize ?? Math.floor(params.size / 2);
1742
+ // Validate mode
1743
+ if (mode !== "batch" && mode !== "moving") {
1744
+ throw new TypeError(`Fft: mode must be 'batch' or 'moving', got '${mode}'`);
1745
+ }
1746
+ // Validate hopSize for moving mode
1747
+ if (mode === "moving") {
1748
+ if (hopSize <= 0 || !Number.isInteger(hopSize)) {
1749
+ throw new TypeError(`Fft: hopSize must be a positive integer, got ${hopSize}`);
1750
+ }
1751
+ if (hopSize > params.size) {
1752
+ throw new TypeError(`Fft: hopSize (${hopSize}) cannot exceed size (${params.size})`);
1753
+ }
1754
+ }
1703
1755
  // Validate type
1704
1756
  const validTypes = ["fft", "dft", "rfft", "rdft"];
1705
1757
  if (!validTypes.includes(type)) {
@@ -1715,14 +1767,32 @@ class DspProcessor {
1715
1767
  !this.isPowerOfTwo(params.size)) {
1716
1768
  throw new TypeError(`Fft: ${type} requires power-of-2 size. Got ${params.size}. Use 'dft' or 'rdft' for arbitrary sizes.`);
1717
1769
  }
1770
+ // MOVING MODE: Delegate to STFT (which uses MovingFftFilter internally)
1771
+ if (mode === "moving") {
1772
+ // Map FFT parameters to STFT parameters
1773
+ const stftMethod = type === "fft" || type === "rfft" ? "fft" : "dft";
1774
+ const stftType = type.includes("r") ? "real" : "complex";
1775
+ return this.stft({
1776
+ windowSize: params.size,
1777
+ hopSize: hopSize,
1778
+ method: stftMethod,
1779
+ type: stftType,
1780
+ forward: forward,
1781
+ output: output,
1782
+ window: "hann", // Default window for moving mode (better spectral leakage control)
1783
+ });
1784
+ }
1785
+ // BATCH MODE: Use stateless FftStage
1718
1786
  this.nativeInstance.addStage("fft", {
1787
+ mode,
1719
1788
  size: params.size,
1789
+ hopSize,
1720
1790
  type,
1721
1791
  forward,
1722
1792
  output,
1723
1793
  });
1724
1794
  const direction = forward ? "forward" : "inverse";
1725
- this.stages.push(`fft:${type}:${params.size}:${direction}:${output}`);
1795
+ this.stages.push(`fft:${type}:${params.size}:batch:${direction}:${output}`);
1726
1796
  return this;
1727
1797
  }
1728
1798
  /**