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