@waveform-playlist/ui-components 9.4.1 → 9.5.1

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.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import react__default, { FunctionComponent, ReactNode, Dispatch, SetStateAction } from 'react';
3
- import { Peaks, Bits, Fade, FadeType, MidiNoteData, SpectrogramData, RenderMode } from '@waveform-playlist/core';
3
+ import { Peaks, Bits, Fade, FadeType, MidiNoteData, RenderMode } from '@waveform-playlist/core';
4
4
  import * as styled_components_dist_utils_hoist from 'styled-components/dist/utils/hoist';
5
5
  import * as styled_components from 'styled-components';
6
6
  import { DefaultTheme } from 'styled-components';
@@ -476,8 +476,6 @@ interface SpectrogramChannelProps {
476
476
  index: number;
477
477
  /** Audio channel index for canvas ID construction. Defaults to `index` when omitted. */
478
478
  channelIndex?: number;
479
- /** Computed spectrogram data (not needed when workerApi is provided) */
480
- data?: SpectrogramData;
481
479
  /** Width in CSS pixels */
482
480
  length: number;
483
481
  /** Height in CSS pixels */
@@ -486,18 +484,10 @@ interface SpectrogramChannelProps {
486
484
  devicePixelRatio?: number;
487
485
  /** Samples per pixel at current zoom level */
488
486
  samplesPerPixel: number;
489
- /** 256-entry RGB LUT (768 bytes) from getColorMap() */
490
- colorLUT?: Uint8Array;
491
- /** Frequency scale function: (freqHz, minF, maxF) => [0,1] */
492
- frequencyScaleFn?: (f: number, minF: number, maxF: number) => number;
493
- /** Min frequency in Hz */
494
- minFrequency?: number;
495
- /** Max frequency in Hz (defaults to sampleRate/2) */
496
- maxFrequency?: number;
497
- /** Worker API for transferring canvas ownership. When provided, rendering is done in the worker. */
498
- workerApi?: SpectrogramWorkerCanvasApi;
487
+ /** Worker API for transferring canvas ownership. Rendering is done in the worker. */
488
+ workerApi: SpectrogramWorkerCanvasApi;
499
489
  /** Clip ID used to construct unique canvas IDs for worker registration */
500
- clipId?: string;
490
+ clipId: string;
501
491
  /** Callback when canvases are registered with the worker, providing canvas IDs and widths */
502
492
  onCanvasesReady?: (canvasIds: string[], canvasWidths: number[]) => void;
503
493
  }
@@ -514,18 +504,8 @@ interface SmartChannelProps {
514
504
  transparentBackground?: boolean;
515
505
  /** Render mode: waveform, spectrogram, or both */
516
506
  renderMode?: RenderMode;
517
- /** Spectrogram data for this channel */
518
- spectrogramData?: SpectrogramData;
519
- /** 256-entry RGB LUT from getColorMap() */
520
- spectrogramColorLUT?: Uint8Array;
521
507
  /** Samples per pixel at current zoom level */
522
508
  samplesPerPixel?: number;
523
- /** Frequency scale function */
524
- spectrogramFrequencyScaleFn?: (f: number, minF: number, maxF: number) => number;
525
- /** Min frequency in Hz */
526
- spectrogramMinFrequency?: number;
527
- /** Max frequency in Hz */
528
- spectrogramMaxFrequency?: number;
529
509
  /** Worker API for OffscreenCanvas transfer */
530
510
  spectrogramWorkerApi?: SpectrogramWorkerCanvasApi;
531
511
  /** Clip ID for worker canvas registration */
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import react__default, { FunctionComponent, ReactNode, Dispatch, SetStateAction } from 'react';
3
- import { Peaks, Bits, Fade, FadeType, MidiNoteData, SpectrogramData, RenderMode } from '@waveform-playlist/core';
3
+ import { Peaks, Bits, Fade, FadeType, MidiNoteData, RenderMode } from '@waveform-playlist/core';
4
4
  import * as styled_components_dist_utils_hoist from 'styled-components/dist/utils/hoist';
5
5
  import * as styled_components from 'styled-components';
6
6
  import { DefaultTheme } from 'styled-components';
@@ -476,8 +476,6 @@ interface SpectrogramChannelProps {
476
476
  index: number;
477
477
  /** Audio channel index for canvas ID construction. Defaults to `index` when omitted. */
478
478
  channelIndex?: number;
479
- /** Computed spectrogram data (not needed when workerApi is provided) */
480
- data?: SpectrogramData;
481
479
  /** Width in CSS pixels */
482
480
  length: number;
483
481
  /** Height in CSS pixels */
@@ -486,18 +484,10 @@ interface SpectrogramChannelProps {
486
484
  devicePixelRatio?: number;
487
485
  /** Samples per pixel at current zoom level */
488
486
  samplesPerPixel: number;
489
- /** 256-entry RGB LUT (768 bytes) from getColorMap() */
490
- colorLUT?: Uint8Array;
491
- /** Frequency scale function: (freqHz, minF, maxF) => [0,1] */
492
- frequencyScaleFn?: (f: number, minF: number, maxF: number) => number;
493
- /** Min frequency in Hz */
494
- minFrequency?: number;
495
- /** Max frequency in Hz (defaults to sampleRate/2) */
496
- maxFrequency?: number;
497
- /** Worker API for transferring canvas ownership. When provided, rendering is done in the worker. */
498
- workerApi?: SpectrogramWorkerCanvasApi;
487
+ /** Worker API for transferring canvas ownership. Rendering is done in the worker. */
488
+ workerApi: SpectrogramWorkerCanvasApi;
499
489
  /** Clip ID used to construct unique canvas IDs for worker registration */
500
- clipId?: string;
490
+ clipId: string;
501
491
  /** Callback when canvases are registered with the worker, providing canvas IDs and widths */
502
492
  onCanvasesReady?: (canvasIds: string[], canvasWidths: number[]) => void;
503
493
  }
@@ -514,18 +504,8 @@ interface SmartChannelProps {
514
504
  transparentBackground?: boolean;
515
505
  /** Render mode: waveform, spectrogram, or both */
516
506
  renderMode?: RenderMode;
517
- /** Spectrogram data for this channel */
518
- spectrogramData?: SpectrogramData;
519
- /** 256-entry RGB LUT from getColorMap() */
520
- spectrogramColorLUT?: Uint8Array;
521
507
  /** Samples per pixel at current zoom level */
522
508
  samplesPerPixel?: number;
523
- /** Frequency scale function */
524
- spectrogramFrequencyScaleFn?: (f: number, minF: number, maxF: number) => number;
525
- /** Min frequency in Hz */
526
- spectrogramMinFrequency?: number;
527
- /** Max frequency in Hz */
528
- spectrogramMaxFrequency?: number;
529
509
  /** Worker API for OffscreenCanvas transfer */
530
510
  spectrogramWorkerApi?: SpectrogramWorkerCanvasApi;
531
511
  /** Clip ID for worker canvas registration */
package/dist/index.js CHANGED
@@ -894,7 +894,7 @@ var Channel = (props) => {
894
894
  const { canvasRef, canvasMapRef } = useChunkedCanvasRefs();
895
895
  const clipOriginX = useClipViewportOrigin();
896
896
  const visibleChunkIndices = useVisibleChunkIndices(length, import_core.MAX_CANVAS_WIDTH, clipOriginX);
897
- (0, import_react4.useEffect)(() => {
897
+ (0, import_react4.useLayoutEffect)(() => {
898
898
  const step = barWidth + barGap;
899
899
  for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
900
900
  const globalPixelOffset = canvasIdx * import_core.MAX_CANVAS_WIDTH;
@@ -1466,7 +1466,7 @@ var PianoRollChannel = ({
1466
1466
  return { minMidi: Math.max(0, min - 1), maxMidi: Math.min(127, max + 1) };
1467
1467
  }, [midiNotes]);
1468
1468
  const color = isSelected ? selectedNoteColor : noteColor;
1469
- (0, import_react8.useEffect)(() => {
1469
+ (0, import_react8.useLayoutEffect)(() => {
1470
1470
  const noteRange = maxMidi - minMidi + 1;
1471
1471
  const noteHeight = Math.max(2, waveHeight / noteRange);
1472
1472
  const pixelsPerSecond = sampleRate / samplesPerPixel;
@@ -2411,7 +2411,6 @@ var import_react20 = require("react");
2411
2411
  var import_styled_components21 = __toESM(require("styled-components"));
2412
2412
  var import_core5 = require("@waveform-playlist/core");
2413
2413
  var import_jsx_runtime23 = require("react/jsx-runtime");
2414
- var LINEAR_FREQUENCY_SCALE = (f, minF, maxF) => (f - minF) / (maxF - minF);
2415
2414
  var Wrapper4 = import_styled_components21.default.div.attrs((props) => ({
2416
2415
  style: {
2417
2416
  top: `${props.$waveHeight * props.$index}px`,
@@ -2436,26 +2435,13 @@ var SpectrogramCanvas = import_styled_components21.default.canvas.attrs((props)
2436
2435
  image-rendering: pixelated;
2437
2436
  image-rendering: crisp-edges;
2438
2437
  `;
2439
- function defaultGetColorMap() {
2440
- const lut = new Uint8Array(256 * 3);
2441
- for (let i = 0; i < 256; i++) {
2442
- lut[i * 3] = lut[i * 3 + 1] = lut[i * 3 + 2] = i;
2443
- }
2444
- return lut;
2445
- }
2446
- var DEFAULT_COLOR_LUT = defaultGetColorMap();
2447
2438
  var SpectrogramChannel = ({
2448
2439
  index,
2449
2440
  channelIndex: channelIndexProp,
2450
- data,
2451
2441
  length,
2452
2442
  waveHeight,
2453
2443
  devicePixelRatio = 1,
2454
- samplesPerPixel,
2455
- colorLUT,
2456
- frequencyScaleFn,
2457
- minFrequency = 0,
2458
- maxFrequency,
2444
+ samplesPerPixel: _samplesPerPixel,
2459
2445
  workerApi,
2460
2446
  clipId,
2461
2447
  onCanvasesReady
@@ -2466,13 +2452,8 @@ var SpectrogramChannel = ({
2466
2452
  const transferredCanvasesRef = (0, import_react20.useRef)(/* @__PURE__ */ new WeakSet());
2467
2453
  const workerApiRef = (0, import_react20.useRef)(workerApi);
2468
2454
  const onCanvasesReadyRef = (0, import_react20.useRef)(onCanvasesReady);
2469
- const isWorkerMode = !!(workerApi && clipId);
2470
2455
  const clipOriginX = useClipViewportOrigin();
2471
2456
  const visibleChunkIndices = useVisibleChunkIndices(length, import_core5.MAX_CANVAS_WIDTH, clipOriginX);
2472
- const lut = colorLUT ?? DEFAULT_COLOR_LUT;
2473
- const maxF = maxFrequency ?? (data ? data.sampleRate / 2 : 22050);
2474
- const scaleFn = frequencyScaleFn ?? LINEAR_FREQUENCY_SCALE;
2475
- const hasCustomFrequencyScale = Boolean(frequencyScaleFn);
2476
2457
  (0, import_react20.useEffect)(() => {
2477
2458
  workerApiRef.current = workerApi;
2478
2459
  }, [workerApi]);
@@ -2480,7 +2461,6 @@ var SpectrogramChannel = ({
2480
2461
  onCanvasesReadyRef.current = onCanvasesReady;
2481
2462
  }, [onCanvasesReady]);
2482
2463
  (0, import_react20.useEffect)(() => {
2483
- if (!isWorkerMode) return;
2484
2464
  const currentWorkerApi = workerApiRef.current;
2485
2465
  if (!currentWorkerApi || !clipId) return;
2486
2466
  const previousCount = registeredIdsRef.current.length;
@@ -2541,7 +2521,7 @@ var SpectrogramChannel = ({
2541
2521
  });
2542
2522
  onCanvasesReadyRef.current?.(allIds, allWidths);
2543
2523
  }
2544
- }, [canvasMapRef, isWorkerMode, clipId, channelIndex, length, visibleChunkIndices]);
2524
+ }, [canvasMapRef, clipId, channelIndex, length, visibleChunkIndices]);
2545
2525
  (0, import_react20.useEffect)(() => {
2546
2526
  return () => {
2547
2527
  const api = workerApiRef.current;
@@ -2556,94 +2536,6 @@ var SpectrogramChannel = ({
2556
2536
  registeredIdsRef.current = [];
2557
2537
  };
2558
2538
  }, []);
2559
- (0, import_react20.useEffect)(() => {
2560
- if (isWorkerMode || !data) return;
2561
- const {
2562
- frequencyBinCount,
2563
- frameCount,
2564
- hopSize,
2565
- sampleRate,
2566
- gainDb,
2567
- rangeDb: rawRangeDb
2568
- } = data;
2569
- const rangeDb = rawRangeDb === 0 ? 1 : rawRangeDb;
2570
- const binToFreq = (bin) => bin / frequencyBinCount * (sampleRate / 2);
2571
- for (const [canvasIdx, canvas] of canvasMapRef.current.entries()) {
2572
- const globalPixelOffset = canvasIdx * import_core5.MAX_CANVAS_WIDTH;
2573
- const ctx = canvas.getContext("2d");
2574
- if (!ctx) continue;
2575
- const canvasWidth = canvas.width / devicePixelRatio;
2576
- const canvasHeight = waveHeight;
2577
- ctx.resetTransform();
2578
- ctx.clearRect(0, 0, canvas.width, canvas.height);
2579
- ctx.imageSmoothingEnabled = false;
2580
- ctx.scale(devicePixelRatio, devicePixelRatio);
2581
- const imgData = ctx.createImageData(canvasWidth, canvasHeight);
2582
- const pixels = imgData.data;
2583
- for (let x = 0; x < canvasWidth; x++) {
2584
- const globalX = globalPixelOffset + x;
2585
- const samplePos = globalX * samplesPerPixel;
2586
- const frame = Math.floor(samplePos / hopSize);
2587
- if (frame < 0 || frame >= frameCount) continue;
2588
- const frameOffset = frame * frequencyBinCount;
2589
- for (let y = 0; y < canvasHeight; y++) {
2590
- const normalizedY = 1 - y / canvasHeight;
2591
- let bin = Math.floor(normalizedY * frequencyBinCount);
2592
- if (hasCustomFrequencyScale) {
2593
- let lo = 0;
2594
- let hi = frequencyBinCount - 1;
2595
- while (lo < hi) {
2596
- const mid = lo + hi >> 1;
2597
- const freq = binToFreq(mid);
2598
- const scaled = scaleFn(freq, minFrequency, maxF);
2599
- if (scaled < normalizedY) {
2600
- lo = mid + 1;
2601
- } else {
2602
- hi = mid;
2603
- }
2604
- }
2605
- bin = lo;
2606
- }
2607
- if (bin < 0 || bin >= frequencyBinCount) continue;
2608
- const db = data.data[frameOffset + bin];
2609
- const normalized = Math.max(0, Math.min(1, (db + rangeDb + gainDb) / rangeDb));
2610
- const colorIdx = Math.floor(normalized * 255);
2611
- const pixelIdx = (y * canvasWidth + x) * 4;
2612
- pixels[pixelIdx] = lut[colorIdx * 3];
2613
- pixels[pixelIdx + 1] = lut[colorIdx * 3 + 1];
2614
- pixels[pixelIdx + 2] = lut[colorIdx * 3 + 2];
2615
- pixels[pixelIdx + 3] = 255;
2616
- }
2617
- }
2618
- ctx.resetTransform();
2619
- ctx.putImageData(imgData, 0, 0);
2620
- if (devicePixelRatio !== 1) {
2621
- const tmpCanvas = document.createElement("canvas");
2622
- tmpCanvas.width = canvasWidth;
2623
- tmpCanvas.height = canvasHeight;
2624
- const tmpCtx = tmpCanvas.getContext("2d");
2625
- if (!tmpCtx) continue;
2626
- tmpCtx.putImageData(imgData, 0, 0);
2627
- ctx.clearRect(0, 0, canvas.width, canvas.height);
2628
- ctx.imageSmoothingEnabled = false;
2629
- ctx.drawImage(tmpCanvas, 0, 0, canvas.width, canvas.height);
2630
- }
2631
- }
2632
- }, [
2633
- canvasMapRef,
2634
- isWorkerMode,
2635
- data,
2636
- length,
2637
- waveHeight,
2638
- devicePixelRatio,
2639
- samplesPerPixel,
2640
- lut,
2641
- minFrequency,
2642
- maxF,
2643
- scaleFn,
2644
- hasCustomFrequencyScale,
2645
- visibleChunkIndices
2646
- ]);
2647
2539
  const canvases = visibleChunkIndices.map((i) => {
2648
2540
  const chunkLeft = i * import_core5.MAX_CANVAS_WIDTH;
2649
2541
  const currentWidth = Math.min(length - chunkLeft, import_core5.MAX_CANVAS_WIDTH);
@@ -2670,12 +2562,7 @@ var SmartChannel = ({
2670
2562
  isSelected,
2671
2563
  transparentBackground,
2672
2564
  renderMode = "waveform",
2673
- spectrogramData,
2674
- spectrogramColorLUT,
2675
2565
  samplesPerPixel: sppProp,
2676
- spectrogramFrequencyScaleFn,
2677
- spectrogramMinFrequency,
2678
- spectrogramMaxFrequency,
2679
2566
  spectrogramWorkerApi,
2680
2567
  spectrogramClipId,
2681
2568
  spectrogramOnCanvasesReady,
@@ -2697,21 +2584,16 @@ var SmartChannel = ({
2697
2584
  const waveOutlineColor = isSelected && theme ? theme.selectedWaveOutlineColor : theme?.waveOutlineColor;
2698
2585
  const waveFillColor = isSelected && theme ? theme.selectedWaveFillColor : theme?.waveFillColor;
2699
2586
  const drawMode = theme?.waveformDrawMode || "inverted";
2700
- const hasSpectrogram = spectrogramData || spectrogramWorkerApi;
2587
+ const hasSpectrogram = spectrogramWorkerApi && spectrogramClipId;
2701
2588
  if (renderMode === "spectrogram" && hasSpectrogram) {
2702
2589
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2703
2590
  SpectrogramChannel,
2704
2591
  {
2705
2592
  index: props.index,
2706
- data: spectrogramData,
2707
2593
  length: props.length,
2708
2594
  waveHeight,
2709
2595
  devicePixelRatio,
2710
2596
  samplesPerPixel,
2711
- colorLUT: spectrogramColorLUT,
2712
- frequencyScaleFn: spectrogramFrequencyScaleFn,
2713
- minFrequency: spectrogramMinFrequency,
2714
- maxFrequency: spectrogramMaxFrequency,
2715
2597
  workerApi: spectrogramWorkerApi,
2716
2598
  clipId: spectrogramClipId,
2717
2599
  onCanvasesReady: spectrogramOnCanvasesReady
@@ -2726,15 +2608,10 @@ var SmartChannel = ({
2726
2608
  {
2727
2609
  index: props.index * 2,
2728
2610
  channelIndex: props.index,
2729
- data: spectrogramData,
2730
2611
  length: props.length,
2731
2612
  waveHeight: halfHeight,
2732
2613
  devicePixelRatio,
2733
2614
  samplesPerPixel,
2734
- colorLUT: spectrogramColorLUT,
2735
- frequencyScaleFn: spectrogramFrequencyScaleFn,
2736
- minFrequency: spectrogramMinFrequency,
2737
- maxFrequency: spectrogramMaxFrequency,
2738
2615
  workerApi: spectrogramWorkerApi,
2739
2616
  clipId: spectrogramClipId,
2740
2617
  onCanvasesReady: spectrogramOnCanvasesReady