@octoseq/mir 0.1.0-main.4ecb074 → 0.1.0-main.5fdb072

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 (47) hide show
  1. package/dist/chunk-HF3QHCRK.js +3234 -0
  2. package/dist/chunk-HF3QHCRK.js.map +1 -0
  3. package/dist/index.d.ts +2122 -47
  4. package/dist/index.js +3524 -39
  5. package/dist/index.js.map +1 -1
  6. package/dist/runMir-CnJQbBr8.d.ts +187 -0
  7. package/dist/runner/runMir.d.ts +2 -2
  8. package/dist/runner/runMir.js +1 -1
  9. package/dist/runner/workerProtocol.d.ts +8 -1
  10. package/dist/runner/workerProtocol.js.map +1 -1
  11. package/dist/types-DqH4umN8.d.ts +761 -0
  12. package/package.json +2 -2
  13. package/src/dsp/activity.ts +544 -0
  14. package/src/dsp/bandCqt.ts +662 -0
  15. package/src/dsp/bandEvents.ts +351 -0
  16. package/src/dsp/bandMask.ts +225 -0
  17. package/src/dsp/bandMir.ts +524 -0
  18. package/src/dsp/bandProposal.ts +552 -0
  19. package/src/dsp/beatCandidates.ts +299 -0
  20. package/src/dsp/cqt.ts +386 -0
  21. package/src/dsp/cqtSignals.ts +462 -0
  22. package/src/dsp/customSignalReduction.ts +841 -0
  23. package/src/dsp/eventToSignal.ts +531 -0
  24. package/src/dsp/frequencyBand.ts +956 -0
  25. package/src/dsp/mel.ts +56 -3
  26. package/src/dsp/musicalTime.ts +240 -0
  27. package/src/dsp/onset.ts +296 -30
  28. package/src/dsp/peakPicking.ts +519 -0
  29. package/src/dsp/phaseAlignment.ts +153 -0
  30. package/src/dsp/pitch.ts +289 -0
  31. package/src/dsp/resample.ts +44 -0
  32. package/src/dsp/signalTransforms.ts +660 -0
  33. package/src/dsp/silenceGating.ts +511 -0
  34. package/src/dsp/spectral.ts +124 -4
  35. package/src/dsp/tempoHypotheses.ts +395 -0
  36. package/src/gpu/bufferPool.ts +266 -0
  37. package/src/gpu/context.ts +30 -6
  38. package/src/gpu/helpers.ts +85 -0
  39. package/src/gpu/melProject.ts +83 -0
  40. package/src/index.ts +366 -5
  41. package/src/runner/runMir.ts +234 -2
  42. package/src/runner/workerProtocol.ts +9 -1
  43. package/src/types.ts +768 -3
  44. package/dist/chunk-DUWYCAVG.js +0 -1525
  45. package/dist/chunk-DUWYCAVG.js.map +0 -1
  46. package/dist/runMir-CSIBwNZ3.d.ts +0 -84
  47. package/dist/types-BE3py4fZ.d.ts +0 -83
@@ -0,0 +1,660 @@
1
+ /**
2
+ * Signal Transforms
3
+ *
4
+ * Transform chain implementation for derived signals.
5
+ * All transforms are pure functions that operate on Float32Array values.
6
+ *
7
+ * Transform categories:
8
+ * - Smooth: movingAverage, exponential, gaussian
9
+ * - Normalize: minMax, robust, zScore
10
+ * - Scale: linear scaling with offset
11
+ * - Polarity: signed, magnitude
12
+ * - Clamp: min/max bounds
13
+ * - Remap: input range to output range with curve
14
+ */
15
+
16
+ // ============================================================================
17
+ // TYPES
18
+ // ============================================================================
19
+
20
+ /**
21
+ * Base transform type.
22
+ */
23
+ interface TransformBase {
24
+ kind: string;
25
+ }
26
+
27
+ /**
28
+ * Smooth with moving average.
29
+ */
30
+ export interface TransformSmoothMovingAverage extends TransformBase {
31
+ kind: "smooth";
32
+ method: "movingAverage";
33
+ windowMs: number;
34
+ }
35
+
36
+ /**
37
+ * Smooth with exponential filter.
38
+ */
39
+ export interface TransformSmoothExponential extends TransformBase {
40
+ kind: "smooth";
41
+ method: "exponential";
42
+ timeConstantMs: number;
43
+ }
44
+
45
+ /**
46
+ * Smooth with Gaussian kernel.
47
+ */
48
+ export interface TransformSmoothGaussian extends TransformBase {
49
+ kind: "smooth";
50
+ method: "gaussian";
51
+ windowMs: number;
52
+ }
53
+
54
+ /**
55
+ * All smoothing transform types.
56
+ */
57
+ export type TransformSmooth =
58
+ | TransformSmoothMovingAverage
59
+ | TransformSmoothExponential
60
+ | TransformSmoothGaussian;
61
+
62
+ /**
63
+ * Normalize to 0-1 range using min/max.
64
+ */
65
+ export interface TransformNormalizeMinMax extends TransformBase {
66
+ kind: "normalize";
67
+ method: "minMax";
68
+ targetMin?: number; // Default: 0
69
+ targetMax?: number; // Default: 1
70
+ }
71
+
72
+ /**
73
+ * Normalize using robust percentile range.
74
+ */
75
+ export interface TransformNormalizeRobust extends TransformBase {
76
+ kind: "normalize";
77
+ method: "robust";
78
+ percentileLow?: number; // Default: 5
79
+ percentileHigh?: number; // Default: 95
80
+ targetMin?: number; // Default: 0
81
+ targetMax?: number; // Default: 1
82
+ }
83
+
84
+ /**
85
+ * Normalize using z-score (mean and std dev).
86
+ */
87
+ export interface TransformNormalizeZScore extends TransformBase {
88
+ kind: "normalize";
89
+ method: "zScore";
90
+ }
91
+
92
+ /**
93
+ * All normalization transform types.
94
+ */
95
+ export type TransformNormalize =
96
+ | TransformNormalizeMinMax
97
+ | TransformNormalizeRobust
98
+ | TransformNormalizeZScore;
99
+
100
+ /**
101
+ * Linear scale and offset.
102
+ */
103
+ export interface TransformScale extends TransformBase {
104
+ kind: "scale";
105
+ scale: number;
106
+ offset: number;
107
+ }
108
+
109
+ /**
110
+ * Polarity mode.
111
+ */
112
+ export interface TransformPolarity extends TransformBase {
113
+ kind: "polarity";
114
+ mode: "signed" | "magnitude";
115
+ }
116
+
117
+ /**
118
+ * Clamp to bounds.
119
+ */
120
+ export interface TransformClamp extends TransformBase {
121
+ kind: "clamp";
122
+ min?: number;
123
+ max?: number;
124
+ }
125
+
126
+ /**
127
+ * Remap from input range to output range.
128
+ */
129
+ export interface TransformRemap extends TransformBase {
130
+ kind: "remap";
131
+ inputMin: number;
132
+ inputMax: number;
133
+ outputMin: number;
134
+ outputMax: number;
135
+ curve?: "linear" | "ease" | "easeIn" | "easeOut";
136
+ }
137
+
138
+ /**
139
+ * Union of all transform step types.
140
+ */
141
+ export type TransformStep =
142
+ | TransformSmooth
143
+ | TransformNormalize
144
+ | TransformScale
145
+ | TransformPolarity
146
+ | TransformClamp
147
+ | TransformRemap;
148
+
149
+ /**
150
+ * A chain of transforms to apply in sequence.
151
+ */
152
+ export type TransformChain = TransformStep[];
153
+
154
+ /**
155
+ * Context for transform chain execution.
156
+ */
157
+ export interface TransformContext {
158
+ /** Sample rate of the signal (samples per second). */
159
+ sampleRate: number;
160
+ /** Time points in seconds (optional, for time-aware transforms). */
161
+ times?: Float32Array;
162
+ }
163
+
164
+ // ============================================================================
165
+ // SMOOTHING IMPLEMENTATIONS
166
+ // ============================================================================
167
+
168
+ /**
169
+ * Apply moving average smoothing.
170
+ */
171
+ function smoothMovingAverage(
172
+ values: Float32Array,
173
+ windowMs: number,
174
+ sampleRate: number
175
+ ): Float32Array {
176
+ const windowSamples = Math.max(1, Math.round((windowMs / 1000) * sampleRate)) | 1;
177
+ if (windowSamples <= 1) return values;
178
+
179
+ const n = values.length;
180
+ const result = new Float32Array(n);
181
+ const halfWindow = Math.floor(windowSamples / 2);
182
+
183
+ for (let i = 0; i < n; i++) {
184
+ const start = Math.max(0, i - halfWindow);
185
+ const end = Math.min(n, i + halfWindow + 1);
186
+ let sum = 0;
187
+ for (let j = start; j < end; j++) {
188
+ sum += values[j]!;
189
+ }
190
+ result[i] = sum / (end - start);
191
+ }
192
+
193
+ return result;
194
+ }
195
+
196
+ /**
197
+ * Apply exponential smoothing (first-order IIR filter).
198
+ */
199
+ function smoothExponential(
200
+ values: Float32Array,
201
+ timeConstantMs: number,
202
+ sampleRate: number
203
+ ): Float32Array {
204
+ const n = values.length;
205
+ if (n === 0) return values;
206
+
207
+ const result = new Float32Array(n);
208
+ const dt = 1 / sampleRate;
209
+ const alpha = 1 - Math.exp(-dt / (timeConstantMs / 1000));
210
+
211
+ result[0] = values[0]!;
212
+ for (let i = 1; i < n; i++) {
213
+ result[i] = result[i - 1]! + alpha * (values[i]! - result[i - 1]!);
214
+ }
215
+
216
+ return result;
217
+ }
218
+
219
+ /**
220
+ * Apply Gaussian smoothing.
221
+ */
222
+ function smoothGaussian(
223
+ values: Float32Array,
224
+ windowMs: number,
225
+ sampleRate: number
226
+ ): Float32Array {
227
+ const windowSamples = Math.max(1, Math.round((windowMs / 1000) * sampleRate));
228
+ if (windowSamples <= 1) return values;
229
+
230
+ const n = values.length;
231
+ const result = new Float32Array(n);
232
+ const sigma = windowSamples / 4;
233
+ const halfWindow = Math.floor(windowSamples / 2);
234
+
235
+ // Precompute Gaussian kernel
236
+ const kernel = new Float32Array(windowSamples);
237
+ let kernelSum = 0;
238
+ for (let i = 0; i < windowSamples; i++) {
239
+ const x = i - halfWindow;
240
+ kernel[i] = Math.exp(-(x * x) / (2 * sigma * sigma));
241
+ kernelSum += kernel[i]!;
242
+ }
243
+ // Normalize kernel
244
+ for (let i = 0; i < windowSamples; i++) {
245
+ kernel[i] = kernel[i]! / kernelSum;
246
+ }
247
+
248
+ // Apply convolution
249
+ for (let i = 0; i < n; i++) {
250
+ let sum = 0;
251
+ let weightSum = 0;
252
+ for (let j = 0; j < windowSamples; j++) {
253
+ const idx = i - halfWindow + j;
254
+ if (idx >= 0 && idx < n) {
255
+ sum += values[idx]! * kernel[j]!;
256
+ weightSum += kernel[j]!;
257
+ }
258
+ }
259
+ result[i] = weightSum > 0 ? sum / weightSum : values[i]!;
260
+ }
261
+
262
+ return result;
263
+ }
264
+
265
+ // ============================================================================
266
+ // NORMALIZATION IMPLEMENTATIONS
267
+ // ============================================================================
268
+
269
+ /**
270
+ * Normalize using min/max.
271
+ */
272
+ function normalizeMinMax(
273
+ values: Float32Array,
274
+ targetMin: number = 0,
275
+ targetMax: number = 1
276
+ ): Float32Array {
277
+ const n = values.length;
278
+ if (n === 0) return values;
279
+
280
+ // Find min and max
281
+ let min = values[0]!;
282
+ let max = values[0]!;
283
+ for (let i = 1; i < n; i++) {
284
+ const v = values[i]!;
285
+ if (v < min) min = v;
286
+ if (v > max) max = v;
287
+ }
288
+
289
+ // Avoid division by zero
290
+ if (max === min) {
291
+ const result = new Float32Array(n);
292
+ result.fill((targetMin + targetMax) / 2);
293
+ return result;
294
+ }
295
+
296
+ // Normalize
297
+ const result = new Float32Array(n);
298
+ const sourceRange = max - min;
299
+ const targetRange = targetMax - targetMin;
300
+ for (let i = 0; i < n; i++) {
301
+ result[i] = targetMin + ((values[i]! - min) / sourceRange) * targetRange;
302
+ }
303
+
304
+ return result;
305
+ }
306
+
307
+ /**
308
+ * Normalize using robust percentile range.
309
+ */
310
+ function normalizeRobust(
311
+ values: Float32Array,
312
+ percentileLow: number = 5,
313
+ percentileHigh: number = 95,
314
+ targetMin: number = 0,
315
+ targetMax: number = 1
316
+ ): Float32Array {
317
+ const n = values.length;
318
+ if (n === 0) return values;
319
+
320
+ // Sort for percentile computation
321
+ const sorted = Array.from(values).sort((a, b) => a - b);
322
+ const lowIdx = Math.floor((percentileLow / 100) * (n - 1));
323
+ const highIdx = Math.floor((percentileHigh / 100) * (n - 1));
324
+ const pLow = sorted[lowIdx]!;
325
+ const pHigh = sorted[highIdx]!;
326
+
327
+ // Avoid division by zero
328
+ if (pHigh === pLow) {
329
+ const result = new Float32Array(n);
330
+ result.fill((targetMin + targetMax) / 2);
331
+ return result;
332
+ }
333
+
334
+ // Normalize and clamp
335
+ const result = new Float32Array(n);
336
+ const sourceRange = pHigh - pLow;
337
+ const targetRange = targetMax - targetMin;
338
+ for (let i = 0; i < n; i++) {
339
+ const normalized = (values[i]! - pLow) / sourceRange;
340
+ const clamped = Math.max(0, Math.min(1, normalized));
341
+ result[i] = targetMin + clamped * targetRange;
342
+ }
343
+
344
+ return result;
345
+ }
346
+
347
+ /**
348
+ * Normalize using z-score.
349
+ */
350
+ function normalizeZScore(values: Float32Array): Float32Array {
351
+ const n = values.length;
352
+ if (n === 0) return values;
353
+
354
+ // Compute mean
355
+ let sum = 0;
356
+ for (let i = 0; i < n; i++) {
357
+ sum += values[i]!;
358
+ }
359
+ const mean = sum / n;
360
+
361
+ // Compute standard deviation
362
+ let variance = 0;
363
+ for (let i = 0; i < n; i++) {
364
+ const diff = values[i]! - mean;
365
+ variance += diff * diff;
366
+ }
367
+ const stdDev = Math.sqrt(variance / n);
368
+
369
+ // Avoid division by zero
370
+ if (stdDev === 0) {
371
+ const result = new Float32Array(n);
372
+ result.fill(0);
373
+ return result;
374
+ }
375
+
376
+ // Normalize
377
+ const result = new Float32Array(n);
378
+ for (let i = 0; i < n; i++) {
379
+ result[i] = (values[i]! - mean) / stdDev;
380
+ }
381
+
382
+ return result;
383
+ }
384
+
385
+ // ============================================================================
386
+ // OTHER TRANSFORM IMPLEMENTATIONS
387
+ // ============================================================================
388
+
389
+ /**
390
+ * Apply linear scale and offset.
391
+ */
392
+ function applyScale(
393
+ values: Float32Array,
394
+ scale: number,
395
+ offset: number
396
+ ): Float32Array {
397
+ const n = values.length;
398
+ const result = new Float32Array(n);
399
+ for (let i = 0; i < n; i++) {
400
+ result[i] = values[i]! * scale + offset;
401
+ }
402
+ return result;
403
+ }
404
+
405
+ /**
406
+ * Apply polarity transformation.
407
+ */
408
+ function applyPolarityTransform(
409
+ values: Float32Array,
410
+ mode: "signed" | "magnitude"
411
+ ): Float32Array {
412
+ if (mode === "signed") {
413
+ return values;
414
+ }
415
+
416
+ const n = values.length;
417
+ const result = new Float32Array(n);
418
+ for (let i = 0; i < n; i++) {
419
+ result[i] = Math.abs(values[i]!);
420
+ }
421
+ return result;
422
+ }
423
+
424
+ /**
425
+ * Apply clamping to bounds.
426
+ */
427
+ function applyClamp(
428
+ values: Float32Array,
429
+ min?: number,
430
+ max?: number
431
+ ): Float32Array {
432
+ const n = values.length;
433
+ const result = new Float32Array(n);
434
+
435
+ for (let i = 0; i < n; i++) {
436
+ let v = values[i]!;
437
+ if (min !== undefined && v < min) v = min;
438
+ if (max !== undefined && v > max) v = max;
439
+ result[i] = v;
440
+ }
441
+
442
+ return result;
443
+ }
444
+
445
+ /**
446
+ * Apply remap with optional curve.
447
+ */
448
+ function applyRemap(
449
+ values: Float32Array,
450
+ inputMin: number,
451
+ inputMax: number,
452
+ outputMin: number,
453
+ outputMax: number,
454
+ curve: "linear" | "ease" | "easeIn" | "easeOut" = "linear"
455
+ ): Float32Array {
456
+ const n = values.length;
457
+ const result = new Float32Array(n);
458
+ const inputRange = inputMax - inputMin;
459
+ const outputRange = outputMax - outputMin;
460
+
461
+ // Avoid division by zero
462
+ if (inputRange === 0) {
463
+ result.fill((outputMin + outputMax) / 2);
464
+ return result;
465
+ }
466
+
467
+ for (let i = 0; i < n; i++) {
468
+ // Normalize to 0-1
469
+ let t = (values[i]! - inputMin) / inputRange;
470
+ t = Math.max(0, Math.min(1, t)); // Clamp
471
+
472
+ // Apply curve
473
+ switch (curve) {
474
+ case "easeIn":
475
+ t = t * t;
476
+ break;
477
+ case "easeOut":
478
+ t = 1 - (1 - t) * (1 - t);
479
+ break;
480
+ case "ease":
481
+ t = t < 0.5
482
+ ? 2 * t * t
483
+ : 1 - Math.pow(-2 * t + 2, 2) / 2;
484
+ break;
485
+ // linear: no change
486
+ }
487
+
488
+ // Map to output range
489
+ result[i] = outputMin + t * outputRange;
490
+ }
491
+
492
+ return result;
493
+ }
494
+
495
+ // ============================================================================
496
+ // TRANSFORM CHAIN EXECUTION
497
+ // ============================================================================
498
+
499
+ /**
500
+ * Apply a single transform step.
501
+ */
502
+ export function applyTransformStep(
503
+ values: Float32Array,
504
+ step: TransformStep,
505
+ context: TransformContext
506
+ ): Float32Array {
507
+ switch (step.kind) {
508
+ case "smooth":
509
+ switch (step.method) {
510
+ case "movingAverage":
511
+ return smoothMovingAverage(values, step.windowMs, context.sampleRate);
512
+ case "exponential":
513
+ return smoothExponential(values, step.timeConstantMs, context.sampleRate);
514
+ case "gaussian":
515
+ return smoothGaussian(values, step.windowMs, context.sampleRate);
516
+ default:
517
+ return values;
518
+ }
519
+
520
+ case "normalize":
521
+ switch (step.method) {
522
+ case "minMax":
523
+ return normalizeMinMax(values, step.targetMin, step.targetMax);
524
+ case "robust":
525
+ return normalizeRobust(
526
+ values,
527
+ step.percentileLow,
528
+ step.percentileHigh,
529
+ step.targetMin,
530
+ step.targetMax
531
+ );
532
+ case "zScore":
533
+ return normalizeZScore(values);
534
+ default:
535
+ return values;
536
+ }
537
+
538
+ case "scale":
539
+ return applyScale(values, step.scale, step.offset);
540
+
541
+ case "polarity":
542
+ return applyPolarityTransform(values, step.mode);
543
+
544
+ case "clamp":
545
+ return applyClamp(values, step.min, step.max);
546
+
547
+ case "remap":
548
+ return applyRemap(
549
+ values,
550
+ step.inputMin,
551
+ step.inputMax,
552
+ step.outputMin,
553
+ step.outputMax,
554
+ step.curve
555
+ );
556
+
557
+ default:
558
+ return values;
559
+ }
560
+ }
561
+
562
+ /**
563
+ * Apply a chain of transforms to a signal.
564
+ */
565
+ export function applyTransformChain(
566
+ values: Float32Array,
567
+ chain: TransformChain,
568
+ context: TransformContext
569
+ ): Float32Array {
570
+ let result = values;
571
+
572
+ for (const step of chain) {
573
+ result = applyTransformStep(result, step, context);
574
+ }
575
+
576
+ return result;
577
+ }
578
+
579
+ // ============================================================================
580
+ // TRANSFORM UTILITIES
581
+ // ============================================================================
582
+
583
+ /**
584
+ * Get human-readable label for a transform step.
585
+ */
586
+ export function getTransformLabel(step: TransformStep): string {
587
+ switch (step.kind) {
588
+ case "smooth":
589
+ switch (step.method) {
590
+ case "movingAverage":
591
+ return `Smooth (${step.windowMs}ms avg)`;
592
+ case "exponential":
593
+ return `Smooth (${step.timeConstantMs}ms exp)`;
594
+ case "gaussian":
595
+ return `Smooth (${step.windowMs}ms gauss)`;
596
+ default:
597
+ return "Smooth";
598
+ }
599
+
600
+ case "normalize":
601
+ switch (step.method) {
602
+ case "minMax":
603
+ return `Normalize (${step.targetMin ?? 0}-${step.targetMax ?? 1})`;
604
+ case "robust":
605
+ return `Normalize (robust ${step.percentileLow ?? 5}-${step.percentileHigh ?? 95}%)`;
606
+ case "zScore":
607
+ return "Normalize (z-score)";
608
+ default:
609
+ return "Normalize";
610
+ }
611
+
612
+ case "scale":
613
+ return step.offset !== 0
614
+ ? `Scale (×${step.scale} + ${step.offset})`
615
+ : `Scale (×${step.scale})`;
616
+
617
+ case "polarity":
618
+ return step.mode === "magnitude" ? "Magnitude" : "Signed";
619
+
620
+ case "clamp":
621
+ if (step.min !== undefined && step.max !== undefined) {
622
+ return `Clamp (${step.min}-${step.max})`;
623
+ } else if (step.min !== undefined) {
624
+ return `Clamp (≥${step.min})`;
625
+ } else if (step.max !== undefined) {
626
+ return `Clamp (≤${step.max})`;
627
+ }
628
+ return "Clamp";
629
+
630
+ case "remap":
631
+ return `Remap (${step.inputMin}-${step.inputMax} → ${step.outputMin}-${step.outputMax})`;
632
+
633
+ default:
634
+ return "Unknown";
635
+ }
636
+ }
637
+
638
+ /**
639
+ * Create a default transform step for a kind.
640
+ */
641
+ export function createDefaultTransform(
642
+ kind: TransformStep["kind"]
643
+ ): TransformStep | null {
644
+ switch (kind) {
645
+ case "smooth":
646
+ return { kind: "smooth", method: "movingAverage", windowMs: 10 };
647
+ case "normalize":
648
+ return { kind: "normalize", method: "minMax", targetMin: 0, targetMax: 1 };
649
+ case "scale":
650
+ return { kind: "scale", scale: 1, offset: 0 };
651
+ case "polarity":
652
+ return { kind: "polarity", mode: "signed" };
653
+ case "clamp":
654
+ return { kind: "clamp", min: 0, max: 1 };
655
+ case "remap":
656
+ return { kind: "remap", inputMin: 0, inputMax: 1, outputMin: 0, outputMax: 1 };
657
+ default:
658
+ return null;
659
+ }
660
+ }