@waveform-playlist/spectrogram 7.1.1 → 7.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -3907,9 +3907,18 @@ function createSpectrogramWorker(worker) {
3907
3907
 
3908
3908
  // src/SpectrogramProvider.tsx
3909
3909
  import { useState as useState2, useEffect as useEffect2, useRef as useRef2, useCallback, useMemo } from "react";
3910
+ import { MAX_CANVAS_WIDTH } from "@waveform-playlist/core";
3910
3911
  import { SpectrogramIntegrationProvider } from "@waveform-playlist/browser";
3911
3912
  import { usePlaylistData, usePlaylistControls } from "@waveform-playlist/browser";
3912
3913
  import { jsx as jsx3 } from "react/jsx-runtime";
3914
+ function extractChunkNumber(canvasId) {
3915
+ const match = canvasId.match(/chunk(\d+)$/);
3916
+ if (!match) {
3917
+ console.warn(`[spectrogram] Unexpected canvas ID format: ${canvasId}`);
3918
+ return 0;
3919
+ }
3920
+ return parseInt(match[1], 10);
3921
+ }
3913
3922
  var SpectrogramProvider = ({
3914
3923
  config: spectrogramConfig,
3915
3924
  colorMap: spectrogramColorMap,
@@ -4146,26 +4155,25 @@ var SpectrogramProvider = ({
4146
4155
  }
4147
4156
  return;
4148
4157
  }
4149
- const getVisibleChunkRange = (canvasWidths, clipPixelOffset = 0) => {
4158
+ const getVisibleChunkRange = (channelInfo, clipPixelOffset = 0) => {
4150
4159
  const container = scrollContainerRef.current;
4151
4160
  if (!container) {
4152
- return { visibleIndices: canvasWidths.map((_, i) => i), remainingIndices: [] };
4161
+ return { visibleIndices: channelInfo.canvasWidths.map((_, i) => i), remainingIndices: [] };
4153
4162
  }
4154
4163
  const scrollLeft = container.scrollLeft;
4155
4164
  const viewportWidth = container.clientWidth;
4156
4165
  const controlWidth = controls.show ? controls.width : 0;
4157
4166
  const visibleIndices = [];
4158
4167
  const remainingIndices = [];
4159
- let offset = 0;
4160
- for (let i = 0; i < canvasWidths.length; i++) {
4161
- const chunkLeft = offset + controlWidth + clipPixelOffset;
4162
- const chunkRight = chunkLeft + canvasWidths[i];
4168
+ for (let i = 0; i < channelInfo.canvasWidths.length; i++) {
4169
+ const chunkNumber = extractChunkNumber(channelInfo.canvasIds[i]);
4170
+ const chunkLeft = chunkNumber * MAX_CANVAS_WIDTH + controlWidth + clipPixelOffset;
4171
+ const chunkRight = chunkLeft + channelInfo.canvasWidths[i];
4163
4172
  if (chunkRight > scrollLeft && chunkLeft < scrollLeft + viewportWidth) {
4164
4173
  visibleIndices.push(i);
4165
4174
  } else {
4166
4175
  remainingIndices.push(i);
4167
4176
  }
4168
- offset += canvasWidths[i];
4169
4177
  }
4170
4178
  return { visibleIndices, remainingIndices };
4171
4179
  };
@@ -4175,11 +4183,8 @@ var SpectrogramProvider = ({
4175
4183
  const canvasWidths = indices.map((i) => channelInfo.canvasWidths[i]);
4176
4184
  const globalPixelOffsets = [];
4177
4185
  for (const idx of indices) {
4178
- let offset = 0;
4179
- for (let j = 0; j < idx; j++) {
4180
- offset += channelInfo.canvasWidths[j];
4181
- }
4182
- globalPixelOffsets.push(offset);
4186
+ const chunkNumber = extractChunkNumber(channelInfo.canvasIds[idx]);
4187
+ globalPixelOffsets.push(chunkNumber * MAX_CANVAS_WIDTH);
4183
4188
  }
4184
4189
  const colorLUT = getColorMap(item.colorMap);
4185
4190
  await api.renderChunks({
@@ -4202,6 +4207,25 @@ var SpectrogramProvider = ({
4202
4207
  const computeAsync = async () => {
4203
4208
  const abortToken = { aborted: false };
4204
4209
  backgroundRenderAbortRef.current = abortToken;
4210
+ const renderBackgroundBatches = async (channelRanges, cacheKey, item) => {
4211
+ const BATCH_SIZE = 4;
4212
+ for (const { ch, channelInfo, remainingIndices } of channelRanges) {
4213
+ for (let batchStart = 0; batchStart < remainingIndices.length; batchStart += BATCH_SIZE) {
4214
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return true;
4215
+ const batch = remainingIndices.slice(batchStart, batchStart + BATCH_SIZE);
4216
+ await new Promise((resolve) => {
4217
+ if (typeof requestIdleCallback === "function") {
4218
+ requestIdleCallback(() => resolve());
4219
+ } else {
4220
+ setTimeout(resolve, 0);
4221
+ }
4222
+ });
4223
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return true;
4224
+ await renderChunkSubset(workerApi, cacheKey, channelInfo, batch, item, ch);
4225
+ }
4226
+ }
4227
+ return false;
4228
+ };
4205
4229
  for (const item of clipsNeedingFFT) {
4206
4230
  if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4207
4231
  try {
@@ -4253,7 +4277,7 @@ var SpectrogramProvider = ({
4253
4277
  for (let ch = 0; ch < numChannels; ch++) {
4254
4278
  const channelInfo = clipCanvasInfo.get(ch);
4255
4279
  if (!channelInfo) continue;
4256
- const { visibleIndices } = getVisibleChunkRange(channelInfo.canvasWidths, clipPixelOffset);
4280
+ const { visibleIndices } = getVisibleChunkRange(channelInfo, clipPixelOffset);
4257
4281
  await renderChunkSubset(workerApi, visibleCacheKey, channelInfo, visibleIndices, item, ch);
4258
4282
  }
4259
4283
  if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
@@ -4269,27 +4293,16 @@ var SpectrogramProvider = ({
4269
4293
  });
4270
4294
  if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4271
4295
  clipCacheKeysRef.current.set(item.clipId, cacheKey);
4296
+ const channelRanges = [];
4272
4297
  for (let ch = 0; ch < numChannels; ch++) {
4273
4298
  const channelInfo = clipCanvasInfo.get(ch);
4274
4299
  if (!channelInfo) continue;
4275
- const { visibleIndices, remainingIndices } = getVisibleChunkRange(channelInfo.canvasWidths, clipPixelOffset);
4276
- await renderChunkSubset(workerApi, cacheKey, channelInfo, visibleIndices, item, ch);
4277
- if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4278
- const BATCH_SIZE = 4;
4279
- for (let batchStart = 0; batchStart < remainingIndices.length; batchStart += BATCH_SIZE) {
4280
- if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4281
- const batch = remainingIndices.slice(batchStart, batchStart + BATCH_SIZE);
4282
- await new Promise((resolve) => {
4283
- if (typeof requestIdleCallback === "function") {
4284
- requestIdleCallback(() => resolve());
4285
- } else {
4286
- setTimeout(resolve, 0);
4287
- }
4288
- });
4289
- if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4290
- await renderChunkSubset(workerApi, cacheKey, channelInfo, batch, item, ch);
4291
- }
4300
+ const range = getVisibleChunkRange(channelInfo, clipPixelOffset);
4301
+ channelRanges.push({ ch, channelInfo, ...range });
4302
+ await renderChunkSubset(workerApi, cacheKey, channelInfo, range.visibleIndices, item, ch);
4292
4303
  }
4304
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4305
+ if (await renderBackgroundBatches(channelRanges, cacheKey, item)) return;
4293
4306
  } else {
4294
4307
  const spectrograms = await workerApi.compute({
4295
4308
  channelDataArrays: item.channelDataArrays,
@@ -4318,27 +4331,16 @@ var SpectrogramProvider = ({
4318
4331
  if (!clipCanvasInfo || clipCanvasInfo.size === 0) continue;
4319
4332
  try {
4320
4333
  const clipPixelOffset = Math.floor(item.clipStartSample / samplesPerPixel);
4334
+ const channelRanges = [];
4321
4335
  for (let ch = 0; ch < item.numChannels; ch++) {
4322
4336
  const channelInfo = clipCanvasInfo.get(ch);
4323
4337
  if (!channelInfo) continue;
4324
- const { visibleIndices, remainingIndices } = getVisibleChunkRange(channelInfo.canvasWidths, clipPixelOffset);
4338
+ const { visibleIndices, remainingIndices } = getVisibleChunkRange(channelInfo, clipPixelOffset);
4339
+ channelRanges.push({ ch, channelInfo, remainingIndices });
4325
4340
  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
4341
  }
4342
+ if (spectrogramGenerationRef.current !== generation || abortToken.aborted) return;
4343
+ if (await renderBackgroundBatches(channelRanges, cacheKey, item)) return;
4342
4344
  } catch (err) {
4343
4345
  console.warn("Spectrogram display re-render error for clip", item.clipId, err);
4344
4346
  }