@waveform-playlist/spectrogram 7.1.1 → 7.1.3

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 CHANGED
@@ -3606,7 +3606,14 @@ var ModalButton = import_styled_components2.default.button`
3606
3606
  }
3607
3607
  `;
3608
3608
  var FFT_SIZES = [256, 512, 1024, 2048, 4096, 8192];
3609
- var WINDOW_FUNCTIONS = ["hann", "hamming", "blackman", "blackman-harris", "bartlett", "rectangular"];
3609
+ var WINDOW_FUNCTIONS = [
3610
+ "hann",
3611
+ "hamming",
3612
+ "blackman",
3613
+ "blackman-harris",
3614
+ "bartlett",
3615
+ "rectangular"
3616
+ ];
3610
3617
  var FREQ_SCALES = ["linear", "logarithmic", "mel", "bark", "erb"];
3611
3618
  var COLOR_MAPS = ["viridis", "magma", "inferno", "grayscale", "igray", "roseus"];
3612
3619
  var SpectrogramSettingsModal = ({
@@ -3628,7 +3635,9 @@ var SpectrogramSettingsModal = ({
3628
3635
  const [gainDb, setGainDb] = (0, import_react.useState)(config.gainDb ?? 20);
3629
3636
  const [rangeDb, setRangeDb] = (0, import_react.useState)(config.rangeDb ?? 80);
3630
3637
  const [zeroPadding, setZeroPadding] = (0, import_react.useState)(config.zeroPaddingFactor ?? 2);
3631
- const [hopSize, setHopSize] = (0, import_react.useState)(config.hopSize ?? Math.floor((config.fftSize ?? 2048) / 4));
3638
+ const [hopSize, setHopSize] = (0, import_react.useState)(
3639
+ config.hopSize ?? Math.floor((config.fftSize ?? 2048) / 4)
3640
+ );
3632
3641
  const [showLabels, setShowLabels] = (0, import_react.useState)(config.labels ?? false);
3633
3642
  (0, import_react.useEffect)(() => {
3634
3643
  setFftSize(config.fftSize ?? 2048);
@@ -3682,11 +3691,18 @@ var SpectrogramSettingsModal = ({
3682
3691
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(FormGrid, { children: [
3683
3692
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Field, { children: [
3684
3693
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Label, { children: "FFT Size" }),
3685
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Select, { value: fftSize, onChange: (e) => {
3686
- const v = Number(e.target.value);
3687
- setFftSize(v);
3688
- setHopSize(Math.floor(v / 4));
3689
- }, children: FFT_SIZES.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: s, children: s }, s)) })
3694
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
3695
+ Select,
3696
+ {
3697
+ value: fftSize,
3698
+ onChange: (e) => {
3699
+ const v = Number(e.target.value);
3700
+ setFftSize(v);
3701
+ setHopSize(Math.floor(v / 4));
3702
+ },
3703
+ children: FFT_SIZES.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: s, children: s }, s))
3704
+ }
3705
+ )
3690
3706
  ] }),
3691
3707
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Field, { children: [
3692
3708
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Label, { children: "Hop Size" }),
@@ -3707,11 +3723,25 @@ var SpectrogramSettingsModal = ({
3707
3723
  ] }),
3708
3724
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Field, { children: [
3709
3725
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Label, { children: "Frequency Scale" }),
3710
- /* @__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)) })
3726
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
3727
+ Select,
3728
+ {
3729
+ value: freqScale,
3730
+ onChange: (e) => setFreqScale(e.target.value),
3731
+ children: FREQ_SCALES.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: s, children: s }, s))
3732
+ }
3733
+ )
3711
3734
  ] }),
3712
3735
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Field, { children: [
3713
3736
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Label, { children: "Color Map" }),
3714
- /* @__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)) })
3737
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
3738
+ Select,
3739
+ {
3740
+ value: localColorMap,
3741
+ onChange: (e) => setLocalColorMap(e.target.value),
3742
+ children: COLOR_MAPS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: c, children: c }, c))
3743
+ }
3744
+ )
3715
3745
  ] }),
3716
3746
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Field, { children: [
3717
3747
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Label, { children: "Min Frequency (Hz)" }),
@@ -3770,7 +3800,14 @@ var SpectrogramSettingsModal = ({
3770
3800
  )
3771
3801
  ] }),
3772
3802
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Field, { $span: true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(CheckboxLabel, { children: [
3773
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: showLabels, onChange: (e) => setShowLabels(e.target.checked) }),
3803
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
3804
+ "input",
3805
+ {
3806
+ type: "checkbox",
3807
+ checked: showLabels,
3808
+ onChange: (e) => setShowLabels(e.target.checked)
3809
+ }
3810
+ ),
3774
3811
  "Show Frequency Labels"
3775
3812
  ] }) })
3776
3813
  ] }),
@@ -3893,10 +3930,7 @@ function createSpectrogramWorker(worker) {
3893
3930
  });
3894
3931
  },
3895
3932
  registerCanvas(canvasId, canvas) {
3896
- worker.postMessage(
3897
- { type: "register-canvas", canvasId, canvas },
3898
- [canvas]
3899
- );
3933
+ worker.postMessage({ type: "register-canvas", canvasId, canvas }, [canvas]);
3900
3934
  },
3901
3935
  unregisterCanvas(canvasId) {
3902
3936
  worker.postMessage({ type: "unregister-canvas", canvasId });
@@ -3950,25 +3984,29 @@ function createSpectrogramWorker(worker) {
3950
3984
 
3951
3985
  // src/SpectrogramProvider.tsx
3952
3986
  var import_react2 = require("react");
3987
+ var import_core = require("@waveform-playlist/core");
3953
3988
  var import_browser = require("@waveform-playlist/browser");
3954
3989
  var import_browser2 = require("@waveform-playlist/browser");
3955
3990
  var import_jsx_runtime3 = require("react/jsx-runtime");
3956
3991
  var import_meta = {};
3992
+ function extractChunkNumber(canvasId) {
3993
+ const match = canvasId.match(/chunk(\d+)$/);
3994
+ if (!match) {
3995
+ console.warn(`[spectrogram] Unexpected canvas ID format: ${canvasId}`);
3996
+ return 0;
3997
+ }
3998
+ return parseInt(match[1], 10);
3999
+ }
3957
4000
  var SpectrogramProvider = ({
3958
4001
  config: spectrogramConfig,
3959
4002
  colorMap: spectrogramColorMap,
3960
4003
  children
3961
4004
  }) => {
3962
- const {
3963
- tracks,
3964
- waveHeight,
3965
- samplesPerPixel,
3966
- isReady,
3967
- mono,
3968
- controls
3969
- } = (0, import_browser2.usePlaylistData)();
4005
+ const { tracks, waveHeight, samplesPerPixel, isReady, mono, controls } = (0, import_browser2.usePlaylistData)();
3970
4006
  const { scrollContainerRef } = (0, import_browser2.usePlaylistControls)();
3971
- const [spectrogramDataMap, setSpectrogramDataMap] = (0, import_react2.useState)(/* @__PURE__ */ new Map());
4007
+ const [spectrogramDataMap, setSpectrogramDataMap] = (0, import_react2.useState)(
4008
+ /* @__PURE__ */ new Map()
4009
+ );
3972
4010
  const [trackSpectrogramOverrides, setTrackSpectrogramOverrides] = (0, import_react2.useState)(/* @__PURE__ */ new Map());
3973
4011
  const spectrogramCanvasRegistryRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
3974
4012
  const [spectrogramCanvasVersion, setSpectrogramCanvasVersion] = (0, import_react2.useState)(0);
@@ -4117,9 +4155,7 @@ var SpectrogramProvider = ({
4117
4155
  if (mode === "waveform") return;
4118
4156
  const trackConfigChanged = configChanged && currentKeys.get(track.id) !== prevKeys.get(track.id);
4119
4157
  const trackFFTChanged = fftKeyChanged && currentFFTKeys.get(track.id) !== prevFFTKeys.get(track.id);
4120
- const hasRegisteredCanvases = canvasVersionChanged && track.clips.some(
4121
- (clip) => spectrogramCanvasRegistryRef.current.has(clip.id)
4122
- );
4158
+ const hasRegisteredCanvases = canvasVersionChanged && track.clips.some((clip) => spectrogramCanvasRegistryRef.current.has(clip.id));
4123
4159
  if (!trackConfigChanged && !hasRegisteredCanvases) return;
4124
4160
  const cfg = trackSpectrogramOverrides.get(track.id)?.config ?? track.spectrogramConfig ?? spectrogramConfig ?? {};
4125
4161
  const cm = trackSpectrogramOverrides.get(track.id)?.colorMap ?? track.spectrogramColorMap ?? spectrogramColorMap ?? "viridis";
@@ -4177,7 +4213,13 @@ var SpectrogramProvider = ({
4177
4213
  } else {
4178
4214
  for (let ch = 0; ch < clip.audioBuffer.numberOfChannels; ch++) {
4179
4215
  channelSpectrograms.push(
4180
- computeSpectrogram(clip.audioBuffer, item.config, item.offsetSamples, item.durationSamples, ch)
4216
+ computeSpectrogram(
4217
+ clip.audioBuffer,
4218
+ item.config,
4219
+ item.offsetSamples,
4220
+ item.durationSamples,
4221
+ ch
4222
+ )
4181
4223
  );
4182
4224
  }
4183
4225
  }
@@ -4190,26 +4232,25 @@ var SpectrogramProvider = ({
4190
4232
  }
4191
4233
  return;
4192
4234
  }
4193
- const getVisibleChunkRange = (canvasWidths, clipPixelOffset = 0) => {
4235
+ const getVisibleChunkRange = (channelInfo, clipPixelOffset = 0) => {
4194
4236
  const container = scrollContainerRef.current;
4195
4237
  if (!container) {
4196
- return { visibleIndices: canvasWidths.map((_, i) => i), remainingIndices: [] };
4238
+ return { visibleIndices: channelInfo.canvasWidths.map((_, i) => i), remainingIndices: [] };
4197
4239
  }
4198
4240
  const scrollLeft = container.scrollLeft;
4199
4241
  const viewportWidth = container.clientWidth;
4200
4242
  const controlWidth = controls.show ? controls.width : 0;
4201
4243
  const visibleIndices = [];
4202
4244
  const remainingIndices = [];
4203
- let offset = 0;
4204
- for (let i = 0; i < canvasWidths.length; i++) {
4205
- const chunkLeft = offset + controlWidth + clipPixelOffset;
4206
- const chunkRight = chunkLeft + canvasWidths[i];
4245
+ for (let i = 0; i < channelInfo.canvasWidths.length; i++) {
4246
+ const chunkNumber = extractChunkNumber(channelInfo.canvasIds[i]);
4247
+ const chunkLeft = chunkNumber * import_core.MAX_CANVAS_WIDTH + controlWidth + clipPixelOffset;
4248
+ const chunkRight = chunkLeft + channelInfo.canvasWidths[i];
4207
4249
  if (chunkRight > scrollLeft && chunkLeft < scrollLeft + viewportWidth) {
4208
4250
  visibleIndices.push(i);
4209
4251
  } else {
4210
4252
  remainingIndices.push(i);
4211
4253
  }
4212
- offset += canvasWidths[i];
4213
4254
  }
4214
4255
  return { visibleIndices, remainingIndices };
4215
4256
  };
@@ -4219,11 +4260,8 @@ var SpectrogramProvider = ({
4219
4260
  const canvasWidths = indices.map((i) => channelInfo.canvasWidths[i]);
4220
4261
  const globalPixelOffsets = [];
4221
4262
  for (const idx of indices) {
4222
- let offset = 0;
4223
- for (let j = 0; j < idx; j++) {
4224
- offset += channelInfo.canvasWidths[j];
4225
- }
4226
- globalPixelOffsets.push(offset);
4263
+ const chunkNumber = extractChunkNumber(channelInfo.canvasIds[idx]);
4264
+ globalPixelOffsets.push(chunkNumber * import_core.MAX_CANVAS_WIDTH);
4227
4265
  }
4228
4266
  const colorLUT = getColorMap(item.colorMap);
4229
4267
  await api.renderChunks({
@@ -4246,6 +4284,25 @@ var SpectrogramProvider = ({
4246
4284
  const computeAsync = async () => {
4247
4285
  const abortToken = { aborted: false };
4248
4286
  backgroundRenderAbortRef.current = abortToken;
4287
+ const renderBackgroundBatches = async (channelRanges, cacheKey, item) => {
4288
+ const BATCH_SIZE = 4;
4289
+ for (const { ch, channelInfo, remainingIndices } of channelRanges) {
4290
+ for (let batchStart = 0; batchStart < remainingIndices.length; batchStart += BATCH_SIZE) {
4291
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return true;
4292
+ const batch = remainingIndices.slice(batchStart, batchStart + BATCH_SIZE);
4293
+ await new Promise((resolve) => {
4294
+ if (typeof requestIdleCallback === "function") {
4295
+ requestIdleCallback(() => resolve());
4296
+ } else {
4297
+ setTimeout(resolve, 0);
4298
+ }
4299
+ });
4300
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return true;
4301
+ await renderChunkSubset(workerApi, cacheKey, channelInfo, batch, item, ch);
4302
+ }
4303
+ }
4304
+ return false;
4305
+ };
4249
4306
  for (const item of clipsNeedingFFT) {
4250
4307
  if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4251
4308
  try {
@@ -4275,7 +4332,10 @@ var SpectrogramProvider = ({
4275
4332
  item.offsetSamples + Math.ceil(localEndPx * samplesPerPixel)
4276
4333
  );
4277
4334
  const paddedStart = Math.max(item.offsetSamples, visStartSample - windowSize);
4278
- const paddedEnd = Math.min(item.offsetSamples + item.durationSamples, visEndSample + windowSize);
4335
+ const paddedEnd = Math.min(
4336
+ item.offsetSamples + item.durationSamples,
4337
+ visEndSample + windowSize
4338
+ );
4279
4339
  if (paddedEnd - paddedStart < item.durationSamples * 0.8) {
4280
4340
  visibleRange = { start: paddedStart, end: paddedEnd };
4281
4341
  }
@@ -4297,8 +4357,15 @@ var SpectrogramProvider = ({
4297
4357
  for (let ch = 0; ch < numChannels; ch++) {
4298
4358
  const channelInfo = clipCanvasInfo.get(ch);
4299
4359
  if (!channelInfo) continue;
4300
- const { visibleIndices } = getVisibleChunkRange(channelInfo.canvasWidths, clipPixelOffset);
4301
- await renderChunkSubset(workerApi, visibleCacheKey, channelInfo, visibleIndices, item, ch);
4360
+ const { visibleIndices } = getVisibleChunkRange(channelInfo, clipPixelOffset);
4361
+ await renderChunkSubset(
4362
+ workerApi,
4363
+ visibleCacheKey,
4364
+ channelInfo,
4365
+ visibleIndices,
4366
+ item,
4367
+ ch
4368
+ );
4302
4369
  }
4303
4370
  if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4304
4371
  }
@@ -4313,27 +4380,23 @@ var SpectrogramProvider = ({
4313
4380
  });
4314
4381
  if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4315
4382
  clipCacheKeysRef.current.set(item.clipId, cacheKey);
4383
+ const channelRanges = [];
4316
4384
  for (let ch = 0; ch < numChannels; ch++) {
4317
4385
  const channelInfo = clipCanvasInfo.get(ch);
4318
4386
  if (!channelInfo) continue;
4319
- const { visibleIndices, remainingIndices } = getVisibleChunkRange(channelInfo.canvasWidths, clipPixelOffset);
4320
- await renderChunkSubset(workerApi, cacheKey, channelInfo, visibleIndices, item, ch);
4321
- if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4322
- const BATCH_SIZE = 4;
4323
- for (let batchStart = 0; batchStart < remainingIndices.length; batchStart += BATCH_SIZE) {
4324
- if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4325
- const batch = remainingIndices.slice(batchStart, batchStart + BATCH_SIZE);
4326
- await new Promise((resolve) => {
4327
- if (typeof requestIdleCallback === "function") {
4328
- requestIdleCallback(() => resolve());
4329
- } else {
4330
- setTimeout(resolve, 0);
4331
- }
4332
- });
4333
- if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4334
- await renderChunkSubset(workerApi, cacheKey, channelInfo, batch, item, ch);
4335
- }
4387
+ const range = getVisibleChunkRange(channelInfo, clipPixelOffset);
4388
+ channelRanges.push({ ch, channelInfo, ...range });
4389
+ await renderChunkSubset(
4390
+ workerApi,
4391
+ cacheKey,
4392
+ channelInfo,
4393
+ range.visibleIndices,
4394
+ item,
4395
+ ch
4396
+ );
4336
4397
  }
4398
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4399
+ if (await renderBackgroundBatches(channelRanges, cacheKey, item)) return;
4337
4400
  } else {
4338
4401
  const spectrograms = await workerApi.compute({
4339
4402
  channelDataArrays: item.channelDataArrays,
@@ -4362,27 +4425,19 @@ var SpectrogramProvider = ({
4362
4425
  if (!clipCanvasInfo || clipCanvasInfo.size === 0) continue;
4363
4426
  try {
4364
4427
  const clipPixelOffset = Math.floor(item.clipStartSample / samplesPerPixel);
4428
+ const channelRanges = [];
4365
4429
  for (let ch = 0; ch < item.numChannels; ch++) {
4366
4430
  const channelInfo = clipCanvasInfo.get(ch);
4367
4431
  if (!channelInfo) continue;
4368
- const { visibleIndices, remainingIndices } = getVisibleChunkRange(channelInfo.canvasWidths, clipPixelOffset);
4432
+ const { visibleIndices, remainingIndices } = getVisibleChunkRange(
4433
+ channelInfo,
4434
+ clipPixelOffset
4435
+ );
4436
+ channelRanges.push({ ch, channelInfo, remainingIndices });
4369
4437
  await renderChunkSubset(workerApi, cacheKey, channelInfo, visibleIndices, item, ch);
4370
- if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4371
- const BATCH_SIZE = 4;
4372
- for (let batchStart = 0; batchStart < remainingIndices.length; batchStart += BATCH_SIZE) {
4373
- if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4374
- const batch = remainingIndices.slice(batchStart, batchStart + BATCH_SIZE);
4375
- await new Promise((resolve) => {
4376
- if (typeof requestIdleCallback === "function") {
4377
- requestIdleCallback(() => resolve());
4378
- } else {
4379
- setTimeout(resolve, 0);
4380
- }
4381
- });
4382
- if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4383
- await renderChunkSubset(workerApi, cacheKey, channelInfo, batch, item, ch);
4384
- }
4385
4438
  }
4439
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4440
+ if (await renderBackgroundBatches(channelRanges, cacheKey, item)) return;
4386
4441
  } catch (err) {
4387
4442
  console.warn("Spectrogram display re-render error for clip", item.clipId, err);
4388
4443
  }
@@ -4391,7 +4446,18 @@ var SpectrogramProvider = ({
4391
4446
  computeAsync().catch((err) => {
4392
4447
  console.error("[waveform-playlist] Spectrogram computation failed:", err);
4393
4448
  });
4394
- }, [tracks, mono, spectrogramConfig, spectrogramColorMap, trackSpectrogramOverrides, waveHeight, samplesPerPixel, spectrogramCanvasVersion, controls, scrollContainerRef]);
4449
+ }, [
4450
+ tracks,
4451
+ mono,
4452
+ spectrogramConfig,
4453
+ spectrogramColorMap,
4454
+ trackSpectrogramOverrides,
4455
+ waveHeight,
4456
+ samplesPerPixel,
4457
+ spectrogramCanvasVersion,
4458
+ controls,
4459
+ scrollContainerRef
4460
+ ]);
4395
4461
  const setTrackRenderMode = (0, import_react2.useCallback)((trackId, mode) => {
4396
4462
  setTrackSpectrogramOverrides((prev) => {
4397
4463
  const next = new Map(prev);
@@ -4400,26 +4466,32 @@ var SpectrogramProvider = ({
4400
4466
  return next;
4401
4467
  });
4402
4468
  }, []);
4403
- const setTrackSpectrogramConfig = (0, import_react2.useCallback)((trackId, config, colorMap) => {
4404
- setTrackSpectrogramOverrides((prev) => {
4405
- const next = new Map(prev);
4406
- const existing = next.get(trackId);
4407
- next.set(trackId, {
4408
- renderMode: existing?.renderMode ?? "waveform",
4409
- config,
4410
- ...colorMap !== void 0 ? { colorMap } : { colorMap: existing?.colorMap }
4469
+ const setTrackSpectrogramConfig = (0, import_react2.useCallback)(
4470
+ (trackId, config, colorMap) => {
4471
+ setTrackSpectrogramOverrides((prev) => {
4472
+ const next = new Map(prev);
4473
+ const existing = next.get(trackId);
4474
+ next.set(trackId, {
4475
+ renderMode: existing?.renderMode ?? "waveform",
4476
+ config,
4477
+ ...colorMap !== void 0 ? { colorMap } : { colorMap: existing?.colorMap }
4478
+ });
4479
+ return next;
4411
4480
  });
4412
- return next;
4413
- });
4414
- }, []);
4415
- const registerSpectrogramCanvases = (0, import_react2.useCallback)((clipId, channelIndex, canvasIds, canvasWidths) => {
4416
- const registry = spectrogramCanvasRegistryRef.current;
4417
- if (!registry.has(clipId)) {
4418
- registry.set(clipId, /* @__PURE__ */ new Map());
4419
- }
4420
- registry.get(clipId).set(channelIndex, { canvasIds, canvasWidths });
4421
- setSpectrogramCanvasVersion((v) => v + 1);
4422
- }, []);
4481
+ },
4482
+ []
4483
+ );
4484
+ const registerSpectrogramCanvases = (0, import_react2.useCallback)(
4485
+ (clipId, channelIndex, canvasIds, canvasWidths) => {
4486
+ const registry = spectrogramCanvasRegistryRef.current;
4487
+ if (!registry.has(clipId)) {
4488
+ registry.set(clipId, /* @__PURE__ */ new Map());
4489
+ }
4490
+ registry.get(clipId).set(channelIndex, { canvasIds, canvasWidths });
4491
+ setSpectrogramCanvasVersion((v) => v + 1);
4492
+ },
4493
+ []
4494
+ );
4423
4495
  const unregisterSpectrogramCanvases = (0, import_react2.useCallback)((clipId, channelIndex) => {
4424
4496
  const registry = spectrogramCanvasRegistryRef.current;
4425
4497
  const clipChannels = registry.get(clipId);
@@ -4430,40 +4502,46 @@ var SpectrogramProvider = ({
4430
4502
  }
4431
4503
  }
4432
4504
  }, []);
4433
- const renderMenuItems = (0, import_react2.useCallback)((props) => {
4434
- return SpectrogramMenuItems({
4435
- renderMode: props.renderMode,
4436
- onRenderModeChange: props.onRenderModeChange,
4437
- onOpenSettings: props.onOpenSettings,
4438
- onClose: props.onClose
4439
- });
4440
- }, []);
4441
- const value = (0, import_react2.useMemo)(() => ({
4442
- spectrogramDataMap,
4443
- trackSpectrogramOverrides,
4444
- spectrogramWorkerApi: spectrogramWorkerReady ? spectrogramWorkerRef.current : null,
4445
- spectrogramConfig,
4446
- spectrogramColorMap,
4447
- setTrackRenderMode,
4448
- setTrackSpectrogramConfig,
4449
- registerSpectrogramCanvases,
4450
- unregisterSpectrogramCanvases,
4451
- renderMenuItems,
4452
- SettingsModal: SpectrogramSettingsModal,
4453
- getColorMap,
4454
- getFrequencyScale
4455
- }), [
4456
- spectrogramDataMap,
4457
- trackSpectrogramOverrides,
4458
- spectrogramWorkerReady,
4459
- spectrogramConfig,
4460
- spectrogramColorMap,
4461
- setTrackRenderMode,
4462
- setTrackSpectrogramConfig,
4463
- registerSpectrogramCanvases,
4464
- unregisterSpectrogramCanvases,
4465
- renderMenuItems
4466
- ]);
4505
+ const renderMenuItems = (0, import_react2.useCallback)(
4506
+ (props) => {
4507
+ return SpectrogramMenuItems({
4508
+ renderMode: props.renderMode,
4509
+ onRenderModeChange: props.onRenderModeChange,
4510
+ onOpenSettings: props.onOpenSettings,
4511
+ onClose: props.onClose
4512
+ });
4513
+ },
4514
+ []
4515
+ );
4516
+ const value = (0, import_react2.useMemo)(
4517
+ () => ({
4518
+ spectrogramDataMap,
4519
+ trackSpectrogramOverrides,
4520
+ spectrogramWorkerApi: spectrogramWorkerReady ? spectrogramWorkerRef.current : null,
4521
+ spectrogramConfig,
4522
+ spectrogramColorMap,
4523
+ setTrackRenderMode,
4524
+ setTrackSpectrogramConfig,
4525
+ registerSpectrogramCanvases,
4526
+ unregisterSpectrogramCanvases,
4527
+ renderMenuItems,
4528
+ SettingsModal: SpectrogramSettingsModal,
4529
+ getColorMap,
4530
+ getFrequencyScale
4531
+ }),
4532
+ [
4533
+ spectrogramDataMap,
4534
+ trackSpectrogramOverrides,
4535
+ spectrogramWorkerReady,
4536
+ spectrogramConfig,
4537
+ spectrogramColorMap,
4538
+ setTrackRenderMode,
4539
+ setTrackSpectrogramConfig,
4540
+ registerSpectrogramCanvases,
4541
+ unregisterSpectrogramCanvases,
4542
+ renderMenuItems
4543
+ ]
4544
+ );
4467
4545
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_browser.SpectrogramIntegrationProvider, { value, children });
4468
4546
  };
4469
4547
  // Annotate the CommonJS export names for ESM import in node: