@waveform-playlist/spectrogram 5.3.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.
package/dist/index.mjs ADDED
@@ -0,0 +1,4440 @@
1
+ // src/computation/fft.ts
2
+ import FFT from "fft.js";
3
+ var fftInstances = /* @__PURE__ */ new Map();
4
+ var complexBuffers = /* @__PURE__ */ new Map();
5
+ function getFftInstance(size) {
6
+ let instance = fftInstances.get(size);
7
+ if (!instance) {
8
+ instance = new FFT(size);
9
+ fftInstances.set(size, instance);
10
+ complexBuffers.set(size, instance.createComplexArray());
11
+ }
12
+ return instance;
13
+ }
14
+ function getComplexBuffer(size) {
15
+ return complexBuffers.get(size);
16
+ }
17
+ function fftMagnitudeDb(real, out) {
18
+ const n = real.length;
19
+ const f = getFftInstance(n);
20
+ const complexOut = getComplexBuffer(n);
21
+ f.realTransform(complexOut, real);
22
+ const half = n >> 1;
23
+ for (let i = 0; i < half; i++) {
24
+ const re = complexOut[i * 2];
25
+ const im = complexOut[i * 2 + 1];
26
+ let db = 20 * Math.log10(Math.sqrt(re * re + im * im) + 1e-10);
27
+ if (db < -160) db = -160;
28
+ out[i] = db;
29
+ }
30
+ }
31
+
32
+ // src/computation/windowFunctions.ts
33
+ function getWindowFunction(name, size, alpha) {
34
+ const window2 = new Float32Array(size);
35
+ const N = size;
36
+ switch (name) {
37
+ case "rectangular":
38
+ for (let i = 0; i < size; i++) window2[i] = 1;
39
+ break;
40
+ case "bartlett":
41
+ for (let i = 0; i < size; i++) {
42
+ window2[i] = 1 - Math.abs((2 * i - N) / N);
43
+ }
44
+ break;
45
+ case "hann":
46
+ for (let i = 0; i < size; i++) {
47
+ window2[i] = 0.5 * (1 - Math.cos(2 * Math.PI * i / N));
48
+ }
49
+ break;
50
+ case "hamming":
51
+ for (let i = 0; i < size; i++) {
52
+ const a = alpha ?? 0.54;
53
+ window2[i] = a - (1 - a) * Math.cos(2 * Math.PI * i / N);
54
+ }
55
+ break;
56
+ case "blackman": {
57
+ const a0 = 0.42;
58
+ const a1 = 0.5;
59
+ const a2 = 0.08;
60
+ for (let i = 0; i < size; i++) {
61
+ window2[i] = a0 - a1 * Math.cos(2 * Math.PI * i / N) + a2 * Math.cos(4 * Math.PI * i / N);
62
+ }
63
+ break;
64
+ }
65
+ case "blackman-harris": {
66
+ const c0 = 0.35875;
67
+ const c1 = 0.48829;
68
+ const c2 = 0.14128;
69
+ const c3 = 0.01168;
70
+ for (let i = 0; i < size; i++) {
71
+ window2[i] = c0 - c1 * Math.cos(2 * Math.PI * i / N) + c2 * Math.cos(4 * Math.PI * i / N) - c3 * Math.cos(6 * Math.PI * i / N);
72
+ }
73
+ break;
74
+ }
75
+ default:
76
+ console.warn(`[spectrogram] Unknown window function "${name}", falling back to hann`);
77
+ for (let i = 0; i < size; i++) {
78
+ window2[i] = 0.5 * (1 - Math.cos(2 * Math.PI * i / N));
79
+ }
80
+ }
81
+ let sum = 0;
82
+ for (let i = 0; i < size; i++) sum += window2[i];
83
+ if (sum > 0) {
84
+ const scale = 2 / sum;
85
+ for (let i = 0; i < size; i++) window2[i] *= scale;
86
+ }
87
+ return window2;
88
+ }
89
+
90
+ // src/computation/computeSpectrogram.ts
91
+ function computeSpectrogram(audioBuffer, config = {}, offsetSamples = 0, durationSamples, channel = 0) {
92
+ const windowSize = config.fftSize ?? 2048;
93
+ const zeroPaddingFactor = config.zeroPaddingFactor ?? 2;
94
+ const actualFftSize = windowSize * zeroPaddingFactor;
95
+ const hopSize = config.hopSize ?? Math.floor(windowSize / 4);
96
+ const windowName = config.windowFunction ?? "hann";
97
+ const gainDb = config.gainDb ?? 20;
98
+ const rangeDb = config.rangeDb ?? 80;
99
+ const alpha = config.alpha;
100
+ const sampleRate = audioBuffer.sampleRate;
101
+ const frequencyBinCount = actualFftSize >> 1;
102
+ const totalSamples = durationSamples ?? audioBuffer.length - offsetSamples;
103
+ const channelIdx = Math.min(channel, audioBuffer.numberOfChannels - 1);
104
+ const channelData = audioBuffer.getChannelData(channelIdx);
105
+ const window2 = getWindowFunction(windowName, windowSize, alpha);
106
+ const frameCount = Math.max(1, Math.floor((totalSamples - windowSize) / hopSize) + 1);
107
+ const data = new Float32Array(frameCount * frequencyBinCount);
108
+ const real = new Float32Array(actualFftSize);
109
+ const dbBuf = new Float32Array(frequencyBinCount);
110
+ for (let frame = 0; frame < frameCount; frame++) {
111
+ const start = offsetSamples + frame * hopSize;
112
+ for (let i = 0; i < windowSize; i++) {
113
+ const sampleIdx = start + i;
114
+ real[i] = sampleIdx < channelData.length ? channelData[sampleIdx] * window2[i] : 0;
115
+ }
116
+ for (let i = windowSize; i < actualFftSize; i++) {
117
+ real[i] = 0;
118
+ }
119
+ fftMagnitudeDb(real, dbBuf);
120
+ data.set(dbBuf, frame * frequencyBinCount);
121
+ }
122
+ return {
123
+ fftSize: actualFftSize,
124
+ windowSize,
125
+ frequencyBinCount,
126
+ sampleRate,
127
+ hopSize,
128
+ frameCount,
129
+ data,
130
+ gainDb,
131
+ rangeDb
132
+ };
133
+ }
134
+ function computeSpectrogramMono(audioBuffer, config = {}, offsetSamples = 0, durationSamples) {
135
+ if (audioBuffer.numberOfChannels === 1) {
136
+ return computeSpectrogram(audioBuffer, config, offsetSamples, durationSamples, 0);
137
+ }
138
+ const windowSize = config.fftSize ?? 2048;
139
+ const zeroPaddingFactor = config.zeroPaddingFactor ?? 2;
140
+ const actualFftSize = windowSize * zeroPaddingFactor;
141
+ const hopSize = config.hopSize ?? Math.floor(windowSize / 4);
142
+ const windowName = config.windowFunction ?? "hann";
143
+ const gainDb = config.gainDb ?? 20;
144
+ const rangeDb = config.rangeDb ?? 80;
145
+ const alpha = config.alpha;
146
+ const sampleRate = audioBuffer.sampleRate;
147
+ const frequencyBinCount = actualFftSize >> 1;
148
+ const totalSamples = durationSamples ?? audioBuffer.length - offsetSamples;
149
+ const numChannels = audioBuffer.numberOfChannels;
150
+ const window2 = getWindowFunction(windowName, windowSize, alpha);
151
+ const frameCount = Math.max(1, Math.floor((totalSamples - windowSize) / hopSize) + 1);
152
+ const data = new Float32Array(frameCount * frequencyBinCount);
153
+ const real = new Float32Array(actualFftSize);
154
+ const dbBuf = new Float32Array(frequencyBinCount);
155
+ const channels = [];
156
+ for (let ch = 0; ch < numChannels; ch++) {
157
+ channels.push(audioBuffer.getChannelData(ch));
158
+ }
159
+ for (let frame = 0; frame < frameCount; frame++) {
160
+ const start = offsetSamples + frame * hopSize;
161
+ for (let i = 0; i < windowSize; i++) {
162
+ const sampleIdx = start + i;
163
+ let sum = 0;
164
+ for (let ch = 0; ch < numChannels; ch++) {
165
+ sum += sampleIdx < channels[ch].length ? channels[ch][sampleIdx] : 0;
166
+ }
167
+ real[i] = sum / numChannels * window2[i];
168
+ }
169
+ for (let i = windowSize; i < actualFftSize; i++) {
170
+ real[i] = 0;
171
+ }
172
+ fftMagnitudeDb(real, dbBuf);
173
+ data.set(dbBuf, frame * frequencyBinCount);
174
+ }
175
+ return {
176
+ fftSize: actualFftSize,
177
+ windowSize,
178
+ frequencyBinCount,
179
+ sampleRate,
180
+ hopSize,
181
+ frameCount,
182
+ data,
183
+ gainDb,
184
+ rangeDb
185
+ };
186
+ }
187
+
188
+ // src/computation/colorMaps.ts
189
+ function interpolateLUT(stops) {
190
+ const lut = new Uint8Array(256 * 3);
191
+ const n = stops.length;
192
+ for (let i = 0; i < 256; i++) {
193
+ const t = i / 255;
194
+ const pos = t * (n - 1);
195
+ const lo = Math.floor(pos);
196
+ const hi = Math.min(lo + 1, n - 1);
197
+ const frac = pos - lo;
198
+ lut[i * 3] = Math.round(stops[lo][0] * (1 - frac) + stops[hi][0] * frac);
199
+ lut[i * 3 + 1] = Math.round(stops[lo][1] * (1 - frac) + stops[hi][1] * frac);
200
+ lut[i * 3 + 2] = Math.round(stops[lo][2] * (1 - frac) + stops[hi][2] * frac);
201
+ }
202
+ return lut;
203
+ }
204
+ var VIRIDIS_LUT = new Uint8Array([
205
+ 68,
206
+ 1,
207
+ 84,
208
+ 68,
209
+ 2,
210
+ 86,
211
+ 69,
212
+ 4,
213
+ 87,
214
+ 69,
215
+ 5,
216
+ 89,
217
+ 70,
218
+ 7,
219
+ 90,
220
+ 70,
221
+ 8,
222
+ 92,
223
+ 70,
224
+ 10,
225
+ 93,
226
+ 70,
227
+ 11,
228
+ 94,
229
+ 71,
230
+ 13,
231
+ 96,
232
+ 71,
233
+ 14,
234
+ 97,
235
+ 71,
236
+ 16,
237
+ 99,
238
+ 71,
239
+ 17,
240
+ 100,
241
+ 71,
242
+ 19,
243
+ 101,
244
+ 72,
245
+ 20,
246
+ 103,
247
+ 72,
248
+ 22,
249
+ 104,
250
+ 72,
251
+ 23,
252
+ 105,
253
+ 72,
254
+ 24,
255
+ 106,
256
+ 72,
257
+ 26,
258
+ 108,
259
+ 72,
260
+ 27,
261
+ 109,
262
+ 72,
263
+ 28,
264
+ 110,
265
+ 72,
266
+ 29,
267
+ 111,
268
+ 72,
269
+ 31,
270
+ 112,
271
+ 72,
272
+ 32,
273
+ 113,
274
+ 72,
275
+ 33,
276
+ 115,
277
+ 72,
278
+ 35,
279
+ 116,
280
+ 72,
281
+ 36,
282
+ 117,
283
+ 72,
284
+ 37,
285
+ 118,
286
+ 72,
287
+ 38,
288
+ 119,
289
+ 72,
290
+ 40,
291
+ 120,
292
+ 72,
293
+ 41,
294
+ 121,
295
+ 71,
296
+ 42,
297
+ 122,
298
+ 71,
299
+ 44,
300
+ 122,
301
+ 71,
302
+ 45,
303
+ 123,
304
+ 71,
305
+ 46,
306
+ 124,
307
+ 71,
308
+ 47,
309
+ 125,
310
+ 70,
311
+ 48,
312
+ 126,
313
+ 70,
314
+ 50,
315
+ 126,
316
+ 70,
317
+ 51,
318
+ 127,
319
+ 70,
320
+ 52,
321
+ 128,
322
+ 69,
323
+ 53,
324
+ 129,
325
+ 69,
326
+ 55,
327
+ 129,
328
+ 69,
329
+ 56,
330
+ 130,
331
+ 68,
332
+ 57,
333
+ 131,
334
+ 68,
335
+ 58,
336
+ 131,
337
+ 68,
338
+ 59,
339
+ 132,
340
+ 67,
341
+ 61,
342
+ 132,
343
+ 67,
344
+ 62,
345
+ 133,
346
+ 66,
347
+ 63,
348
+ 133,
349
+ 66,
350
+ 64,
351
+ 134,
352
+ 66,
353
+ 65,
354
+ 134,
355
+ 65,
356
+ 66,
357
+ 135,
358
+ 65,
359
+ 68,
360
+ 135,
361
+ 64,
362
+ 69,
363
+ 136,
364
+ 64,
365
+ 70,
366
+ 136,
367
+ 63,
368
+ 71,
369
+ 136,
370
+ 63,
371
+ 72,
372
+ 137,
373
+ 62,
374
+ 73,
375
+ 137,
376
+ 62,
377
+ 74,
378
+ 137,
379
+ 62,
380
+ 76,
381
+ 138,
382
+ 61,
383
+ 77,
384
+ 138,
385
+ 61,
386
+ 78,
387
+ 138,
388
+ 60,
389
+ 79,
390
+ 138,
391
+ 60,
392
+ 80,
393
+ 139,
394
+ 59,
395
+ 81,
396
+ 139,
397
+ 59,
398
+ 82,
399
+ 139,
400
+ 58,
401
+ 83,
402
+ 139,
403
+ 58,
404
+ 84,
405
+ 140,
406
+ 57,
407
+ 85,
408
+ 140,
409
+ 57,
410
+ 86,
411
+ 140,
412
+ 56,
413
+ 88,
414
+ 140,
415
+ 56,
416
+ 89,
417
+ 140,
418
+ 55,
419
+ 90,
420
+ 140,
421
+ 55,
422
+ 91,
423
+ 141,
424
+ 54,
425
+ 92,
426
+ 141,
427
+ 54,
428
+ 93,
429
+ 141,
430
+ 53,
431
+ 94,
432
+ 141,
433
+ 53,
434
+ 95,
435
+ 141,
436
+ 52,
437
+ 96,
438
+ 141,
439
+ 52,
440
+ 97,
441
+ 141,
442
+ 51,
443
+ 98,
444
+ 141,
445
+ 51,
446
+ 99,
447
+ 141,
448
+ 50,
449
+ 100,
450
+ 142,
451
+ 50,
452
+ 101,
453
+ 142,
454
+ 49,
455
+ 102,
456
+ 142,
457
+ 49,
458
+ 103,
459
+ 142,
460
+ 49,
461
+ 104,
462
+ 142,
463
+ 48,
464
+ 105,
465
+ 142,
466
+ 48,
467
+ 106,
468
+ 142,
469
+ 47,
470
+ 107,
471
+ 142,
472
+ 47,
473
+ 108,
474
+ 142,
475
+ 46,
476
+ 109,
477
+ 142,
478
+ 46,
479
+ 110,
480
+ 142,
481
+ 46,
482
+ 111,
483
+ 142,
484
+ 45,
485
+ 112,
486
+ 142,
487
+ 45,
488
+ 113,
489
+ 142,
490
+ 44,
491
+ 113,
492
+ 142,
493
+ 44,
494
+ 114,
495
+ 142,
496
+ 44,
497
+ 115,
498
+ 142,
499
+ 43,
500
+ 116,
501
+ 142,
502
+ 43,
503
+ 117,
504
+ 142,
505
+ 42,
506
+ 118,
507
+ 142,
508
+ 42,
509
+ 119,
510
+ 142,
511
+ 42,
512
+ 120,
513
+ 142,
514
+ 41,
515
+ 121,
516
+ 142,
517
+ 41,
518
+ 122,
519
+ 142,
520
+ 41,
521
+ 123,
522
+ 142,
523
+ 40,
524
+ 124,
525
+ 142,
526
+ 40,
527
+ 125,
528
+ 142,
529
+ 39,
530
+ 126,
531
+ 142,
532
+ 39,
533
+ 127,
534
+ 142,
535
+ 39,
536
+ 128,
537
+ 142,
538
+ 38,
539
+ 129,
540
+ 142,
541
+ 38,
542
+ 130,
543
+ 142,
544
+ 38,
545
+ 130,
546
+ 142,
547
+ 37,
548
+ 131,
549
+ 142,
550
+ 37,
551
+ 132,
552
+ 142,
553
+ 37,
554
+ 133,
555
+ 142,
556
+ 36,
557
+ 134,
558
+ 142,
559
+ 36,
560
+ 135,
561
+ 142,
562
+ 35,
563
+ 136,
564
+ 142,
565
+ 35,
566
+ 137,
567
+ 142,
568
+ 35,
569
+ 138,
570
+ 141,
571
+ 34,
572
+ 139,
573
+ 141,
574
+ 34,
575
+ 140,
576
+ 141,
577
+ 34,
578
+ 141,
579
+ 141,
580
+ 33,
581
+ 142,
582
+ 141,
583
+ 33,
584
+ 143,
585
+ 141,
586
+ 33,
587
+ 144,
588
+ 141,
589
+ 33,
590
+ 145,
591
+ 140,
592
+ 32,
593
+ 146,
594
+ 140,
595
+ 32,
596
+ 146,
597
+ 140,
598
+ 32,
599
+ 147,
600
+ 140,
601
+ 31,
602
+ 148,
603
+ 140,
604
+ 31,
605
+ 149,
606
+ 139,
607
+ 31,
608
+ 150,
609
+ 139,
610
+ 31,
611
+ 151,
612
+ 139,
613
+ 31,
614
+ 152,
615
+ 139,
616
+ 31,
617
+ 153,
618
+ 138,
619
+ 31,
620
+ 154,
621
+ 138,
622
+ 30,
623
+ 155,
624
+ 138,
625
+ 30,
626
+ 156,
627
+ 137,
628
+ 30,
629
+ 157,
630
+ 137,
631
+ 31,
632
+ 158,
633
+ 137,
634
+ 31,
635
+ 159,
636
+ 136,
637
+ 31,
638
+ 160,
639
+ 136,
640
+ 31,
641
+ 161,
642
+ 136,
643
+ 31,
644
+ 161,
645
+ 135,
646
+ 31,
647
+ 162,
648
+ 135,
649
+ 32,
650
+ 163,
651
+ 134,
652
+ 32,
653
+ 164,
654
+ 134,
655
+ 33,
656
+ 165,
657
+ 133,
658
+ 33,
659
+ 166,
660
+ 133,
661
+ 34,
662
+ 167,
663
+ 133,
664
+ 34,
665
+ 168,
666
+ 132,
667
+ 35,
668
+ 169,
669
+ 131,
670
+ 36,
671
+ 170,
672
+ 131,
673
+ 37,
674
+ 171,
675
+ 130,
676
+ 37,
677
+ 172,
678
+ 130,
679
+ 38,
680
+ 173,
681
+ 129,
682
+ 39,
683
+ 173,
684
+ 129,
685
+ 40,
686
+ 174,
687
+ 128,
688
+ 41,
689
+ 175,
690
+ 127,
691
+ 42,
692
+ 176,
693
+ 127,
694
+ 44,
695
+ 177,
696
+ 126,
697
+ 45,
698
+ 178,
699
+ 125,
700
+ 46,
701
+ 179,
702
+ 124,
703
+ 47,
704
+ 180,
705
+ 124,
706
+ 49,
707
+ 181,
708
+ 123,
709
+ 50,
710
+ 182,
711
+ 122,
712
+ 52,
713
+ 182,
714
+ 121,
715
+ 53,
716
+ 183,
717
+ 121,
718
+ 55,
719
+ 184,
720
+ 120,
721
+ 56,
722
+ 185,
723
+ 119,
724
+ 58,
725
+ 186,
726
+ 118,
727
+ 59,
728
+ 187,
729
+ 117,
730
+ 61,
731
+ 188,
732
+ 116,
733
+ 63,
734
+ 188,
735
+ 115,
736
+ 64,
737
+ 189,
738
+ 114,
739
+ 66,
740
+ 190,
741
+ 113,
742
+ 68,
743
+ 191,
744
+ 112,
745
+ 70,
746
+ 192,
747
+ 111,
748
+ 72,
749
+ 193,
750
+ 110,
751
+ 74,
752
+ 193,
753
+ 109,
754
+ 76,
755
+ 194,
756
+ 108,
757
+ 78,
758
+ 195,
759
+ 107,
760
+ 80,
761
+ 196,
762
+ 106,
763
+ 82,
764
+ 197,
765
+ 105,
766
+ 84,
767
+ 197,
768
+ 104,
769
+ 86,
770
+ 198,
771
+ 103,
772
+ 88,
773
+ 199,
774
+ 101,
775
+ 90,
776
+ 200,
777
+ 100,
778
+ 92,
779
+ 200,
780
+ 99,
781
+ 94,
782
+ 201,
783
+ 98,
784
+ 96,
785
+ 202,
786
+ 96,
787
+ 99,
788
+ 203,
789
+ 95,
790
+ 101,
791
+ 203,
792
+ 94,
793
+ 103,
794
+ 204,
795
+ 92,
796
+ 105,
797
+ 205,
798
+ 91,
799
+ 108,
800
+ 205,
801
+ 90,
802
+ 110,
803
+ 206,
804
+ 88,
805
+ 112,
806
+ 207,
807
+ 87,
808
+ 115,
809
+ 208,
810
+ 86,
811
+ 117,
812
+ 208,
813
+ 84,
814
+ 119,
815
+ 209,
816
+ 83,
817
+ 122,
818
+ 209,
819
+ 81,
820
+ 124,
821
+ 210,
822
+ 80,
823
+ 127,
824
+ 211,
825
+ 78,
826
+ 129,
827
+ 211,
828
+ 77,
829
+ 132,
830
+ 212,
831
+ 75,
832
+ 134,
833
+ 213,
834
+ 73,
835
+ 137,
836
+ 213,
837
+ 72,
838
+ 139,
839
+ 214,
840
+ 70,
841
+ 142,
842
+ 214,
843
+ 69,
844
+ 144,
845
+ 215,
846
+ 67,
847
+ 147,
848
+ 215,
849
+ 65,
850
+ 149,
851
+ 216,
852
+ 64,
853
+ 152,
854
+ 216,
855
+ 62,
856
+ 155,
857
+ 217,
858
+ 60,
859
+ 157,
860
+ 217,
861
+ 59,
862
+ 160,
863
+ 218,
864
+ 57,
865
+ 162,
866
+ 218,
867
+ 55,
868
+ 165,
869
+ 219,
870
+ 54,
871
+ 168,
872
+ 219,
873
+ 52,
874
+ 170,
875
+ 220,
876
+ 50,
877
+ 173,
878
+ 220,
879
+ 48,
880
+ 176,
881
+ 221,
882
+ 47,
883
+ 178,
884
+ 221,
885
+ 45,
886
+ 181,
887
+ 222,
888
+ 43,
889
+ 184,
890
+ 222,
891
+ 41,
892
+ 186,
893
+ 222,
894
+ 40,
895
+ 189,
896
+ 223,
897
+ 38,
898
+ 192,
899
+ 223,
900
+ 37,
901
+ 194,
902
+ 223,
903
+ 35,
904
+ 197,
905
+ 224,
906
+ 33,
907
+ 200,
908
+ 224,
909
+ 32,
910
+ 202,
911
+ 225,
912
+ 31,
913
+ 205,
914
+ 225,
915
+ 29,
916
+ 208,
917
+ 225,
918
+ 28,
919
+ 210,
920
+ 226,
921
+ 27,
922
+ 213,
923
+ 226,
924
+ 26,
925
+ 216,
926
+ 226,
927
+ 25,
928
+ 218,
929
+ 227,
930
+ 25,
931
+ 221,
932
+ 227,
933
+ 24,
934
+ 223,
935
+ 227,
936
+ 24,
937
+ 226,
938
+ 228,
939
+ 24,
940
+ 229,
941
+ 228,
942
+ 25,
943
+ 231,
944
+ 228,
945
+ 25,
946
+ 234,
947
+ 229,
948
+ 26,
949
+ 236,
950
+ 229,
951
+ 27,
952
+ 239,
953
+ 229,
954
+ 28,
955
+ 241,
956
+ 229,
957
+ 29,
958
+ 244,
959
+ 230,
960
+ 30,
961
+ 246,
962
+ 230,
963
+ 32,
964
+ 248,
965
+ 230,
966
+ 33,
967
+ 251,
968
+ 231,
969
+ 35,
970
+ 253,
971
+ 231,
972
+ 37
973
+ ]);
974
+ var MAGMA_LUT = new Uint8Array([
975
+ 0,
976
+ 0,
977
+ 4,
978
+ 1,
979
+ 0,
980
+ 5,
981
+ 1,
982
+ 1,
983
+ 6,
984
+ 1,
985
+ 1,
986
+ 8,
987
+ 2,
988
+ 1,
989
+ 9,
990
+ 2,
991
+ 2,
992
+ 11,
993
+ 2,
994
+ 2,
995
+ 13,
996
+ 3,
997
+ 3,
998
+ 15,
999
+ 3,
1000
+ 3,
1001
+ 18,
1002
+ 4,
1003
+ 4,
1004
+ 20,
1005
+ 5,
1006
+ 4,
1007
+ 22,
1008
+ 6,
1009
+ 5,
1010
+ 24,
1011
+ 6,
1012
+ 5,
1013
+ 26,
1014
+ 7,
1015
+ 6,
1016
+ 28,
1017
+ 8,
1018
+ 7,
1019
+ 30,
1020
+ 9,
1021
+ 7,
1022
+ 32,
1023
+ 10,
1024
+ 8,
1025
+ 34,
1026
+ 11,
1027
+ 9,
1028
+ 36,
1029
+ 12,
1030
+ 9,
1031
+ 38,
1032
+ 13,
1033
+ 10,
1034
+ 41,
1035
+ 14,
1036
+ 11,
1037
+ 43,
1038
+ 16,
1039
+ 11,
1040
+ 45,
1041
+ 17,
1042
+ 12,
1043
+ 47,
1044
+ 18,
1045
+ 13,
1046
+ 49,
1047
+ 19,
1048
+ 13,
1049
+ 52,
1050
+ 20,
1051
+ 14,
1052
+ 54,
1053
+ 21,
1054
+ 14,
1055
+ 56,
1056
+ 22,
1057
+ 15,
1058
+ 59,
1059
+ 24,
1060
+ 15,
1061
+ 61,
1062
+ 25,
1063
+ 16,
1064
+ 63,
1065
+ 26,
1066
+ 16,
1067
+ 66,
1068
+ 28,
1069
+ 16,
1070
+ 68,
1071
+ 29,
1072
+ 17,
1073
+ 71,
1074
+ 30,
1075
+ 17,
1076
+ 73,
1077
+ 32,
1078
+ 17,
1079
+ 75,
1080
+ 33,
1081
+ 17,
1082
+ 78,
1083
+ 34,
1084
+ 17,
1085
+ 80,
1086
+ 36,
1087
+ 18,
1088
+ 83,
1089
+ 37,
1090
+ 18,
1091
+ 85,
1092
+ 39,
1093
+ 18,
1094
+ 88,
1095
+ 41,
1096
+ 17,
1097
+ 90,
1098
+ 42,
1099
+ 17,
1100
+ 92,
1101
+ 44,
1102
+ 17,
1103
+ 95,
1104
+ 45,
1105
+ 17,
1106
+ 97,
1107
+ 47,
1108
+ 17,
1109
+ 99,
1110
+ 49,
1111
+ 17,
1112
+ 101,
1113
+ 51,
1114
+ 16,
1115
+ 103,
1116
+ 52,
1117
+ 16,
1118
+ 105,
1119
+ 54,
1120
+ 16,
1121
+ 107,
1122
+ 56,
1123
+ 16,
1124
+ 108,
1125
+ 57,
1126
+ 15,
1127
+ 110,
1128
+ 59,
1129
+ 15,
1130
+ 112,
1131
+ 61,
1132
+ 15,
1133
+ 113,
1134
+ 63,
1135
+ 15,
1136
+ 114,
1137
+ 64,
1138
+ 15,
1139
+ 116,
1140
+ 66,
1141
+ 15,
1142
+ 117,
1143
+ 68,
1144
+ 15,
1145
+ 118,
1146
+ 69,
1147
+ 16,
1148
+ 119,
1149
+ 71,
1150
+ 16,
1151
+ 120,
1152
+ 73,
1153
+ 16,
1154
+ 120,
1155
+ 74,
1156
+ 16,
1157
+ 121,
1158
+ 76,
1159
+ 17,
1160
+ 122,
1161
+ 78,
1162
+ 17,
1163
+ 123,
1164
+ 79,
1165
+ 18,
1166
+ 123,
1167
+ 81,
1168
+ 18,
1169
+ 124,
1170
+ 82,
1171
+ 19,
1172
+ 124,
1173
+ 84,
1174
+ 19,
1175
+ 125,
1176
+ 86,
1177
+ 20,
1178
+ 125,
1179
+ 87,
1180
+ 21,
1181
+ 126,
1182
+ 89,
1183
+ 21,
1184
+ 126,
1185
+ 90,
1186
+ 22,
1187
+ 126,
1188
+ 92,
1189
+ 22,
1190
+ 127,
1191
+ 93,
1192
+ 23,
1193
+ 127,
1194
+ 95,
1195
+ 24,
1196
+ 127,
1197
+ 96,
1198
+ 24,
1199
+ 128,
1200
+ 98,
1201
+ 25,
1202
+ 128,
1203
+ 100,
1204
+ 26,
1205
+ 128,
1206
+ 101,
1207
+ 26,
1208
+ 128,
1209
+ 103,
1210
+ 27,
1211
+ 128,
1212
+ 104,
1213
+ 28,
1214
+ 129,
1215
+ 106,
1216
+ 28,
1217
+ 129,
1218
+ 107,
1219
+ 29,
1220
+ 129,
1221
+ 109,
1222
+ 29,
1223
+ 129,
1224
+ 110,
1225
+ 30,
1226
+ 129,
1227
+ 112,
1228
+ 31,
1229
+ 129,
1230
+ 114,
1231
+ 31,
1232
+ 129,
1233
+ 115,
1234
+ 32,
1235
+ 129,
1236
+ 117,
1237
+ 33,
1238
+ 129,
1239
+ 118,
1240
+ 33,
1241
+ 129,
1242
+ 120,
1243
+ 34,
1244
+ 129,
1245
+ 121,
1246
+ 34,
1247
+ 130,
1248
+ 123,
1249
+ 35,
1250
+ 130,
1251
+ 124,
1252
+ 35,
1253
+ 130,
1254
+ 126,
1255
+ 36,
1256
+ 130,
1257
+ 128,
1258
+ 37,
1259
+ 130,
1260
+ 129,
1261
+ 37,
1262
+ 129,
1263
+ 131,
1264
+ 38,
1265
+ 129,
1266
+ 132,
1267
+ 38,
1268
+ 129,
1269
+ 134,
1270
+ 39,
1271
+ 129,
1272
+ 136,
1273
+ 39,
1274
+ 129,
1275
+ 137,
1276
+ 40,
1277
+ 129,
1278
+ 139,
1279
+ 41,
1280
+ 129,
1281
+ 140,
1282
+ 41,
1283
+ 129,
1284
+ 142,
1285
+ 42,
1286
+ 129,
1287
+ 144,
1288
+ 42,
1289
+ 129,
1290
+ 145,
1291
+ 43,
1292
+ 129,
1293
+ 147,
1294
+ 43,
1295
+ 128,
1296
+ 148,
1297
+ 44,
1298
+ 128,
1299
+ 150,
1300
+ 44,
1301
+ 128,
1302
+ 152,
1303
+ 45,
1304
+ 128,
1305
+ 153,
1306
+ 45,
1307
+ 128,
1308
+ 155,
1309
+ 46,
1310
+ 127,
1311
+ 156,
1312
+ 46,
1313
+ 127,
1314
+ 158,
1315
+ 47,
1316
+ 127,
1317
+ 160,
1318
+ 47,
1319
+ 127,
1320
+ 161,
1321
+ 48,
1322
+ 126,
1323
+ 163,
1324
+ 48,
1325
+ 126,
1326
+ 165,
1327
+ 49,
1328
+ 126,
1329
+ 166,
1330
+ 49,
1331
+ 125,
1332
+ 168,
1333
+ 50,
1334
+ 125,
1335
+ 170,
1336
+ 51,
1337
+ 125,
1338
+ 171,
1339
+ 51,
1340
+ 124,
1341
+ 173,
1342
+ 52,
1343
+ 124,
1344
+ 174,
1345
+ 52,
1346
+ 123,
1347
+ 176,
1348
+ 53,
1349
+ 123,
1350
+ 178,
1351
+ 53,
1352
+ 123,
1353
+ 179,
1354
+ 54,
1355
+ 122,
1356
+ 181,
1357
+ 54,
1358
+ 122,
1359
+ 183,
1360
+ 55,
1361
+ 121,
1362
+ 184,
1363
+ 55,
1364
+ 121,
1365
+ 186,
1366
+ 56,
1367
+ 120,
1368
+ 188,
1369
+ 57,
1370
+ 120,
1371
+ 189,
1372
+ 57,
1373
+ 119,
1374
+ 191,
1375
+ 58,
1376
+ 119,
1377
+ 192,
1378
+ 58,
1379
+ 118,
1380
+ 194,
1381
+ 59,
1382
+ 117,
1383
+ 196,
1384
+ 60,
1385
+ 117,
1386
+ 197,
1387
+ 60,
1388
+ 116,
1389
+ 199,
1390
+ 61,
1391
+ 115,
1392
+ 200,
1393
+ 62,
1394
+ 115,
1395
+ 202,
1396
+ 62,
1397
+ 114,
1398
+ 204,
1399
+ 63,
1400
+ 113,
1401
+ 205,
1402
+ 64,
1403
+ 113,
1404
+ 207,
1405
+ 64,
1406
+ 112,
1407
+ 208,
1408
+ 65,
1409
+ 111,
1410
+ 210,
1411
+ 66,
1412
+ 111,
1413
+ 211,
1414
+ 67,
1415
+ 110,
1416
+ 213,
1417
+ 68,
1418
+ 109,
1419
+ 214,
1420
+ 69,
1421
+ 108,
1422
+ 216,
1423
+ 69,
1424
+ 108,
1425
+ 217,
1426
+ 70,
1427
+ 107,
1428
+ 219,
1429
+ 71,
1430
+ 106,
1431
+ 220,
1432
+ 72,
1433
+ 105,
1434
+ 222,
1435
+ 73,
1436
+ 104,
1437
+ 223,
1438
+ 74,
1439
+ 104,
1440
+ 224,
1441
+ 76,
1442
+ 103,
1443
+ 226,
1444
+ 77,
1445
+ 102,
1446
+ 227,
1447
+ 78,
1448
+ 101,
1449
+ 228,
1450
+ 79,
1451
+ 100,
1452
+ 229,
1453
+ 80,
1454
+ 100,
1455
+ 231,
1456
+ 82,
1457
+ 99,
1458
+ 232,
1459
+ 83,
1460
+ 98,
1461
+ 233,
1462
+ 84,
1463
+ 98,
1464
+ 234,
1465
+ 86,
1466
+ 97,
1467
+ 235,
1468
+ 87,
1469
+ 96,
1470
+ 236,
1471
+ 88,
1472
+ 96,
1473
+ 237,
1474
+ 90,
1475
+ 95,
1476
+ 238,
1477
+ 91,
1478
+ 94,
1479
+ 239,
1480
+ 93,
1481
+ 94,
1482
+ 240,
1483
+ 95,
1484
+ 94,
1485
+ 241,
1486
+ 96,
1487
+ 93,
1488
+ 242,
1489
+ 98,
1490
+ 93,
1491
+ 242,
1492
+ 100,
1493
+ 92,
1494
+ 243,
1495
+ 101,
1496
+ 92,
1497
+ 244,
1498
+ 103,
1499
+ 92,
1500
+ 244,
1501
+ 105,
1502
+ 92,
1503
+ 245,
1504
+ 107,
1505
+ 92,
1506
+ 246,
1507
+ 108,
1508
+ 92,
1509
+ 246,
1510
+ 110,
1511
+ 92,
1512
+ 247,
1513
+ 112,
1514
+ 92,
1515
+ 247,
1516
+ 114,
1517
+ 92,
1518
+ 248,
1519
+ 116,
1520
+ 92,
1521
+ 248,
1522
+ 118,
1523
+ 92,
1524
+ 249,
1525
+ 120,
1526
+ 93,
1527
+ 249,
1528
+ 121,
1529
+ 93,
1530
+ 249,
1531
+ 123,
1532
+ 93,
1533
+ 250,
1534
+ 125,
1535
+ 94,
1536
+ 250,
1537
+ 127,
1538
+ 94,
1539
+ 250,
1540
+ 129,
1541
+ 95,
1542
+ 251,
1543
+ 131,
1544
+ 95,
1545
+ 251,
1546
+ 133,
1547
+ 96,
1548
+ 251,
1549
+ 135,
1550
+ 97,
1551
+ 252,
1552
+ 137,
1553
+ 97,
1554
+ 252,
1555
+ 138,
1556
+ 98,
1557
+ 252,
1558
+ 140,
1559
+ 99,
1560
+ 252,
1561
+ 142,
1562
+ 100,
1563
+ 252,
1564
+ 144,
1565
+ 101,
1566
+ 253,
1567
+ 146,
1568
+ 102,
1569
+ 253,
1570
+ 148,
1571
+ 103,
1572
+ 253,
1573
+ 150,
1574
+ 104,
1575
+ 253,
1576
+ 152,
1577
+ 105,
1578
+ 253,
1579
+ 154,
1580
+ 106,
1581
+ 253,
1582
+ 155,
1583
+ 107,
1584
+ 254,
1585
+ 157,
1586
+ 108,
1587
+ 254,
1588
+ 159,
1589
+ 109,
1590
+ 254,
1591
+ 161,
1592
+ 110,
1593
+ 254,
1594
+ 163,
1595
+ 111,
1596
+ 254,
1597
+ 165,
1598
+ 113,
1599
+ 254,
1600
+ 167,
1601
+ 114,
1602
+ 254,
1603
+ 169,
1604
+ 115,
1605
+ 254,
1606
+ 170,
1607
+ 116,
1608
+ 254,
1609
+ 172,
1610
+ 118,
1611
+ 254,
1612
+ 174,
1613
+ 119,
1614
+ 254,
1615
+ 176,
1616
+ 120,
1617
+ 254,
1618
+ 178,
1619
+ 122,
1620
+ 254,
1621
+ 180,
1622
+ 123,
1623
+ 254,
1624
+ 182,
1625
+ 124,
1626
+ 254,
1627
+ 183,
1628
+ 126,
1629
+ 254,
1630
+ 185,
1631
+ 127,
1632
+ 254,
1633
+ 187,
1634
+ 129,
1635
+ 254,
1636
+ 189,
1637
+ 130,
1638
+ 254,
1639
+ 191,
1640
+ 132,
1641
+ 254,
1642
+ 193,
1643
+ 133,
1644
+ 254,
1645
+ 194,
1646
+ 135,
1647
+ 254,
1648
+ 196,
1649
+ 136,
1650
+ 254,
1651
+ 198,
1652
+ 138,
1653
+ 254,
1654
+ 200,
1655
+ 140,
1656
+ 254,
1657
+ 202,
1658
+ 141,
1659
+ 254,
1660
+ 204,
1661
+ 143,
1662
+ 254,
1663
+ 205,
1664
+ 144,
1665
+ 254,
1666
+ 207,
1667
+ 146,
1668
+ 254,
1669
+ 209,
1670
+ 148,
1671
+ 254,
1672
+ 211,
1673
+ 149,
1674
+ 254,
1675
+ 213,
1676
+ 151,
1677
+ 254,
1678
+ 215,
1679
+ 153,
1680
+ 254,
1681
+ 216,
1682
+ 154,
1683
+ 253,
1684
+ 218,
1685
+ 156,
1686
+ 253,
1687
+ 220,
1688
+ 158,
1689
+ 253,
1690
+ 222,
1691
+ 160,
1692
+ 253,
1693
+ 224,
1694
+ 161,
1695
+ 253,
1696
+ 226,
1697
+ 163,
1698
+ 253,
1699
+ 227,
1700
+ 165,
1701
+ 253,
1702
+ 229,
1703
+ 167,
1704
+ 253,
1705
+ 231,
1706
+ 169,
1707
+ 253,
1708
+ 233,
1709
+ 170,
1710
+ 253,
1711
+ 235,
1712
+ 172,
1713
+ 252,
1714
+ 236,
1715
+ 174,
1716
+ 252,
1717
+ 238,
1718
+ 176,
1719
+ 252,
1720
+ 240,
1721
+ 178,
1722
+ 252,
1723
+ 242,
1724
+ 180,
1725
+ 252,
1726
+ 244,
1727
+ 182,
1728
+ 252,
1729
+ 246,
1730
+ 184,
1731
+ 252,
1732
+ 247,
1733
+ 185,
1734
+ 252,
1735
+ 249,
1736
+ 187,
1737
+ 252,
1738
+ 251,
1739
+ 189,
1740
+ 252,
1741
+ 253,
1742
+ 191
1743
+ ]);
1744
+ var INFERNO_LUT = new Uint8Array([
1745
+ 0,
1746
+ 0,
1747
+ 4,
1748
+ 1,
1749
+ 0,
1750
+ 5,
1751
+ 1,
1752
+ 1,
1753
+ 6,
1754
+ 1,
1755
+ 1,
1756
+ 8,
1757
+ 2,
1758
+ 1,
1759
+ 10,
1760
+ 2,
1761
+ 2,
1762
+ 12,
1763
+ 2,
1764
+ 2,
1765
+ 14,
1766
+ 3,
1767
+ 2,
1768
+ 16,
1769
+ 4,
1770
+ 3,
1771
+ 18,
1772
+ 4,
1773
+ 3,
1774
+ 20,
1775
+ 5,
1776
+ 4,
1777
+ 23,
1778
+ 6,
1779
+ 4,
1780
+ 25,
1781
+ 7,
1782
+ 5,
1783
+ 27,
1784
+ 8,
1785
+ 5,
1786
+ 29,
1787
+ 9,
1788
+ 6,
1789
+ 31,
1790
+ 10,
1791
+ 7,
1792
+ 34,
1793
+ 11,
1794
+ 7,
1795
+ 36,
1796
+ 12,
1797
+ 8,
1798
+ 38,
1799
+ 13,
1800
+ 8,
1801
+ 41,
1802
+ 14,
1803
+ 9,
1804
+ 43,
1805
+ 16,
1806
+ 9,
1807
+ 45,
1808
+ 17,
1809
+ 10,
1810
+ 48,
1811
+ 18,
1812
+ 10,
1813
+ 50,
1814
+ 20,
1815
+ 11,
1816
+ 52,
1817
+ 21,
1818
+ 11,
1819
+ 55,
1820
+ 22,
1821
+ 11,
1822
+ 57,
1823
+ 24,
1824
+ 12,
1825
+ 60,
1826
+ 25,
1827
+ 12,
1828
+ 62,
1829
+ 27,
1830
+ 12,
1831
+ 65,
1832
+ 28,
1833
+ 12,
1834
+ 67,
1835
+ 30,
1836
+ 12,
1837
+ 69,
1838
+ 31,
1839
+ 12,
1840
+ 72,
1841
+ 33,
1842
+ 12,
1843
+ 74,
1844
+ 35,
1845
+ 12,
1846
+ 76,
1847
+ 36,
1848
+ 12,
1849
+ 79,
1850
+ 38,
1851
+ 12,
1852
+ 81,
1853
+ 40,
1854
+ 11,
1855
+ 83,
1856
+ 41,
1857
+ 11,
1858
+ 85,
1859
+ 43,
1860
+ 11,
1861
+ 87,
1862
+ 45,
1863
+ 11,
1864
+ 89,
1865
+ 47,
1866
+ 10,
1867
+ 91,
1868
+ 49,
1869
+ 10,
1870
+ 92,
1871
+ 50,
1872
+ 10,
1873
+ 94,
1874
+ 52,
1875
+ 10,
1876
+ 95,
1877
+ 54,
1878
+ 9,
1879
+ 97,
1880
+ 56,
1881
+ 9,
1882
+ 98,
1883
+ 57,
1884
+ 9,
1885
+ 99,
1886
+ 59,
1887
+ 9,
1888
+ 100,
1889
+ 61,
1890
+ 9,
1891
+ 101,
1892
+ 62,
1893
+ 9,
1894
+ 102,
1895
+ 64,
1896
+ 10,
1897
+ 103,
1898
+ 66,
1899
+ 10,
1900
+ 104,
1901
+ 68,
1902
+ 10,
1903
+ 104,
1904
+ 69,
1905
+ 10,
1906
+ 105,
1907
+ 71,
1908
+ 11,
1909
+ 106,
1910
+ 73,
1911
+ 11,
1912
+ 106,
1913
+ 74,
1914
+ 12,
1915
+ 107,
1916
+ 76,
1917
+ 12,
1918
+ 107,
1919
+ 77,
1920
+ 13,
1921
+ 108,
1922
+ 79,
1923
+ 13,
1924
+ 108,
1925
+ 81,
1926
+ 14,
1927
+ 108,
1928
+ 82,
1929
+ 14,
1930
+ 109,
1931
+ 84,
1932
+ 15,
1933
+ 109,
1934
+ 85,
1935
+ 15,
1936
+ 109,
1937
+ 87,
1938
+ 16,
1939
+ 110,
1940
+ 89,
1941
+ 16,
1942
+ 110,
1943
+ 90,
1944
+ 17,
1945
+ 110,
1946
+ 92,
1947
+ 18,
1948
+ 110,
1949
+ 93,
1950
+ 18,
1951
+ 110,
1952
+ 95,
1953
+ 19,
1954
+ 110,
1955
+ 97,
1956
+ 19,
1957
+ 110,
1958
+ 98,
1959
+ 20,
1960
+ 110,
1961
+ 100,
1962
+ 21,
1963
+ 110,
1964
+ 101,
1965
+ 21,
1966
+ 110,
1967
+ 103,
1968
+ 22,
1969
+ 110,
1970
+ 105,
1971
+ 22,
1972
+ 110,
1973
+ 106,
1974
+ 23,
1975
+ 110,
1976
+ 108,
1977
+ 24,
1978
+ 110,
1979
+ 109,
1980
+ 24,
1981
+ 110,
1982
+ 111,
1983
+ 25,
1984
+ 110,
1985
+ 113,
1986
+ 25,
1987
+ 110,
1988
+ 114,
1989
+ 26,
1990
+ 110,
1991
+ 116,
1992
+ 26,
1993
+ 110,
1994
+ 117,
1995
+ 27,
1996
+ 110,
1997
+ 119,
1998
+ 28,
1999
+ 109,
2000
+ 120,
2001
+ 28,
2002
+ 109,
2003
+ 122,
2004
+ 29,
2005
+ 109,
2006
+ 124,
2007
+ 29,
2008
+ 109,
2009
+ 125,
2010
+ 30,
2011
+ 109,
2012
+ 127,
2013
+ 30,
2014
+ 108,
2015
+ 128,
2016
+ 31,
2017
+ 108,
2018
+ 130,
2019
+ 32,
2020
+ 108,
2021
+ 132,
2022
+ 32,
2023
+ 107,
2024
+ 133,
2025
+ 33,
2026
+ 107,
2027
+ 135,
2028
+ 33,
2029
+ 107,
2030
+ 136,
2031
+ 34,
2032
+ 106,
2033
+ 138,
2034
+ 34,
2035
+ 106,
2036
+ 140,
2037
+ 35,
2038
+ 105,
2039
+ 141,
2040
+ 35,
2041
+ 105,
2042
+ 143,
2043
+ 36,
2044
+ 105,
2045
+ 144,
2046
+ 37,
2047
+ 104,
2048
+ 146,
2049
+ 37,
2050
+ 104,
2051
+ 147,
2052
+ 38,
2053
+ 103,
2054
+ 149,
2055
+ 38,
2056
+ 103,
2057
+ 151,
2058
+ 39,
2059
+ 102,
2060
+ 152,
2061
+ 39,
2062
+ 102,
2063
+ 154,
2064
+ 40,
2065
+ 101,
2066
+ 155,
2067
+ 41,
2068
+ 100,
2069
+ 157,
2070
+ 41,
2071
+ 100,
2072
+ 159,
2073
+ 42,
2074
+ 99,
2075
+ 160,
2076
+ 42,
2077
+ 99,
2078
+ 162,
2079
+ 43,
2080
+ 98,
2081
+ 163,
2082
+ 44,
2083
+ 97,
2084
+ 165,
2085
+ 44,
2086
+ 96,
2087
+ 166,
2088
+ 45,
2089
+ 96,
2090
+ 168,
2091
+ 46,
2092
+ 95,
2093
+ 169,
2094
+ 46,
2095
+ 94,
2096
+ 171,
2097
+ 47,
2098
+ 94,
2099
+ 173,
2100
+ 48,
2101
+ 93,
2102
+ 174,
2103
+ 48,
2104
+ 92,
2105
+ 176,
2106
+ 49,
2107
+ 91,
2108
+ 177,
2109
+ 50,
2110
+ 90,
2111
+ 179,
2112
+ 50,
2113
+ 90,
2114
+ 180,
2115
+ 51,
2116
+ 89,
2117
+ 182,
2118
+ 52,
2119
+ 88,
2120
+ 183,
2121
+ 53,
2122
+ 87,
2123
+ 185,
2124
+ 53,
2125
+ 86,
2126
+ 186,
2127
+ 54,
2128
+ 85,
2129
+ 188,
2130
+ 55,
2131
+ 84,
2132
+ 189,
2133
+ 56,
2134
+ 83,
2135
+ 191,
2136
+ 57,
2137
+ 82,
2138
+ 192,
2139
+ 58,
2140
+ 81,
2141
+ 193,
2142
+ 58,
2143
+ 80,
2144
+ 195,
2145
+ 59,
2146
+ 79,
2147
+ 196,
2148
+ 60,
2149
+ 78,
2150
+ 198,
2151
+ 61,
2152
+ 77,
2153
+ 199,
2154
+ 62,
2155
+ 76,
2156
+ 200,
2157
+ 63,
2158
+ 75,
2159
+ 202,
2160
+ 64,
2161
+ 74,
2162
+ 203,
2163
+ 65,
2164
+ 73,
2165
+ 204,
2166
+ 66,
2167
+ 72,
2168
+ 206,
2169
+ 67,
2170
+ 71,
2171
+ 207,
2172
+ 68,
2173
+ 70,
2174
+ 208,
2175
+ 69,
2176
+ 69,
2177
+ 210,
2178
+ 70,
2179
+ 68,
2180
+ 211,
2181
+ 71,
2182
+ 67,
2183
+ 212,
2184
+ 72,
2185
+ 66,
2186
+ 213,
2187
+ 74,
2188
+ 65,
2189
+ 215,
2190
+ 75,
2191
+ 63,
2192
+ 216,
2193
+ 76,
2194
+ 62,
2195
+ 217,
2196
+ 77,
2197
+ 61,
2198
+ 218,
2199
+ 78,
2200
+ 60,
2201
+ 219,
2202
+ 80,
2203
+ 59,
2204
+ 221,
2205
+ 81,
2206
+ 58,
2207
+ 222,
2208
+ 82,
2209
+ 56,
2210
+ 223,
2211
+ 83,
2212
+ 55,
2213
+ 224,
2214
+ 85,
2215
+ 54,
2216
+ 225,
2217
+ 86,
2218
+ 53,
2219
+ 226,
2220
+ 87,
2221
+ 52,
2222
+ 227,
2223
+ 89,
2224
+ 51,
2225
+ 228,
2226
+ 90,
2227
+ 49,
2228
+ 229,
2229
+ 92,
2230
+ 48,
2231
+ 230,
2232
+ 93,
2233
+ 47,
2234
+ 231,
2235
+ 94,
2236
+ 46,
2237
+ 232,
2238
+ 96,
2239
+ 45,
2240
+ 233,
2241
+ 97,
2242
+ 43,
2243
+ 234,
2244
+ 99,
2245
+ 42,
2246
+ 235,
2247
+ 100,
2248
+ 41,
2249
+ 235,
2250
+ 102,
2251
+ 40,
2252
+ 236,
2253
+ 103,
2254
+ 38,
2255
+ 237,
2256
+ 105,
2257
+ 37,
2258
+ 238,
2259
+ 106,
2260
+ 36,
2261
+ 239,
2262
+ 108,
2263
+ 35,
2264
+ 239,
2265
+ 110,
2266
+ 33,
2267
+ 240,
2268
+ 111,
2269
+ 32,
2270
+ 241,
2271
+ 113,
2272
+ 31,
2273
+ 241,
2274
+ 115,
2275
+ 29,
2276
+ 242,
2277
+ 116,
2278
+ 28,
2279
+ 243,
2280
+ 118,
2281
+ 27,
2282
+ 243,
2283
+ 120,
2284
+ 25,
2285
+ 244,
2286
+ 121,
2287
+ 24,
2288
+ 245,
2289
+ 123,
2290
+ 23,
2291
+ 245,
2292
+ 125,
2293
+ 21,
2294
+ 246,
2295
+ 126,
2296
+ 20,
2297
+ 246,
2298
+ 128,
2299
+ 19,
2300
+ 247,
2301
+ 130,
2302
+ 18,
2303
+ 247,
2304
+ 132,
2305
+ 16,
2306
+ 248,
2307
+ 133,
2308
+ 15,
2309
+ 248,
2310
+ 135,
2311
+ 14,
2312
+ 248,
2313
+ 137,
2314
+ 12,
2315
+ 249,
2316
+ 139,
2317
+ 11,
2318
+ 249,
2319
+ 140,
2320
+ 10,
2321
+ 249,
2322
+ 142,
2323
+ 9,
2324
+ 250,
2325
+ 144,
2326
+ 8,
2327
+ 250,
2328
+ 146,
2329
+ 7,
2330
+ 250,
2331
+ 148,
2332
+ 7,
2333
+ 251,
2334
+ 150,
2335
+ 6,
2336
+ 251,
2337
+ 151,
2338
+ 6,
2339
+ 251,
2340
+ 153,
2341
+ 6,
2342
+ 251,
2343
+ 155,
2344
+ 6,
2345
+ 251,
2346
+ 157,
2347
+ 7,
2348
+ 252,
2349
+ 159,
2350
+ 7,
2351
+ 252,
2352
+ 161,
2353
+ 8,
2354
+ 252,
2355
+ 163,
2356
+ 9,
2357
+ 252,
2358
+ 165,
2359
+ 10,
2360
+ 252,
2361
+ 166,
2362
+ 12,
2363
+ 252,
2364
+ 168,
2365
+ 13,
2366
+ 252,
2367
+ 170,
2368
+ 15,
2369
+ 252,
2370
+ 172,
2371
+ 17,
2372
+ 252,
2373
+ 174,
2374
+ 18,
2375
+ 252,
2376
+ 176,
2377
+ 20,
2378
+ 252,
2379
+ 178,
2380
+ 22,
2381
+ 252,
2382
+ 180,
2383
+ 24,
2384
+ 251,
2385
+ 182,
2386
+ 26,
2387
+ 251,
2388
+ 184,
2389
+ 29,
2390
+ 251,
2391
+ 186,
2392
+ 31,
2393
+ 251,
2394
+ 188,
2395
+ 33,
2396
+ 251,
2397
+ 190,
2398
+ 35,
2399
+ 250,
2400
+ 192,
2401
+ 38,
2402
+ 250,
2403
+ 194,
2404
+ 40,
2405
+ 250,
2406
+ 196,
2407
+ 42,
2408
+ 250,
2409
+ 198,
2410
+ 45,
2411
+ 249,
2412
+ 199,
2413
+ 47,
2414
+ 249,
2415
+ 201,
2416
+ 50,
2417
+ 249,
2418
+ 203,
2419
+ 53,
2420
+ 248,
2421
+ 205,
2422
+ 55,
2423
+ 248,
2424
+ 207,
2425
+ 58,
2426
+ 247,
2427
+ 209,
2428
+ 61,
2429
+ 247,
2430
+ 211,
2431
+ 64,
2432
+ 246,
2433
+ 213,
2434
+ 67,
2435
+ 246,
2436
+ 215,
2437
+ 70,
2438
+ 245,
2439
+ 217,
2440
+ 73,
2441
+ 245,
2442
+ 219,
2443
+ 76,
2444
+ 244,
2445
+ 221,
2446
+ 79,
2447
+ 244,
2448
+ 223,
2449
+ 83,
2450
+ 244,
2451
+ 225,
2452
+ 86,
2453
+ 243,
2454
+ 227,
2455
+ 90,
2456
+ 243,
2457
+ 229,
2458
+ 93,
2459
+ 242,
2460
+ 230,
2461
+ 97,
2462
+ 242,
2463
+ 232,
2464
+ 101,
2465
+ 242,
2466
+ 234,
2467
+ 105,
2468
+ 241,
2469
+ 236,
2470
+ 109,
2471
+ 241,
2472
+ 237,
2473
+ 113,
2474
+ 241,
2475
+ 239,
2476
+ 117,
2477
+ 241,
2478
+ 241,
2479
+ 121,
2480
+ 242,
2481
+ 242,
2482
+ 125,
2483
+ 242,
2484
+ 244,
2485
+ 130,
2486
+ 243,
2487
+ 245,
2488
+ 134,
2489
+ 243,
2490
+ 246,
2491
+ 138,
2492
+ 244,
2493
+ 248,
2494
+ 142,
2495
+ 245,
2496
+ 249,
2497
+ 146,
2498
+ 246,
2499
+ 250,
2500
+ 150,
2501
+ 248,
2502
+ 251,
2503
+ 154,
2504
+ 249,
2505
+ 252,
2506
+ 157,
2507
+ 250,
2508
+ 253,
2509
+ 161,
2510
+ 252,
2511
+ 255,
2512
+ 164
2513
+ ]);
2514
+ var ROSEUS_LUT = new Uint8Array([
2515
+ 1,
2516
+ 1,
2517
+ 1,
2518
+ 1,
2519
+ 2,
2520
+ 2,
2521
+ 2,
2522
+ 2,
2523
+ 2,
2524
+ 2,
2525
+ 3,
2526
+ 3,
2527
+ 2,
2528
+ 3,
2529
+ 4,
2530
+ 2,
2531
+ 4,
2532
+ 5,
2533
+ 2,
2534
+ 5,
2535
+ 6,
2536
+ 3,
2537
+ 6,
2538
+ 7,
2539
+ 3,
2540
+ 7,
2541
+ 8,
2542
+ 3,
2543
+ 8,
2544
+ 10,
2545
+ 3,
2546
+ 9,
2547
+ 12,
2548
+ 3,
2549
+ 10,
2550
+ 14,
2551
+ 3,
2552
+ 12,
2553
+ 16,
2554
+ 3,
2555
+ 13,
2556
+ 17,
2557
+ 3,
2558
+ 14,
2559
+ 19,
2560
+ 2,
2561
+ 15,
2562
+ 21,
2563
+ 2,
2564
+ 16,
2565
+ 23,
2566
+ 2,
2567
+ 17,
2568
+ 25,
2569
+ 2,
2570
+ 18,
2571
+ 27,
2572
+ 2,
2573
+ 19,
2574
+ 30,
2575
+ 1,
2576
+ 20,
2577
+ 32,
2578
+ 1,
2579
+ 21,
2580
+ 34,
2581
+ 1,
2582
+ 22,
2583
+ 36,
2584
+ 1,
2585
+ 23,
2586
+ 38,
2587
+ 1,
2588
+ 24,
2589
+ 40,
2590
+ 0,
2591
+ 25,
2592
+ 43,
2593
+ 0,
2594
+ 26,
2595
+ 45,
2596
+ 0,
2597
+ 27,
2598
+ 47,
2599
+ 0,
2600
+ 27,
2601
+ 50,
2602
+ 0,
2603
+ 28,
2604
+ 52,
2605
+ 0,
2606
+ 29,
2607
+ 54,
2608
+ 0,
2609
+ 30,
2610
+ 57,
2611
+ 0,
2612
+ 30,
2613
+ 59,
2614
+ 1,
2615
+ 31,
2616
+ 62,
2617
+ 1,
2618
+ 32,
2619
+ 64,
2620
+ 1,
2621
+ 32,
2622
+ 67,
2623
+ 2,
2624
+ 33,
2625
+ 69,
2626
+ 3,
2627
+ 33,
2628
+ 72,
2629
+ 4,
2630
+ 34,
2631
+ 74,
2632
+ 5,
2633
+ 35,
2634
+ 77,
2635
+ 6,
2636
+ 35,
2637
+ 79,
2638
+ 8,
2639
+ 35,
2640
+ 82,
2641
+ 9,
2642
+ 36,
2643
+ 84,
2644
+ 11,
2645
+ 36,
2646
+ 86,
2647
+ 13,
2648
+ 37,
2649
+ 89,
2650
+ 15,
2651
+ 37,
2652
+ 91,
2653
+ 17,
2654
+ 37,
2655
+ 94,
2656
+ 19,
2657
+ 37,
2658
+ 96,
2659
+ 21,
2660
+ 38,
2661
+ 99,
2662
+ 23,
2663
+ 38,
2664
+ 101,
2665
+ 25,
2666
+ 38,
2667
+ 104,
2668
+ 27,
2669
+ 38,
2670
+ 106,
2671
+ 29,
2672
+ 38,
2673
+ 108,
2674
+ 31,
2675
+ 38,
2676
+ 111,
2677
+ 33,
2678
+ 38,
2679
+ 113,
2680
+ 35,
2681
+ 38,
2682
+ 115,
2683
+ 38,
2684
+ 38,
2685
+ 118,
2686
+ 40,
2687
+ 38,
2688
+ 120,
2689
+ 42,
2690
+ 38,
2691
+ 122,
2692
+ 44,
2693
+ 38,
2694
+ 124,
2695
+ 46,
2696
+ 38,
2697
+ 126,
2698
+ 49,
2699
+ 38,
2700
+ 128,
2701
+ 51,
2702
+ 38,
2703
+ 130,
2704
+ 53,
2705
+ 37,
2706
+ 132,
2707
+ 55,
2708
+ 37,
2709
+ 134,
2710
+ 58,
2711
+ 37,
2712
+ 136,
2713
+ 60,
2714
+ 36,
2715
+ 138,
2716
+ 62,
2717
+ 36,
2718
+ 139,
2719
+ 65,
2720
+ 36,
2721
+ 141,
2722
+ 67,
2723
+ 35,
2724
+ 143,
2725
+ 69,
2726
+ 35,
2727
+ 144,
2728
+ 72,
2729
+ 35,
2730
+ 146,
2731
+ 74,
2732
+ 34,
2733
+ 147,
2734
+ 76,
2735
+ 34,
2736
+ 149,
2737
+ 79,
2738
+ 33,
2739
+ 150,
2740
+ 81,
2741
+ 33,
2742
+ 151,
2743
+ 84,
2744
+ 32,
2745
+ 152,
2746
+ 86,
2747
+ 32,
2748
+ 153,
2749
+ 88,
2750
+ 31,
2751
+ 154,
2752
+ 91,
2753
+ 31,
2754
+ 155,
2755
+ 93,
2756
+ 30,
2757
+ 156,
2758
+ 95,
2759
+ 29,
2760
+ 157,
2761
+ 98,
2762
+ 29,
2763
+ 158,
2764
+ 100,
2765
+ 28,
2766
+ 159,
2767
+ 103,
2768
+ 28,
2769
+ 159,
2770
+ 105,
2771
+ 27,
2772
+ 160,
2773
+ 107,
2774
+ 27,
2775
+ 160,
2776
+ 110,
2777
+ 26,
2778
+ 161,
2779
+ 112,
2780
+ 26,
2781
+ 161,
2782
+ 114,
2783
+ 25,
2784
+ 161,
2785
+ 117,
2786
+ 25,
2787
+ 162,
2788
+ 119,
2789
+ 24,
2790
+ 162,
2791
+ 121,
2792
+ 24,
2793
+ 162,
2794
+ 124,
2795
+ 23,
2796
+ 162,
2797
+ 126,
2798
+ 23,
2799
+ 162,
2800
+ 128,
2801
+ 23,
2802
+ 162,
2803
+ 131,
2804
+ 22,
2805
+ 161,
2806
+ 133,
2807
+ 22,
2808
+ 161,
2809
+ 135,
2810
+ 22,
2811
+ 161,
2812
+ 137,
2813
+ 22,
2814
+ 161,
2815
+ 140,
2816
+ 22,
2817
+ 160,
2818
+ 142,
2819
+ 22,
2820
+ 160,
2821
+ 144,
2822
+ 22,
2823
+ 159,
2824
+ 146,
2825
+ 22,
2826
+ 159,
2827
+ 148,
2828
+ 22,
2829
+ 158,
2830
+ 150,
2831
+ 22,
2832
+ 157,
2833
+ 153,
2834
+ 22,
2835
+ 157,
2836
+ 155,
2837
+ 23,
2838
+ 156,
2839
+ 157,
2840
+ 23,
2841
+ 155,
2842
+ 159,
2843
+ 23,
2844
+ 154,
2845
+ 161,
2846
+ 24,
2847
+ 153,
2848
+ 163,
2849
+ 24,
2850
+ 152,
2851
+ 165,
2852
+ 25,
2853
+ 151,
2854
+ 167,
2855
+ 26,
2856
+ 150,
2857
+ 169,
2858
+ 26,
2859
+ 149,
2860
+ 171,
2861
+ 27,
2862
+ 148,
2863
+ 173,
2864
+ 28,
2865
+ 147,
2866
+ 175,
2867
+ 29,
2868
+ 146,
2869
+ 177,
2870
+ 29,
2871
+ 145,
2872
+ 179,
2873
+ 30,
2874
+ 144,
2875
+ 181,
2876
+ 31,
2877
+ 142,
2878
+ 183,
2879
+ 32,
2880
+ 141,
2881
+ 184,
2882
+ 33,
2883
+ 140,
2884
+ 186,
2885
+ 34,
2886
+ 139,
2887
+ 188,
2888
+ 35,
2889
+ 137,
2890
+ 190,
2891
+ 36,
2892
+ 136,
2893
+ 192,
2894
+ 37,
2895
+ 135,
2896
+ 193,
2897
+ 39,
2898
+ 133,
2899
+ 195,
2900
+ 40,
2901
+ 132,
2902
+ 197,
2903
+ 41,
2904
+ 130,
2905
+ 198,
2906
+ 42,
2907
+ 129,
2908
+ 200,
2909
+ 43,
2910
+ 128,
2911
+ 202,
2912
+ 45,
2913
+ 126,
2914
+ 203,
2915
+ 46,
2916
+ 125,
2917
+ 205,
2918
+ 47,
2919
+ 123,
2920
+ 206,
2921
+ 48,
2922
+ 122,
2923
+ 208,
2924
+ 50,
2925
+ 120,
2926
+ 209,
2927
+ 51,
2928
+ 119,
2929
+ 211,
2930
+ 52,
2931
+ 117,
2932
+ 212,
2933
+ 54,
2934
+ 116,
2935
+ 214,
2936
+ 55,
2937
+ 114,
2938
+ 215,
2939
+ 57,
2940
+ 113,
2941
+ 217,
2942
+ 58,
2943
+ 111,
2944
+ 218,
2945
+ 60,
2946
+ 110,
2947
+ 219,
2948
+ 61,
2949
+ 109,
2950
+ 221,
2951
+ 63,
2952
+ 107,
2953
+ 222,
2954
+ 64,
2955
+ 106,
2956
+ 223,
2957
+ 66,
2958
+ 104,
2959
+ 225,
2960
+ 67,
2961
+ 103,
2962
+ 226,
2963
+ 69,
2964
+ 101,
2965
+ 227,
2966
+ 70,
2967
+ 100,
2968
+ 228,
2969
+ 72,
2970
+ 99,
2971
+ 229,
2972
+ 73,
2973
+ 97,
2974
+ 230,
2975
+ 75,
2976
+ 96,
2977
+ 231,
2978
+ 77,
2979
+ 94,
2980
+ 233,
2981
+ 78,
2982
+ 93,
2983
+ 234,
2984
+ 80,
2985
+ 92,
2986
+ 235,
2987
+ 82,
2988
+ 91,
2989
+ 236,
2990
+ 83,
2991
+ 89,
2992
+ 237,
2993
+ 85,
2994
+ 88,
2995
+ 237,
2996
+ 87,
2997
+ 87,
2998
+ 238,
2999
+ 89,
3000
+ 86,
3001
+ 239,
3002
+ 90,
3003
+ 84,
3004
+ 240,
3005
+ 92,
3006
+ 83,
3007
+ 241,
3008
+ 94,
3009
+ 82,
3010
+ 242,
3011
+ 96,
3012
+ 81,
3013
+ 242,
3014
+ 97,
3015
+ 80,
3016
+ 243,
3017
+ 99,
3018
+ 79,
3019
+ 244,
3020
+ 101,
3021
+ 78,
3022
+ 245,
3023
+ 103,
3024
+ 77,
3025
+ 245,
3026
+ 105,
3027
+ 76,
3028
+ 246,
3029
+ 107,
3030
+ 75,
3031
+ 246,
3032
+ 108,
3033
+ 74,
3034
+ 247,
3035
+ 110,
3036
+ 74,
3037
+ 248,
3038
+ 112,
3039
+ 73,
3040
+ 248,
3041
+ 114,
3042
+ 72,
3043
+ 248,
3044
+ 116,
3045
+ 72,
3046
+ 249,
3047
+ 118,
3048
+ 71,
3049
+ 249,
3050
+ 120,
3051
+ 71,
3052
+ 250,
3053
+ 122,
3054
+ 70,
3055
+ 250,
3056
+ 124,
3057
+ 70,
3058
+ 250,
3059
+ 126,
3060
+ 70,
3061
+ 251,
3062
+ 128,
3063
+ 70,
3064
+ 251,
3065
+ 130,
3066
+ 69,
3067
+ 251,
3068
+ 132,
3069
+ 70,
3070
+ 251,
3071
+ 134,
3072
+ 70,
3073
+ 251,
3074
+ 136,
3075
+ 70,
3076
+ 252,
3077
+ 138,
3078
+ 70,
3079
+ 252,
3080
+ 140,
3081
+ 70,
3082
+ 252,
3083
+ 142,
3084
+ 71,
3085
+ 252,
3086
+ 144,
3087
+ 72,
3088
+ 252,
3089
+ 146,
3090
+ 72,
3091
+ 252,
3092
+ 148,
3093
+ 73,
3094
+ 252,
3095
+ 150,
3096
+ 74,
3097
+ 251,
3098
+ 152,
3099
+ 75,
3100
+ 251,
3101
+ 154,
3102
+ 76,
3103
+ 251,
3104
+ 156,
3105
+ 77,
3106
+ 251,
3107
+ 158,
3108
+ 78,
3109
+ 251,
3110
+ 160,
3111
+ 80,
3112
+ 251,
3113
+ 162,
3114
+ 81,
3115
+ 250,
3116
+ 164,
3117
+ 83,
3118
+ 250,
3119
+ 166,
3120
+ 85,
3121
+ 250,
3122
+ 168,
3123
+ 87,
3124
+ 249,
3125
+ 170,
3126
+ 88,
3127
+ 249,
3128
+ 172,
3129
+ 90,
3130
+ 248,
3131
+ 174,
3132
+ 93,
3133
+ 248,
3134
+ 176,
3135
+ 95,
3136
+ 248,
3137
+ 178,
3138
+ 97,
3139
+ 247,
3140
+ 180,
3141
+ 99,
3142
+ 247,
3143
+ 182,
3144
+ 102,
3145
+ 246,
3146
+ 184,
3147
+ 104,
3148
+ 246,
3149
+ 186,
3150
+ 107,
3151
+ 245,
3152
+ 188,
3153
+ 110,
3154
+ 244,
3155
+ 190,
3156
+ 112,
3157
+ 244,
3158
+ 192,
3159
+ 115,
3160
+ 243,
3161
+ 194,
3162
+ 118,
3163
+ 243,
3164
+ 195,
3165
+ 121,
3166
+ 242,
3167
+ 197,
3168
+ 124,
3169
+ 242,
3170
+ 199,
3171
+ 127,
3172
+ 241,
3173
+ 201,
3174
+ 131,
3175
+ 240,
3176
+ 203,
3177
+ 134,
3178
+ 240,
3179
+ 205,
3180
+ 137,
3181
+ 239,
3182
+ 207,
3183
+ 140,
3184
+ 239,
3185
+ 208,
3186
+ 144,
3187
+ 238,
3188
+ 210,
3189
+ 147,
3190
+ 238,
3191
+ 212,
3192
+ 151,
3193
+ 237,
3194
+ 213,
3195
+ 154,
3196
+ 237,
3197
+ 215,
3198
+ 158,
3199
+ 236,
3200
+ 217,
3201
+ 161,
3202
+ 236,
3203
+ 218,
3204
+ 165,
3205
+ 236,
3206
+ 220,
3207
+ 169,
3208
+ 236,
3209
+ 222,
3210
+ 172,
3211
+ 235,
3212
+ 223,
3213
+ 176,
3214
+ 235,
3215
+ 225,
3216
+ 180,
3217
+ 235,
3218
+ 226,
3219
+ 183,
3220
+ 235,
3221
+ 228,
3222
+ 187,
3223
+ 235,
3224
+ 229,
3225
+ 191,
3226
+ 235,
3227
+ 230,
3228
+ 194,
3229
+ 236,
3230
+ 232,
3231
+ 198,
3232
+ 236,
3233
+ 233,
3234
+ 201,
3235
+ 236,
3236
+ 234,
3237
+ 205,
3238
+ 237,
3239
+ 236,
3240
+ 208,
3241
+ 237,
3242
+ 237,
3243
+ 212,
3244
+ 238,
3245
+ 238,
3246
+ 215,
3247
+ 239,
3248
+ 239,
3249
+ 219,
3250
+ 240,
3251
+ 240,
3252
+ 222,
3253
+ 241,
3254
+ 242,
3255
+ 225,
3256
+ 242,
3257
+ 243,
3258
+ 228,
3259
+ 243,
3260
+ 244,
3261
+ 231,
3262
+ 244,
3263
+ 245,
3264
+ 234,
3265
+ 246,
3266
+ 246,
3267
+ 237,
3268
+ 247,
3269
+ 247,
3270
+ 240,
3271
+ 249,
3272
+ 248,
3273
+ 242,
3274
+ 251,
3275
+ 249,
3276
+ 245,
3277
+ 253,
3278
+ 250,
3279
+ 247,
3280
+ 254,
3281
+ 251,
3282
+ 249
3283
+ ]);
3284
+ var lutCache = /* @__PURE__ */ new Map();
3285
+ function grayscaleLUT() {
3286
+ let lut = lutCache.get("grayscale");
3287
+ if (!lut) {
3288
+ lut = new Uint8Array(256 * 3);
3289
+ for (let i = 0; i < 256; i++) {
3290
+ lut[i * 3] = lut[i * 3 + 1] = lut[i * 3 + 2] = i;
3291
+ }
3292
+ lutCache.set("grayscale", lut);
3293
+ }
3294
+ return lut;
3295
+ }
3296
+ function igrayLUT() {
3297
+ let lut = lutCache.get("igray");
3298
+ if (!lut) {
3299
+ lut = new Uint8Array(256 * 3);
3300
+ for (let i = 0; i < 256; i++) {
3301
+ const v = 255 - i;
3302
+ lut[i * 3] = lut[i * 3 + 1] = lut[i * 3 + 2] = v;
3303
+ }
3304
+ lutCache.set("igray", lut);
3305
+ }
3306
+ return lut;
3307
+ }
3308
+ function getColorMap(value) {
3309
+ if (Array.isArray(value)) {
3310
+ return interpolateLUT(value);
3311
+ }
3312
+ switch (value) {
3313
+ case "viridis":
3314
+ return VIRIDIS_LUT;
3315
+ case "magma":
3316
+ return MAGMA_LUT;
3317
+ case "inferno":
3318
+ return INFERNO_LUT;
3319
+ case "roseus":
3320
+ return ROSEUS_LUT;
3321
+ case "grayscale":
3322
+ return grayscaleLUT();
3323
+ case "igray":
3324
+ return igrayLUT();
3325
+ default:
3326
+ return VIRIDIS_LUT;
3327
+ }
3328
+ }
3329
+
3330
+ // src/computation/frequencyScales.ts
3331
+ function linearScale(f, minF, maxF) {
3332
+ if (maxF === minF) return 0;
3333
+ return (f - minF) / (maxF - minF);
3334
+ }
3335
+ function logarithmicScale(f, minF, maxF) {
3336
+ const logMin = Math.log2(Math.max(minF, 1));
3337
+ const logMax = Math.log2(maxF);
3338
+ if (logMax === logMin) return 0;
3339
+ return (Math.log2(Math.max(f, 1)) - logMin) / (logMax - logMin);
3340
+ }
3341
+ function hzToMel(f) {
3342
+ return 2595 * Math.log10(1 + f / 700);
3343
+ }
3344
+ function melScale(f, minF, maxF) {
3345
+ const melMin = hzToMel(minF);
3346
+ const melMax = hzToMel(maxF);
3347
+ if (melMax === melMin) return 0;
3348
+ return (hzToMel(f) - melMin) / (melMax - melMin);
3349
+ }
3350
+ function hzToBark(f) {
3351
+ return 13 * Math.atan(76e-5 * f) + 3.5 * Math.atan((f / 7500) ** 2);
3352
+ }
3353
+ function barkScale(f, minF, maxF) {
3354
+ const barkMin = hzToBark(minF);
3355
+ const barkMax = hzToBark(maxF);
3356
+ if (barkMax === barkMin) return 0;
3357
+ return (hzToBark(f) - barkMin) / (barkMax - barkMin);
3358
+ }
3359
+ function hzToErb(f) {
3360
+ return 21.4 * Math.log10(1 + 437e-5 * f);
3361
+ }
3362
+ function erbScale(f, minF, maxF) {
3363
+ const erbMin = hzToErb(minF);
3364
+ const erbMax = hzToErb(maxF);
3365
+ if (erbMax === erbMin) return 0;
3366
+ return (hzToErb(f) - erbMin) / (erbMax - erbMin);
3367
+ }
3368
+ function getFrequencyScale(name) {
3369
+ switch (name) {
3370
+ case "logarithmic":
3371
+ return logarithmicScale;
3372
+ case "mel":
3373
+ return melScale;
3374
+ case "bark":
3375
+ return barkScale;
3376
+ case "erb":
3377
+ return erbScale;
3378
+ case "linear":
3379
+ return linearScale;
3380
+ default:
3381
+ console.warn(`[spectrogram] Unknown frequency scale "${name}", falling back to linear`);
3382
+ return linearScale;
3383
+ }
3384
+ }
3385
+
3386
+ // src/components/SpectrogramMenuItems.tsx
3387
+ import styled from "styled-components";
3388
+ import { jsx, jsxs } from "react/jsx-runtime";
3389
+ var SectionLabel = styled.div`
3390
+ font-size: 0.65rem;
3391
+ font-weight: 600;
3392
+ text-transform: uppercase;
3393
+ letter-spacing: 0.05em;
3394
+ opacity: 0.5;
3395
+ margin-bottom: 0.25rem;
3396
+ `;
3397
+ var RadioLabel = styled.label`
3398
+ display: flex;
3399
+ align-items: center;
3400
+ gap: 0.4rem;
3401
+ padding: 0.2rem 0;
3402
+ font-size: 0.8rem;
3403
+ cursor: pointer;
3404
+
3405
+ &:hover {
3406
+ opacity: 0.8;
3407
+ }
3408
+ `;
3409
+ var SettingsButton = styled.button`
3410
+ background: none;
3411
+ border: none;
3412
+ color: inherit;
3413
+ cursor: pointer;
3414
+ font-size: 0.8rem;
3415
+ padding: 0.35rem 0.75rem;
3416
+ width: 100%;
3417
+ text-align: left;
3418
+
3419
+ &:hover {
3420
+ background: rgba(128, 128, 128, 0.15);
3421
+ }
3422
+ `;
3423
+ var DropdownSection = styled.div`
3424
+ padding: 0.25rem 0.75rem;
3425
+ `;
3426
+ var RENDER_MODES = [
3427
+ { value: "waveform", label: "Waveform" },
3428
+ { value: "spectrogram", label: "Spectrogram" },
3429
+ { value: "both", label: "Both" }
3430
+ ];
3431
+ function SpectrogramMenuItems({
3432
+ renderMode,
3433
+ onRenderModeChange,
3434
+ onOpenSettings,
3435
+ onClose
3436
+ }) {
3437
+ return [
3438
+ {
3439
+ id: "spectrogram-display",
3440
+ label: "Display",
3441
+ content: /* @__PURE__ */ jsxs(DropdownSection, { children: [
3442
+ /* @__PURE__ */ jsx(SectionLabel, { children: "Display" }),
3443
+ RENDER_MODES.map(({ value, label }) => /* @__PURE__ */ jsxs(RadioLabel, { children: [
3444
+ /* @__PURE__ */ jsx(
3445
+ "input",
3446
+ {
3447
+ type: "radio",
3448
+ name: "render-mode",
3449
+ checked: renderMode === value,
3450
+ onChange: () => {
3451
+ onRenderModeChange(value);
3452
+ onClose?.();
3453
+ }
3454
+ }
3455
+ ),
3456
+ label
3457
+ ] }, value))
3458
+ ] })
3459
+ },
3460
+ {
3461
+ id: "spectrogram-settings",
3462
+ content: /* @__PURE__ */ jsx(
3463
+ SettingsButton,
3464
+ {
3465
+ onClick: (e) => {
3466
+ e.stopPropagation();
3467
+ onClose?.();
3468
+ onOpenSettings();
3469
+ },
3470
+ onMouseDown: (e) => e.stopPropagation(),
3471
+ children: "Spectrogram Settings..."
3472
+ }
3473
+ )
3474
+ }
3475
+ ];
3476
+ }
3477
+
3478
+ // src/components/SpectrogramSettingsModal.tsx
3479
+ import { useRef, useEffect, useState } from "react";
3480
+ import styled2 from "styled-components";
3481
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
3482
+ var StyledDialog = styled2.dialog`
3483
+ border: 1px solid rgba(128, 128, 128, 0.4);
3484
+ border-radius: 8px;
3485
+ padding: 1.5rem;
3486
+ background: ${(p) => p.theme.timescaleBackgroundColor ?? "#222"};
3487
+ color: ${(p) => p.theme.textColor ?? "inherit"};
3488
+ min-width: 380px;
3489
+ max-width: 500px;
3490
+
3491
+ &::backdrop {
3492
+ background: rgba(0, 0, 0, 0.5);
3493
+ }
3494
+ `;
3495
+ var Title = styled2.h3`
3496
+ margin: 0 0 1rem;
3497
+ font-size: 1rem;
3498
+ `;
3499
+ var FormGrid = styled2.div`
3500
+ display: grid;
3501
+ grid-template-columns: 1fr 1fr;
3502
+ gap: 0.75rem;
3503
+ `;
3504
+ var Field = styled2.div`
3505
+ display: flex;
3506
+ flex-direction: column;
3507
+ gap: 0.2rem;
3508
+ grid-column: ${(p) => p.$span ? "1 / -1" : "auto"};
3509
+ `;
3510
+ var Label = styled2.label`
3511
+ font-size: 0.7rem;
3512
+ font-weight: 600;
3513
+ text-transform: uppercase;
3514
+ letter-spacing: 0.05em;
3515
+ opacity: 0.6;
3516
+ `;
3517
+ var Select = styled2.select`
3518
+ padding: 0.3rem 0.4rem;
3519
+ border: 1px solid rgba(128, 128, 128, 0.4);
3520
+ border-radius: 4px;
3521
+ background: rgba(128, 128, 128, 0.15);
3522
+ color: inherit;
3523
+ font-size: 0.85rem;
3524
+ `;
3525
+ var NumberInput = styled2.input`
3526
+ padding: 0.3rem 0.4rem;
3527
+ border: 1px solid rgba(128, 128, 128, 0.4);
3528
+ border-radius: 4px;
3529
+ background: rgba(128, 128, 128, 0.15);
3530
+ color: inherit;
3531
+ font-size: 0.85rem;
3532
+ width: 100%;
3533
+ box-sizing: border-box;
3534
+ `;
3535
+ var RangeRow = styled2.div`
3536
+ display: flex;
3537
+ align-items: center;
3538
+ gap: 0.5rem;
3539
+ `;
3540
+ var RangeValue = styled2.span`
3541
+ font-size: 0.75rem;
3542
+ font-family: monospace;
3543
+ opacity: 0.6;
3544
+ min-width: 3ch;
3545
+ text-align: right;
3546
+ `;
3547
+ var CheckboxLabel = styled2.label`
3548
+ display: flex;
3549
+ align-items: center;
3550
+ gap: 0.4rem;
3551
+ font-size: 0.85rem;
3552
+ cursor: pointer;
3553
+ `;
3554
+ var ButtonRow = styled2.div`
3555
+ display: flex;
3556
+ justify-content: flex-end;
3557
+ gap: 0.5rem;
3558
+ margin-top: 1.25rem;
3559
+ `;
3560
+ var ModalButton = styled2.button`
3561
+ padding: 0.4rem 1rem;
3562
+ border: 1px solid rgba(128, 128, 128, 0.4);
3563
+ border-radius: 4px;
3564
+ cursor: pointer;
3565
+ font-size: 0.85rem;
3566
+ background: ${(p) => p.$primary ? "var(--ifm-color-primary, #4a9)" : "transparent"};
3567
+ color: ${(p) => p.$primary ? "#fff" : "inherit"};
3568
+
3569
+ &:hover {
3570
+ opacity: 0.85;
3571
+ }
3572
+ `;
3573
+ var FFT_SIZES = [256, 512, 1024, 2048, 4096, 8192];
3574
+ var WINDOW_FUNCTIONS = ["hann", "hamming", "blackman", "blackman-harris", "bartlett", "rectangular"];
3575
+ var FREQ_SCALES = ["linear", "logarithmic", "mel", "bark", "erb"];
3576
+ var COLOR_MAPS = ["viridis", "magma", "inferno", "grayscale", "igray", "roseus"];
3577
+ var SpectrogramSettingsModal = ({
3578
+ open,
3579
+ onClose,
3580
+ config,
3581
+ colorMap,
3582
+ onApply
3583
+ }) => {
3584
+ const dialogRef = useRef(null);
3585
+ const [fftSize, setFftSize] = useState(config.fftSize ?? 2048);
3586
+ const [windowFn, setWindowFn] = useState(config.windowFunction ?? "hann");
3587
+ const [freqScale, setFreqScale] = useState(config.frequencyScale ?? "mel");
3588
+ const [localColorMap, setLocalColorMap] = useState(
3589
+ typeof colorMap === "string" ? colorMap : "viridis"
3590
+ );
3591
+ const [minFreq, setMinFreq] = useState(config.minFrequency ?? 0);
3592
+ const [maxFreq, setMaxFreq] = useState(config.maxFrequency ?? 2e4);
3593
+ const [gainDb, setGainDb] = useState(config.gainDb ?? 20);
3594
+ const [rangeDb, setRangeDb] = useState(config.rangeDb ?? 80);
3595
+ const [zeroPadding, setZeroPadding] = useState(config.zeroPaddingFactor ?? 2);
3596
+ const [hopSize, setHopSize] = useState(config.hopSize ?? Math.floor((config.fftSize ?? 2048) / 4));
3597
+ const [showLabels, setShowLabels] = useState(config.labels ?? false);
3598
+ useEffect(() => {
3599
+ setFftSize(config.fftSize ?? 2048);
3600
+ setWindowFn(config.windowFunction ?? "hann");
3601
+ setFreqScale(config.frequencyScale ?? "mel");
3602
+ setLocalColorMap(typeof colorMap === "string" ? colorMap : "viridis");
3603
+ setMinFreq(config.minFrequency ?? 0);
3604
+ setMaxFreq(config.maxFrequency ?? 2e4);
3605
+ setGainDb(config.gainDb ?? 20);
3606
+ setRangeDb(config.rangeDb ?? 80);
3607
+ setZeroPadding(config.zeroPaddingFactor ?? 2);
3608
+ setHopSize(config.hopSize ?? Math.floor((config.fftSize ?? 2048) / 4));
3609
+ setShowLabels(config.labels ?? false);
3610
+ }, [config, colorMap]);
3611
+ useEffect(() => {
3612
+ const dialog = dialogRef.current;
3613
+ if (!dialog) return;
3614
+ if (open && !dialog.open) {
3615
+ dialog.showModal();
3616
+ } else if (!open && dialog.open) {
3617
+ dialog.close();
3618
+ }
3619
+ const handleClose = () => {
3620
+ if (open) {
3621
+ onClose();
3622
+ }
3623
+ };
3624
+ dialog.addEventListener("close", handleClose);
3625
+ return () => dialog.removeEventListener("close", handleClose);
3626
+ }, [open, onClose]);
3627
+ const handleApply = () => {
3628
+ onApply(
3629
+ {
3630
+ fftSize,
3631
+ windowFunction: windowFn,
3632
+ frequencyScale: freqScale,
3633
+ minFrequency: minFreq,
3634
+ maxFrequency: maxFreq,
3635
+ gainDb,
3636
+ rangeDb,
3637
+ zeroPaddingFactor: zeroPadding,
3638
+ hopSize,
3639
+ labels: showLabels
3640
+ },
3641
+ localColorMap
3642
+ );
3643
+ onClose();
3644
+ };
3645
+ return /* @__PURE__ */ jsxs2(StyledDialog, { ref: dialogRef, children: [
3646
+ /* @__PURE__ */ jsx2(Title, { children: "Spectrogram Settings" }),
3647
+ /* @__PURE__ */ jsxs2(FormGrid, { children: [
3648
+ /* @__PURE__ */ jsxs2(Field, { children: [
3649
+ /* @__PURE__ */ jsx2(Label, { children: "FFT Size" }),
3650
+ /* @__PURE__ */ jsx2(Select, { value: fftSize, onChange: (e) => {
3651
+ const v = Number(e.target.value);
3652
+ setFftSize(v);
3653
+ setHopSize(Math.floor(v / 4));
3654
+ }, children: FFT_SIZES.map((s) => /* @__PURE__ */ jsx2("option", { value: s, children: s }, s)) })
3655
+ ] }),
3656
+ /* @__PURE__ */ jsxs2(Field, { children: [
3657
+ /* @__PURE__ */ jsx2(Label, { children: "Hop Size" }),
3658
+ /* @__PURE__ */ jsx2(Select, { value: hopSize, onChange: (e) => setHopSize(Number(e.target.value)), children: [
3659
+ { label: `${fftSize} (no overlap)`, value: fftSize },
3660
+ { label: `${Math.floor(fftSize / 2)} (50%)`, value: Math.floor(fftSize / 2) },
3661
+ { label: `${Math.floor(fftSize / 4)} (75%)`, value: Math.floor(fftSize / 4) },
3662
+ { label: `${Math.floor(fftSize / 8)} (87.5%)`, value: Math.floor(fftSize / 8) }
3663
+ ].map((o) => /* @__PURE__ */ jsx2("option", { value: o.value, children: o.label }, o.value)) })
3664
+ ] }),
3665
+ /* @__PURE__ */ jsxs2(Field, { children: [
3666
+ /* @__PURE__ */ jsx2(Label, { children: "Zero Padding" }),
3667
+ /* @__PURE__ */ jsx2(Select, { value: zeroPadding, onChange: (e) => setZeroPadding(Number(e.target.value)), children: [1, 2, 4, 8, 16].map((v) => /* @__PURE__ */ jsx2("option", { value: v, children: v }, v)) })
3668
+ ] }),
3669
+ /* @__PURE__ */ jsxs2(Field, { children: [
3670
+ /* @__PURE__ */ jsx2(Label, { children: "Window Function" }),
3671
+ /* @__PURE__ */ jsx2(Select, { value: windowFn, onChange: (e) => setWindowFn(e.target.value), children: WINDOW_FUNCTIONS.map((w) => /* @__PURE__ */ jsx2("option", { value: w, children: w }, w)) })
3672
+ ] }),
3673
+ /* @__PURE__ */ jsxs2(Field, { children: [
3674
+ /* @__PURE__ */ jsx2(Label, { children: "Frequency Scale" }),
3675
+ /* @__PURE__ */ jsx2(Select, { value: freqScale, onChange: (e) => setFreqScale(e.target.value), children: FREQ_SCALES.map((s) => /* @__PURE__ */ jsx2("option", { value: s, children: s }, s)) })
3676
+ ] }),
3677
+ /* @__PURE__ */ jsxs2(Field, { children: [
3678
+ /* @__PURE__ */ jsx2(Label, { children: "Color Map" }),
3679
+ /* @__PURE__ */ jsx2(Select, { value: localColorMap, onChange: (e) => setLocalColorMap(e.target.value), children: COLOR_MAPS.map((c) => /* @__PURE__ */ jsx2("option", { value: c, children: c }, c)) })
3680
+ ] }),
3681
+ /* @__PURE__ */ jsxs2(Field, { children: [
3682
+ /* @__PURE__ */ jsx2(Label, { children: "Min Frequency (Hz)" }),
3683
+ /* @__PURE__ */ jsx2(
3684
+ NumberInput,
3685
+ {
3686
+ type: "number",
3687
+ min: 0,
3688
+ max: 5e3,
3689
+ step: 50,
3690
+ value: minFreq,
3691
+ onChange: (e) => setMinFreq(Number(e.target.value))
3692
+ }
3693
+ )
3694
+ ] }),
3695
+ /* @__PURE__ */ jsxs2(Field, { children: [
3696
+ /* @__PURE__ */ jsx2(Label, { children: "Max Frequency (Hz)" }),
3697
+ /* @__PURE__ */ jsx2(
3698
+ NumberInput,
3699
+ {
3700
+ type: "number",
3701
+ min: 1e3,
3702
+ max: 22050,
3703
+ step: 50,
3704
+ value: maxFreq,
3705
+ onChange: (e) => setMaxFreq(Number(e.target.value))
3706
+ }
3707
+ )
3708
+ ] }),
3709
+ /* @__PURE__ */ jsxs2(Field, { children: [
3710
+ /* @__PURE__ */ jsx2(Label, { children: "Range (dB)" }),
3711
+ /* @__PURE__ */ jsx2(
3712
+ NumberInput,
3713
+ {
3714
+ type: "number",
3715
+ min: 1,
3716
+ max: 120,
3717
+ step: 1,
3718
+ value: rangeDb,
3719
+ onChange: (e) => setRangeDb(Number(e.target.value))
3720
+ }
3721
+ )
3722
+ ] }),
3723
+ /* @__PURE__ */ jsxs2(Field, { children: [
3724
+ /* @__PURE__ */ jsx2(Label, { children: "Gain (dB)" }),
3725
+ /* @__PURE__ */ jsx2(
3726
+ NumberInput,
3727
+ {
3728
+ type: "number",
3729
+ min: -20,
3730
+ max: 60,
3731
+ step: 1,
3732
+ value: gainDb,
3733
+ onChange: (e) => setGainDb(Number(e.target.value))
3734
+ }
3735
+ )
3736
+ ] }),
3737
+ /* @__PURE__ */ jsx2(Field, { $span: true, children: /* @__PURE__ */ jsxs2(CheckboxLabel, { children: [
3738
+ /* @__PURE__ */ jsx2("input", { type: "checkbox", checked: showLabels, onChange: (e) => setShowLabels(e.target.checked) }),
3739
+ "Show Frequency Labels"
3740
+ ] }) })
3741
+ ] }),
3742
+ /* @__PURE__ */ jsxs2(ButtonRow, { children: [
3743
+ /* @__PURE__ */ jsx2(ModalButton, { onClick: onClose, children: "Cancel" }),
3744
+ /* @__PURE__ */ jsx2(ModalButton, { $primary: true, onClick: handleApply, children: "Apply" })
3745
+ ] })
3746
+ ] });
3747
+ };
3748
+
3749
+ // src/worker/createSpectrogramWorker.ts
3750
+ var idCounter = 0;
3751
+ function createSpectrogramWorker(worker) {
3752
+ const pending = /* @__PURE__ */ new Map();
3753
+ const registeredClipIds = /* @__PURE__ */ new Set();
3754
+ let terminated = false;
3755
+ worker.onmessage = (e) => {
3756
+ const msg = e.data;
3757
+ const entry = pending.get(msg.id);
3758
+ if (entry) {
3759
+ pending.delete(msg.id);
3760
+ switch (msg.type) {
3761
+ case "error":
3762
+ entry.reject(new Error(msg.error));
3763
+ break;
3764
+ case "cache-key":
3765
+ entry.resolve({ cacheKey: msg.cacheKey });
3766
+ break;
3767
+ case "done":
3768
+ entry.resolve(void 0);
3769
+ break;
3770
+ case "spectrograms":
3771
+ entry.resolve(msg.spectrograms);
3772
+ break;
3773
+ }
3774
+ } else if (msg.id) {
3775
+ console.warn(`[spectrogram] Received response for unknown message ID: ${msg.id}`);
3776
+ }
3777
+ };
3778
+ worker.onerror = (e) => {
3779
+ terminated = true;
3780
+ for (const [, entry] of pending) {
3781
+ entry.reject(e.error ?? new Error(e.message));
3782
+ }
3783
+ pending.clear();
3784
+ };
3785
+ return {
3786
+ compute(params) {
3787
+ if (terminated) return Promise.reject(new Error("Worker terminated"));
3788
+ const id = String(++idCounter);
3789
+ return new Promise((resolve, reject) => {
3790
+ pending.set(id, { resolve, reject });
3791
+ const transferableArrays = params.channelDataArrays.map((arr) => arr.slice());
3792
+ const transferables = transferableArrays.map((arr) => arr.buffer);
3793
+ worker.postMessage(
3794
+ {
3795
+ id,
3796
+ channelDataArrays: transferableArrays,
3797
+ config: params.config,
3798
+ sampleRate: params.sampleRate,
3799
+ offsetSamples: params.offsetSamples,
3800
+ durationSamples: params.durationSamples,
3801
+ mono: params.mono
3802
+ },
3803
+ transferables
3804
+ );
3805
+ });
3806
+ },
3807
+ computeFFT(params) {
3808
+ if (terminated) return Promise.reject(new Error("Worker terminated"));
3809
+ const id = String(++idCounter);
3810
+ return new Promise((resolve, reject) => {
3811
+ pending.set(id, { resolve, reject });
3812
+ const isPreRegistered = registeredClipIds.has(params.clipId);
3813
+ const transferableArrays = isPreRegistered ? [] : params.channelDataArrays.map((arr) => arr.slice());
3814
+ const transferables = transferableArrays.map((arr) => arr.buffer);
3815
+ worker.postMessage(
3816
+ {
3817
+ type: "compute-fft",
3818
+ id,
3819
+ clipId: params.clipId,
3820
+ channelDataArrays: transferableArrays,
3821
+ config: params.config,
3822
+ sampleRate: params.sampleRate,
3823
+ offsetSamples: params.offsetSamples,
3824
+ durationSamples: params.durationSamples,
3825
+ mono: params.mono,
3826
+ ...params.sampleRange ? { sampleRange: params.sampleRange } : {}
3827
+ },
3828
+ transferables
3829
+ );
3830
+ });
3831
+ },
3832
+ renderChunks(params) {
3833
+ if (terminated) return Promise.reject(new Error("Worker terminated"));
3834
+ const id = String(++idCounter);
3835
+ return new Promise((resolve, reject) => {
3836
+ pending.set(id, { resolve, reject });
3837
+ worker.postMessage({
3838
+ type: "render-chunks",
3839
+ id,
3840
+ cacheKey: params.cacheKey,
3841
+ canvasIds: params.canvasIds,
3842
+ canvasWidths: params.canvasWidths,
3843
+ globalPixelOffsets: params.globalPixelOffsets,
3844
+ canvasHeight: params.canvasHeight,
3845
+ devicePixelRatio: params.devicePixelRatio,
3846
+ samplesPerPixel: params.samplesPerPixel,
3847
+ colorLUT: params.colorLUT,
3848
+ frequencyScale: params.frequencyScale,
3849
+ minFrequency: params.minFrequency,
3850
+ maxFrequency: params.maxFrequency,
3851
+ gainDb: params.gainDb,
3852
+ rangeDb: params.rangeDb,
3853
+ channelIndex: params.channelIndex
3854
+ });
3855
+ });
3856
+ },
3857
+ registerCanvas(canvasId, canvas) {
3858
+ worker.postMessage(
3859
+ { type: "register-canvas", canvasId, canvas },
3860
+ [canvas]
3861
+ );
3862
+ },
3863
+ unregisterCanvas(canvasId) {
3864
+ worker.postMessage({ type: "unregister-canvas", canvasId });
3865
+ },
3866
+ registerAudioData(clipId, channelDataArrays, sampleRate) {
3867
+ const transferableArrays = channelDataArrays.map((arr) => arr.slice());
3868
+ const transferables = transferableArrays.map((arr) => arr.buffer);
3869
+ worker.postMessage(
3870
+ { type: "register-audio-data", clipId, channelDataArrays: transferableArrays, sampleRate },
3871
+ transferables
3872
+ );
3873
+ registeredClipIds.add(clipId);
3874
+ },
3875
+ unregisterAudioData(clipId) {
3876
+ worker.postMessage({ type: "unregister-audio-data", clipId });
3877
+ registeredClipIds.delete(clipId);
3878
+ },
3879
+ computeAndRender(params) {
3880
+ if (terminated) return Promise.reject(new Error("Worker terminated"));
3881
+ const id = String(++idCounter);
3882
+ return new Promise((resolve, reject) => {
3883
+ pending.set(id, { resolve, reject });
3884
+ const transferableArrays = params.channelDataArrays.map((arr) => arr.slice());
3885
+ const transferables = transferableArrays.map((arr) => arr.buffer);
3886
+ worker.postMessage(
3887
+ {
3888
+ type: "compute-render",
3889
+ id,
3890
+ channelDataArrays: transferableArrays,
3891
+ config: params.config,
3892
+ sampleRate: params.sampleRate,
3893
+ offsetSamples: params.offsetSamples,
3894
+ durationSamples: params.durationSamples,
3895
+ mono: params.mono,
3896
+ render: params.render
3897
+ },
3898
+ transferables
3899
+ );
3900
+ });
3901
+ },
3902
+ terminate() {
3903
+ terminated = true;
3904
+ worker.terminate();
3905
+ for (const [, entry] of pending) {
3906
+ entry.reject(new Error("Worker terminated"));
3907
+ }
3908
+ pending.clear();
3909
+ }
3910
+ };
3911
+ }
3912
+
3913
+ // src/SpectrogramProvider.tsx
3914
+ import { useState as useState2, useEffect as useEffect2, useRef as useRef2, useCallback, useMemo } from "react";
3915
+ import { SpectrogramIntegrationProvider } from "@waveform-playlist/browser";
3916
+ import { usePlaylistData, usePlaylistControls } from "@waveform-playlist/browser";
3917
+ import { jsx as jsx3 } from "react/jsx-runtime";
3918
+ var SpectrogramProvider = ({
3919
+ config: spectrogramConfig,
3920
+ colorMap: spectrogramColorMap,
3921
+ children
3922
+ }) => {
3923
+ const {
3924
+ tracks,
3925
+ waveHeight,
3926
+ samplesPerPixel,
3927
+ isReady,
3928
+ mono,
3929
+ controls
3930
+ } = usePlaylistData();
3931
+ const { scrollContainerRef } = usePlaylistControls();
3932
+ const [spectrogramDataMap, setSpectrogramDataMap] = useState2(/* @__PURE__ */ new Map());
3933
+ const [trackSpectrogramOverrides, setTrackSpectrogramOverrides] = useState2(/* @__PURE__ */ new Map());
3934
+ const spectrogramCanvasRegistryRef = useRef2(/* @__PURE__ */ new Map());
3935
+ const [spectrogramCanvasVersion, setSpectrogramCanvasVersion] = useState2(0);
3936
+ const prevSpectrogramConfigRef = useRef2(/* @__PURE__ */ new Map());
3937
+ const prevSpectrogramFFTKeyRef = useRef2(/* @__PURE__ */ new Map());
3938
+ const spectrogramWorkerRef = useRef2(null);
3939
+ const spectrogramGenerationRef = useRef2(0);
3940
+ const prevCanvasVersionRef = useRef2(0);
3941
+ const [spectrogramWorkerReady, setSpectrogramWorkerReady] = useState2(false);
3942
+ const clipCacheKeysRef = useRef2(/* @__PURE__ */ new Map());
3943
+ const backgroundRenderAbortRef = useRef2(null);
3944
+ const registeredAudioClipIdsRef = useRef2(/* @__PURE__ */ new Set());
3945
+ useEffect2(() => {
3946
+ return () => {
3947
+ spectrogramWorkerRef.current?.terminate();
3948
+ spectrogramWorkerRef.current = null;
3949
+ };
3950
+ }, []);
3951
+ useEffect2(() => {
3952
+ if (!isReady || tracks.length === 0) return;
3953
+ let workerApi = spectrogramWorkerRef.current;
3954
+ if (!workerApi) {
3955
+ try {
3956
+ const rawWorker = new Worker(
3957
+ new URL("@waveform-playlist/spectrogram/worker/spectrogram.worker", import.meta.url),
3958
+ { type: "module" }
3959
+ );
3960
+ workerApi = createSpectrogramWorker(rawWorker);
3961
+ spectrogramWorkerRef.current = workerApi;
3962
+ setSpectrogramWorkerReady(true);
3963
+ } catch {
3964
+ console.warn("Spectrogram Web Worker unavailable for pre-transfer");
3965
+ return;
3966
+ }
3967
+ }
3968
+ const currentClipIds = /* @__PURE__ */ new Set();
3969
+ for (const track of tracks) {
3970
+ for (const clip of track.clips) {
3971
+ if (!clip.audioBuffer) continue;
3972
+ currentClipIds.add(clip.id);
3973
+ if (!registeredAudioClipIdsRef.current.has(clip.id)) {
3974
+ const channelDataArrays = [];
3975
+ for (let ch = 0; ch < clip.audioBuffer.numberOfChannels; ch++) {
3976
+ channelDataArrays.push(clip.audioBuffer.getChannelData(ch));
3977
+ }
3978
+ workerApi.registerAudioData(clip.id, channelDataArrays, clip.audioBuffer.sampleRate);
3979
+ registeredAudioClipIdsRef.current.add(clip.id);
3980
+ }
3981
+ }
3982
+ }
3983
+ for (const clipId of registeredAudioClipIdsRef.current) {
3984
+ if (!currentClipIds.has(clipId)) {
3985
+ workerApi.unregisterAudioData(clipId);
3986
+ registeredAudioClipIdsRef.current.delete(clipId);
3987
+ }
3988
+ }
3989
+ }, [isReady, tracks]);
3990
+ useEffect2(() => {
3991
+ if (tracks.length === 0) return;
3992
+ const currentKeys = /* @__PURE__ */ new Map();
3993
+ const currentFFTKeys = /* @__PURE__ */ new Map();
3994
+ tracks.forEach((track) => {
3995
+ const mode = trackSpectrogramOverrides.get(track.id)?.renderMode ?? track.renderMode ?? "waveform";
3996
+ if (mode === "waveform") return;
3997
+ const cfg = trackSpectrogramOverrides.get(track.id)?.config ?? track.spectrogramConfig ?? spectrogramConfig;
3998
+ const cm = trackSpectrogramOverrides.get(track.id)?.colorMap ?? track.spectrogramColorMap ?? spectrogramColorMap;
3999
+ currentKeys.set(track.id, JSON.stringify({ mode, cfg, cm, mono }));
4000
+ const computeConfig = {
4001
+ fftSize: cfg?.fftSize,
4002
+ hopSize: cfg?.hopSize,
4003
+ windowFunction: cfg?.windowFunction,
4004
+ alpha: cfg?.alpha,
4005
+ zeroPaddingFactor: cfg?.zeroPaddingFactor
4006
+ };
4007
+ currentFFTKeys.set(track.id, JSON.stringify({ mode, mono, ...computeConfig }));
4008
+ });
4009
+ const prevKeys = prevSpectrogramConfigRef.current;
4010
+ const prevFFTKeys = prevSpectrogramFFTKeyRef.current;
4011
+ let configChanged = currentKeys.size !== prevKeys.size;
4012
+ if (!configChanged) {
4013
+ for (const [idx, key] of currentKeys) {
4014
+ if (prevKeys.get(idx) !== key) {
4015
+ configChanged = true;
4016
+ break;
4017
+ }
4018
+ }
4019
+ }
4020
+ let fftKeyChanged = currentFFTKeys.size !== prevFFTKeys.size;
4021
+ if (!fftKeyChanged) {
4022
+ for (const [idx, key] of currentFFTKeys) {
4023
+ if (prevFFTKeys.get(idx) !== key) {
4024
+ fftKeyChanged = true;
4025
+ break;
4026
+ }
4027
+ }
4028
+ }
4029
+ const canvasVersionChanged = spectrogramCanvasVersion !== prevCanvasVersionRef.current;
4030
+ prevCanvasVersionRef.current = spectrogramCanvasVersion;
4031
+ if (!configChanged && !canvasVersionChanged) return;
4032
+ if (configChanged) {
4033
+ prevSpectrogramConfigRef.current = currentKeys;
4034
+ prevSpectrogramFFTKeyRef.current = currentFFTKeys;
4035
+ }
4036
+ if (configChanged) {
4037
+ setSpectrogramDataMap((prevMap) => {
4038
+ const activeClipIds = /* @__PURE__ */ new Set();
4039
+ for (const track of tracks) {
4040
+ const mode = trackSpectrogramOverrides.get(track.id)?.renderMode ?? track.renderMode ?? "waveform";
4041
+ if (mode === "spectrogram" || mode === "both") {
4042
+ for (const clip of track.clips) {
4043
+ activeClipIds.add(clip.id);
4044
+ }
4045
+ }
4046
+ }
4047
+ const newMap = new Map(prevMap);
4048
+ for (const clipId of newMap.keys()) {
4049
+ if (!activeClipIds.has(clipId)) {
4050
+ newMap.delete(clipId);
4051
+ }
4052
+ }
4053
+ return newMap;
4054
+ });
4055
+ }
4056
+ if (backgroundRenderAbortRef.current) {
4057
+ backgroundRenderAbortRef.current.aborted = true;
4058
+ }
4059
+ const generation = ++spectrogramGenerationRef.current;
4060
+ let workerApi = spectrogramWorkerRef.current;
4061
+ if (!workerApi) {
4062
+ try {
4063
+ const rawWorker = new Worker(
4064
+ new URL("@waveform-playlist/spectrogram/worker/spectrogram.worker", import.meta.url),
4065
+ { type: "module" }
4066
+ );
4067
+ workerApi = createSpectrogramWorker(rawWorker);
4068
+ spectrogramWorkerRef.current = workerApi;
4069
+ setSpectrogramWorkerReady(true);
4070
+ } catch {
4071
+ console.warn("Spectrogram Web Worker unavailable, falling back to synchronous computation");
4072
+ }
4073
+ }
4074
+ const clipsNeedingFFT = [];
4075
+ const clipsNeedingDisplayOnly = [];
4076
+ tracks.forEach((track, i) => {
4077
+ const mode = trackSpectrogramOverrides.get(track.id)?.renderMode ?? track.renderMode ?? "waveform";
4078
+ if (mode === "waveform") return;
4079
+ const trackConfigChanged = configChanged && currentKeys.get(track.id) !== prevKeys.get(track.id);
4080
+ const trackFFTChanged = fftKeyChanged && currentFFTKeys.get(track.id) !== prevFFTKeys.get(track.id);
4081
+ const hasRegisteredCanvases = canvasVersionChanged && track.clips.some(
4082
+ (clip) => spectrogramCanvasRegistryRef.current.has(clip.id)
4083
+ );
4084
+ if (!trackConfigChanged && !hasRegisteredCanvases) return;
4085
+ const cfg = trackSpectrogramOverrides.get(track.id)?.config ?? track.spectrogramConfig ?? spectrogramConfig ?? {};
4086
+ const cm = trackSpectrogramOverrides.get(track.id)?.colorMap ?? track.spectrogramColorMap ?? spectrogramColorMap ?? "viridis";
4087
+ for (const clip of track.clips) {
4088
+ if (!clip.audioBuffer) continue;
4089
+ const monoFlag = mono || clip.audioBuffer.numberOfChannels === 1;
4090
+ if (!trackFFTChanged && !hasRegisteredCanvases && clipCacheKeysRef.current.has(clip.id)) {
4091
+ clipsNeedingDisplayOnly.push({
4092
+ clipId: clip.id,
4093
+ trackIndex: i,
4094
+ config: cfg,
4095
+ clipStartSample: clip.startSample,
4096
+ monoFlag,
4097
+ colorMap: cm,
4098
+ numChannels: monoFlag ? 1 : clip.audioBuffer.numberOfChannels
4099
+ });
4100
+ continue;
4101
+ }
4102
+ const channelDataArrays = [];
4103
+ for (let ch = 0; ch < clip.audioBuffer.numberOfChannels; ch++) {
4104
+ channelDataArrays.push(clip.audioBuffer.getChannelData(ch));
4105
+ }
4106
+ clipsNeedingFFT.push({
4107
+ clipId: clip.id,
4108
+ trackIndex: i,
4109
+ channelDataArrays,
4110
+ config: cfg,
4111
+ sampleRate: clip.audioBuffer.sampleRate,
4112
+ offsetSamples: clip.offsetSamples,
4113
+ durationSamples: clip.durationSamples,
4114
+ clipStartSample: clip.startSample,
4115
+ monoFlag,
4116
+ colorMap: cm
4117
+ });
4118
+ }
4119
+ });
4120
+ if (clipsNeedingFFT.length === 0 && clipsNeedingDisplayOnly.length === 0) return;
4121
+ if (!workerApi) {
4122
+ try {
4123
+ setSpectrogramDataMap((prevMap) => {
4124
+ const newMap = new Map(prevMap);
4125
+ for (const item of clipsNeedingFFT) {
4126
+ const clip = tracks.flatMap((t) => t.clips).find((c) => c.id === item.clipId);
4127
+ if (!clip?.audioBuffer) continue;
4128
+ const channelSpectrograms = [];
4129
+ if (item.monoFlag) {
4130
+ channelSpectrograms.push(
4131
+ computeSpectrogramMono(
4132
+ clip.audioBuffer,
4133
+ item.config,
4134
+ item.offsetSamples,
4135
+ item.durationSamples
4136
+ )
4137
+ );
4138
+ } else {
4139
+ for (let ch = 0; ch < clip.audioBuffer.numberOfChannels; ch++) {
4140
+ channelSpectrograms.push(
4141
+ computeSpectrogram(clip.audioBuffer, item.config, item.offsetSamples, item.durationSamples, ch)
4142
+ );
4143
+ }
4144
+ }
4145
+ newMap.set(item.clipId, channelSpectrograms);
4146
+ }
4147
+ return newMap;
4148
+ });
4149
+ } catch (err) {
4150
+ console.error("[waveform-playlist] Synchronous spectrogram computation failed:", err);
4151
+ }
4152
+ return;
4153
+ }
4154
+ const getVisibleChunkRange = (canvasWidths, clipPixelOffset = 0) => {
4155
+ const container = scrollContainerRef.current;
4156
+ if (!container) {
4157
+ return { visibleIndices: canvasWidths.map((_, i) => i), remainingIndices: [] };
4158
+ }
4159
+ const scrollLeft = container.scrollLeft;
4160
+ const viewportWidth = container.clientWidth;
4161
+ const controlWidth = controls.show ? controls.width : 0;
4162
+ const visibleIndices = [];
4163
+ const remainingIndices = [];
4164
+ let offset = 0;
4165
+ for (let i = 0; i < canvasWidths.length; i++) {
4166
+ const chunkLeft = offset + controlWidth + clipPixelOffset;
4167
+ const chunkRight = chunkLeft + canvasWidths[i];
4168
+ if (chunkRight > scrollLeft && chunkLeft < scrollLeft + viewportWidth) {
4169
+ visibleIndices.push(i);
4170
+ } else {
4171
+ remainingIndices.push(i);
4172
+ }
4173
+ offset += canvasWidths[i];
4174
+ }
4175
+ return { visibleIndices, remainingIndices };
4176
+ };
4177
+ const renderChunkSubset = async (api, cacheKey, channelInfo, indices, item, channelIndex) => {
4178
+ if (indices.length === 0) return;
4179
+ const canvasIds = indices.map((i) => channelInfo.canvasIds[i]);
4180
+ const canvasWidths = indices.map((i) => channelInfo.canvasWidths[i]);
4181
+ const globalPixelOffsets = [];
4182
+ for (const idx of indices) {
4183
+ let offset = 0;
4184
+ for (let j = 0; j < idx; j++) {
4185
+ offset += channelInfo.canvasWidths[j];
4186
+ }
4187
+ globalPixelOffsets.push(offset);
4188
+ }
4189
+ const colorLUT = getColorMap(item.colorMap);
4190
+ await api.renderChunks({
4191
+ cacheKey,
4192
+ canvasIds,
4193
+ canvasWidths,
4194
+ globalPixelOffsets,
4195
+ canvasHeight: waveHeight,
4196
+ devicePixelRatio: typeof window !== "undefined" ? window.devicePixelRatio : 1,
4197
+ samplesPerPixel,
4198
+ colorLUT,
4199
+ frequencyScale: item.config.frequencyScale ?? "mel",
4200
+ minFrequency: item.config.minFrequency ?? 0,
4201
+ maxFrequency: item.config.maxFrequency ?? 0,
4202
+ gainDb: item.config.gainDb ?? 20,
4203
+ rangeDb: item.config.rangeDb ?? 80,
4204
+ channelIndex
4205
+ });
4206
+ };
4207
+ const computeAsync = async () => {
4208
+ const abortToken = { aborted: false };
4209
+ backgroundRenderAbortRef.current = abortToken;
4210
+ for (const item of clipsNeedingFFT) {
4211
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4212
+ try {
4213
+ const clipCanvasInfo = spectrogramCanvasRegistryRef.current.get(item.clipId);
4214
+ if (clipCanvasInfo && clipCanvasInfo.size > 0) {
4215
+ const numChannels = item.monoFlag ? 1 : item.channelDataArrays.length;
4216
+ const clipPixelOffset = Math.floor(item.clipStartSample / samplesPerPixel);
4217
+ const container = scrollContainerRef.current;
4218
+ const windowSize = item.config.fftSize ?? 2048;
4219
+ let visibleRange;
4220
+ if (container) {
4221
+ const scrollLeft = container.scrollLeft;
4222
+ const viewportWidth = container.clientWidth;
4223
+ const controlWidth = controls.show ? controls.width : 0;
4224
+ const vpStartPx = Math.max(0, scrollLeft - controlWidth);
4225
+ const vpEndPx = vpStartPx + viewportWidth;
4226
+ const clipStartPx = clipPixelOffset;
4227
+ const clipEndPx = clipStartPx + Math.ceil(item.durationSamples / samplesPerPixel);
4228
+ const overlapStartPx = Math.max(vpStartPx, clipStartPx);
4229
+ const overlapEndPx = Math.min(vpEndPx, clipEndPx);
4230
+ if (overlapEndPx > overlapStartPx) {
4231
+ const localStartPx = overlapStartPx - clipStartPx;
4232
+ const localEndPx = overlapEndPx - clipStartPx;
4233
+ const visStartSample = item.offsetSamples + Math.floor(localStartPx * samplesPerPixel);
4234
+ const visEndSample = Math.min(
4235
+ item.offsetSamples + item.durationSamples,
4236
+ item.offsetSamples + Math.ceil(localEndPx * samplesPerPixel)
4237
+ );
4238
+ const paddedStart = Math.max(item.offsetSamples, visStartSample - windowSize);
4239
+ const paddedEnd = Math.min(item.offsetSamples + item.durationSamples, visEndSample + windowSize);
4240
+ if (paddedEnd - paddedStart < item.durationSamples * 0.8) {
4241
+ visibleRange = { start: paddedStart, end: paddedEnd };
4242
+ }
4243
+ }
4244
+ }
4245
+ const fullClipAlreadyCached = clipCacheKeysRef.current.has(item.clipId);
4246
+ if (visibleRange && !fullClipAlreadyCached) {
4247
+ const { cacheKey: visibleCacheKey } = await workerApi.computeFFT({
4248
+ clipId: item.clipId,
4249
+ channelDataArrays: item.channelDataArrays,
4250
+ config: item.config,
4251
+ sampleRate: item.sampleRate,
4252
+ offsetSamples: item.offsetSamples,
4253
+ durationSamples: item.durationSamples,
4254
+ mono: item.monoFlag,
4255
+ sampleRange: visibleRange
4256
+ });
4257
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4258
+ for (let ch = 0; ch < numChannels; ch++) {
4259
+ const channelInfo = clipCanvasInfo.get(ch);
4260
+ if (!channelInfo) continue;
4261
+ const { visibleIndices } = getVisibleChunkRange(channelInfo.canvasWidths, clipPixelOffset);
4262
+ await renderChunkSubset(workerApi, visibleCacheKey, channelInfo, visibleIndices, item, ch);
4263
+ }
4264
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4265
+ }
4266
+ const { cacheKey } = await workerApi.computeFFT({
4267
+ clipId: item.clipId,
4268
+ channelDataArrays: item.channelDataArrays,
4269
+ config: item.config,
4270
+ sampleRate: item.sampleRate,
4271
+ offsetSamples: item.offsetSamples,
4272
+ durationSamples: item.durationSamples,
4273
+ mono: item.monoFlag
4274
+ });
4275
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4276
+ clipCacheKeysRef.current.set(item.clipId, cacheKey);
4277
+ for (let ch = 0; ch < numChannels; ch++) {
4278
+ const channelInfo = clipCanvasInfo.get(ch);
4279
+ if (!channelInfo) continue;
4280
+ const { visibleIndices, remainingIndices } = getVisibleChunkRange(channelInfo.canvasWidths, clipPixelOffset);
4281
+ await renderChunkSubset(workerApi, cacheKey, channelInfo, visibleIndices, item, ch);
4282
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4283
+ const BATCH_SIZE = 4;
4284
+ for (let batchStart = 0; batchStart < remainingIndices.length; batchStart += BATCH_SIZE) {
4285
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4286
+ const batch = remainingIndices.slice(batchStart, batchStart + BATCH_SIZE);
4287
+ await new Promise((resolve) => {
4288
+ if (typeof requestIdleCallback === "function") {
4289
+ requestIdleCallback(() => resolve());
4290
+ } else {
4291
+ setTimeout(resolve, 0);
4292
+ }
4293
+ });
4294
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4295
+ await renderChunkSubset(workerApi, cacheKey, channelInfo, batch, item, ch);
4296
+ }
4297
+ }
4298
+ } else {
4299
+ const spectrograms = await workerApi.compute({
4300
+ channelDataArrays: item.channelDataArrays,
4301
+ config: item.config,
4302
+ sampleRate: item.sampleRate,
4303
+ offsetSamples: item.offsetSamples,
4304
+ durationSamples: item.durationSamples,
4305
+ mono: item.monoFlag
4306
+ });
4307
+ if (spectrogramGenerationRef.current !== generation) return;
4308
+ setSpectrogramDataMap((prevMap) => {
4309
+ const newMap = new Map(prevMap);
4310
+ newMap.set(item.clipId, spectrograms);
4311
+ return newMap;
4312
+ });
4313
+ }
4314
+ } catch (err) {
4315
+ console.warn("Spectrogram worker error for clip", item.clipId, err);
4316
+ }
4317
+ }
4318
+ for (const item of clipsNeedingDisplayOnly) {
4319
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4320
+ const cacheKey = clipCacheKeysRef.current.get(item.clipId);
4321
+ if (!cacheKey) continue;
4322
+ const clipCanvasInfo = spectrogramCanvasRegistryRef.current.get(item.clipId);
4323
+ if (!clipCanvasInfo || clipCanvasInfo.size === 0) continue;
4324
+ try {
4325
+ const clipPixelOffset = Math.floor(item.clipStartSample / samplesPerPixel);
4326
+ for (let ch = 0; ch < item.numChannels; ch++) {
4327
+ const channelInfo = clipCanvasInfo.get(ch);
4328
+ if (!channelInfo) continue;
4329
+ const { visibleIndices, remainingIndices } = getVisibleChunkRange(channelInfo.canvasWidths, clipPixelOffset);
4330
+ await renderChunkSubset(workerApi, cacheKey, channelInfo, visibleIndices, item, ch);
4331
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4332
+ const BATCH_SIZE = 4;
4333
+ for (let batchStart = 0; batchStart < remainingIndices.length; batchStart += BATCH_SIZE) {
4334
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4335
+ const batch = remainingIndices.slice(batchStart, batchStart + BATCH_SIZE);
4336
+ await new Promise((resolve) => {
4337
+ if (typeof requestIdleCallback === "function") {
4338
+ requestIdleCallback(() => resolve());
4339
+ } else {
4340
+ setTimeout(resolve, 0);
4341
+ }
4342
+ });
4343
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4344
+ await renderChunkSubset(workerApi, cacheKey, channelInfo, batch, item, ch);
4345
+ }
4346
+ }
4347
+ } catch (err) {
4348
+ console.warn("Spectrogram display re-render error for clip", item.clipId, err);
4349
+ }
4350
+ }
4351
+ };
4352
+ computeAsync().catch((err) => {
4353
+ console.error("[waveform-playlist] Spectrogram computation failed:", err);
4354
+ });
4355
+ }, [tracks, mono, spectrogramConfig, spectrogramColorMap, trackSpectrogramOverrides, waveHeight, samplesPerPixel, spectrogramCanvasVersion, controls, scrollContainerRef]);
4356
+ const setTrackRenderMode = useCallback((trackId, mode) => {
4357
+ setTrackSpectrogramOverrides((prev) => {
4358
+ const next = new Map(prev);
4359
+ const existing = next.get(trackId);
4360
+ next.set(trackId, { ...existing, renderMode: mode });
4361
+ return next;
4362
+ });
4363
+ }, []);
4364
+ const setTrackSpectrogramConfig = useCallback((trackId, config, colorMap) => {
4365
+ setTrackSpectrogramOverrides((prev) => {
4366
+ const next = new Map(prev);
4367
+ const existing = next.get(trackId);
4368
+ next.set(trackId, {
4369
+ renderMode: existing?.renderMode ?? "waveform",
4370
+ config,
4371
+ ...colorMap !== void 0 ? { colorMap } : { colorMap: existing?.colorMap }
4372
+ });
4373
+ return next;
4374
+ });
4375
+ }, []);
4376
+ const registerSpectrogramCanvases = useCallback((clipId, channelIndex, canvasIds, canvasWidths) => {
4377
+ const registry = spectrogramCanvasRegistryRef.current;
4378
+ if (!registry.has(clipId)) {
4379
+ registry.set(clipId, /* @__PURE__ */ new Map());
4380
+ }
4381
+ registry.get(clipId).set(channelIndex, { canvasIds, canvasWidths });
4382
+ setSpectrogramCanvasVersion((v) => v + 1);
4383
+ }, []);
4384
+ const unregisterSpectrogramCanvases = useCallback((clipId, channelIndex) => {
4385
+ const registry = spectrogramCanvasRegistryRef.current;
4386
+ const clipChannels = registry.get(clipId);
4387
+ if (clipChannels) {
4388
+ clipChannels.delete(channelIndex);
4389
+ if (clipChannels.size === 0) {
4390
+ registry.delete(clipId);
4391
+ }
4392
+ }
4393
+ }, []);
4394
+ const renderMenuItems = useCallback((props) => {
4395
+ return SpectrogramMenuItems({
4396
+ renderMode: props.renderMode,
4397
+ onRenderModeChange: props.onRenderModeChange,
4398
+ onOpenSettings: props.onOpenSettings,
4399
+ onClose: props.onClose
4400
+ });
4401
+ }, []);
4402
+ const value = useMemo(() => ({
4403
+ spectrogramDataMap,
4404
+ trackSpectrogramOverrides,
4405
+ spectrogramWorkerApi: spectrogramWorkerReady ? spectrogramWorkerRef.current : null,
4406
+ spectrogramConfig,
4407
+ spectrogramColorMap,
4408
+ setTrackRenderMode,
4409
+ setTrackSpectrogramConfig,
4410
+ registerSpectrogramCanvases,
4411
+ unregisterSpectrogramCanvases,
4412
+ renderMenuItems,
4413
+ SettingsModal: SpectrogramSettingsModal,
4414
+ getColorMap,
4415
+ getFrequencyScale
4416
+ }), [
4417
+ spectrogramDataMap,
4418
+ trackSpectrogramOverrides,
4419
+ spectrogramWorkerReady,
4420
+ spectrogramConfig,
4421
+ spectrogramColorMap,
4422
+ setTrackRenderMode,
4423
+ setTrackSpectrogramConfig,
4424
+ registerSpectrogramCanvases,
4425
+ unregisterSpectrogramCanvases,
4426
+ renderMenuItems
4427
+ ]);
4428
+ return /* @__PURE__ */ jsx3(SpectrogramIntegrationProvider, { value, children });
4429
+ };
4430
+ export {
4431
+ SpectrogramMenuItems,
4432
+ SpectrogramProvider,
4433
+ SpectrogramSettingsModal,
4434
+ computeSpectrogram,
4435
+ computeSpectrogramMono,
4436
+ createSpectrogramWorker,
4437
+ getColorMap,
4438
+ getFrequencyScale
4439
+ };
4440
+ //# sourceMappingURL=index.mjs.map