@waveform-playlist/browser 13.1.3 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -50,30 +50,52 @@ var __async = (__this, __arguments, generator) => {
50
50
  });
51
51
  };
52
52
 
53
- // src/index.tsx
54
- import * as Tone2 from "tone";
55
-
56
53
  // src/WaveformPlaylistContext.tsx
57
54
  import {
58
55
  createContext,
59
56
  useContext,
60
- useState as useState15,
61
- useEffect as useEffect10,
62
- useRef as useRef15,
63
- useCallback as useCallback19,
64
- useMemo as useMemo4
57
+ useState as useState9,
58
+ useEffect as useEffect5,
59
+ useRef as useRef9,
60
+ useCallback as useCallback13,
61
+ useMemo as useMemo3
65
62
  } from "react";
66
63
  import { ThemeProvider } from "styled-components";
67
- import {
68
- configureGlobalContext,
69
- createToneAdapter,
70
- getGlobalAudioContext as getGlobalAudioContext4
71
- } from "@waveform-playlist/playout";
64
+
65
+ // src/playout/resolvePlayoutAdapter.ts
66
+ var INSTALL_HINT = "@waveform-playlist/playout (and its peer `tone`) is required for the default WebAudio engine. Install with: npm install @waveform-playlist/playout tone \u2014 or pass a custom `createAdapter`.";
67
+ function resolvePlayoutAdapter(opts) {
68
+ return __async(this, null, function* () {
69
+ if (opts.createAdapter) {
70
+ return opts.createAdapter();
71
+ }
72
+ let mod;
73
+ try {
74
+ mod = yield import("@waveform-playlist/playout");
75
+ } catch (originalErr) {
76
+ console.warn(
77
+ "[waveform-playlist] @waveform-playlist/playout dynamic import failed: " + String(originalErr)
78
+ );
79
+ throw new Error(INSTALL_HINT);
80
+ }
81
+ if (opts.sampleRate !== void 0) {
82
+ try {
83
+ mod.configureGlobalContext({ sampleRate: opts.sampleRate });
84
+ } catch (ctxErr) {
85
+ console.warn(
86
+ "[waveform-playlist] configureGlobalContext failed (continuing with default rate): " + String(ctxErr)
87
+ );
88
+ }
89
+ }
90
+ return mod.createToneAdapter({ effects: opts.effects, soundFontCache: opts.soundFontCache });
91
+ });
92
+ }
93
+
94
+ // src/WaveformPlaylistContext.tsx
72
95
  import { PlaylistEngine } from "@waveform-playlist/engine";
73
96
  import {
74
97
  defaultTheme
75
98
  } from "@waveform-playlist/ui-components";
76
- import { getContext as getContext2 } from "tone";
77
99
 
78
100
  // src/waveformDataLoader.ts
79
101
  import WaveformData from "waveform-data";
@@ -239,9 +261,11 @@ function extractPeaksFromWaveformDataFull(waveformData, samplesPerPixel, isMono,
239
261
  }
240
262
 
241
263
  // src/soundFontSync.ts
242
- import { isToneAdapter } from "@waveform-playlist/playout";
264
+ function supportsSoundFont(adapter) {
265
+ return adapter != null && typeof adapter.setSoundFontCache === "function";
266
+ }
243
267
  function syncSoundFontCacheToAdapter(adapter, cache) {
244
- if (!isToneAdapter(adapter)) return;
268
+ if (!supportsSoundFont(adapter)) return;
245
269
  adapter.setSoundFontCache(cache);
246
270
  }
247
271
 
@@ -501,224 +525,6 @@ function useUndoState({ engineRef }) {
501
525
  };
502
526
  }
503
527
 
504
- // src/hooks/useAudioEffects.ts
505
- import { useRef as useRef7, useCallback as useCallback7 } from "react";
506
- import { Analyser } from "tone";
507
- var useMasterAnalyser = (fftSize = 256) => {
508
- const analyserRef = useRef7(null);
509
- const masterEffects = useCallback7(
510
- (masterGainNode, destination, _isOffline) => {
511
- const analyserNode = new Analyser("fft", fftSize);
512
- masterGainNode.connect(analyserNode);
513
- masterGainNode.connect(destination);
514
- analyserRef.current = analyserNode;
515
- return function cleanup() {
516
- analyserNode.dispose();
517
- analyserRef.current = null;
518
- };
519
- },
520
- [fftSize]
521
- );
522
- return { analyserRef, masterEffects };
523
- };
524
-
525
- // src/hooks/useAudioTracks.ts
526
- import { useState as useState8, useEffect, useRef as useRef8, useMemo } from "react";
527
- import {
528
- createTrack,
529
- createClipFromSeconds
530
- } from "@waveform-playlist/core";
531
- import * as Tone from "tone";
532
- function buildTrackFromConfig(config, index, audioBuffer, stableIds, contextSampleRate = 48e3) {
533
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
534
- const buffer = audioBuffer != null ? audioBuffer : config.audioBuffer;
535
- const sampleRate = (_c = (_b = buffer == null ? void 0 : buffer.sampleRate) != null ? _b : (_a = config.waveformData) == null ? void 0 : _a.sample_rate) != null ? _c : contextSampleRate;
536
- const sourceDuration = (_g = (_e = buffer == null ? void 0 : buffer.duration) != null ? _e : (_d = config.waveformData) == null ? void 0 : _d.duration) != null ? _g : config.duration != null ? config.duration + ((_f = config.offset) != null ? _f : 0) : void 0;
537
- if (sourceDuration === void 0) {
538
- console.warn(
539
- `[waveform-playlist] Track ${index + 1} ("${(_h = config.name) != null ? _h : "unnamed"}"): Cannot create track \u2014 provide duration, audioBuffer, or waveformData with duration.`
540
- );
541
- return null;
542
- }
543
- const clip = createClipFromSeconds({
544
- audioBuffer: buffer,
545
- sampleRate,
546
- sourceDuration,
547
- startTime: (_i = config.startTime) != null ? _i : 0,
548
- duration: (_j = config.duration) != null ? _j : sourceDuration,
549
- offset: (_k = config.offset) != null ? _k : 0,
550
- name: config.name || `Track ${index + 1}`,
551
- fadeIn: config.fadeIn,
552
- fadeOut: config.fadeOut,
553
- waveformData: config.waveformData
554
- });
555
- if (isNaN(clip.startSample) || isNaN(clip.durationSamples) || isNaN(clip.offsetSamples)) {
556
- console.error(
557
- `[waveform-playlist] Invalid clip values for track ${index + 1} ("${(_l = config.name) != null ? _l : "unnamed"}"): startSample=${clip.startSample}, durationSamples=${clip.durationSamples}, offsetSamples=${clip.offsetSamples}`
558
- );
559
- return null;
560
- }
561
- const track = __spreadProps(__spreadValues({}, createTrack({
562
- name: config.name || `Track ${index + 1}`,
563
- clips: [clip],
564
- muted: (_m = config.muted) != null ? _m : false,
565
- soloed: (_n = config.soloed) != null ? _n : false,
566
- volume: (_o = config.volume) != null ? _o : 1,
567
- pan: (_p = config.pan) != null ? _p : 0,
568
- color: config.color
569
- })), {
570
- effects: config.effects,
571
- renderMode: config.renderMode,
572
- spectrogramConfig: config.spectrogramConfig,
573
- spectrogramColorMap: config.spectrogramColorMap
574
- });
575
- const existingIds = stableIds.get(index);
576
- if (existingIds) {
577
- track.id = existingIds.trackId;
578
- track.clips[0] = __spreadProps(__spreadValues({}, track.clips[0]), { id: existingIds.clipId });
579
- } else {
580
- stableIds.set(index, { trackId: track.id, clipId: track.clips[0].id });
581
- }
582
- return track;
583
- }
584
- function useAudioTracks(configs, options = {}) {
585
- const { immediate = false, progressive = false } = options;
586
- const isImmediate = immediate || progressive;
587
- const [loading, setLoading] = useState8(true);
588
- const [error, setError] = useState8(null);
589
- const [loadedCount, setLoadedCount] = useState8(0);
590
- const totalCount = configs.length;
591
- const [loadedBuffers, setLoadedBuffers] = useState8(/* @__PURE__ */ new Map());
592
- const stableIdsRef = useRef8(/* @__PURE__ */ new Map());
593
- const contextSampleRateRef = useRef8(48e3);
594
- const derivedTracks = useMemo(() => {
595
- if (!isImmediate) return null;
596
- const result = [];
597
- for (let i = 0; i < configs.length; i++) {
598
- const track = buildTrackFromConfig(
599
- configs[i],
600
- i,
601
- loadedBuffers.get(i),
602
- stableIdsRef.current,
603
- contextSampleRateRef.current
604
- );
605
- if (track) result.push(track);
606
- }
607
- return result;
608
- }, [isImmediate, configs, loadedBuffers]);
609
- const [tracks, setTracks] = useState8(derivedTracks != null ? derivedTracks : []);
610
- const prevDerivedRef = useRef8(derivedTracks);
611
- if (derivedTracks !== prevDerivedRef.current) {
612
- prevDerivedRef.current = derivedTracks;
613
- if (derivedTracks) setTracks(derivedTracks);
614
- }
615
- useEffect(() => {
616
- if (configs.length === 0) {
617
- setTracks([]);
618
- setLoading(false);
619
- setLoadedCount(0);
620
- return;
621
- }
622
- let cancelled = false;
623
- const abortController = new AbortController();
624
- const loadTracks = () => __async(null, null, function* () {
625
- try {
626
- setLoading(true);
627
- setError(null);
628
- setLoadedCount(0);
629
- if (isImmediate) {
630
- setLoadedBuffers(/* @__PURE__ */ new Map());
631
- }
632
- const audioContext = Tone.getContext().rawContext;
633
- contextSampleRateRef.current = audioContext.sampleRate;
634
- const loadPromises = configs.map((config, index) => __async(null, null, function* () {
635
- if (config.audioBuffer) {
636
- if (isImmediate && !cancelled) {
637
- setLoadedBuffers((prev) => {
638
- const next = new Map(prev);
639
- next.set(index, config.audioBuffer);
640
- return next;
641
- });
642
- setLoadedCount((prev) => prev + 1);
643
- return;
644
- }
645
- return buildTrackFromConfig(
646
- config,
647
- index,
648
- config.audioBuffer,
649
- stableIdsRef.current,
650
- audioContext.sampleRate
651
- );
652
- }
653
- if (!config.src && config.waveformData) {
654
- if (isImmediate && !cancelled) {
655
- setLoadedCount((prev) => prev + 1);
656
- return;
657
- }
658
- return buildTrackFromConfig(
659
- config,
660
- index,
661
- void 0,
662
- stableIdsRef.current,
663
- audioContext.sampleRate
664
- );
665
- }
666
- if (!config.src) {
667
- throw new Error(`Track ${index + 1}: Must provide src, audioBuffer, or waveformData`);
668
- }
669
- const response = yield fetch(config.src, { signal: abortController.signal });
670
- if (!response.ok) {
671
- throw new Error(`Failed to fetch ${config.src}: ${response.statusText}`);
672
- }
673
- const arrayBuffer = yield response.arrayBuffer();
674
- const audioBuffer = yield audioContext.decodeAudioData(arrayBuffer);
675
- if (!audioBuffer || !audioBuffer.sampleRate || !audioBuffer.duration) {
676
- throw new Error(`Invalid audio buffer for ${config.src}`);
677
- }
678
- if (isImmediate && !cancelled) {
679
- setLoadedBuffers((prev) => {
680
- const next = new Map(prev);
681
- next.set(index, audioBuffer);
682
- return next;
683
- });
684
- setLoadedCount((prev) => prev + 1);
685
- return;
686
- }
687
- return buildTrackFromConfig(
688
- config,
689
- index,
690
- audioBuffer,
691
- stableIdsRef.current,
692
- audioContext.sampleRate
693
- );
694
- }));
695
- const loadedTracks = yield Promise.all(loadPromises);
696
- if (!cancelled) {
697
- if (!isImmediate) {
698
- const validTracks = loadedTracks.filter((t) => t != null);
699
- setTracks(validTracks);
700
- setLoadedCount(validTracks.length);
701
- }
702
- setLoading(false);
703
- }
704
- } catch (err) {
705
- if (!cancelled) {
706
- const errorMessage = err instanceof Error ? err.message : "Unknown error loading audio";
707
- setError(errorMessage);
708
- setLoading(false);
709
- console.error(`[waveform-playlist] Error loading audio tracks: ${errorMessage}`);
710
- }
711
- }
712
- });
713
- loadTracks();
714
- return () => {
715
- cancelled = true;
716
- abortController.abort();
717
- };
718
- }, [configs, isImmediate]);
719
- return { tracks, loading, error, loadedCount, totalCount };
720
- }
721
-
722
528
  // src/hooks/useClipDragHandlers.ts
723
529
  import React from "react";
724
530
 
@@ -955,13 +761,13 @@ function useClipDragHandlers({
955
761
 
956
762
  // src/hooks/useAnnotationDragHandlers.ts
957
763
  import React2 from "react";
958
- import { getGlobalAudioContext } from "@waveform-playlist/playout";
959
764
  var LINK_THRESHOLD = 0.01;
960
765
  function useAnnotationDragHandlers({
961
766
  annotations,
962
767
  onAnnotationsChange,
963
768
  samplesPerPixel,
964
- sampleRate = getGlobalAudioContext().sampleRate,
769
+ // Default mirrors the engine default; the providers always pass the real rate from context (#510).
770
+ sampleRate = 48e3,
965
771
  duration,
966
772
  linkEndpoints
967
773
  }) {
@@ -1119,7 +925,7 @@ function updateAnnotationBoundaries({
1119
925
  }
1120
926
 
1121
927
  // src/hooks/useDragSensors.ts
1122
- import { useMemo as useMemo2 } from "react";
928
+ import { useMemo } from "react";
1123
929
  import { PointerSensor, PointerActivationConstraints } from "@dnd-kit/dom";
1124
930
  function useDragSensors(options = {}) {
1125
931
  const {
@@ -1128,7 +934,7 @@ function useDragSensors(options = {}) {
1128
934
  touchTolerance = 5,
1129
935
  mouseDistance = 1
1130
936
  } = options;
1131
- return useMemo2(() => {
937
+ return useMemo(() => {
1132
938
  if (touchOptimized) {
1133
939
  return [
1134
940
  PointerSensor.configure({
@@ -1157,14 +963,14 @@ function useDragSensors(options = {}) {
1157
963
  }
1158
964
 
1159
965
  // src/hooks/useClipSplitting.ts
1160
- import { useCallback as useCallback8 } from "react";
966
+ import { useCallback as useCallback7 } from "react";
1161
967
  import { calculateSplitPoint, canSplitAt } from "@waveform-playlist/engine";
1162
968
  var useClipSplitting = (options) => {
1163
969
  const { tracks, engineRef } = options;
1164
970
  const { sampleRate } = usePlaylistData();
1165
971
  const { currentTimeRef } = usePlaybackAnimation();
1166
972
  const { selectedTrackId } = usePlaylistState();
1167
- const splitClipAt = useCallback8(
973
+ const splitClipAt = useCallback7(
1168
974
  (trackIndex, clipIndex, splitTime) => {
1169
975
  const { samplesPerPixel } = options;
1170
976
  const track = tracks[trackIndex];
@@ -1188,7 +994,7 @@ var useClipSplitting = (options) => {
1188
994
  },
1189
995
  [tracks, options, engineRef, sampleRate]
1190
996
  );
1191
- const splitClipAtPlayhead = useCallback8(() => {
997
+ const splitClipAtPlayhead = useCallback7(() => {
1192
998
  var _a;
1193
999
  if (!selectedTrackId) {
1194
1000
  console.warn("[waveform-playlist] No track selected \u2014 click a clip to select a track first");
@@ -1219,16 +1025,16 @@ var useClipSplitting = (options) => {
1219
1025
  };
1220
1026
 
1221
1027
  // src/hooks/useKeyboardShortcuts.ts
1222
- import { useEffect as useEffect2, useCallback as useCallback9 } from "react";
1028
+ import { useEffect, useCallback as useCallback8 } from "react";
1223
1029
  import { handleKeyboardEvent } from "@waveform-playlist/core";
1224
1030
  import { handleKeyboardEvent as handleKeyboardEvent2, getShortcutLabel } from "@waveform-playlist/core";
1225
1031
  var useKeyboardShortcuts = (options) => {
1226
1032
  const { shortcuts, enabled = true } = options;
1227
- const handleKeyDown = useCallback9(
1033
+ const handleKeyDown = useCallback8(
1228
1034
  (event) => handleKeyboardEvent(event, shortcuts, enabled),
1229
1035
  [shortcuts, enabled]
1230
1036
  );
1231
- useEffect2(() => {
1037
+ useEffect(() => {
1232
1038
  if (!enabled) return;
1233
1039
  window.addEventListener("keydown", handleKeyDown);
1234
1040
  return () => {
@@ -1238,22 +1044,22 @@ var useKeyboardShortcuts = (options) => {
1238
1044
  };
1239
1045
 
1240
1046
  // src/hooks/usePlaybackShortcuts.ts
1241
- import { useCallback as useCallback10 } from "react";
1047
+ import { useCallback as useCallback9 } from "react";
1242
1048
  var usePlaybackShortcuts = (options = {}) => {
1243
1049
  const { enabled = true, additionalShortcuts = [], shortcuts: overrideShortcuts } = options;
1244
1050
  const { isPlaying } = usePlaybackAnimation();
1245
1051
  const { setCurrentTime, play, pause, stop } = usePlaylistControls();
1246
- const togglePlayPause = useCallback10(() => {
1052
+ const togglePlayPause = useCallback9(() => {
1247
1053
  if (isPlaying) {
1248
1054
  pause();
1249
1055
  } else {
1250
1056
  play();
1251
1057
  }
1252
1058
  }, [isPlaying, play, pause]);
1253
- const stopPlayback = useCallback10(() => {
1059
+ const stopPlayback = useCallback9(() => {
1254
1060
  stop();
1255
1061
  }, [stop]);
1256
- const rewindToStart = useCallback10(() => {
1062
+ const rewindToStart = useCallback9(() => {
1257
1063
  setCurrentTime(0);
1258
1064
  if (isPlaying) {
1259
1065
  play(0);
@@ -1293,7 +1099,7 @@ var usePlaybackShortcuts = (options = {}) => {
1293
1099
  };
1294
1100
 
1295
1101
  // src/hooks/useAnnotationKeyboardControls.ts
1296
- import { useCallback as useCallback11, useMemo as useMemo3, useEffect as useEffect3 } from "react";
1102
+ import { useCallback as useCallback10, useMemo as useMemo2, useEffect as useEffect2 } from "react";
1297
1103
  var LINK_THRESHOLD2 = 0.01;
1298
1104
  var TIME_DELTA = 0.01;
1299
1105
  function useAnnotationKeyboardControls({
@@ -1313,11 +1119,11 @@ function useAnnotationKeyboardControls({
1313
1119
  const playlistData = usePlaylistDataOptional();
1314
1120
  const samplesPerPixel = samplesPerPixelProp != null ? samplesPerPixelProp : playlistData == null ? void 0 : playlistData.samplesPerPixel;
1315
1121
  const sampleRate = sampleRateProp != null ? sampleRateProp : playlistData == null ? void 0 : playlistData.sampleRate;
1316
- const activeIndex = useMemo3(() => {
1122
+ const activeIndex = useMemo2(() => {
1317
1123
  if (!activeAnnotationId) return -1;
1318
1124
  return annotations.findIndex((a) => a.id === activeAnnotationId);
1319
1125
  }, [annotations, activeAnnotationId]);
1320
- const scrollToAnnotation = useCallback11(
1126
+ const scrollToAnnotation = useCallback10(
1321
1127
  (annotationId) => {
1322
1128
  if (!(scrollContainerRef == null ? void 0 : scrollContainerRef.current) || !samplesPerPixel || !sampleRate) return;
1323
1129
  const annotation = annotations.find((a) => a.id === annotationId);
@@ -1340,12 +1146,12 @@ function useAnnotationKeyboardControls({
1340
1146
  },
1341
1147
  [annotations, scrollContainerRef, samplesPerPixel, sampleRate]
1342
1148
  );
1343
- useEffect3(() => {
1149
+ useEffect2(() => {
1344
1150
  if (activeAnnotationId && (scrollContainerRef == null ? void 0 : scrollContainerRef.current) && samplesPerPixel && sampleRate) {
1345
1151
  scrollToAnnotation(activeAnnotationId);
1346
1152
  }
1347
1153
  }, [activeAnnotationId, scrollToAnnotation, scrollContainerRef, samplesPerPixel, sampleRate]);
1348
- const moveStartBoundary = useCallback11(
1154
+ const moveStartBoundary = useCallback10(
1349
1155
  (delta) => {
1350
1156
  if (activeIndex < 0) return;
1351
1157
  const annotation = annotations[activeIndex];
@@ -1374,7 +1180,7 @@ function useAnnotationKeyboardControls({
1374
1180
  },
1375
1181
  [annotations, activeIndex, linkEndpoints, onAnnotationsChange]
1376
1182
  );
1377
- const moveEndBoundary = useCallback11(
1183
+ const moveEndBoundary = useCallback10(
1378
1184
  (delta) => {
1379
1185
  if (activeIndex < 0) return;
1380
1186
  const annotation = annotations[activeIndex];
@@ -1434,7 +1240,7 @@ function useAnnotationKeyboardControls({
1434
1240
  },
1435
1241
  [annotations, activeIndex, duration, linkEndpoints, onAnnotationsChange]
1436
1242
  );
1437
- const selectPrevious = useCallback11(() => {
1243
+ const selectPrevious = useCallback10(() => {
1438
1244
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1439
1245
  if (activeIndex <= 0) {
1440
1246
  onActiveAnnotationChange(annotations[annotations.length - 1].id);
@@ -1442,7 +1248,7 @@ function useAnnotationKeyboardControls({
1442
1248
  onActiveAnnotationChange(annotations[activeIndex - 1].id);
1443
1249
  }
1444
1250
  }, [annotations, activeIndex, onActiveAnnotationChange]);
1445
- const selectNext = useCallback11(() => {
1251
+ const selectNext = useCallback10(() => {
1446
1252
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1447
1253
  if (activeIndex < 0 || activeIndex >= annotations.length - 1) {
1448
1254
  onActiveAnnotationChange(annotations[0].id);
@@ -1450,25 +1256,25 @@ function useAnnotationKeyboardControls({
1450
1256
  onActiveAnnotationChange(annotations[activeIndex + 1].id);
1451
1257
  }
1452
1258
  }, [annotations, activeIndex, onActiveAnnotationChange]);
1453
- const selectFirst = useCallback11(() => {
1259
+ const selectFirst = useCallback10(() => {
1454
1260
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1455
1261
  onActiveAnnotationChange(annotations[0].id);
1456
1262
  }, [annotations, onActiveAnnotationChange]);
1457
- const selectLast = useCallback11(() => {
1263
+ const selectLast = useCallback10(() => {
1458
1264
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1459
1265
  onActiveAnnotationChange(annotations[annotations.length - 1].id);
1460
1266
  }, [annotations, onActiveAnnotationChange]);
1461
- const clearSelection = useCallback11(() => {
1267
+ const clearSelection = useCallback10(() => {
1462
1268
  if (!onActiveAnnotationChange) return;
1463
1269
  onActiveAnnotationChange(null);
1464
1270
  }, [onActiveAnnotationChange]);
1465
- const playActiveAnnotation = useCallback11(() => {
1271
+ const playActiveAnnotation = useCallback10(() => {
1466
1272
  if (activeIndex < 0 || !onPlay) return;
1467
1273
  const annotation = annotations[activeIndex];
1468
1274
  const playDuration = !continuousPlay ? annotation.end - annotation.start : void 0;
1469
1275
  onPlay(annotation.start, playDuration);
1470
1276
  }, [annotations, activeIndex, continuousPlay, onPlay]);
1471
- const activeAnnotationShortcuts = useMemo3(
1277
+ const activeAnnotationShortcuts = useMemo2(
1472
1278
  () => [
1473
1279
  {
1474
1280
  key: "[",
@@ -1505,7 +1311,7 @@ function useAnnotationKeyboardControls({
1505
1311
  ],
1506
1312
  [moveStartBoundary, moveEndBoundary, playActiveAnnotation]
1507
1313
  );
1508
- const navigationShortcuts = useMemo3(
1314
+ const navigationShortcuts = useMemo2(
1509
1315
  () => [
1510
1316
  {
1511
1317
  key: "ArrowUp",
@@ -1532,1452 +1338,65 @@ function useAnnotationKeyboardControls({
1532
1338
  preventDefault: true
1533
1339
  },
1534
1340
  {
1535
- key: "Home",
1536
- action: selectFirst,
1537
- description: "Select first annotation",
1538
- preventDefault: true
1539
- },
1540
- {
1541
- key: "End",
1542
- action: selectLast,
1543
- description: "Select last annotation",
1544
- preventDefault: true
1545
- },
1546
- {
1547
- key: "Escape",
1548
- action: clearSelection,
1549
- description: "Deselect annotation",
1550
- preventDefault: true
1551
- }
1552
- ],
1553
- [selectPrevious, selectNext, selectFirst, selectLast, clearSelection]
1554
- );
1555
- useKeyboardShortcuts({
1556
- shortcuts: activeAnnotationShortcuts,
1557
- enabled: enabled && activeIndex >= 0
1558
- });
1559
- useKeyboardShortcuts({
1560
- shortcuts: navigationShortcuts,
1561
- enabled: enabled && annotations.length > 0 && !!onActiveAnnotationChange
1562
- });
1563
- return {
1564
- moveStartBoundary,
1565
- moveEndBoundary,
1566
- selectPrevious,
1567
- selectNext,
1568
- selectFirst,
1569
- selectLast,
1570
- clearSelection,
1571
- scrollToAnnotation,
1572
- playActiveAnnotation
1573
- };
1574
- }
1575
-
1576
- // src/hooks/useDynamicEffects.ts
1577
- import { useState as useState9, useCallback as useCallback12, useRef as useRef9, useEffect as useEffect4 } from "react";
1578
-
1579
- // src/effects/effectDefinitions.ts
1580
- var effectDefinitions = [
1581
- // === REVERB EFFECTS ===
1582
- {
1583
- id: "reverb",
1584
- name: "Reverb",
1585
- category: "reverb",
1586
- description: "Simple convolution reverb with adjustable decay time",
1587
- parameters: [
1588
- {
1589
- name: "decay",
1590
- label: "Decay",
1591
- type: "number",
1592
- min: 0.1,
1593
- max: 10,
1594
- step: 0.1,
1595
- default: 1.5,
1596
- unit: "s"
1597
- },
1598
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1599
- ]
1600
- },
1601
- {
1602
- id: "freeverb",
1603
- name: "Freeverb",
1604
- category: "reverb",
1605
- description: "Classic Schroeder/Moorer reverb with room size and dampening",
1606
- parameters: [
1607
- {
1608
- name: "roomSize",
1609
- label: "Room Size",
1610
- type: "number",
1611
- min: 0,
1612
- max: 1,
1613
- step: 0.01,
1614
- default: 0.7
1615
- },
1616
- {
1617
- name: "dampening",
1618
- label: "Dampening",
1619
- type: "number",
1620
- min: 0,
1621
- max: 1e4,
1622
- step: 100,
1623
- default: 3e3,
1624
- unit: "Hz"
1625
- },
1626
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1627
- ]
1628
- },
1629
- {
1630
- id: "jcReverb",
1631
- name: "JC Reverb",
1632
- category: "reverb",
1633
- description: "Attempt at Roland JC-120 chorus reverb emulation",
1634
- parameters: [
1635
- {
1636
- name: "roomSize",
1637
- label: "Room Size",
1638
- type: "number",
1639
- min: 0,
1640
- max: 1,
1641
- step: 0.01,
1642
- default: 0.5
1643
- },
1644
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1645
- ]
1646
- },
1647
- // === DELAY EFFECTS ===
1648
- {
1649
- id: "feedbackDelay",
1650
- name: "Feedback Delay",
1651
- category: "delay",
1652
- description: "Delay line with feedback for echo effects",
1653
- parameters: [
1654
- {
1655
- name: "delayTime",
1656
- label: "Delay Time",
1657
- type: "number",
1658
- min: 0,
1659
- max: 1,
1660
- step: 0.01,
1661
- default: 0.25,
1662
- unit: "s"
1663
- },
1664
- {
1665
- name: "feedback",
1666
- label: "Feedback",
1667
- type: "number",
1668
- min: 0,
1669
- max: 0.95,
1670
- step: 0.01,
1671
- default: 0.5
1672
- },
1673
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1674
- ]
1675
- },
1676
- {
1677
- id: "pingPongDelay",
1678
- name: "Ping Pong Delay",
1679
- category: "delay",
1680
- description: "Stereo delay bouncing between left and right channels",
1681
- parameters: [
1682
- {
1683
- name: "delayTime",
1684
- label: "Delay Time",
1685
- type: "number",
1686
- min: 0,
1687
- max: 1,
1688
- step: 0.01,
1689
- default: 0.25,
1690
- unit: "s"
1691
- },
1692
- {
1693
- name: "feedback",
1694
- label: "Feedback",
1695
- type: "number",
1696
- min: 0,
1697
- max: 0.95,
1698
- step: 0.01,
1699
- default: 0.5
1700
- },
1701
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1702
- ]
1703
- },
1704
- // === MODULATION EFFECTS ===
1705
- {
1706
- id: "chorus",
1707
- name: "Chorus",
1708
- category: "modulation",
1709
- description: "Creates thickness by layering detuned copies of the signal",
1710
- parameters: [
1711
- {
1712
- name: "frequency",
1713
- label: "Rate",
1714
- type: "number",
1715
- min: 0.1,
1716
- max: 10,
1717
- step: 0.1,
1718
- default: 1.5,
1719
- unit: "Hz"
1720
- },
1721
- {
1722
- name: "delayTime",
1723
- label: "Delay",
1724
- type: "number",
1725
- min: 0,
1726
- max: 20,
1727
- step: 0.5,
1728
- default: 3.5,
1729
- unit: "ms"
1730
- },
1731
- { name: "depth", label: "Depth", type: "number", min: 0, max: 1, step: 0.01, default: 0.7 },
1732
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1733
- ]
1734
- },
1735
- {
1736
- id: "phaser",
1737
- name: "Phaser",
1738
- category: "modulation",
1739
- description: "Classic phaser effect using allpass filters",
1740
- parameters: [
1741
- {
1742
- name: "frequency",
1743
- label: "Rate",
1744
- type: "number",
1745
- min: 0.1,
1746
- max: 10,
1747
- step: 0.1,
1748
- default: 0.5,
1749
- unit: "Hz"
1750
- },
1751
- { name: "octaves", label: "Octaves", type: "number", min: 1, max: 6, step: 1, default: 3 },
1752
- {
1753
- name: "baseFrequency",
1754
- label: "Base Freq",
1755
- type: "number",
1756
- min: 100,
1757
- max: 2e3,
1758
- step: 10,
1759
- default: 350,
1760
- unit: "Hz"
1761
- },
1762
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1763
- ]
1764
- },
1765
- {
1766
- id: "tremolo",
1767
- name: "Tremolo",
1768
- category: "modulation",
1769
- description: "Rhythmic volume modulation",
1770
- parameters: [
1771
- {
1772
- name: "frequency",
1773
- label: "Rate",
1774
- type: "number",
1775
- min: 0.1,
1776
- max: 20,
1777
- step: 0.1,
1778
- default: 4,
1779
- unit: "Hz"
1780
- },
1781
- { name: "depth", label: "Depth", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 },
1782
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1783
- ]
1784
- },
1785
- {
1786
- id: "vibrato",
1787
- name: "Vibrato",
1788
- category: "modulation",
1789
- description: "Pitch modulation effect",
1790
- parameters: [
1791
- {
1792
- name: "frequency",
1793
- label: "Rate",
1794
- type: "number",
1795
- min: 0.1,
1796
- max: 20,
1797
- step: 0.1,
1798
- default: 5,
1799
- unit: "Hz"
1800
- },
1801
- { name: "depth", label: "Depth", type: "number", min: 0, max: 1, step: 0.01, default: 0.1 },
1802
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1803
- ]
1804
- },
1805
- {
1806
- id: "autoPanner",
1807
- name: "Auto Panner",
1808
- category: "modulation",
1809
- description: "Automatic left-right panning",
1810
- parameters: [
1811
- {
1812
- name: "frequency",
1813
- label: "Rate",
1814
- type: "number",
1815
- min: 0.1,
1816
- max: 10,
1817
- step: 0.1,
1818
- default: 1,
1819
- unit: "Hz"
1820
- },
1821
- { name: "depth", label: "Depth", type: "number", min: 0, max: 1, step: 0.01, default: 1 },
1822
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1823
- ]
1824
- },
1825
- // === FILTER EFFECTS ===
1826
- {
1827
- id: "autoFilter",
1828
- name: "Auto Filter",
1829
- category: "filter",
1830
- description: "Automated filter sweep with LFO",
1831
- parameters: [
1832
- {
1833
- name: "frequency",
1834
- label: "Rate",
1835
- type: "number",
1836
- min: 0.1,
1837
- max: 10,
1838
- step: 0.1,
1839
- default: 1,
1840
- unit: "Hz"
1841
- },
1842
- {
1843
- name: "baseFrequency",
1844
- label: "Base Freq",
1845
- type: "number",
1846
- min: 20,
1847
- max: 2e3,
1848
- step: 10,
1849
- default: 200,
1850
- unit: "Hz"
1851
- },
1852
- {
1853
- name: "octaves",
1854
- label: "Octaves",
1855
- type: "number",
1856
- min: 0.5,
1857
- max: 8,
1858
- step: 0.5,
1859
- default: 2.6
1860
- },
1861
- { name: "depth", label: "Depth", type: "number", min: 0, max: 1, step: 0.01, default: 1 },
1862
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1863
- ]
1864
- },
1865
- {
1866
- id: "autoWah",
1867
- name: "Auto Wah",
1868
- category: "filter",
1869
- description: "Envelope follower filter effect",
1870
- parameters: [
1871
- {
1872
- name: "baseFrequency",
1873
- label: "Base Freq",
1874
- type: "number",
1875
- min: 20,
1876
- max: 500,
1877
- step: 10,
1878
- default: 100,
1879
- unit: "Hz"
1880
- },
1881
- { name: "octaves", label: "Octaves", type: "number", min: 1, max: 8, step: 1, default: 6 },
1882
- {
1883
- name: "sensitivity",
1884
- label: "Sensitivity",
1885
- type: "number",
1886
- min: -40,
1887
- max: 0,
1888
- step: 1,
1889
- default: 0,
1890
- unit: "dB"
1891
- },
1892
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1893
- ]
1894
- },
1895
- {
1896
- id: "eq3",
1897
- name: "3-Band EQ",
1898
- category: "filter",
1899
- description: "Three band equalizer with low, mid, and high controls",
1900
- parameters: [
1901
- {
1902
- name: "low",
1903
- label: "Low",
1904
- type: "number",
1905
- min: -24,
1906
- max: 24,
1907
- step: 0.5,
1908
- default: 0,
1909
- unit: "dB"
1910
- },
1911
- {
1912
- name: "mid",
1913
- label: "Mid",
1914
- type: "number",
1915
- min: -24,
1916
- max: 24,
1917
- step: 0.5,
1918
- default: 0,
1919
- unit: "dB"
1920
- },
1921
- {
1922
- name: "high",
1923
- label: "High",
1924
- type: "number",
1925
- min: -24,
1926
- max: 24,
1927
- step: 0.5,
1928
- default: 0,
1929
- unit: "dB"
1930
- },
1931
- {
1932
- name: "lowFrequency",
1933
- label: "Low Freq",
1934
- type: "number",
1935
- min: 20,
1936
- max: 500,
1937
- step: 10,
1938
- default: 400,
1939
- unit: "Hz"
1940
- },
1941
- {
1942
- name: "highFrequency",
1943
- label: "High Freq",
1944
- type: "number",
1945
- min: 1e3,
1946
- max: 1e4,
1947
- step: 100,
1948
- default: 2500,
1949
- unit: "Hz"
1950
- }
1951
- ]
1952
- },
1953
- // === DISTORTION EFFECTS ===
1954
- {
1955
- id: "distortion",
1956
- name: "Distortion",
1957
- category: "distortion",
1958
- description: "Wave shaping distortion effect",
1959
- parameters: [
1960
- {
1961
- name: "distortion",
1962
- label: "Drive",
1963
- type: "number",
1964
- min: 0,
1965
- max: 1,
1966
- step: 0.01,
1967
- default: 0.4
1968
- },
1969
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1970
- ]
1971
- },
1972
- {
1973
- id: "bitCrusher",
1974
- name: "Bit Crusher",
1975
- category: "distortion",
1976
- description: "Reduces bit depth for lo-fi digital texture",
1977
- parameters: [
1978
- { name: "bits", label: "Bits", type: "number", min: 1, max: 16, step: 1, default: 4 },
1979
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1980
- ]
1981
- },
1982
- {
1983
- id: "chebyshev",
1984
- name: "Chebyshev",
1985
- category: "distortion",
1986
- description: "Waveshaping distortion using Chebyshev polynomials",
1987
- parameters: [
1988
- { name: "order", label: "Order", type: "number", min: 1, max: 100, step: 1, default: 50 },
1989
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1990
- ]
1991
- },
1992
- // === DYNAMICS EFFECTS ===
1993
- {
1994
- id: "compressor",
1995
- name: "Compressor",
1996
- category: "dynamics",
1997
- description: "Dynamic range compressor",
1998
- parameters: [
1999
- {
2000
- name: "threshold",
2001
- label: "Threshold",
2002
- type: "number",
2003
- min: -60,
2004
- max: 0,
2005
- step: 1,
2006
- default: -24,
2007
- unit: "dB"
2008
- },
2009
- { name: "ratio", label: "Ratio", type: "number", min: 1, max: 20, step: 0.5, default: 4 },
2010
- {
2011
- name: "attack",
2012
- label: "Attack",
2013
- type: "number",
2014
- min: 0,
2015
- max: 1,
2016
- step: 1e-3,
2017
- default: 3e-3,
2018
- unit: "s"
2019
- },
2020
- {
2021
- name: "release",
2022
- label: "Release",
2023
- type: "number",
2024
- min: 0,
2025
- max: 1,
2026
- step: 0.01,
2027
- default: 0.25,
2028
- unit: "s"
2029
- },
2030
- {
2031
- name: "knee",
2032
- label: "Knee",
2033
- type: "number",
2034
- min: 0,
2035
- max: 40,
2036
- step: 1,
2037
- default: 30,
2038
- unit: "dB"
2039
- }
2040
- ]
2041
- },
2042
- {
2043
- id: "limiter",
2044
- name: "Limiter",
2045
- category: "dynamics",
2046
- description: "Hard limiter to prevent clipping",
2047
- parameters: [
2048
- {
2049
- name: "threshold",
2050
- label: "Threshold",
2051
- type: "number",
2052
- min: -12,
2053
- max: 0,
2054
- step: 0.5,
2055
- default: -6,
2056
- unit: "dB"
2057
- }
2058
- ]
2059
- },
2060
- {
2061
- id: "gate",
2062
- name: "Gate",
2063
- category: "dynamics",
2064
- description: "Noise gate to silence signal below threshold",
2065
- parameters: [
2066
- {
2067
- name: "threshold",
2068
- label: "Threshold",
2069
- type: "number",
2070
- min: -100,
2071
- max: 0,
2072
- step: 1,
2073
- default: -40,
2074
- unit: "dB"
2075
- },
2076
- {
2077
- name: "attack",
2078
- label: "Attack",
2079
- type: "number",
2080
- min: 0,
2081
- max: 0.3,
2082
- step: 1e-3,
2083
- default: 1e-3,
2084
- unit: "s"
2085
- },
2086
- {
2087
- name: "release",
2088
- label: "Release",
2089
- type: "number",
2090
- min: 0,
2091
- max: 0.5,
2092
- step: 0.01,
2093
- default: 0.1,
2094
- unit: "s"
2095
- }
2096
- ]
2097
- },
2098
- // === SPATIAL EFFECTS ===
2099
- {
2100
- id: "stereoWidener",
2101
- name: "Stereo Widener",
2102
- category: "spatial",
2103
- description: "Expands or narrows the stereo image",
2104
- parameters: [
2105
- { name: "width", label: "Width", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
2106
- ]
2107
- }
2108
- ];
2109
- var getEffectDefinition = (id) => {
2110
- return effectDefinitions.find((def) => def.id === id);
2111
- };
2112
- var getEffectsByCategory = (category) => {
2113
- return effectDefinitions.filter((def) => def.category === category);
2114
- };
2115
- var effectCategories = [
2116
- { id: "reverb", name: "Reverb" },
2117
- { id: "delay", name: "Delay" },
2118
- { id: "modulation", name: "Modulation" },
2119
- { id: "filter", name: "Filter" },
2120
- { id: "distortion", name: "Distortion" },
2121
- { id: "dynamics", name: "Dynamics" },
2122
- { id: "spatial", name: "Spatial" }
2123
- ];
2124
-
2125
- // src/effects/effectFactory.ts
2126
- import {
2127
- Reverb,
2128
- Freeverb,
2129
- JCReverb,
2130
- FeedbackDelay,
2131
- PingPongDelay,
2132
- Chorus,
2133
- Phaser,
2134
- Tremolo,
2135
- Vibrato,
2136
- AutoPanner,
2137
- AutoFilter,
2138
- AutoWah,
2139
- EQ3,
2140
- Distortion,
2141
- BitCrusher,
2142
- Chebyshev,
2143
- Compressor,
2144
- Limiter,
2145
- Gate,
2146
- StereoWidener
2147
- } from "tone";
2148
- function asEffectConstructor(ctor) {
2149
- return ctor;
2150
- }
2151
- var effectConstructors = {
2152
- reverb: asEffectConstructor(Reverb),
2153
- freeverb: asEffectConstructor(Freeverb),
2154
- jcReverb: asEffectConstructor(JCReverb),
2155
- feedbackDelay: asEffectConstructor(FeedbackDelay),
2156
- pingPongDelay: asEffectConstructor(PingPongDelay),
2157
- chorus: asEffectConstructor(Chorus),
2158
- phaser: asEffectConstructor(Phaser),
2159
- tremolo: asEffectConstructor(Tremolo),
2160
- vibrato: asEffectConstructor(Vibrato),
2161
- autoPanner: asEffectConstructor(AutoPanner),
2162
- autoFilter: asEffectConstructor(AutoFilter),
2163
- autoWah: asEffectConstructor(AutoWah),
2164
- eq3: asEffectConstructor(EQ3),
2165
- distortion: asEffectConstructor(Distortion),
2166
- bitCrusher: asEffectConstructor(BitCrusher),
2167
- chebyshev: asEffectConstructor(Chebyshev),
2168
- compressor: asEffectConstructor(Compressor),
2169
- limiter: asEffectConstructor(Limiter),
2170
- gate: asEffectConstructor(Gate),
2171
- stereoWidener: asEffectConstructor(StereoWidener)
2172
- };
2173
- var instanceCounter = 0;
2174
- var generateInstanceId = () => {
2175
- return `effect_${Date.now()}_${++instanceCounter}`;
2176
- };
2177
- function createEffectInstance(definition, initialParams) {
2178
- const Constructor = effectConstructors[definition.id];
2179
- if (!Constructor) {
2180
- throw new Error(`Unknown effect type: ${definition.id}`);
2181
- }
2182
- const options = {};
2183
- definition.parameters.forEach((param) => {
2184
- var _a;
2185
- const value = (_a = initialParams == null ? void 0 : initialParams[param.name]) != null ? _a : param.default;
2186
- options[param.name] = value;
2187
- });
2188
- const effect = new Constructor(options);
2189
- const instanceId = generateInstanceId();
2190
- const effectRecord = effect;
2191
- return {
2192
- effect,
2193
- id: definition.id,
2194
- instanceId,
2195
- dispose() {
2196
- try {
2197
- effect.disconnect();
2198
- effect.dispose();
2199
- } catch (e) {
2200
- console.warn(
2201
- `[waveform-playlist] Error disposing effect "${definition.id}" (${instanceId}):`,
2202
- e
2203
- );
2204
- }
2205
- },
2206
- setParameter(name, value) {
2207
- const prop = effectRecord[name];
2208
- if (name === "wet") {
2209
- const wetProp = effectRecord["wet"];
2210
- if (wetProp && typeof wetProp === "object" && "value" in wetProp) {
2211
- wetProp.value = value;
2212
- return;
2213
- }
2214
- }
2215
- if (prop !== void 0) {
2216
- if (prop && typeof prop === "object" && "value" in prop) {
2217
- prop.value = value;
2218
- } else {
2219
- effectRecord[name] = value;
2220
- }
2221
- }
2222
- },
2223
- getParameter(name) {
2224
- if (name === "wet") {
2225
- const wetProp = effectRecord["wet"];
2226
- if (wetProp && typeof wetProp === "object" && "value" in wetProp) {
2227
- return wetProp.value;
2228
- }
2229
- }
2230
- const prop = effectRecord[name];
2231
- if (prop !== void 0) {
2232
- if (prop && typeof prop === "object" && "value" in prop) {
2233
- return prop.value;
2234
- }
2235
- return prop;
2236
- }
2237
- return void 0;
2238
- },
2239
- connect(destination) {
2240
- effect.connect(destination);
2241
- },
2242
- disconnect() {
2243
- try {
2244
- effect.disconnect();
2245
- } catch (e) {
2246
- console.warn(
2247
- `[waveform-playlist] Error disconnecting effect "${definition.id}" (${instanceId}):`,
2248
- e
2249
- );
2250
- }
2251
- }
2252
- };
2253
- }
2254
- function createEffectChain(effects) {
2255
- if (effects.length === 0) {
2256
- throw new Error("Cannot create effect chain with no effects");
2257
- }
2258
- for (let i = 0; i < effects.length - 1; i++) {
2259
- effects[i].effect.connect(effects[i + 1].effect);
2260
- }
2261
- return {
2262
- input: effects[0].effect,
2263
- output: effects[effects.length - 1].effect,
2264
- dispose() {
2265
- effects.forEach((e) => e.dispose());
2266
- }
2267
- };
2268
- }
2269
-
2270
- // src/hooks/useDynamicEffects.ts
2271
- import { Analyser as Analyser2 } from "tone";
2272
- function useDynamicEffects(fftSize = 256) {
2273
- const [activeEffects, setActiveEffects] = useState9([]);
2274
- const activeEffectsRef = useRef9(activeEffects);
2275
- activeEffectsRef.current = activeEffects;
2276
- const effectInstancesRef = useRef9(/* @__PURE__ */ new Map());
2277
- const analyserRef = useRef9(null);
2278
- const graphNodesRef = useRef9(null);
2279
- const rebuildChain = useCallback12((effects) => {
2280
- const nodes = graphNodesRef.current;
2281
- if (!nodes) return;
2282
- const { masterGainNode, destination, analyserNode } = nodes;
2283
- try {
2284
- masterGainNode.disconnect();
2285
- } catch (e) {
2286
- console.warn("[waveform-playlist] Error disconnecting master effects chain:", e);
2287
- }
2288
- const instances = effects.map((ae) => effectInstancesRef.current.get(ae.instanceId)).filter((inst) => inst !== void 0);
2289
- if (instances.length === 0) {
2290
- masterGainNode.connect(analyserNode);
2291
- analyserNode.connect(destination);
2292
- } else {
2293
- let currentNode = masterGainNode;
2294
- instances.forEach((inst) => {
2295
- try {
2296
- inst.disconnect();
2297
- } catch (e) {
2298
- console.warn(`[waveform-playlist] Error disconnecting effect "${inst.id}":`, e);
2299
- }
2300
- currentNode.connect(inst.effect);
2301
- currentNode = inst.effect;
2302
- });
2303
- currentNode.connect(analyserNode);
2304
- analyserNode.connect(destination);
2305
- }
2306
- }, []);
2307
- const addEffect = useCallback12((effectId) => {
2308
- const definition = getEffectDefinition(effectId);
2309
- if (!definition) {
2310
- console.error(`Unknown effect: ${effectId}`);
2311
- return;
2312
- }
2313
- const params = {};
2314
- definition.parameters.forEach((p) => {
2315
- params[p.name] = p.default;
2316
- });
2317
- const instance = createEffectInstance(definition, params);
2318
- effectInstancesRef.current.set(instance.instanceId, instance);
2319
- const newActiveEffect = {
2320
- instanceId: instance.instanceId,
2321
- effectId: definition.id,
2322
- definition,
2323
- params,
2324
- bypassed: false
2325
- };
2326
- setActiveEffects((prev) => [...prev, newActiveEffect]);
2327
- }, []);
2328
- const removeEffect = useCallback12((instanceId) => {
2329
- const instance = effectInstancesRef.current.get(instanceId);
2330
- if (instance) {
2331
- instance.dispose();
2332
- effectInstancesRef.current.delete(instanceId);
2333
- }
2334
- setActiveEffects((prev) => prev.filter((e) => e.instanceId !== instanceId));
2335
- }, []);
2336
- const updateParameter = useCallback12(
2337
- (instanceId, paramName, value) => {
2338
- const instance = effectInstancesRef.current.get(instanceId);
2339
- if (instance) {
2340
- instance.setParameter(paramName, value);
2341
- }
2342
- setActiveEffects(
2343
- (prev) => prev.map(
2344
- (e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { params: __spreadProps(__spreadValues({}, e.params), { [paramName]: value }) }) : e
2345
- )
2346
- );
2347
- },
2348
- []
2349
- );
2350
- const toggleBypass = useCallback12((instanceId) => {
2351
- var _a;
2352
- const effect = activeEffectsRef.current.find((e) => e.instanceId === instanceId);
2353
- if (!effect) return;
2354
- const newBypassed = !effect.bypassed;
2355
- const instance = effectInstancesRef.current.get(instanceId);
2356
- if (instance) {
2357
- const originalWet = (_a = effect.params.wet) != null ? _a : 1;
2358
- instance.setParameter("wet", newBypassed ? 0 : originalWet);
2359
- }
2360
- setActiveEffects(
2361
- (prev) => prev.map((e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { bypassed: newBypassed }) : e)
2362
- );
2363
- }, []);
2364
- const reorderEffects = useCallback12((fromIndex, toIndex) => {
2365
- setActiveEffects((prev) => {
2366
- const newEffects = [...prev];
2367
- const [removed] = newEffects.splice(fromIndex, 1);
2368
- newEffects.splice(toIndex, 0, removed);
2369
- return newEffects;
2370
- });
2371
- }, []);
2372
- const clearAllEffects = useCallback12(() => {
2373
- effectInstancesRef.current.forEach((inst) => inst.dispose());
2374
- effectInstancesRef.current.clear();
2375
- setActiveEffects([]);
2376
- }, []);
2377
- useEffect4(() => {
2378
- rebuildChain(activeEffects);
2379
- }, [activeEffects, rebuildChain]);
2380
- const masterEffects = useCallback12(
2381
- (masterGainNode, destination, _isOffline) => {
2382
- const analyserNode = new Analyser2("fft", fftSize);
2383
- analyserRef.current = analyserNode;
2384
- graphNodesRef.current = {
2385
- masterGainNode,
2386
- destination,
2387
- analyserNode
2388
- };
2389
- const effects = activeEffectsRef.current;
2390
- const instances = effects.map((ae) => effectInstancesRef.current.get(ae.instanceId)).filter((inst) => inst !== void 0);
2391
- if (instances.length === 0) {
2392
- masterGainNode.connect(analyserNode);
2393
- analyserNode.connect(destination);
2394
- } else {
2395
- let currentNode = masterGainNode;
2396
- instances.forEach((inst) => {
2397
- currentNode.connect(inst.effect);
2398
- currentNode = inst.effect;
2399
- });
2400
- currentNode.connect(analyserNode);
2401
- analyserNode.connect(destination);
2402
- }
2403
- return function cleanup() {
2404
- analyserNode.dispose();
2405
- analyserRef.current = null;
2406
- graphNodesRef.current = null;
2407
- };
2408
- },
2409
- [fftSize]
2410
- // Only fftSize - reads effects from ref
2411
- );
2412
- useEffect4(() => {
2413
- const effectInstances = effectInstancesRef.current;
2414
- return () => {
2415
- effectInstances.forEach((inst) => inst.dispose());
2416
- effectInstances.clear();
2417
- };
2418
- }, []);
2419
- const createOfflineEffectsFunction = useCallback12(() => {
2420
- const nonBypassedEffects = activeEffects.filter((e) => !e.bypassed);
2421
- if (nonBypassedEffects.length === 0) {
2422
- return void 0;
2423
- }
2424
- return (masterGainNode, destination, _isOffline) => {
2425
- const offlineInstances = [];
2426
- for (const activeEffect of nonBypassedEffects) {
2427
- const instance = createEffectInstance(activeEffect.definition, activeEffect.params);
2428
- offlineInstances.push(instance);
2429
- }
2430
- if (offlineInstances.length === 0) {
2431
- masterGainNode.connect(destination);
2432
- } else {
2433
- let currentNode = masterGainNode;
2434
- offlineInstances.forEach((inst) => {
2435
- currentNode.connect(inst.effect);
2436
- currentNode = inst.effect;
2437
- });
2438
- currentNode.connect(destination);
2439
- }
2440
- return function cleanup() {
2441
- offlineInstances.forEach((inst) => inst.dispose());
2442
- };
2443
- };
2444
- }, [activeEffects]);
2445
- return {
2446
- activeEffects,
2447
- availableEffects: effectDefinitions,
2448
- addEffect,
2449
- removeEffect,
2450
- updateParameter,
2451
- toggleBypass,
2452
- reorderEffects,
2453
- clearAllEffects,
2454
- masterEffects,
2455
- createOfflineEffectsFunction,
2456
- analyserRef
2457
- };
2458
- }
2459
-
2460
- // src/hooks/useTrackDynamicEffects.ts
2461
- import { useState as useState10, useCallback as useCallback13, useRef as useRef10, useEffect as useEffect5 } from "react";
2462
- function useTrackDynamicEffects() {
2463
- const [trackEffectsState, setTrackEffectsState] = useState10(
2464
- /* @__PURE__ */ new Map()
2465
- );
2466
- const trackEffectInstancesRef = useRef10(/* @__PURE__ */ new Map());
2467
- const trackGraphNodesRef = useRef10(/* @__PURE__ */ new Map());
2468
- const rebuildTrackChain = useCallback13((trackId, trackEffects) => {
2469
- const nodes = trackGraphNodesRef.current.get(trackId);
2470
- if (!nodes) return;
2471
- const { graphEnd, masterGainNode } = nodes;
2472
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2473
- try {
2474
- graphEnd.disconnect();
2475
- } catch (e) {
2476
- console.warn(`[waveform-playlist] Error disconnecting track "${trackId}" effect chain:`, e);
2477
- }
2478
- const instances = trackEffects.map((ae) => instancesMap == null ? void 0 : instancesMap.get(ae.instanceId)).filter((inst) => inst !== void 0);
2479
- if (instances.length === 0) {
2480
- graphEnd.connect(masterGainNode);
2481
- } else {
2482
- let currentNode = graphEnd;
2483
- instances.forEach((inst) => {
2484
- try {
2485
- inst.disconnect();
2486
- } catch (e) {
2487
- console.warn(
2488
- `[waveform-playlist] Error disconnecting effect "${inst.id}" on track "${trackId}":`,
2489
- e
2490
- );
2491
- }
2492
- currentNode.connect(inst.effect);
2493
- currentNode = inst.effect;
2494
- });
2495
- currentNode.connect(masterGainNode);
2496
- }
2497
- }, []);
2498
- const addEffectToTrack = useCallback13((trackId, effectId) => {
2499
- const definition = getEffectDefinition(effectId);
2500
- if (!definition) {
2501
- console.error(`Unknown effect: ${effectId}`);
2502
- return;
2503
- }
2504
- const params = {};
2505
- definition.parameters.forEach((p) => {
2506
- params[p.name] = p.default;
2507
- });
2508
- const instance = createEffectInstance(definition, params);
2509
- if (!trackEffectInstancesRef.current.has(trackId)) {
2510
- trackEffectInstancesRef.current.set(trackId, /* @__PURE__ */ new Map());
2511
- }
2512
- trackEffectInstancesRef.current.get(trackId).set(instance.instanceId, instance);
2513
- const newActiveEffect = {
2514
- instanceId: instance.instanceId,
2515
- effectId: definition.id,
2516
- definition,
2517
- params,
2518
- bypassed: false
2519
- };
2520
- setTrackEffectsState((prev) => {
2521
- const newState = new Map(prev);
2522
- const existing = newState.get(trackId) || [];
2523
- newState.set(trackId, [...existing, newActiveEffect]);
2524
- return newState;
2525
- });
2526
- }, []);
2527
- const removeEffectFromTrack = useCallback13((trackId, instanceId) => {
2528
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2529
- const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
2530
- if (instance) {
2531
- instance.dispose();
2532
- instancesMap == null ? void 0 : instancesMap.delete(instanceId);
2533
- }
2534
- setTrackEffectsState((prev) => {
2535
- const newState = new Map(prev);
2536
- const existing = newState.get(trackId) || [];
2537
- newState.set(
2538
- trackId,
2539
- existing.filter((e) => e.instanceId !== instanceId)
2540
- );
2541
- return newState;
2542
- });
2543
- }, []);
2544
- const updateTrackEffectParameter = useCallback13(
2545
- (trackId, instanceId, paramName, value) => {
2546
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2547
- const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
2548
- if (instance) {
2549
- instance.setParameter(paramName, value);
2550
- }
2551
- setTrackEffectsState((prev) => {
2552
- const newState = new Map(prev);
2553
- const existing = newState.get(trackId) || [];
2554
- newState.set(
2555
- trackId,
2556
- existing.map(
2557
- (e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { params: __spreadProps(__spreadValues({}, e.params), { [paramName]: value }) }) : e
2558
- )
2559
- );
2560
- return newState;
2561
- });
2562
- },
2563
- []
2564
- );
2565
- const toggleBypass = useCallback13((trackId, instanceId) => {
2566
- var _a;
2567
- const trackEffects = trackEffectsStateRef.current.get(trackId) || [];
2568
- const effect = trackEffects.find((e) => e.instanceId === instanceId);
2569
- if (!effect) return;
2570
- const newBypassed = !effect.bypassed;
2571
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2572
- const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
2573
- if (instance) {
2574
- const originalWet = (_a = effect.params.wet) != null ? _a : 1;
2575
- instance.setParameter("wet", newBypassed ? 0 : originalWet);
2576
- }
2577
- setTrackEffectsState((prev) => {
2578
- const newState = new Map(prev);
2579
- const existing = newState.get(trackId) || [];
2580
- newState.set(
2581
- trackId,
2582
- existing.map((e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { bypassed: newBypassed }) : e)
2583
- );
2584
- return newState;
2585
- });
2586
- }, []);
2587
- const clearTrackEffects = useCallback13((trackId) => {
2588
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2589
- if (instancesMap) {
2590
- instancesMap.forEach((inst) => inst.dispose());
2591
- instancesMap.clear();
2592
- }
2593
- setTrackEffectsState((prev) => {
2594
- const newState = new Map(prev);
2595
- newState.set(trackId, []);
2596
- return newState;
2597
- });
2598
- }, []);
2599
- const trackEffectsStateRef = useRef10(trackEffectsState);
2600
- trackEffectsStateRef.current = trackEffectsState;
2601
- const getTrackEffectsFunction = useCallback13(
2602
- (trackId) => {
2603
- return (graphEnd, masterGainNode, _isOffline) => {
2604
- trackGraphNodesRef.current.set(trackId, {
2605
- graphEnd,
2606
- masterGainNode
2607
- });
2608
- const trackEffects = trackEffectsStateRef.current.get(trackId) || [];
2609
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2610
- const instances = trackEffects.map((ae) => instancesMap == null ? void 0 : instancesMap.get(ae.instanceId)).filter((inst) => inst !== void 0);
2611
- if (instances.length === 0) {
2612
- graphEnd.connect(masterGainNode);
2613
- } else {
2614
- let currentNode = graphEnd;
2615
- instances.forEach((inst) => {
2616
- currentNode.connect(inst.effect);
2617
- currentNode = inst.effect;
2618
- });
2619
- currentNode.connect(masterGainNode);
2620
- }
2621
- return function cleanup() {
2622
- trackGraphNodesRef.current.delete(trackId);
2623
- };
2624
- };
2625
- },
2626
- []
2627
- // No dependencies - stable function that reads from refs
2628
- );
2629
- useEffect5(() => {
2630
- trackEffectsState.forEach((effects, trackId) => {
2631
- rebuildTrackChain(trackId, effects);
2632
- });
2633
- }, [trackEffectsState, rebuildTrackChain]);
2634
- useEffect5(() => {
2635
- const trackEffectInstances = trackEffectInstancesRef.current;
2636
- return () => {
2637
- trackEffectInstances.forEach((instancesMap) => {
2638
- instancesMap.forEach((inst) => inst.dispose());
2639
- instancesMap.clear();
2640
- });
2641
- trackEffectInstances.clear();
2642
- };
2643
- }, []);
2644
- const createOfflineTrackEffectsFunction = useCallback13(
2645
- (trackId) => {
2646
- const trackEffects = trackEffectsState.get(trackId) || [];
2647
- const nonBypassedEffects = trackEffects.filter((e) => !e.bypassed);
2648
- if (nonBypassedEffects.length === 0) {
2649
- return void 0;
2650
- }
2651
- return (graphEnd, masterGainNode, _isOffline) => {
2652
- const offlineInstances = [];
2653
- for (const activeEffect of nonBypassedEffects) {
2654
- const instance = createEffectInstance(activeEffect.definition, activeEffect.params);
2655
- offlineInstances.push(instance);
2656
- }
2657
- if (offlineInstances.length === 0) {
2658
- graphEnd.connect(masterGainNode);
2659
- } else {
2660
- let currentNode = graphEnd;
2661
- offlineInstances.forEach((inst) => {
2662
- currentNode.connect(inst.effect);
2663
- currentNode = inst.effect;
2664
- });
2665
- currentNode.connect(masterGainNode);
2666
- }
2667
- return function cleanup() {
2668
- offlineInstances.forEach((inst) => inst.dispose());
2669
- };
2670
- };
2671
- },
2672
- [trackEffectsState]
2673
- );
2674
- return {
2675
- trackEffectsState,
2676
- addEffectToTrack,
2677
- removeEffectFromTrack,
2678
- updateTrackEffectParameter,
2679
- toggleBypass,
2680
- clearTrackEffects,
2681
- getTrackEffectsFunction,
2682
- createOfflineTrackEffectsFunction,
2683
- availableEffects: effectDefinitions
2684
- };
2685
- }
2686
-
2687
- // src/hooks/useExportWav.ts
2688
- import { useState as useState11, useCallback as useCallback14 } from "react";
2689
- import {
2690
- gainToDb,
2691
- trackChannelCount,
2692
- applyFadeIn,
2693
- applyFadeOut
2694
- } from "@waveform-playlist/core";
2695
- import {
2696
- getUnderlyingAudioParam,
2697
- getGlobalAudioContext as getGlobalAudioContext2
2698
- } from "@waveform-playlist/playout";
2699
-
2700
- // src/utils/wavEncoder.ts
2701
- function encodeWav(audioBuffer, options = {}) {
2702
- const { bitDepth = 16 } = options;
2703
- const numChannels = audioBuffer.numberOfChannels;
2704
- const sampleRate = audioBuffer.sampleRate;
2705
- const numSamples = audioBuffer.length;
2706
- const bytesPerSample = bitDepth / 8;
2707
- const blockAlign = numChannels * bytesPerSample;
2708
- const byteRate = sampleRate * blockAlign;
2709
- const dataSize = numSamples * blockAlign;
2710
- const headerSize = 44;
2711
- const totalSize = headerSize + dataSize;
2712
- const buffer = new ArrayBuffer(totalSize);
2713
- const view = new DataView(buffer);
2714
- writeString(view, 0, "RIFF");
2715
- view.setUint32(4, totalSize - 8, true);
2716
- writeString(view, 8, "WAVE");
2717
- writeString(view, 12, "fmt ");
2718
- view.setUint32(16, 16, true);
2719
- view.setUint16(20, bitDepth === 32 ? 3 : 1, true);
2720
- view.setUint16(22, numChannels, true);
2721
- view.setUint32(24, sampleRate, true);
2722
- view.setUint32(28, byteRate, true);
2723
- view.setUint16(32, blockAlign, true);
2724
- view.setUint16(34, bitDepth, true);
2725
- writeString(view, 36, "data");
2726
- view.setUint32(40, dataSize, true);
2727
- const channelData = [];
2728
- for (let ch = 0; ch < numChannels; ch++) {
2729
- channelData.push(audioBuffer.getChannelData(ch));
2730
- }
2731
- let offset = headerSize;
2732
- if (bitDepth === 16) {
2733
- for (let i = 0; i < numSamples; i++) {
2734
- for (let ch = 0; ch < numChannels; ch++) {
2735
- const sample = channelData[ch][i];
2736
- const clampedSample = Math.max(-1, Math.min(1, sample));
2737
- const intSample = clampedSample < 0 ? clampedSample * 32768 : clampedSample * 32767;
2738
- view.setInt16(offset, intSample, true);
2739
- offset += 2;
2740
- }
2741
- }
2742
- } else {
2743
- for (let i = 0; i < numSamples; i++) {
2744
- for (let ch = 0; ch < numChannels; ch++) {
2745
- view.setFloat32(offset, channelData[ch][i], true);
2746
- offset += 4;
2747
- }
2748
- }
2749
- }
2750
- return new Blob([buffer], { type: "audio/wav" });
2751
- }
2752
- function writeString(view, offset, str) {
2753
- for (let i = 0; i < str.length; i++) {
2754
- view.setUint8(offset + i, str.charCodeAt(i));
2755
- }
2756
- }
2757
- function downloadBlob(blob, filename) {
2758
- const url = URL.createObjectURL(blob);
2759
- const a = document.createElement("a");
2760
- a.href = url;
2761
- a.download = filename;
2762
- a.style.display = "none";
2763
- document.body.appendChild(a);
2764
- a.click();
2765
- document.body.removeChild(a);
2766
- URL.revokeObjectURL(url);
2767
- }
2768
-
2769
- // src/hooks/useExportWav.ts
2770
- function useExportWav() {
2771
- const [isExporting, setIsExporting] = useState11(false);
2772
- const [progress, setProgress] = useState11(0);
2773
- const [error, setError] = useState11(null);
2774
- const exportWav = useCallback14(
2775
- (_0, _1, ..._2) => __async(null, [_0, _1, ..._2], function* (tracks, trackStates, options = {}) {
2776
- const {
2777
- filename = "export",
2778
- mode = "master",
2779
- trackIndex,
2780
- autoDownload = true,
2781
- applyEffects = true,
2782
- effectsFunction,
2783
- createOfflineTrackEffects,
2784
- bitDepth = 16,
2785
- onProgress
2786
- } = options;
2787
- setIsExporting(true);
2788
- setProgress(0);
2789
- setError(null);
2790
- try {
2791
- if (tracks.length === 0) {
2792
- throw new Error("No tracks to export");
2793
- }
2794
- if (mode === "individual" && (trackIndex === void 0 || trackIndex < 0 || trackIndex >= tracks.length)) {
2795
- throw new Error("Invalid track index for individual export");
2796
- }
2797
- const sampleRate = getGlobalAudioContext2().sampleRate;
2798
- let totalDurationSamples = 0;
2799
- for (const track of tracks) {
2800
- for (const clip of track.clips) {
2801
- const clipEndSample = clip.startSample + clip.durationSamples;
2802
- totalDurationSamples = Math.max(totalDurationSamples, clipEndSample);
2803
- }
2804
- }
2805
- totalDurationSamples += Math.round(sampleRate * 0.1);
2806
- const duration = totalDurationSamples / sampleRate;
2807
- const tracksToRender = mode === "individual" ? [{ track: tracks[trackIndex], state: trackStates[trackIndex], index: trackIndex }] : tracks.map((track, index) => ({ track, state: trackStates[index], index }));
2808
- const hasSolo = mode === "master" && trackStates.some((state) => state.soloed);
2809
- const reportProgress = (p) => {
2810
- setProgress(p);
2811
- onProgress == null ? void 0 : onProgress(p);
2812
- };
2813
- const renderedBuffer = yield renderOffline(
2814
- tracksToRender,
2815
- hasSolo,
2816
- duration,
2817
- sampleRate,
2818
- applyEffects,
2819
- effectsFunction,
2820
- createOfflineTrackEffects,
2821
- reportProgress
2822
- );
2823
- reportProgress(0.9);
2824
- const blob = encodeWav(renderedBuffer, { bitDepth });
2825
- reportProgress(1);
2826
- if (autoDownload) {
2827
- const exportFilename = mode === "individual" ? `${filename}_${tracks[trackIndex].name}` : filename;
2828
- downloadBlob(blob, `${exportFilename}.wav`);
2829
- }
2830
- return {
2831
- audioBuffer: renderedBuffer,
2832
- blob,
2833
- duration
2834
- };
2835
- } catch (err) {
2836
- const message = err instanceof Error ? err.message : "Export failed";
2837
- setError(message);
2838
- throw err;
2839
- } finally {
2840
- setIsExporting(false);
1341
+ key: "Home",
1342
+ action: selectFirst,
1343
+ description: "Select first annotation",
1344
+ preventDefault: true
1345
+ },
1346
+ {
1347
+ key: "End",
1348
+ action: selectLast,
1349
+ description: "Select last annotation",
1350
+ preventDefault: true
1351
+ },
1352
+ {
1353
+ key: "Escape",
1354
+ action: clearSelection,
1355
+ description: "Deselect annotation",
1356
+ preventDefault: true
2841
1357
  }
2842
- }),
2843
- []
1358
+ ],
1359
+ [selectPrevious, selectNext, selectFirst, selectLast, clearSelection]
2844
1360
  );
1361
+ useKeyboardShortcuts({
1362
+ shortcuts: activeAnnotationShortcuts,
1363
+ enabled: enabled && activeIndex >= 0
1364
+ });
1365
+ useKeyboardShortcuts({
1366
+ shortcuts: navigationShortcuts,
1367
+ enabled: enabled && annotations.length > 0 && !!onActiveAnnotationChange
1368
+ });
2845
1369
  return {
2846
- exportWav,
2847
- isExporting,
2848
- progress,
2849
- error
1370
+ moveStartBoundary,
1371
+ moveEndBoundary,
1372
+ selectPrevious,
1373
+ selectNext,
1374
+ selectFirst,
1375
+ selectLast,
1376
+ clearSelection,
1377
+ scrollToAnnotation,
1378
+ playActiveAnnotation
2850
1379
  };
2851
1380
  }
2852
- function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffects, effectsFunction, createOfflineTrackEffects, onProgress) {
2853
- return __async(this, null, function* () {
2854
- const { Offline, Volume: Volume2, Gain, Panner, Player, ToneAudioBuffer } = yield import("tone");
2855
- onProgress(0.1);
2856
- const audibleTracks = tracksToRender.filter(({ state }) => {
2857
- if (state.muted && !state.soloed) return false;
2858
- if (hasSolo && !state.soloed) return false;
2859
- return true;
2860
- });
2861
- const outputChannels = audibleTracks.reduce(
2862
- (max, { track }) => Math.max(max, trackChannelCount(track)),
2863
- 1
2864
- );
2865
- let buffer;
2866
- try {
2867
- buffer = yield Offline(
2868
- (_0) => __async(null, [_0], function* ({ transport, destination }) {
2869
- const masterVolume = new Volume2(0);
2870
- if (effectsFunction && applyEffects) {
2871
- effectsFunction(masterVolume, destination, true);
2872
- } else {
2873
- masterVolume.connect(destination);
2874
- }
2875
- for (const { track, state } of audibleTracks) {
2876
- const trackVolume = new Volume2(gainToDb(state.volume));
2877
- const trackPan = new Panner({ pan: state.pan, channelCount: trackChannelCount(track) });
2878
- const trackMute = new Gain(state.muted ? 0 : 1);
2879
- const trackEffects = createOfflineTrackEffects == null ? void 0 : createOfflineTrackEffects(track.id);
2880
- if (trackEffects && applyEffects) {
2881
- trackEffects(trackMute, masterVolume, true);
2882
- } else {
2883
- trackMute.connect(masterVolume);
2884
- }
2885
- trackPan.connect(trackMute);
2886
- trackVolume.connect(trackPan);
2887
- for (const clip of track.clips) {
2888
- const {
2889
- audioBuffer,
2890
- startSample,
2891
- durationSamples,
2892
- offsetSamples,
2893
- gain: clipGain,
2894
- fadeIn,
2895
- fadeOut
2896
- } = clip;
2897
- if (!audioBuffer) {
2898
- console.warn(
2899
- '[waveform-playlist] Skipping clip "' + (clip.name || clip.id) + '" - no audioBuffer for export'
2900
- );
2901
- continue;
2902
- }
2903
- const startTime = startSample / sampleRate;
2904
- const clipDuration = durationSamples / sampleRate;
2905
- const offset = offsetSamples / sampleRate;
2906
- const toneBuffer = new ToneAudioBuffer(audioBuffer);
2907
- const player = new Player(toneBuffer);
2908
- const fadeGain = new Gain(clipGain);
2909
- player.connect(fadeGain);
2910
- fadeGain.connect(trackVolume);
2911
- if (applyEffects) {
2912
- const audioParam = getUnderlyingAudioParam(fadeGain.gain);
2913
- if (audioParam) {
2914
- applyClipFades(audioParam, clipGain, startTime, clipDuration, fadeIn, fadeOut);
2915
- } else if (fadeIn || fadeOut) {
2916
- console.warn(
2917
- '[waveform-playlist] Cannot apply fades for clip "' + (clip.name || clip.id) + '" - AudioParam not accessible'
2918
- );
2919
- }
2920
- }
2921
- player.start(startTime, offset, clipDuration);
2922
- }
2923
- }
2924
- transport.start(0);
2925
- }),
2926
- duration,
2927
- outputChannels,
2928
- sampleRate
2929
- );
2930
- } catch (err) {
2931
- if (err instanceof Error) {
2932
- throw err;
2933
- } else {
2934
- throw new Error("Tone.Offline rendering failed: " + String(err));
2935
- }
2936
- }
2937
- onProgress(0.9);
2938
- const result = buffer.get();
2939
- if (!result) {
2940
- throw new Error("Offline rendering produced no audio buffer");
2941
- }
2942
- return result;
2943
- });
2944
- }
2945
- function applyClipFades(gainParam, clipGain, startTime, clipDuration, fadeIn, fadeOut) {
2946
- if (fadeIn) {
2947
- gainParam.setValueAtTime(0, startTime);
2948
- } else {
2949
- gainParam.setValueAtTime(clipGain, startTime);
2950
- }
2951
- if (fadeIn) {
2952
- applyFadeIn(gainParam, startTime, fadeIn.duration, fadeIn.type || "linear", 0, clipGain);
2953
- }
2954
- if (fadeOut) {
2955
- const fadeOutStart = startTime + clipDuration - fadeOut.duration;
2956
- if (!fadeIn || fadeIn.duration < clipDuration - fadeOut.duration) {
2957
- gainParam.setValueAtTime(clipGain, fadeOutStart);
2958
- }
2959
- applyFadeOut(gainParam, fadeOutStart, fadeOut.duration, fadeOut.type || "linear", clipGain, 0);
2960
- }
2961
- }
2962
1381
 
2963
1382
  // src/hooks/useAnimationFrameLoop.ts
2964
- import { useCallback as useCallback15, useEffect as useEffect6, useRef as useRef11 } from "react";
1383
+ import { useCallback as useCallback11, useEffect as useEffect3, useRef as useRef7 } from "react";
2965
1384
  var useAnimationFrameLoop = () => {
2966
- const animationFrameRef = useRef11(null);
2967
- const stopAnimationFrameLoop = useCallback15(() => {
1385
+ const animationFrameRef = useRef7(null);
1386
+ const stopAnimationFrameLoop = useCallback11(() => {
2968
1387
  if (animationFrameRef.current !== null) {
2969
1388
  cancelAnimationFrame(animationFrameRef.current);
2970
1389
  animationFrameRef.current = null;
2971
1390
  }
2972
1391
  }, []);
2973
- const startAnimationFrameLoop = useCallback15(
1392
+ const startAnimationFrameLoop = useCallback11(
2974
1393
  (callback) => {
2975
1394
  stopAnimationFrameLoop();
2976
1395
  animationFrameRef.current = requestAnimationFrame(callback);
2977
1396
  },
2978
1397
  [stopAnimationFrameLoop]
2979
1398
  );
2980
- useEffect6(() => {
1399
+ useEffect3(() => {
2981
1400
  return () => {
2982
1401
  stopAnimationFrameLoop();
2983
1402
  };
@@ -2990,7 +1409,7 @@ var useAnimationFrameLoop = () => {
2990
1409
  };
2991
1410
 
2992
1411
  // src/hooks/useWaveformDataCache.ts
2993
- import { useState as useState12, useEffect as useEffect7, useRef as useRef12, useCallback as useCallback16 } from "react";
1412
+ import { useState as useState8, useEffect as useEffect4, useRef as useRef8, useCallback as useCallback12 } from "react";
2994
1413
 
2995
1414
  // src/workers/peaksWorker.ts
2996
1415
  import WaveformData2 from "waveform-data";
@@ -3222,20 +1641,20 @@ function createPeaksWorker() {
3222
1641
 
3223
1642
  // src/hooks/useWaveformDataCache.ts
3224
1643
  function useWaveformDataCache(tracks, baseScale) {
3225
- const [cache, setCache] = useState12(() => /* @__PURE__ */ new Map());
3226
- const [isGenerating, setIsGenerating] = useState12(false);
3227
- const workerRef = useRef12(null);
3228
- const generatedByBufferRef = useRef12(/* @__PURE__ */ new WeakMap());
3229
- const inflightByBufferRef = useRef12(/* @__PURE__ */ new WeakMap());
3230
- const subscribersByBufferRef = useRef12(/* @__PURE__ */ new WeakMap());
3231
- const pendingCountRef = useRef12(0);
3232
- const getWorker = useCallback16(() => {
1644
+ const [cache, setCache] = useState8(() => /* @__PURE__ */ new Map());
1645
+ const [isGenerating, setIsGenerating] = useState8(false);
1646
+ const workerRef = useRef8(null);
1647
+ const generatedByBufferRef = useRef8(/* @__PURE__ */ new WeakMap());
1648
+ const inflightByBufferRef = useRef8(/* @__PURE__ */ new WeakMap());
1649
+ const subscribersByBufferRef = useRef8(/* @__PURE__ */ new WeakMap());
1650
+ const pendingCountRef = useRef8(0);
1651
+ const getWorker = useCallback12(() => {
3233
1652
  if (!workerRef.current) {
3234
1653
  workerRef.current = createPeaksWorker();
3235
1654
  }
3236
1655
  return workerRef.current;
3237
1656
  }, []);
3238
- useEffect7(() => {
1657
+ useEffect4(() => {
3239
1658
  let cancelled = false;
3240
1659
  const generatedByBuffer = generatedByBufferRef.current;
3241
1660
  const inflightByBuffer = inflightByBufferRef.current;
@@ -3340,7 +1759,7 @@ function useWaveformDataCache(tracks, baseScale) {
3340
1759
  setIsGenerating(false);
3341
1760
  };
3342
1761
  }, [tracks, baseScale, getWorker]);
3343
- useEffect7(() => {
1762
+ useEffect4(() => {
3344
1763
  return () => {
3345
1764
  var _a;
3346
1765
  (_a = workerRef.current) == null ? void 0 : _a.terminate();
@@ -3350,226 +1769,6 @@ function useWaveformDataCache(tracks, baseScale) {
3350
1769
  return { cache, isGenerating };
3351
1770
  }
3352
1771
 
3353
- // src/hooks/useDynamicTracks.ts
3354
- import { useState as useState13, useCallback as useCallback17, useRef as useRef13, useEffect as useEffect8 } from "react";
3355
- import { createTrack as createTrack2, createClipFromSeconds as createClipFromSeconds2 } from "@waveform-playlist/core";
3356
- import { getGlobalAudioContext as getGlobalAudioContext3 } from "@waveform-playlist/playout";
3357
- function getSourceName(source) {
3358
- var _a, _b, _c, _d, _e;
3359
- if (source instanceof File) {
3360
- return source.name.replace(/\.[^/.]+$/, "");
3361
- }
3362
- if (source instanceof Blob) {
3363
- return "Untitled";
3364
- }
3365
- if (typeof source === "string") {
3366
- return (_b = (_a = source.split("/").pop()) == null ? void 0 : _a.replace(/\.[^/.]+$/, "")) != null ? _b : "Untitled";
3367
- }
3368
- return (_e = (_d = source.name) != null ? _d : (_c = source.src.split("/").pop()) == null ? void 0 : _c.replace(/\.[^/.]+$/, "")) != null ? _e : "Untitled";
3369
- }
3370
- function decodeSource(source, audioContext, signal) {
3371
- return __async(this, null, function* () {
3372
- const name = getSourceName(source);
3373
- if (source instanceof Blob) {
3374
- const arrayBuffer2 = yield source.arrayBuffer();
3375
- signal == null ? void 0 : signal.throwIfAborted();
3376
- const audioBuffer2 = yield audioContext.decodeAudioData(arrayBuffer2);
3377
- return { audioBuffer: audioBuffer2, name };
3378
- }
3379
- const url = typeof source === "string" ? source : source.src;
3380
- const response = yield fetch(url, { signal });
3381
- if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
3382
- const arrayBuffer = yield response.arrayBuffer();
3383
- signal == null ? void 0 : signal.throwIfAborted();
3384
- const audioBuffer = yield audioContext.decodeAudioData(arrayBuffer);
3385
- return { audioBuffer, name };
3386
- });
3387
- }
3388
- function useDynamicTracks() {
3389
- const [tracks, setTracks] = useState13([]);
3390
- const [loadingCount, setLoadingCount] = useState13(0);
3391
- const [errors, setErrors] = useState13([]);
3392
- const cancelledRef = useRef13(false);
3393
- const loadingIdsRef = useRef13(/* @__PURE__ */ new Set());
3394
- const abortControllersRef = useRef13(/* @__PURE__ */ new Map());
3395
- useEffect8(() => {
3396
- const controllers = abortControllersRef.current;
3397
- return () => {
3398
- cancelledRef.current = true;
3399
- for (const controller of controllers.values()) {
3400
- controller.abort();
3401
- }
3402
- controllers.clear();
3403
- };
3404
- }, []);
3405
- const addTracks = useCallback17((sources) => {
3406
- if (sources.length === 0) return;
3407
- const audioContext = getGlobalAudioContext3();
3408
- const placeholders = sources.map((source) => ({
3409
- track: createTrack2({ name: `${getSourceName(source)} (loading...)`, clips: [] }),
3410
- source
3411
- }));
3412
- setTracks((prev) => [...prev, ...placeholders.map((p) => p.track)]);
3413
- setLoadingCount((prev) => prev + sources.length);
3414
- for (const { track, source } of placeholders) {
3415
- loadingIdsRef.current.add(track.id);
3416
- const controller = new AbortController();
3417
- abortControllersRef.current.set(track.id, controller);
3418
- (() => __async(null, null, function* () {
3419
- try {
3420
- const { audioBuffer, name } = yield decodeSource(source, audioContext, controller.signal);
3421
- const clip = createClipFromSeconds2({
3422
- audioBuffer,
3423
- startTime: 0,
3424
- duration: audioBuffer.duration,
3425
- offset: 0,
3426
- name
3427
- });
3428
- if (!cancelledRef.current && loadingIdsRef.current.has(track.id)) {
3429
- setTracks(
3430
- (prev) => prev.map((t) => t.id === track.id ? __spreadProps(__spreadValues({}, t), { name, clips: [clip] }) : t)
3431
- );
3432
- }
3433
- } catch (error) {
3434
- if (error instanceof DOMException && error.name === "AbortError") return;
3435
- console.warn("[waveform-playlist] Error loading audio:", error);
3436
- if (!cancelledRef.current && loadingIdsRef.current.has(track.id)) {
3437
- setTracks((prev) => prev.filter((t) => t.id !== track.id));
3438
- setErrors((prev) => [
3439
- ...prev,
3440
- {
3441
- name: getSourceName(source),
3442
- error: error instanceof Error ? error : new Error(String(error))
3443
- }
3444
- ]);
3445
- }
3446
- } finally {
3447
- abortControllersRef.current.delete(track.id);
3448
- if (!cancelledRef.current && loadingIdsRef.current.delete(track.id)) {
3449
- setLoadingCount((prev) => prev - 1);
3450
- }
3451
- }
3452
- }))();
3453
- }
3454
- }, []);
3455
- const removeTrack = useCallback17((trackId) => {
3456
- setTracks((prev) => prev.filter((t) => t.id !== trackId));
3457
- const controller = abortControllersRef.current.get(trackId);
3458
- if (controller) {
3459
- controller.abort();
3460
- abortControllersRef.current.delete(trackId);
3461
- }
3462
- if (loadingIdsRef.current.delete(trackId)) {
3463
- setLoadingCount((prev) => prev - 1);
3464
- }
3465
- }, []);
3466
- return {
3467
- tracks,
3468
- addTracks,
3469
- removeTrack,
3470
- loadingCount,
3471
- isLoading: loadingCount > 0,
3472
- errors
3473
- };
3474
- }
3475
-
3476
- // src/hooks/useOutputMeter.ts
3477
- import { useEffect as useEffect9, useState as useState14, useRef as useRef14, useCallback as useCallback18 } from "react";
3478
- import { getGlobalContext } from "@waveform-playlist/playout";
3479
- import { gainToNormalized } from "@waveform-playlist/core";
3480
- import { meterProcessorUrl } from "@waveform-playlist/worklets";
3481
- var PEAK_DECAY = 0.98;
3482
- function useOutputMeter(options = {}) {
3483
- const { channelCount = 2, updateRate = 60, isPlaying = false } = options;
3484
- const [levels, setLevels] = useState14(() => new Array(channelCount).fill(0));
3485
- const [peakLevels, setPeakLevels] = useState14(() => new Array(channelCount).fill(0));
3486
- const [rmsLevels, setRmsLevels] = useState14(() => new Array(channelCount).fill(0));
3487
- const workletNodeRef = useRef14(null);
3488
- const smoothedPeakRef = useRef14(new Array(channelCount).fill(0));
3489
- const [meterError, setMeterError] = useState14(null);
3490
- const resetPeak = useCallback18(
3491
- () => setPeakLevels(new Array(channelCount).fill(0)),
3492
- [channelCount]
3493
- );
3494
- useEffect9(() => {
3495
- if (!isPlaying) {
3496
- const zeros = new Array(channelCount).fill(0);
3497
- smoothedPeakRef.current = new Array(channelCount).fill(0);
3498
- setLevels(zeros);
3499
- setRmsLevels(zeros);
3500
- setPeakLevels(zeros);
3501
- }
3502
- }, [isPlaying, channelCount]);
3503
- useEffect9(() => {
3504
- let isMounted = true;
3505
- const setup = () => __async(null, null, function* () {
3506
- const context = getGlobalContext();
3507
- const rawCtx = context.rawContext;
3508
- yield rawCtx.audioWorklet.addModule(meterProcessorUrl);
3509
- if (!isMounted) return;
3510
- const workletNode = context.createAudioWorkletNode("meter-processor", {
3511
- channelCount,
3512
- channelCountMode: "explicit",
3513
- processorOptions: {
3514
- numberOfChannels: channelCount,
3515
- updateRate
3516
- }
3517
- });
3518
- workletNodeRef.current = workletNode;
3519
- workletNode.onprocessorerror = (event) => {
3520
- console.warn("[waveform-playlist] Output meter worklet processor error:", String(event));
3521
- };
3522
- const destination = context.destination;
3523
- destination.chain(workletNode);
3524
- smoothedPeakRef.current = new Array(channelCount).fill(0);
3525
- workletNode.port.onmessage = (event) => {
3526
- var _a;
3527
- if (!isMounted) return;
3528
- const { peak, rms } = event.data;
3529
- const smoothed = smoothedPeakRef.current;
3530
- const peakValues = [];
3531
- const rmsValues = [];
3532
- for (let ch = 0; ch < peak.length; ch++) {
3533
- smoothed[ch] = Math.max(peak[ch], ((_a = smoothed[ch]) != null ? _a : 0) * PEAK_DECAY);
3534
- peakValues.push(gainToNormalized(smoothed[ch]));
3535
- rmsValues.push(gainToNormalized(rms[ch]));
3536
- }
3537
- setLevels(peakValues);
3538
- setRmsLevels(rmsValues);
3539
- setPeakLevels((prev) => peakValues.map((val, i) => {
3540
- var _a2;
3541
- return Math.max((_a2 = prev[i]) != null ? _a2 : 0, val);
3542
- }));
3543
- };
3544
- });
3545
- setup().catch((err) => {
3546
- console.warn("[waveform-playlist] Failed to set up output meter:", String(err));
3547
- if (isMounted) {
3548
- setMeterError(err instanceof Error ? err : new Error(String(err)));
3549
- }
3550
- });
3551
- return () => {
3552
- isMounted = false;
3553
- if (workletNodeRef.current) {
3554
- try {
3555
- const context = getGlobalContext();
3556
- context.destination.chain();
3557
- } catch (err) {
3558
- console.warn("[waveform-playlist] Failed to restore destination chain:", String(err));
3559
- }
3560
- try {
3561
- workletNodeRef.current.disconnect();
3562
- workletNodeRef.current.port.close();
3563
- } catch (err) {
3564
- console.warn("[waveform-playlist] Output meter disconnect during cleanup:", String(err));
3565
- }
3566
- workletNodeRef.current = null;
3567
- }
3568
- };
3569
- }, [channelCount, updateRate]);
3570
- return { levels, peakLevels, rmsLevels, resetPeak, error: meterError };
3571
- }
3572
-
3573
1772
  // src/WaveformPlaylistContext.tsx
3574
1773
  import { jsx } from "react/jsx-runtime";
3575
1774
  var PlaybackAnimationContext = createContext(null);
@@ -3589,6 +1788,7 @@ var WaveformPlaylistProvider = ({
3589
1788
  annotationList,
3590
1789
  effects,
3591
1790
  onReady,
1791
+ onError,
3592
1792
  onAnnotationUpdate: _onAnnotationUpdate,
3593
1793
  onAnnotationsChange,
3594
1794
  barWidth = 1,
@@ -3599,18 +1799,19 @@ var WaveformPlaylistProvider = ({
3599
1799
  deferEngineRebuild = false,
3600
1800
  indefinitePlayback = false,
3601
1801
  sampleRate: sampleRateProp,
1802
+ createAdapter,
3602
1803
  children
3603
1804
  }) => {
3604
1805
  var _a, _b, _c, _d;
3605
1806
  const progressBarWidth = progressBarWidthProp != null ? progressBarWidthProp : barWidth + barGap;
3606
- const indefinitePlaybackRef = useRef15(indefinitePlayback);
1807
+ const indefinitePlaybackRef = useRef9(indefinitePlayback);
3607
1808
  indefinitePlaybackRef.current = indefinitePlayback;
3608
- const stableZoomLevels = useMemo4(
1809
+ const stableZoomLevels = useMemo3(
3609
1810
  () => zoomLevels,
3610
1811
  // eslint-disable-next-line react-hooks/exhaustive-deps
3611
1812
  [zoomLevels == null ? void 0 : zoomLevels.join(",")]
3612
1813
  );
3613
- const annotations = useMemo4(() => {
1814
+ const annotations = useMemo3(() => {
3614
1815
  if (!(annotationList == null ? void 0 : annotationList.annotations)) return [];
3615
1816
  if (process.env.NODE_ENV !== "production" && annotationList.annotations.length > 0) {
3616
1817
  const first = annotationList.annotations[0];
@@ -3623,63 +1824,50 @@ var WaveformPlaylistProvider = ({
3623
1824
  }
3624
1825
  return annotationList.annotations;
3625
1826
  }, [annotationList == null ? void 0 : annotationList.annotations]);
3626
- const annotationsRef = useRef15(annotations);
1827
+ const annotationsRef = useRef9(annotations);
3627
1828
  annotationsRef.current = annotations;
3628
- const [activeAnnotationId, setActiveAnnotationIdState] = useState15(null);
3629
- const [isPlaying, setIsPlaying] = useState15(false);
3630
- const [currentTime, setCurrentTime] = useState15(0);
3631
- const [duration, setDuration] = useState15(0);
3632
- const [audioBuffers, setAudioBuffers] = useState15([]);
3633
- const [peaksDataArray, setPeaksDataArray] = useState15([]);
3634
- const [trackStates, setTrackStates] = useState15([]);
3635
- const [isAutomaticScroll, setIsAutomaticScroll] = useState15(automaticScroll);
3636
- const [continuousPlay, setContinuousPlayState] = useState15(
1829
+ const [activeAnnotationId, setActiveAnnotationIdState] = useState9(null);
1830
+ const [isPlaying, setIsPlaying] = useState9(false);
1831
+ const [currentTime, setCurrentTime] = useState9(0);
1832
+ const [duration, setDuration] = useState9(0);
1833
+ const [audioBuffers, setAudioBuffers] = useState9([]);
1834
+ const [peaksDataArray, setPeaksDataArray] = useState9([]);
1835
+ const [trackStates, setTrackStates] = useState9([]);
1836
+ const [isAutomaticScroll, setIsAutomaticScroll] = useState9(automaticScroll);
1837
+ const [continuousPlay, setContinuousPlayState] = useState9(
3637
1838
  (_a = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _a : false
3638
1839
  );
3639
- const [linkEndpoints, setLinkEndpoints] = useState15((_b = annotationList == null ? void 0 : annotationList.linkEndpoints) != null ? _b : false);
3640
- const [annotationsEditable, setAnnotationsEditable] = useState15((_c = annotationList == null ? void 0 : annotationList.editable) != null ? _c : false);
3641
- const [isReady, setIsReady] = useState15(false);
3642
- const engineRef = useRef15(null);
3643
- const adapterRef = useRef15(null);
3644
- const audioInitializedRef = useRef15(false);
3645
- const isPlayingRef = useRef15(false);
1840
+ const [linkEndpoints, setLinkEndpoints] = useState9((_b = annotationList == null ? void 0 : annotationList.linkEndpoints) != null ? _b : false);
1841
+ const [annotationsEditable, setAnnotationsEditable] = useState9((_c = annotationList == null ? void 0 : annotationList.editable) != null ? _c : false);
1842
+ const [isReady, setIsReady] = useState9(false);
1843
+ const engineRef = useRef9(null);
1844
+ const adapterRef = useRef9(null);
1845
+ const audioInitializedRef = useRef9(false);
1846
+ const isPlayingRef = useRef9(false);
3646
1847
  isPlayingRef.current = isPlaying;
3647
- const playStartPositionRef = useRef15(0);
3648
- const currentTimeRef = useRef15(0);
3649
- const visualTimeRef = useRef15(0);
3650
- const tracksRef = useRef15(tracks);
3651
- const soundFontCacheRef = useRef15(soundFontCache);
1848
+ const playStartPositionRef = useRef9(0);
1849
+ const currentTimeRef = useRef9(0);
1850
+ const visualTimeRef = useRef9(0);
1851
+ const tracksRef = useRef9(tracks);
1852
+ const soundFontCacheRef = useRef9(soundFontCache);
3652
1853
  soundFontCacheRef.current = soundFontCache;
3653
- const trackStatesRef = useRef15(trackStates);
3654
- const playbackStartTimeRef = useRef15(0);
3655
- const audioStartPositionRef = useRef15(0);
3656
- const playbackEndTimeRef = useRef15(null);
3657
- const scrollContainerRef = useRef15(null);
3658
- const isAutomaticScrollRef = useRef15(false);
3659
- const frameCallbacksRef = useRef15(/* @__PURE__ */ new Map());
3660
- const continuousPlayRef = useRef15((_d = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _d : false);
3661
- const activeAnnotationIdRef = useRef15(null);
3662
- const engineTracksRef = useRef15(null);
3663
- const lastTracksVersionRef = useRef15(0);
3664
- const skipEngineDisposeRef = useRef15(false);
3665
- const isDraggingRef = useRef15(false);
3666
- const prevTracksRef = useRef15([]);
3667
- const samplesPerPixelRef = useRef15(initialSamplesPerPixel);
3668
- const [initialSampleRate] = useState15(() => {
3669
- if (typeof AudioContext === "undefined") return sampleRateProp != null ? sampleRateProp : 48e3;
3670
- try {
3671
- if (sampleRateProp !== void 0) {
3672
- return configureGlobalContext({ sampleRate: sampleRateProp });
3673
- }
3674
- return getGlobalAudioContext4().sampleRate;
3675
- } catch (err) {
3676
- console.warn(
3677
- "[waveform-playlist] Failed to configure AudioContext: " + String(err) + " \u2014 falling back to " + (sampleRateProp != null ? sampleRateProp : 48e3) + " Hz"
3678
- );
3679
- return sampleRateProp != null ? sampleRateProp : 48e3;
3680
- }
3681
- });
3682
- const sampleRateRef = useRef15(initialSampleRate);
1854
+ const trackStatesRef = useRef9(trackStates);
1855
+ const playbackStartTimeRef = useRef9(0);
1856
+ const audioStartPositionRef = useRef9(0);
1857
+ const playbackEndTimeRef = useRef9(null);
1858
+ const scrollContainerRef = useRef9(null);
1859
+ const isAutomaticScrollRef = useRef9(false);
1860
+ const frameCallbacksRef = useRef9(/* @__PURE__ */ new Map());
1861
+ const continuousPlayRef = useRef9((_d = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _d : false);
1862
+ const activeAnnotationIdRef = useRef9(null);
1863
+ const engineTracksRef = useRef9(null);
1864
+ const lastTracksVersionRef = useRef9(0);
1865
+ const skipEngineDisposeRef = useRef9(false);
1866
+ const isDraggingRef = useRef9(false);
1867
+ const prevTracksRef = useRef9([]);
1868
+ const samplesPerPixelRef = useRef9(initialSamplesPerPixel);
1869
+ const [initialSampleRate] = useState9(() => sampleRateProp != null ? sampleRateProp : 48e3);
1870
+ const sampleRateRef = useRef9(initialSampleRate);
3683
1871
  const { timeFormat, setTimeFormat, formatTime: formatTime2 } = useTimeFormat();
3684
1872
  const zoom = useZoomControls({ engineRef, initialSamplesPerPixel });
3685
1873
  const { samplesPerPixel, onEngineState: onZoomEngineState } = zoom;
@@ -3724,20 +1912,20 @@ var WaveformPlaylistProvider = ({
3724
1912
  onEngineState: onUndoEngineState
3725
1913
  } = useUndoState({ engineRef });
3726
1914
  const { animationFrameRef, startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();
3727
- const baseScale = useMemo4(
1915
+ const baseScale = useMemo3(
3728
1916
  () => Math.min(...stableZoomLevels != null ? stableZoomLevels : [256, 512, 1024, 2048, 4096, 8192]),
3729
1917
  [stableZoomLevels]
3730
1918
  );
3731
1919
  const { cache: waveformDataCache } = useWaveformDataCache(tracks, baseScale);
3732
- const setContinuousPlay = useCallback19((value) => {
1920
+ const setContinuousPlay = useCallback13((value) => {
3733
1921
  continuousPlayRef.current = value;
3734
1922
  setContinuousPlayState(value);
3735
1923
  }, []);
3736
- const setActiveAnnotationId = useCallback19((value) => {
1924
+ const setActiveAnnotationId = useCallback13((value) => {
3737
1925
  activeAnnotationIdRef.current = value;
3738
1926
  setActiveAnnotationIdState(value);
3739
1927
  }, []);
3740
- const setLoopRegionFromSelection = useCallback19(() => {
1928
+ const setLoopRegionFromSelection = useCallback13(() => {
3741
1929
  var _a2, _b2;
3742
1930
  const start = (_a2 = selectionStartRef.current) != null ? _a2 : 0;
3743
1931
  const end = (_b2 = selectionEndRef.current) != null ? _b2 : 0;
@@ -3745,10 +1933,10 @@ var WaveformPlaylistProvider = ({
3745
1933
  setLoopRegion(start, end);
3746
1934
  }
3747
1935
  }, [setLoopRegion, selectionStartRef, selectionEndRef]);
3748
- useEffect10(() => {
1936
+ useEffect5(() => {
3749
1937
  isAutomaticScrollRef.current = isAutomaticScroll;
3750
1938
  }, [isAutomaticScroll]);
3751
- useEffect10(() => {
1939
+ useEffect5(() => {
3752
1940
  trackStatesRef.current = trackStates;
3753
1941
  }, [trackStates]);
3754
1942
  tracksRef.current = tracks;
@@ -3759,7 +1947,7 @@ var WaveformPlaylistProvider = ({
3759
1947
  return current === pt;
3760
1948
  });
3761
1949
  skipEngineDisposeRef.current = isEngineTracks || isDraggingRef.current || isIncrementalAdd;
3762
- useEffect10(() => {
1950
+ useEffect5(() => {
3763
1951
  if (!scrollContainerRef.current || duration === 0) return;
3764
1952
  const container = scrollContainerRef.current;
3765
1953
  const oldSamplesPerPixel = samplesPerPixelRef.current;
@@ -3775,8 +1963,8 @@ var WaveformPlaylistProvider = ({
3775
1963
  container.scrollLeft = newScrollLeft;
3776
1964
  samplesPerPixelRef.current = newSamplesPerPixel;
3777
1965
  }, [samplesPerPixel, duration]);
3778
- const pendingResumeRef = useRef15(null);
3779
- useEffect10(() => {
1966
+ const pendingResumeRef = useRef9(null);
1967
+ useEffect5(() => {
3780
1968
  var _a2, _b2, _c2, _d2;
3781
1969
  if (isEngineTracks || isDraggingRef.current) {
3782
1970
  if (isEngineTracks) {
@@ -3881,6 +2069,7 @@ var WaveformPlaylistProvider = ({
3881
2069
  stopAnimationFrameLoop();
3882
2070
  pendingResumeRef.current = { position: resumePosition };
3883
2071
  }
2072
+ let cancelled = false;
3884
2073
  const loadAudio = () => __async(null, null, function* () {
3885
2074
  var _a3, _b3, _c3, _d3, _e;
3886
2075
  try {
@@ -3923,7 +2112,17 @@ var WaveformPlaylistProvider = ({
3923
2112
  lastTracksVersionRef.current = 0;
3924
2113
  engineTracksRef.current = null;
3925
2114
  audioInitializedRef.current = false;
3926
- const adapter = createToneAdapter({ effects, soundFontCache: soundFontCacheRef.current });
2115
+ const adapter = yield resolvePlayoutAdapter({
2116
+ createAdapter,
2117
+ effects,
2118
+ soundFontCache: soundFontCacheRef.current,
2119
+ sampleRate: sampleRateProp
2120
+ });
2121
+ if (cancelled) {
2122
+ adapter.dispose();
2123
+ return;
2124
+ }
2125
+ sampleRateRef.current = adapter.audioContext.sampleRate;
3927
2126
  adapterRef.current = adapter;
3928
2127
  const engine = new PlaylistEngine({
3929
2128
  adapter,
@@ -3981,11 +2180,23 @@ var WaveformPlaylistProvider = ({
3981
2180
  prevTracksRef.current = tracks;
3982
2181
  onReady == null ? void 0 : onReady();
3983
2182
  } catch (error) {
3984
- console.warn("[waveform-playlist] Error loading audio:", String(error));
2183
+ if (!engineRef.current && adapterRef.current) {
2184
+ try {
2185
+ adapterRef.current.dispose();
2186
+ } catch (disposeErr) {
2187
+ console.warn(
2188
+ "[waveform-playlist] adapter dispose after load failure threw: " + String(disposeErr)
2189
+ );
2190
+ }
2191
+ adapterRef.current = null;
2192
+ }
2193
+ console.warn("[waveform-playlist] Error loading audio: " + String(error));
2194
+ onError == null ? void 0 : onError(error instanceof Error ? error : new Error(String(error)));
3985
2195
  }
3986
2196
  });
3987
2197
  loadAudio();
3988
2198
  return () => {
2199
+ cancelled = true;
3989
2200
  if (skipEngineDisposeRef.current) {
3990
2201
  skipEngineDisposeRef.current = false;
3991
2202
  return;
@@ -4008,6 +2219,7 @@ var WaveformPlaylistProvider = ({
4008
2219
  // to the live adapter by the sync effect below; only adapter creation reads it
4009
2220
  // (via soundFontCacheRef).
4010
2221
  onReady,
2222
+ onError,
4011
2223
  effects,
4012
2224
  stopAnimationFrameLoop,
4013
2225
  onSelectionEngineState,
@@ -4026,10 +2238,10 @@ var WaveformPlaylistProvider = ({
4026
2238
  stableZoomLevels,
4027
2239
  deferEngineRebuild
4028
2240
  ]);
4029
- useEffect10(() => {
2241
+ useEffect5(() => {
4030
2242
  syncSoundFontCacheToAdapter(adapterRef.current, soundFontCache);
4031
2243
  }, [soundFontCache]);
4032
- useEffect10(() => {
2244
+ useEffect5(() => {
4033
2245
  if (tracks.length === 0) return;
4034
2246
  const allTrackPeaks = tracks.map((track) => {
4035
2247
  const clipPeaks = track.clips.map((clip) => {
@@ -4110,8 +2322,15 @@ var WaveformPlaylistProvider = ({
4110
2322
  });
4111
2323
  setPeaksDataArray(allTrackPeaks);
4112
2324
  }, [tracks, samplesPerPixel, mono, waveformDataCache, deferEngineRebuild]);
4113
- const getPlaybackTimeFallbackWarnedRef = useRef15(false);
4114
- const getPlaybackTime = useCallback19(() => {
2325
+ const getAudioContextTime = useCallback13(
2326
+ () => {
2327
+ var _a2, _b2;
2328
+ return (_b2 = (_a2 = adapterRef.current) == null ? void 0 : _a2.audioContext.currentTime) != null ? _b2 : 0;
2329
+ },
2330
+ []
2331
+ );
2332
+ const getPlaybackTimeFallbackWarnedRef = useRef9(false);
2333
+ const getPlaybackTime = useCallback13(() => {
4115
2334
  var _a2, _b2;
4116
2335
  if (engineRef.current) {
4117
2336
  return engineRef.current.getCurrentTime();
@@ -4122,30 +2341,35 @@ var WaveformPlaylistProvider = ({
4122
2341
  "[waveform-playlist] getPlaybackTime called without engine. Falling back to manual elapsed time (loop wrapping will not work)."
4123
2342
  );
4124
2343
  }
4125
- const elapsed = getContext2().currentTime - ((_a2 = playbackStartTimeRef.current) != null ? _a2 : 0);
2344
+ const elapsed = getAudioContextTime() - ((_a2 = playbackStartTimeRef.current) != null ? _a2 : 0);
4126
2345
  return ((_b2 = audioStartPositionRef.current) != null ? _b2 : 0) + elapsed;
4127
- }, []);
4128
- const toVisualTime = useCallback19((rawTime) => {
2346
+ }, [getAudioContextTime]);
2347
+ const toVisualTime = useCallback13((rawTime) => {
4129
2348
  return Number.isFinite(rawTime) ? Math.max(0, rawTime) : 0;
4130
2349
  }, []);
4131
- const setCurrentTimeRefs = useCallback19(
2350
+ const setCurrentTimeRefs = useCallback13(
4132
2351
  (rawTime) => {
4133
2352
  currentTimeRef.current = rawTime;
4134
2353
  visualTimeRef.current = toVisualTime(rawTime);
4135
2354
  },
4136
2355
  [toVisualTime]
4137
2356
  );
4138
- const getLookAhead = useCallback19(() => {
2357
+ const getLookAhead = useCallback13(() => {
4139
2358
  var _a2, _b2;
4140
2359
  return (_b2 = (_a2 = engineRef.current) == null ? void 0 : _a2.lookAhead) != null ? _b2 : 0;
4141
2360
  }, []);
4142
- const registerFrameCallback = useCallback19((id, cb) => {
2361
+ const getOutputLatency = useCallback13(() => {
2362
+ var _a2;
2363
+ const audioCtx = (_a2 = adapterRef.current) == null ? void 0 : _a2.audioContext;
2364
+ return audioCtx && "outputLatency" in audioCtx ? audioCtx.outputLatency : 0;
2365
+ }, []);
2366
+ const registerFrameCallback = useCallback13((id, cb) => {
4143
2367
  frameCallbacksRef.current.set(id, cb);
4144
2368
  }, []);
4145
- const unregisterFrameCallback = useCallback19((id) => {
2369
+ const unregisterFrameCallback = useCallback13((id) => {
4146
2370
  frameCallbacksRef.current.delete(id);
4147
2371
  }, []);
4148
- const startAnimationLoop = useCallback19(() => {
2372
+ const startAnimationLoop = useCallback13(() => {
4149
2373
  const updateTime = () => {
4150
2374
  const time = getPlaybackTime();
4151
2375
  currentTimeRef.current = time;
@@ -4233,15 +2457,14 @@ var WaveformPlaylistProvider = ({
4233
2457
  setCurrentTimeRefs
4234
2458
  ]);
4235
2459
  const stopAnimationLoop = stopAnimationFrameLoop;
4236
- useEffect10(() => {
2460
+ useEffect5(() => {
4237
2461
  const reschedulePlayback = () => __async(null, null, function* () {
4238
2462
  if (isPlaying && animationFrameRef.current && engineRef.current) {
4239
2463
  if (continuousPlay) {
4240
2464
  const currentPos = currentTimeRef.current;
4241
2465
  engineRef.current.stop();
4242
2466
  stopAnimationLoop();
4243
- const context = getContext2();
4244
- const timeNow = context.currentTime;
2467
+ const timeNow = getAudioContextTime();
4245
2468
  playbackStartTimeRef.current = timeNow;
4246
2469
  audioStartPositionRef.current = currentPos;
4247
2470
  engineRef.current.play(currentPos);
@@ -4257,14 +2480,20 @@ var WaveformPlaylistProvider = ({
4257
2480
  setIsPlaying(false);
4258
2481
  stopAnimationLoop();
4259
2482
  });
4260
- }, [continuousPlay, isPlaying, startAnimationLoop, stopAnimationLoop, animationFrameRef]);
4261
- useEffect10(() => {
2483
+ }, [
2484
+ continuousPlay,
2485
+ isPlaying,
2486
+ startAnimationLoop,
2487
+ stopAnimationLoop,
2488
+ animationFrameRef,
2489
+ getAudioContextTime
2490
+ ]);
2491
+ useEffect5(() => {
4262
2492
  const resumePlayback = () => __async(null, null, function* () {
4263
2493
  if (pendingResumeRef.current && engineRef.current) {
4264
2494
  const { position } = pendingResumeRef.current;
4265
2495
  pendingResumeRef.current = null;
4266
- const context = getContext2();
4267
- const timeNow = context.currentTime;
2496
+ const timeNow = getAudioContextTime();
4268
2497
  playbackStartTimeRef.current = timeNow;
4269
2498
  audioStartPositionRef.current = position;
4270
2499
  if (!audioInitializedRef.current) {
@@ -4281,8 +2510,8 @@ var WaveformPlaylistProvider = ({
4281
2510
  setIsPlaying(false);
4282
2511
  stopAnimationLoop();
4283
2512
  });
4284
- }, [tracks, startAnimationLoop, stopAnimationLoop]);
4285
- const play = useCallback19(
2513
+ }, [tracks, startAnimationLoop, stopAnimationLoop, getAudioContextTime]);
2514
+ const play = useCallback13(
4286
2515
  (startTime, playDuration) => __async(null, null, function* () {
4287
2516
  if (!engineRef.current) return;
4288
2517
  const actualStartTime = startTime != null ? startTime : currentTimeRef.current;
@@ -4291,8 +2520,7 @@ var WaveformPlaylistProvider = ({
4291
2520
  engineRef.current.stop();
4292
2521
  engineRef.current.seek(actualStartTime);
4293
2522
  stopAnimationLoop();
4294
- const context = getContext2();
4295
- const startTimeNow = context.currentTime;
2523
+ const startTimeNow = getAudioContextTime();
4296
2524
  playbackStartTimeRef.current = startTimeNow;
4297
2525
  audioStartPositionRef.current = actualStartTime;
4298
2526
  playbackEndTimeRef.current = playDuration !== void 0 ? actualStartTime + playDuration : null;
@@ -4311,9 +2539,9 @@ var WaveformPlaylistProvider = ({
4311
2539
  setIsPlaying(true);
4312
2540
  startAnimationLoop();
4313
2541
  }),
4314
- [startAnimationLoop, stopAnimationLoop, setCurrentTimeRefs]
2542
+ [startAnimationLoop, stopAnimationLoop, setCurrentTimeRefs, getAudioContextTime]
4315
2543
  );
4316
- const pause = useCallback19(() => {
2544
+ const pause = useCallback13(() => {
4317
2545
  if (!engineRef.current) return;
4318
2546
  const pauseTime = getPlaybackTime();
4319
2547
  engineRef.current.pause();
@@ -4322,7 +2550,7 @@ var WaveformPlaylistProvider = ({
4322
2550
  setCurrentTimeRefs(pauseTime);
4323
2551
  setCurrentTime(pauseTime);
4324
2552
  }, [stopAnimationLoop, getPlaybackTime, setCurrentTimeRefs]);
4325
- const stop = useCallback19(() => {
2553
+ const stop = useCallback13(() => {
4326
2554
  if (!engineRef.current) return;
4327
2555
  engineRef.current.stop();
4328
2556
  setIsPlaying(false);
@@ -4331,7 +2559,7 @@ var WaveformPlaylistProvider = ({
4331
2559
  setCurrentTime(playStartPositionRef.current);
4332
2560
  setActiveAnnotationId(null);
4333
2561
  }, [stopAnimationLoop, setActiveAnnotationId, setCurrentTimeRefs]);
4334
- const seekTo = useCallback19(
2562
+ const seekTo = useCallback13(
4335
2563
  (time) => {
4336
2564
  const clampedTime = Math.max(0, Math.min(time, duration));
4337
2565
  setCurrentTimeRefs(clampedTime);
@@ -4342,7 +2570,7 @@ var WaveformPlaylistProvider = ({
4342
2570
  },
4343
2571
  [duration, isPlaying, play, setCurrentTimeRefs]
4344
2572
  );
4345
- const setTrackMute = useCallback19(
2573
+ const setTrackMute = useCallback13(
4346
2574
  (trackIndex, muted) => {
4347
2575
  var _a2;
4348
2576
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4356,7 +2584,7 @@ var WaveformPlaylistProvider = ({
4356
2584
  },
4357
2585
  [trackStates]
4358
2586
  );
4359
- const setTrackSolo = useCallback19(
2587
+ const setTrackSolo = useCallback13(
4360
2588
  (trackIndex, soloed) => {
4361
2589
  var _a2;
4362
2590
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4370,7 +2598,7 @@ var WaveformPlaylistProvider = ({
4370
2598
  },
4371
2599
  [trackStates]
4372
2600
  );
4373
- const setTrackVolume = useCallback19(
2601
+ const setTrackVolume = useCallback13(
4374
2602
  (trackIndex, volume2) => {
4375
2603
  var _a2;
4376
2604
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4384,7 +2612,7 @@ var WaveformPlaylistProvider = ({
4384
2612
  },
4385
2613
  [trackStates]
4386
2614
  );
4387
- const setTrackPan = useCallback19(
2615
+ const setTrackPan = useCallback13(
4388
2616
  (trackIndex, pan) => {
4389
2617
  var _a2;
4390
2618
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4398,7 +2626,7 @@ var WaveformPlaylistProvider = ({
4398
2626
  },
4399
2627
  [trackStates]
4400
2628
  );
4401
- const setSelection = useCallback19(
2629
+ const setSelection = useCallback13(
4402
2630
  (start, end) => {
4403
2631
  setSelectionEngine(start, end);
4404
2632
  setCurrentTimeRefs(start);
@@ -4411,12 +2639,12 @@ var WaveformPlaylistProvider = ({
4411
2639
  },
4412
2640
  [isPlaying, setSelectionEngine, setCurrentTimeRefs]
4413
2641
  );
4414
- const setScrollContainer = useCallback19((element) => {
2642
+ const setScrollContainer = useCallback13((element) => {
4415
2643
  scrollContainerRef.current = element;
4416
2644
  }, []);
4417
- const onAnnotationsChangeRef = useRef15(onAnnotationsChange);
2645
+ const onAnnotationsChangeRef = useRef9(onAnnotationsChange);
4418
2646
  onAnnotationsChangeRef.current = onAnnotationsChange;
4419
- const setAnnotations = useCallback19(
2647
+ const setAnnotations = useCallback13(
4420
2648
  (action) => {
4421
2649
  const updated = typeof action === "function" ? action(annotationsRef.current) : action;
4422
2650
  if (!onAnnotationsChangeRef.current) {
@@ -4434,7 +2662,7 @@ var WaveformPlaylistProvider = ({
4434
2662
  const sampleRate = sampleRateRef.current;
4435
2663
  const timeScaleHeight = timescale ? 30 : 0;
4436
2664
  const minimumPlaylistHeight = tracks.length * waveHeight + timeScaleHeight;
4437
- const animationValue = useMemo4(
2665
+ const animationValue = useMemo3(
4438
2666
  () => ({
4439
2667
  isPlaying,
4440
2668
  currentTime,
@@ -4443,7 +2671,9 @@ var WaveformPlaylistProvider = ({
4443
2671
  playbackStartTimeRef,
4444
2672
  audioStartPositionRef,
4445
2673
  getPlaybackTime,
2674
+ getAudioContextTime,
4446
2675
  getLookAhead,
2676
+ getOutputLatency,
4447
2677
  registerFrameCallback,
4448
2678
  unregisterFrameCallback
4449
2679
  }),
@@ -4455,12 +2685,14 @@ var WaveformPlaylistProvider = ({
4455
2685
  playbackStartTimeRef,
4456
2686
  audioStartPositionRef,
4457
2687
  getPlaybackTime,
2688
+ getAudioContextTime,
4458
2689
  getLookAhead,
2690
+ getOutputLatency,
4459
2691
  registerFrameCallback,
4460
2692
  unregisterFrameCallback
4461
2693
  ]
4462
2694
  );
4463
- const stateValue = useMemo4(
2695
+ const stateValue = useMemo3(
4464
2696
  () => ({
4465
2697
  continuousPlay,
4466
2698
  linkEndpoints,
@@ -4496,17 +2728,17 @@ var WaveformPlaylistProvider = ({
4496
2728
  canRedo
4497
2729
  ]
4498
2730
  );
4499
- const setCurrentTimeControl = useCallback19(
2731
+ const setCurrentTimeControl = useCallback13(
4500
2732
  (time) => {
4501
2733
  setCurrentTimeRefs(time);
4502
2734
  setCurrentTime(time);
4503
2735
  },
4504
2736
  [setCurrentTimeRefs]
4505
2737
  );
4506
- const setAutomaticScrollControl = useCallback19((enabled) => {
2738
+ const setAutomaticScrollControl = useCallback13((enabled) => {
4507
2739
  setIsAutomaticScroll(enabled);
4508
2740
  }, []);
4509
- const controlsValue = useMemo4(
2741
+ const controlsValue = useMemo3(
4510
2742
  () => ({
4511
2743
  // Playback controls
4512
2744
  play,
@@ -4582,7 +2814,7 @@ var WaveformPlaylistProvider = ({
4582
2814
  redo
4583
2815
  ]
4584
2816
  );
4585
- const dataValue = useMemo4(
2817
+ const dataValue = useMemo3(
4586
2818
  () => ({
4587
2819
  duration,
4588
2820
  audioBuffers,
@@ -4671,14 +2903,38 @@ var usePlaylistDataOptional = () => useContext(PlaylistDataContext);
4671
2903
  import {
4672
2904
  createContext as createContext2,
4673
2905
  useContext as useContext2,
4674
- useState as useState16,
4675
- useEffect as useEffect11,
4676
- useRef as useRef16,
4677
- useCallback as useCallback20,
4678
- useMemo as useMemo5
2906
+ useState as useState10,
2907
+ useEffect as useEffect6,
2908
+ useRef as useRef10,
2909
+ useCallback as useCallback14,
2910
+ useMemo as useMemo4
4679
2911
  } from "react";
4680
2912
  import { ThemeProvider as ThemeProvider2 } from "styled-components";
4681
- import { MediaElementPlayout } from "@waveform-playlist/media-element-playout";
2913
+
2914
+ // src/playout/resolveMediaElementPlayout.ts
2915
+ var INSTALL_HINT2 = "@waveform-playlist/media-element-playout is required for the default MediaElement engine. Install with: npm install @waveform-playlist/media-element-playout \u2014 or pass a custom `createPlayout`.";
2916
+ function resolveMediaElementPlayout(opts) {
2917
+ return __async(this, null, function* () {
2918
+ if (opts.createPlayout) {
2919
+ return opts.createPlayout();
2920
+ }
2921
+ let mod;
2922
+ try {
2923
+ mod = yield import("@waveform-playlist/media-element-playout");
2924
+ } catch (originalErr) {
2925
+ console.warn(
2926
+ "[waveform-playlist] @waveform-playlist/media-element-playout dynamic import failed: " + String(originalErr)
2927
+ );
2928
+ throw new Error(INSTALL_HINT2);
2929
+ }
2930
+ return new mod.MediaElementPlayout({
2931
+ playbackRate: opts.playbackRate,
2932
+ preservesPitch: opts.preservesPitch
2933
+ });
2934
+ });
2935
+ }
2936
+
2937
+ // src/MediaElementPlaylistContext.tsx
4682
2938
  import { defaultTheme as defaultTheme2 } from "@waveform-playlist/ui-components";
4683
2939
  import { jsx as jsx2 } from "react/jsx-runtime";
4684
2940
  var MediaElementAnimationContext = createContext2(null);
@@ -4702,16 +2958,18 @@ var MediaElementPlaylistProvider = ({
4702
2958
  audioContext,
4703
2959
  onAnnotationsChange,
4704
2960
  onReady,
2961
+ onError,
2962
+ createPlayout,
4705
2963
  children
4706
2964
  }) => {
4707
2965
  var _a;
4708
2966
  const progressBarWidth = progressBarWidthProp != null ? progressBarWidthProp : barWidth + barGap;
4709
- const [isPlaying, setIsPlaying] = useState16(false);
4710
- const [currentTime, setCurrentTime] = useState16(0);
4711
- const [duration, setDuration] = useState16(0);
4712
- const [peaksDataArray, setPeaksDataArray] = useState16([]);
4713
- const [playbackRate, setPlaybackRateState] = useState16(initialPlaybackRate);
4714
- const annotations = useMemo5(() => {
2967
+ const [isPlaying, setIsPlaying] = useState10(false);
2968
+ const [currentTime, setCurrentTime] = useState10(0);
2969
+ const [duration, setDuration] = useState10(0);
2970
+ const [peaksDataArray, setPeaksDataArray] = useState10([]);
2971
+ const [playbackRate, setPlaybackRateState] = useState10(initialPlaybackRate);
2972
+ const annotations = useMemo4(() => {
4715
2973
  if (!(annotationList == null ? void 0 : annotationList.annotations)) return [];
4716
2974
  if (process.env.NODE_ENV !== "production" && annotationList.annotations.length > 0) {
4717
2975
  const first = annotationList.annotations[0];
@@ -4724,73 +2982,92 @@ var MediaElementPlaylistProvider = ({
4724
2982
  }
4725
2983
  return annotationList.annotations;
4726
2984
  }, [annotationList == null ? void 0 : annotationList.annotations]);
4727
- const annotationsRef = useRef16(annotations);
2985
+ const annotationsRef = useRef10(annotations);
4728
2986
  annotationsRef.current = annotations;
4729
- const [activeAnnotationId, setActiveAnnotationIdState] = useState16(null);
4730
- const [continuousPlay, setContinuousPlayState] = useState16(
2987
+ const [activeAnnotationId, setActiveAnnotationIdState] = useState10(null);
2988
+ const [continuousPlay, setContinuousPlayState] = useState10(
4731
2989
  (_a = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _a : false
4732
2990
  );
4733
- const [samplesPerPixel] = useState16(initialSamplesPerPixel);
4734
- const [isAutomaticScroll, setIsAutomaticScroll] = useState16(automaticScroll);
4735
- const playoutRef = useRef16(null);
4736
- const currentTimeRef = useRef16(0);
4737
- const continuousPlayRef = useRef16(continuousPlay);
4738
- const activeAnnotationIdRef = useRef16(null);
4739
- const scrollContainerRef = useRef16(null);
4740
- const isAutomaticScrollRef = useRef16(automaticScroll);
4741
- const samplesPerPixelRef = useRef16(initialSamplesPerPixel);
2991
+ const [samplesPerPixel] = useState10(initialSamplesPerPixel);
2992
+ const [isAutomaticScroll, setIsAutomaticScroll] = useState10(automaticScroll);
2993
+ const playoutRef = useRef10(null);
2994
+ const currentTimeRef = useRef10(0);
2995
+ const continuousPlayRef = useRef10(continuousPlay);
2996
+ const activeAnnotationIdRef = useRef10(null);
2997
+ const scrollContainerRef = useRef10(null);
2998
+ const isAutomaticScrollRef = useRef10(automaticScroll);
2999
+ const samplesPerPixelRef = useRef10(initialSamplesPerPixel);
4742
3000
  const { startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();
4743
- useEffect11(() => {
3001
+ useEffect6(() => {
4744
3002
  continuousPlayRef.current = continuousPlay;
4745
3003
  }, [continuousPlay]);
4746
- useEffect11(() => {
3004
+ useEffect6(() => {
4747
3005
  isAutomaticScrollRef.current = isAutomaticScroll;
4748
3006
  }, [isAutomaticScroll]);
4749
- const setActiveAnnotationId = useCallback20((value) => {
3007
+ const setActiveAnnotationId = useCallback14((value) => {
4750
3008
  activeAnnotationIdRef.current = value;
4751
3009
  setActiveAnnotationIdState(value);
4752
3010
  }, []);
4753
- const setContinuousPlay = useCallback20((value) => {
3011
+ const setContinuousPlay = useCallback14((value) => {
4754
3012
  continuousPlayRef.current = value;
4755
3013
  setContinuousPlayState(value);
4756
3014
  }, []);
4757
- const setScrollContainer = useCallback20((element) => {
3015
+ const setScrollContainer = useCallback14((element) => {
4758
3016
  scrollContainerRef.current = element;
4759
3017
  }, []);
4760
3018
  const sampleRate = track.waveformData.sample_rate;
4761
- useEffect11(() => {
4762
- var _a2, _b;
4763
- const playout = new MediaElementPlayout({
4764
- playbackRate: initialPlaybackRate,
4765
- preservesPitch
4766
- });
4767
- playout.addTrack({
4768
- source: track.source,
4769
- peaks: track.waveformData,
4770
- name: track.name,
4771
- audioContext,
4772
- fadeIn: track.fadeIn,
4773
- fadeOut: track.fadeOut
4774
- });
4775
- const mediaTrack = playout.getTrack((_b = (_a2 = playout["track"]) == null ? void 0 : _a2.id) != null ? _b : "");
4776
- if (mediaTrack) {
4777
- mediaTrack.setOnTimeUpdateCallback((time) => {
4778
- currentTimeRef.current = time;
4779
- });
4780
- }
4781
- playout.setOnPlaybackComplete(() => {
4782
- stopAnimationFrameLoop();
4783
- setIsPlaying(false);
4784
- setActiveAnnotationId(null);
4785
- currentTimeRef.current = 0;
4786
- setCurrentTime(0);
4787
- });
4788
- playoutRef.current = playout;
4789
- setDuration(track.waveformData.duration);
4790
- onReady == null ? void 0 : onReady();
3019
+ useEffect6(() => {
3020
+ let cancelled = false;
3021
+ let createdPlayout = null;
3022
+ (() => __async(null, null, function* () {
3023
+ var _a2, _b;
3024
+ try {
3025
+ const playout = yield resolveMediaElementPlayout({
3026
+ createPlayout,
3027
+ playbackRate: initialPlaybackRate,
3028
+ preservesPitch
3029
+ });
3030
+ if (cancelled) {
3031
+ playout.dispose();
3032
+ return;
3033
+ }
3034
+ createdPlayout = playout;
3035
+ playout.addTrack({
3036
+ source: track.source,
3037
+ peaks: track.waveformData,
3038
+ name: track.name,
3039
+ audioContext,
3040
+ fadeIn: track.fadeIn,
3041
+ fadeOut: track.fadeOut
3042
+ });
3043
+ const mediaTrack = playout.getTrack((_b = (_a2 = playout["track"]) == null ? void 0 : _a2.id) != null ? _b : "");
3044
+ if (mediaTrack) {
3045
+ mediaTrack.setOnTimeUpdateCallback((time) => {
3046
+ currentTimeRef.current = time;
3047
+ });
3048
+ }
3049
+ playout.setOnPlaybackComplete(() => {
3050
+ stopAnimationFrameLoop();
3051
+ setIsPlaying(false);
3052
+ setActiveAnnotationId(null);
3053
+ currentTimeRef.current = 0;
3054
+ setCurrentTime(0);
3055
+ });
3056
+ playoutRef.current = playout;
3057
+ setDuration(track.waveformData.duration);
3058
+ onReady == null ? void 0 : onReady();
3059
+ } catch (err) {
3060
+ console.warn("[waveform-playlist] MediaElement playout init failed: " + String(err));
3061
+ onError == null ? void 0 : onError(err instanceof Error ? err : new Error(String(err)));
3062
+ }
3063
+ }))();
4791
3064
  return () => {
3065
+ cancelled = true;
4792
3066
  stopAnimationFrameLoop();
4793
- playout.dispose();
3067
+ if (createdPlayout) {
3068
+ createdPlayout.dispose();
3069
+ }
3070
+ playoutRef.current = null;
4794
3071
  };
4795
3072
  }, [
4796
3073
  track.source,
@@ -4802,10 +3079,12 @@ var MediaElementPlaylistProvider = ({
4802
3079
  initialPlaybackRate,
4803
3080
  preservesPitch,
4804
3081
  onReady,
3082
+ onError,
4805
3083
  stopAnimationFrameLoop,
4806
- setActiveAnnotationId
3084
+ setActiveAnnotationId,
3085
+ createPlayout
4807
3086
  ]);
4808
- useEffect11(() => {
3087
+ useEffect6(() => {
4809
3088
  var _a2;
4810
3089
  try {
4811
3090
  const extractedPeaks = extractPeaksFromWaveformData(
@@ -4834,7 +3113,7 @@ var MediaElementPlaylistProvider = ({
4834
3113
  console.warn("[waveform-playlist] Failed to extract peaks from waveform data:", err);
4835
3114
  }
4836
3115
  }, [track.waveformData, track.name, samplesPerPixel, sampleRate]);
4837
- const startAnimationLoop = useCallback20(() => {
3116
+ const startAnimationLoop = useCallback14(() => {
4838
3117
  const updateTime = () => {
4839
3118
  var _a2, _b, _c;
4840
3119
  const time = (_b = (_a2 = playoutRef.current) == null ? void 0 : _a2.getCurrentTime()) != null ? _b : 0;
@@ -4877,7 +3156,7 @@ var MediaElementPlaylistProvider = ({
4877
3156
  startAnimationFrameLoop(updateTime);
4878
3157
  }, [setActiveAnnotationId, sampleRate, startAnimationFrameLoop]);
4879
3158
  const stopAnimationLoop = stopAnimationFrameLoop;
4880
- const play = useCallback20(
3159
+ const play = useCallback14(
4881
3160
  (startTime) => {
4882
3161
  if (!playoutRef.current) return;
4883
3162
  const actualStartTime = startTime != null ? startTime : currentTimeRef.current;
@@ -4887,14 +3166,14 @@ var MediaElementPlaylistProvider = ({
4887
3166
  },
4888
3167
  [startAnimationLoop]
4889
3168
  );
4890
- const pause = useCallback20(() => {
3169
+ const pause = useCallback14(() => {
4891
3170
  if (!playoutRef.current) return;
4892
3171
  playoutRef.current.pause();
4893
3172
  setIsPlaying(false);
4894
3173
  stopAnimationLoop();
4895
3174
  setCurrentTime(playoutRef.current.getCurrentTime());
4896
3175
  }, [stopAnimationLoop]);
4897
- const stop = useCallback20(() => {
3176
+ const stop = useCallback14(() => {
4898
3177
  if (!playoutRef.current) return;
4899
3178
  playoutRef.current.stop();
4900
3179
  setIsPlaying(false);
@@ -4903,7 +3182,7 @@ var MediaElementPlaylistProvider = ({
4903
3182
  setCurrentTime(0);
4904
3183
  setActiveAnnotationId(null);
4905
3184
  }, [stopAnimationLoop, setActiveAnnotationId]);
4906
- const seekTo = useCallback20(
3185
+ const seekTo = useCallback14(
4907
3186
  (time) => {
4908
3187
  const clampedTime = Math.max(0, Math.min(time, duration));
4909
3188
  currentTimeRef.current = clampedTime;
@@ -4914,7 +3193,7 @@ var MediaElementPlaylistProvider = ({
4914
3193
  },
4915
3194
  [duration]
4916
3195
  );
4917
- const setPlaybackRate = useCallback20((rate) => {
3196
+ const setPlaybackRate = useCallback14((rate) => {
4918
3197
  const clampedRate = Math.max(0.5, Math.min(2, rate));
4919
3198
  setPlaybackRateState(clampedRate);
4920
3199
  if (playoutRef.current) {
@@ -4922,7 +3201,7 @@ var MediaElementPlaylistProvider = ({
4922
3201
  }
4923
3202
  }, []);
4924
3203
  const timeScaleHeight = timescale ? 30 : 0;
4925
- const animationValue = useMemo5(
3204
+ const animationValue = useMemo4(
4926
3205
  () => ({
4927
3206
  isPlaying,
4928
3207
  currentTime,
@@ -4930,7 +3209,7 @@ var MediaElementPlaylistProvider = ({
4930
3209
  }),
4931
3210
  [isPlaying, currentTime]
4932
3211
  );
4933
- const stateValue = useMemo5(
3212
+ const stateValue = useMemo4(
4934
3213
  () => ({
4935
3214
  continuousPlay,
4936
3215
  annotations,
@@ -4940,9 +3219,9 @@ var MediaElementPlaylistProvider = ({
4940
3219
  }),
4941
3220
  [continuousPlay, annotations, activeAnnotationId, playbackRate, isAutomaticScroll]
4942
3221
  );
4943
- const onAnnotationsChangeRef = useRef16(onAnnotationsChange);
3222
+ const onAnnotationsChangeRef = useRef10(onAnnotationsChange);
4944
3223
  onAnnotationsChangeRef.current = onAnnotationsChange;
4945
- const setAnnotations = useCallback20(
3224
+ const setAnnotations = useCallback14(
4946
3225
  (action) => {
4947
3226
  const updated = typeof action === "function" ? action(annotationsRef.current) : action;
4948
3227
  if (!onAnnotationsChangeRef.current) {
@@ -4957,7 +3236,7 @@ var MediaElementPlaylistProvider = ({
4957
3236
  },
4958
3237
  []
4959
3238
  );
4960
- const controlsValue = useMemo5(
3239
+ const controlsValue = useMemo4(
4961
3240
  () => ({
4962
3241
  play,
4963
3242
  pause,
@@ -4985,7 +3264,7 @@ var MediaElementPlaylistProvider = ({
4985
3264
  setScrollContainer
4986
3265
  ]
4987
3266
  );
4988
- const dataValue = useMemo5(
3267
+ const dataValue = useMemo4(
4989
3268
  () => ({
4990
3269
  duration,
4991
3270
  peaksDataArray,
@@ -5049,7 +3328,7 @@ var useMediaElementData = () => {
5049
3328
  };
5050
3329
 
5051
3330
  // src/components/PlaybackControls.tsx
5052
- import { useCallback as useCallback21 } from "react";
3331
+ import { useCallback as useCallback15 } from "react";
5053
3332
  import { BaseControlButton } from "@waveform-playlist/ui-components";
5054
3333
  import { jsx as jsx3 } from "react/jsx-runtime";
5055
3334
  var PlayButton = ({ className }) => {
@@ -5185,7 +3464,7 @@ var ClearAllButton = ({
5185
3464
  className
5186
3465
  }) => {
5187
3466
  const { stop } = usePlaylistControls();
5188
- const handleClick = useCallback21(() => {
3467
+ const handleClick = useCallback15(() => {
5189
3468
  stop();
5190
3469
  onClearAll();
5191
3470
  }, [stop, onClearAll]);
@@ -5213,7 +3492,7 @@ var ZoomOutButton = ({
5213
3492
  };
5214
3493
 
5215
3494
  // src/components/ContextualControls.tsx
5216
- import { useRef as useRef17, useEffect as useEffect12 } from "react";
3495
+ import { useRef as useRef11, useEffect as useEffect7 } from "react";
5217
3496
  import {
5218
3497
  MasterVolumeControl as BaseMasterVolumeControl,
5219
3498
  TimeFormatSelect as BaseTimeFormatSelect,
@@ -5252,10 +3531,10 @@ var PositionDisplay = styled.span`
5252
3531
  `;
5253
3532
  var AudioPosition = ({ className }) => {
5254
3533
  var _a;
5255
- const timeRef = useRef17(null);
3534
+ const timeRef = useRef11(null);
5256
3535
  const { isPlaying, currentTimeRef, registerFrameCallback, unregisterFrameCallback } = usePlaybackAnimation();
5257
3536
  const { timeFormat: format } = usePlaylistData();
5258
- useEffect12(() => {
3537
+ useEffect7(() => {
5259
3538
  const id = "audio-position";
5260
3539
  if (isPlaying) {
5261
3540
  registerFrameCallback(id, ({ time }) => {
@@ -5266,7 +3545,7 @@ var AudioPosition = ({ className }) => {
5266
3545
  }
5267
3546
  return () => unregisterFrameCallback(id);
5268
3547
  }, [isPlaying, format, registerFrameCallback, unregisterFrameCallback]);
5269
- useEffect12(() => {
3548
+ useEffect7(() => {
5270
3549
  var _a2;
5271
3550
  if (!isPlaying && timeRef.current) {
5272
3551
  timeRef.current.textContent = formatTime((_a2 = currentTimeRef.current) != null ? _a2 : 0, format);
@@ -5343,53 +3622,6 @@ var DownloadAnnotationsButton = ({
5343
3622
  return /* @__PURE__ */ jsx6(Base, { annotations, filename, className });
5344
3623
  };
5345
3624
 
5346
- // src/components/ExportControls.tsx
5347
- import { BaseControlButton as BaseControlButton3 } from "@waveform-playlist/ui-components";
5348
- import { jsx as jsx7 } from "react/jsx-runtime";
5349
- var ExportWavButton = ({
5350
- label = "Export WAV",
5351
- filename = "export",
5352
- mode = "master",
5353
- trackIndex,
5354
- bitDepth = 16,
5355
- applyEffects = true,
5356
- effectsFunction,
5357
- createOfflineTrackEffects,
5358
- className,
5359
- onExportComplete,
5360
- onExportError
5361
- }) => {
5362
- const { tracks, trackStates } = usePlaylistData();
5363
- const { exportWav, isExporting, progress } = useExportWav();
5364
- const handleExport = () => __async(null, null, function* () {
5365
- try {
5366
- const result = yield exportWav(tracks, trackStates, {
5367
- filename,
5368
- mode,
5369
- trackIndex,
5370
- bitDepth,
5371
- applyEffects,
5372
- effectsFunction,
5373
- createOfflineTrackEffects,
5374
- autoDownload: true
5375
- });
5376
- onExportComplete == null ? void 0 : onExportComplete(result.blob);
5377
- } catch (error) {
5378
- onExportError == null ? void 0 : onExportError(error instanceof Error ? error : new Error("Export failed"));
5379
- }
5380
- });
5381
- const buttonLabel = isExporting ? `Exporting ${Math.round(progress * 100)}%` : label;
5382
- return /* @__PURE__ */ jsx7(
5383
- BaseControlButton3,
5384
- {
5385
- onClick: handleExport,
5386
- disabled: isExporting || tracks.length === 0,
5387
- className,
5388
- children: buttonLabel
5389
- }
5390
- );
5391
- };
5392
-
5393
3625
  // src/contexts/ClipInteractionContext.tsx
5394
3626
  import { createContext as createContext4, useContext as useContext4 } from "react";
5395
3627
  var ClipInteractionContext = createContext4(false);
@@ -5399,10 +3631,9 @@ function useClipInteractionEnabled() {
5399
3631
  }
5400
3632
 
5401
3633
  // src/components/PlaylistVisualization.tsx
5402
- import { useContext as useContext6, useRef as useRef20, useState as useState17, useMemo as useMemo6, useCallback as useCallback22 } from "react";
3634
+ import { useContext as useContext6, useRef as useRef14, useState as useState11, useMemo as useMemo5, useCallback as useCallback16 } from "react";
5403
3635
  import { createPortal } from "react-dom";
5404
3636
  import styled4 from "styled-components";
5405
- import { getGlobalAudioContext as getGlobalAudioContext5 } from "@waveform-playlist/playout";
5406
3637
  import {
5407
3638
  Playlist,
5408
3639
  Track as TrackComponent,
@@ -5430,9 +3661,9 @@ import {
5430
3661
  import { audibleLatencySamples } from "@waveform-playlist/core";
5431
3662
 
5432
3663
  // src/components/AnimatedPlayhead.tsx
5433
- import { useRef as useRef18, useEffect as useEffect13 } from "react";
3664
+ import { useRef as useRef12, useEffect as useEffect8 } from "react";
5434
3665
  import styled2 from "styled-components";
5435
- import { jsx as jsx8 } from "react/jsx-runtime";
3666
+ import { jsx as jsx7 } from "react/jsx-runtime";
5436
3667
  var PlayheadLine = styled2.div.attrs((props) => ({
5437
3668
  style: {
5438
3669
  width: `${props.$width}px`,
@@ -5448,7 +3679,7 @@ var PlayheadLine = styled2.div.attrs((props) => ({
5448
3679
  will-change: transform;
5449
3680
  `;
5450
3681
  var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5451
- const playheadRef = useRef18(null);
3682
+ const playheadRef = useRef12(null);
5452
3683
  const {
5453
3684
  isPlaying,
5454
3685
  currentTimeRef,
@@ -5457,7 +3688,7 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5457
3688
  unregisterFrameCallback
5458
3689
  } = usePlaybackAnimation();
5459
3690
  const { samplesPerPixel, sampleRate, progressBarWidth } = usePlaylistData();
5460
- useEffect13(() => {
3691
+ useEffect8(() => {
5461
3692
  const id = "playhead";
5462
3693
  if (isPlaying) {
5463
3694
  registerFrameCallback(id, ({ visualTime, sampleRate: sr, samplesPerPixel: spp }) => {
@@ -5469,7 +3700,7 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5469
3700
  }
5470
3701
  return () => unregisterFrameCallback(id);
5471
3702
  }, [isPlaying, registerFrameCallback, unregisterFrameCallback]);
5472
- useEffect13(() => {
3703
+ useEffect8(() => {
5473
3704
  var _a, _b;
5474
3705
  if (!isPlaying && playheadRef.current) {
5475
3706
  const time = (_b = (_a = visualTimeRef.current) != null ? _a : currentTimeRef.current) != null ? _b : 0;
@@ -5477,11 +3708,11 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5477
3708
  playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;
5478
3709
  }
5479
3710
  });
5480
- return /* @__PURE__ */ jsx8(PlayheadLine, { ref: playheadRef, $color: color, $width: progressBarWidth, "data-playhead": true });
3711
+ return /* @__PURE__ */ jsx7(PlayheadLine, { ref: playheadRef, $color: color, $width: progressBarWidth, "data-playhead": true });
5481
3712
  };
5482
3713
 
5483
3714
  // src/components/ChannelWithProgress.tsx
5484
- import { useId, useRef as useRef19, useEffect as useEffect14 } from "react";
3715
+ import { useId, useRef as useRef13, useEffect as useEffect9 } from "react";
5485
3716
  import styled3 from "styled-components";
5486
3717
  import {
5487
3718
  clipPixelWidth as computeClipPixelWidth
@@ -5492,7 +3723,7 @@ import {
5492
3723
  usePlaylistInfo,
5493
3724
  waveformColorToCss
5494
3725
  } from "@waveform-playlist/ui-components";
5495
- import { Fragment, jsx as jsx9, jsxs } from "react/jsx-runtime";
3726
+ import { Fragment, jsx as jsx8, jsxs } from "react/jsx-runtime";
5496
3727
  var ChannelWrapper = styled3.div`
5497
3728
  position: relative;
5498
3729
  `;
@@ -5546,7 +3777,7 @@ var ChannelWithProgress = (_a) => {
5546
3777
  "clipSampleRate",
5547
3778
  "clipOffsetSeconds"
5548
3779
  ]);
5549
- const progressRef = useRef19(null);
3780
+ const progressRef = useRef13(null);
5550
3781
  const callbackId = useId();
5551
3782
  const theme = useTheme();
5552
3783
  const { waveHeight } = usePlaylistInfo();
@@ -5564,7 +3795,7 @@ var ChannelWithProgress = (_a) => {
5564
3795
  clipDurationSamples,
5565
3796
  samplesPerPixel
5566
3797
  );
5567
- useEffect14(() => {
3798
+ useEffect9(() => {
5568
3799
  if (isPlaying) {
5569
3800
  registerFrameCallback(callbackId, ({ visualTime, sampleRate: sr }) => {
5570
3801
  if (progressRef.current) {
@@ -5592,7 +3823,7 @@ var ChannelWithProgress = (_a) => {
5592
3823
  registerFrameCallback,
5593
3824
  unregisterFrameCallback
5594
3825
  ]);
5595
- useEffect14(() => {
3826
+ useEffect9(() => {
5596
3827
  var _a2, _b2;
5597
3828
  if (!isPlaying && progressRef.current) {
5598
3829
  const currentTime = (_b2 = (_a2 = visualTimeRef.current) != null ? _a2 : currentTimeRef.current) != null ? _b2 : 0;
@@ -5627,7 +3858,7 @@ var ChannelWithProgress = (_a) => {
5627
3858
  const waveformBackgroundCss = waveformColorToCss(backgroundColor);
5628
3859
  return /* @__PURE__ */ jsxs(ChannelWrapper, { children: [
5629
3860
  isBothMode ? /* @__PURE__ */ jsxs(Fragment, { children: [
5630
- /* @__PURE__ */ jsx9(
3861
+ /* @__PURE__ */ jsx8(
5631
3862
  Background,
5632
3863
  {
5633
3864
  $color: "#000",
@@ -5636,7 +3867,7 @@ var ChannelWithProgress = (_a) => {
5636
3867
  $width: smartChannelProps.length
5637
3868
  }
5638
3869
  ),
5639
- /* @__PURE__ */ jsx9(
3870
+ /* @__PURE__ */ jsx8(
5640
3871
  Background,
5641
3872
  {
5642
3873
  $color: waveformBackgroundCss,
@@ -5645,7 +3876,7 @@ var ChannelWithProgress = (_a) => {
5645
3876
  $width: smartChannelProps.length
5646
3877
  }
5647
3878
  )
5648
- ] }) : /* @__PURE__ */ jsx9(
3879
+ ] }) : /* @__PURE__ */ jsx8(
5649
3880
  Background,
5650
3881
  {
5651
3882
  $color: backgroundCss,
@@ -5654,7 +3885,7 @@ var ChannelWithProgress = (_a) => {
5654
3885
  $width: smartChannelProps.length
5655
3886
  }
5656
3887
  ),
5657
- !isPianoRollMode && /* @__PURE__ */ jsx9(
3888
+ !isPianoRollMode && /* @__PURE__ */ jsx8(
5658
3889
  ProgressOverlay,
5659
3890
  {
5660
3891
  ref: progressRef,
@@ -5664,7 +3895,7 @@ var ChannelWithProgress = (_a) => {
5664
3895
  $width: clipPixelWidth
5665
3896
  }
5666
3897
  ),
5667
- /* @__PURE__ */ jsx9(ChannelContainer, { children: /* @__PURE__ */ jsx9(
3898
+ /* @__PURE__ */ jsx8(ChannelContainer, { children: /* @__PURE__ */ jsx8(
5668
3899
  SmartChannel,
5669
3900
  __spreadProps(__spreadValues({}, smartChannelProps), {
5670
3901
  transparentBackground: true,
@@ -5691,7 +3922,7 @@ function useSpectrogramIntegration() {
5691
3922
  }
5692
3923
 
5693
3924
  // src/components/PlaylistVisualization.tsx
5694
- import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
3925
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
5695
3926
  var DEFAULT_EMPTY_TRACK_DURATION = 60;
5696
3927
  var ControlSlot = styled4.div.attrs((props) => ({
5697
3928
  style: { height: `${props.$height}px` }
@@ -5710,7 +3941,8 @@ var CustomPlayhead = ({ renderPlayhead, color, samplesPerPixel, sampleRate }) =>
5710
3941
  visualTimeRef,
5711
3942
  playbackStartTimeRef,
5712
3943
  audioStartPositionRef,
5713
- getPlaybackTime
3944
+ getPlaybackTime,
3945
+ getAudioContextTime
5714
3946
  } = usePlaybackAnimation();
5715
3947
  const visualTime = (_b = (_a = visualTimeRef.current) != null ? _a : currentTimeRef.current) != null ? _b : 0;
5716
3948
  return renderPlayhead({
@@ -5724,7 +3956,7 @@ var CustomPlayhead = ({ renderPlayhead, color, samplesPerPixel, sampleRate }) =>
5724
3956
  samplesPerPixel,
5725
3957
  sampleRate,
5726
3958
  controlsOffset: 0,
5727
- getAudioContextTime: () => getGlobalAudioContext5().currentTime,
3959
+ getAudioContextTime,
5728
3960
  getPlaybackTime
5729
3961
  });
5730
3962
  };
@@ -5749,7 +3981,7 @@ var PlaylistVisualization = ({
5749
3981
  }) => {
5750
3982
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
5751
3983
  const theme = useTheme2();
5752
- const { isPlaying, getLookAhead } = usePlaybackAnimation();
3984
+ const { isPlaying, getLookAhead, getOutputLatency } = usePlaybackAnimation();
5753
3985
  const {
5754
3986
  selectionStart,
5755
3987
  selectionEnd,
@@ -5795,7 +4027,7 @@ var PlaylistVisualization = ({
5795
4027
  mono
5796
4028
  } = usePlaylistData();
5797
4029
  const spectrogram = useContext6(SpectrogramIntegrationContext);
5798
- const perTrackSpectrogramHelpers = useMemo6(() => {
4030
+ const perTrackSpectrogramHelpers = useMemo5(() => {
5799
4031
  if (!spectrogram)
5800
4032
  return /* @__PURE__ */ new Map();
5801
4033
  const helpers = /* @__PURE__ */ new Map();
@@ -5814,11 +4046,11 @@ var PlaylistVisualization = ({
5814
4046
  });
5815
4047
  return helpers;
5816
4048
  }, [tracks, spectrogram]);
5817
- const [settingsModalTrackId, setSettingsModalTrackId] = useState17(null);
5818
- const [isSelecting, setIsSelecting] = useState17(false);
5819
- const mouseDownTimeRef = useRef20(0);
5820
- const scrollContainerRef = useRef20(null);
5821
- const handleScrollContainerRef = useCallback22(
4049
+ const [settingsModalTrackId, setSettingsModalTrackId] = useState11(null);
4050
+ const [isSelecting, setIsSelecting] = useState11(false);
4051
+ const mouseDownTimeRef = useRef14(0);
4052
+ const scrollContainerRef = useRef14(null);
4053
+ const handleScrollContainerRef = useCallback16(
5822
4054
  (element) => {
5823
4055
  scrollContainerRef.current = element;
5824
4056
  setScrollContainer(element);
@@ -5856,7 +4088,7 @@ var PlaylistVisualization = ({
5856
4088
  );
5857
4089
  }
5858
4090
  });
5859
- const selectTrack = useCallback22(
4091
+ const selectTrack = useCallback16(
5860
4092
  (trackIndex) => {
5861
4093
  if (trackIndex >= 0 && trackIndex < tracks.length) {
5862
4094
  const track = tracks[trackIndex];
@@ -5926,7 +4158,7 @@ var PlaylistVisualization = ({
5926
4158
  };
5927
4159
  const hasClips = tracks.some((track) => track.clips.length > 0);
5928
4160
  if (hasClips && peaksDataArray.length === 0) {
5929
- return /* @__PURE__ */ jsx10("div", { className, children: "Loading waveform..." });
4161
+ return /* @__PURE__ */ jsx9("div", { className, children: "Loading waveform..." });
5930
4162
  }
5931
4163
  const trackControlsSlots = controls.show ? peaksDataArray.map((trackClipPeaks, trackIndex) => {
5932
4164
  var _a2, _b2, _c2;
@@ -5945,7 +4177,7 @@ var PlaylistVisualization = ({
5945
4177
  const slotHeight = waveHeight * maxChannels + (showClipHeaders ? CLIP_HEADER_HEIGHT : 0);
5946
4178
  const trackControlContent = renderTrackControls ? renderTrackControls(trackIndex) : /* @__PURE__ */ jsxs2(Controls, { onClick: () => selectTrack(trackIndex), children: [
5947
4179
  /* @__PURE__ */ jsxs2(Header, { style: { justifyContent: "center", position: "relative" }, children: [
5948
- onRemoveTrack && /* @__PURE__ */ jsx10(
4180
+ onRemoveTrack && /* @__PURE__ */ jsx9(
5949
4181
  CloseButton,
5950
4182
  {
5951
4183
  onClick: (e) => {
@@ -5954,7 +4186,7 @@ var PlaylistVisualization = ({
5954
4186
  }
5955
4187
  }
5956
4188
  ),
5957
- /* @__PURE__ */ jsx10(
4189
+ /* @__PURE__ */ jsx9(
5958
4190
  "span",
5959
4191
  {
5960
4192
  style: {
@@ -5967,7 +4199,7 @@ var PlaylistVisualization = ({
5967
4199
  children: trackState.name || `Track ${trackIndex + 1}`
5968
4200
  }
5969
4201
  ),
5970
- (spectrogram == null ? void 0 : spectrogram.renderMenuItems) && /* @__PURE__ */ jsx10("span", { style: { position: "absolute", right: 0, top: 0 }, children: /* @__PURE__ */ jsx10(
4202
+ (spectrogram == null ? void 0 : spectrogram.renderMenuItems) && /* @__PURE__ */ jsx9("span", { style: { position: "absolute", right: 0, top: 0 }, children: /* @__PURE__ */ jsx9(
5971
4203
  TrackMenu,
5972
4204
  {
5973
4205
  items: (onClose) => spectrogram.renderMenuItems({
@@ -5980,7 +4212,7 @@ var PlaylistVisualization = ({
5980
4212
  ) })
5981
4213
  ] }),
5982
4214
  /* @__PURE__ */ jsxs2(ButtonGroup, { children: [
5983
- /* @__PURE__ */ jsx10(
4215
+ /* @__PURE__ */ jsx9(
5984
4216
  Button,
5985
4217
  {
5986
4218
  $variant: trackState.muted ? "danger" : "outline",
@@ -5988,7 +4220,7 @@ var PlaylistVisualization = ({
5988
4220
  children: "Mute"
5989
4221
  }
5990
4222
  ),
5991
- /* @__PURE__ */ jsx10(
4223
+ /* @__PURE__ */ jsx9(
5992
4224
  Button,
5993
4225
  {
5994
4226
  $variant: trackState.soloed ? "info" : "outline",
@@ -5998,8 +4230,8 @@ var PlaylistVisualization = ({
5998
4230
  )
5999
4231
  ] }),
6000
4232
  /* @__PURE__ */ jsxs2(SliderWrapper, { children: [
6001
- /* @__PURE__ */ jsx10(VolumeDownIcon, {}),
6002
- /* @__PURE__ */ jsx10(
4233
+ /* @__PURE__ */ jsx9(VolumeDownIcon, {}),
4234
+ /* @__PURE__ */ jsx9(
6003
4235
  Slider,
6004
4236
  {
6005
4237
  min: "0",
@@ -6009,11 +4241,11 @@ var PlaylistVisualization = ({
6009
4241
  onChange: (e) => setTrackVolume(trackIndex, parseFloat(e.target.value))
6010
4242
  }
6011
4243
  ),
6012
- /* @__PURE__ */ jsx10(VolumeUpIcon, {})
4244
+ /* @__PURE__ */ jsx9(VolumeUpIcon, {})
6013
4245
  ] }),
6014
4246
  /* @__PURE__ */ jsxs2(SliderWrapper, { children: [
6015
- /* @__PURE__ */ jsx10("span", { children: "L" }),
6016
- /* @__PURE__ */ jsx10(
4247
+ /* @__PURE__ */ jsx9("span", { children: "L" }),
4248
+ /* @__PURE__ */ jsx9(
6017
4249
  Slider,
6018
4250
  {
6019
4251
  min: "-1",
@@ -6023,10 +4255,10 @@ var PlaylistVisualization = ({
6023
4255
  onChange: (e) => setTrackPan(trackIndex, parseFloat(e.target.value))
6024
4256
  }
6025
4257
  ),
6026
- /* @__PURE__ */ jsx10("span", { children: "R" })
4258
+ /* @__PURE__ */ jsx9("span", { children: "R" })
6027
4259
  ] })
6028
4260
  ] });
6029
- return /* @__PURE__ */ jsx10(
4261
+ return /* @__PURE__ */ jsx9(
6030
4262
  ControlSlot,
6031
4263
  {
6032
4264
  $height: slotHeight,
@@ -6037,7 +4269,7 @@ var PlaylistVisualization = ({
6037
4269
  );
6038
4270
  }) : void 0;
6039
4271
  return /* @__PURE__ */ jsxs2(DevicePixelRatioProvider, { children: [
6040
- /* @__PURE__ */ jsx10(
4272
+ /* @__PURE__ */ jsx9(
6041
4273
  PlaylistInfoContext.Provider,
6042
4274
  {
6043
4275
  value: {
@@ -6051,7 +4283,7 @@ var PlaylistVisualization = ({
6051
4283
  barWidth,
6052
4284
  barGap
6053
4285
  },
6054
- children: /* @__PURE__ */ jsx10(
4286
+ children: /* @__PURE__ */ jsx9(
6055
4287
  Playlist,
6056
4288
  {
6057
4289
  theme,
@@ -6069,8 +4301,8 @@ var PlaylistVisualization = ({
6069
4301
  trackControlsSlots,
6070
4302
  timescaleGapHeight: timeScaleHeight > 0 ? timeScaleHeight + 1 : 0,
6071
4303
  timescale: timeScaleHeight > 0 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
6072
- /* @__PURE__ */ jsx10(SmartScale, { renderTick }),
6073
- isLoopEnabled && /* @__PURE__ */ jsx10(
4304
+ /* @__PURE__ */ jsx9(SmartScale, { renderTick }),
4305
+ isLoopEnabled && /* @__PURE__ */ jsx9(
6074
4306
  TimescaleLoopRegion,
6075
4307
  {
6076
4308
  startPosition: Math.min(loopStart, loopEnd) * sampleRate / samplesPerPixel,
@@ -6116,7 +4348,7 @@ var PlaylistVisualization = ({
6116
4348
  const helpers = perTrackSpectrogramHelpers.get(track.id);
6117
4349
  const trackCfg = helpers == null ? void 0 : helpers.config;
6118
4350
  if (!(trackCfg == null ? void 0 : trackCfg.labels) || !helpers) return null;
6119
- return /* @__PURE__ */ jsx10(
4351
+ return /* @__PURE__ */ jsx9(
6120
4352
  SpectrogramLabels,
6121
4353
  {
6122
4354
  waveHeight,
@@ -6134,7 +4366,7 @@ var PlaylistVisualization = ({
6134
4366
  trackClipPeaks.map((clip, clipIndex) => {
6135
4367
  const peaksData = clip.peaks;
6136
4368
  const width = peaksData.length;
6137
- return /* @__PURE__ */ jsx10(
4369
+ return /* @__PURE__ */ jsx9(
6138
4370
  Clip,
6139
4371
  {
6140
4372
  clipId: clip.clipId,
@@ -6164,7 +4396,7 @@ var PlaylistVisualization = ({
6164
4396
  selectTrack(trackIndex);
6165
4397
  },
6166
4398
  children: peaksData.data.map((channelPeaks, channelIndex) => {
6167
- return /* @__PURE__ */ jsx10(
4399
+ return /* @__PURE__ */ jsx9(
6168
4400
  ChannelWithProgress,
6169
4401
  {
6170
4402
  index: channelIndex,
@@ -6191,8 +4423,7 @@ var PlaylistVisualization = ({
6191
4423
  );
6192
4424
  }),
6193
4425
  (recordingState == null ? void 0 : recordingState.isRecording) && recordingState.trackId === track.id && ((_d2 = recordingState.peaks[0]) == null ? void 0 : _d2.length) > 0 && (() => {
6194
- const audioCtx = getGlobalAudioContext5();
6195
- const outputLatency = "outputLatency" in audioCtx ? audioCtx.outputLatency : 0;
4426
+ const outputLatency = getOutputLatency();
6196
4427
  const lookAhead = getLookAhead();
6197
4428
  const latencyOffsetSamples = audibleLatencySamples(
6198
4429
  outputLatency,
@@ -6208,7 +4439,7 @@ var PlaylistVisualization = ({
6208
4439
  const previewChannels = (mono ? recordingState.peaks.slice(0, 1) : recordingState.peaks).map(
6209
4440
  (channelPeaks) => skipPeakElements > 0 && skipPeakElements < channelPeaks.length ? channelPeaks.subarray(skipPeakElements) : channelPeaks
6210
4441
  );
6211
- return /* @__PURE__ */ jsx10(
4442
+ return /* @__PURE__ */ jsx9(
6212
4443
  Clip,
6213
4444
  {
6214
4445
  clipId: "recording-preview",
@@ -6222,7 +4453,7 @@ var PlaylistVisualization = ({
6222
4453
  disableHeaderDrag: true,
6223
4454
  isSelected: track.id === selectedTrackId,
6224
4455
  trackId: track.id,
6225
- children: previewChannels.map((channelPeaks, chIdx) => /* @__PURE__ */ jsx10(
4456
+ children: previewChannels.map((channelPeaks, chIdx) => /* @__PURE__ */ jsx9(
6226
4457
  ChannelWithProgress,
6227
4458
  {
6228
4459
  index: chIdx,
@@ -6244,11 +4475,11 @@ var PlaylistVisualization = ({
6244
4475
  track.id
6245
4476
  );
6246
4477
  }),
6247
- annotations.length > 0 && annotationIntegration && /* @__PURE__ */ jsx10(annotationIntegration.AnnotationBoxesWrapper, { height: 30, width: tracksFullWidth, children: annotations.map((annotation, index) => {
4478
+ annotations.length > 0 && annotationIntegration && /* @__PURE__ */ jsx9(annotationIntegration.AnnotationBoxesWrapper, { height: 30, width: tracksFullWidth, children: annotations.map((annotation, index) => {
6248
4479
  const startPosition = annotation.start * sampleRate / samplesPerPixel;
6249
4480
  const endPosition = annotation.end * sampleRate / samplesPerPixel;
6250
4481
  const label = getAnnotationBoxLabel ? getAnnotationBoxLabel(annotation, index) : annotation.id;
6251
- return /* @__PURE__ */ jsx10(
4482
+ return /* @__PURE__ */ jsx9(
6252
4483
  annotationIntegration.AnnotationBox,
6253
4484
  {
6254
4485
  annotationId: annotation.id,
@@ -6264,7 +4495,7 @@ var PlaylistVisualization = ({
6264
4495
  annotation.id
6265
4496
  );
6266
4497
  }) }),
6267
- selectionStart !== selectionEnd && /* @__PURE__ */ jsx10(
4498
+ selectionStart !== selectionEnd && /* @__PURE__ */ jsx9(
6268
4499
  Selection,
6269
4500
  {
6270
4501
  startPosition: Math.min(selectionStart, selectionEnd) * sampleRate / samplesPerPixel,
@@ -6272,7 +4503,7 @@ var PlaylistVisualization = ({
6272
4503
  color: theme.selectionColor
6273
4504
  }
6274
4505
  ),
6275
- (isPlaying || selectionStart === selectionEnd) && (renderPlayhead ? /* @__PURE__ */ jsx10(
4506
+ (isPlaying || selectionStart === selectionEnd) && (renderPlayhead ? /* @__PURE__ */ jsx9(
6276
4507
  CustomPlayhead,
6277
4508
  {
6278
4509
  renderPlayhead,
@@ -6280,14 +4511,14 @@ var PlaylistVisualization = ({
6280
4511
  samplesPerPixel,
6281
4512
  sampleRate
6282
4513
  }
6283
- ) : /* @__PURE__ */ jsx10(AnimatedPlayhead, { color: theme.playheadColor }))
4514
+ ) : /* @__PURE__ */ jsx9(AnimatedPlayhead, { color: theme.playheadColor }))
6284
4515
  ] })
6285
4516
  }
6286
4517
  )
6287
4518
  }
6288
4519
  ),
6289
4520
  (spectrogram == null ? void 0 : spectrogram.SettingsModal) && typeof document !== "undefined" && createPortal(
6290
- /* @__PURE__ */ jsx10(
4521
+ /* @__PURE__ */ jsx9(
6291
4522
  spectrogram.SettingsModal,
6292
4523
  {
6293
4524
  open: settingsModalTrackId !== null,
@@ -6307,8 +4538,8 @@ var PlaylistVisualization = ({
6307
4538
  };
6308
4539
 
6309
4540
  // src/components/PlaylistAnnotationList.tsx
6310
- import { useCallback as useCallback23 } from "react";
6311
- import { jsx as jsx11 } from "react/jsx-runtime";
4541
+ import { useCallback as useCallback17 } from "react";
4542
+ import { jsx as jsx10 } from "react/jsx-runtime";
6312
4543
  var PlaylistAnnotationList = ({
6313
4544
  height,
6314
4545
  renderAnnotationItem,
@@ -6322,7 +4553,7 @@ var PlaylistAnnotationList = ({
6322
4553
  const integration = useAnnotationIntegration();
6323
4554
  const { setAnnotations } = usePlaylistControls();
6324
4555
  const resolvedConfig = annotationListConfig != null ? annotationListConfig : { linkEndpoints, continuousPlay };
6325
- const handleAnnotationUpdate = useCallback23(
4556
+ const handleAnnotationUpdate = useCallback17(
6326
4557
  (updatedAnnotations) => {
6327
4558
  setAnnotations(updatedAnnotations);
6328
4559
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6330,7 +4561,7 @@ var PlaylistAnnotationList = ({
6330
4561
  [setAnnotations, onAnnotationUpdate]
6331
4562
  );
6332
4563
  const { AnnotationText } = integration;
6333
- return /* @__PURE__ */ jsx11(
4564
+ return /* @__PURE__ */ jsx10(
6334
4565
  AnnotationText,
6335
4566
  {
6336
4567
  annotations,
@@ -6349,7 +4580,7 @@ var PlaylistAnnotationList = ({
6349
4580
  };
6350
4581
 
6351
4582
  // src/components/Waveform.tsx
6352
- import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs3 } from "react/jsx-runtime";
4583
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
6353
4584
  var Waveform = ({
6354
4585
  renderTrackControls,
6355
4586
  renderTick,
@@ -6374,7 +4605,7 @@ var Waveform = ({
6374
4605
  const clipInteractionEnabled = useClipInteractionEnabled();
6375
4606
  const effectiveInteractiveClips = interactiveClips || clipInteractionEnabled;
6376
4607
  return /* @__PURE__ */ jsxs3(Fragment3, { children: [
6377
- /* @__PURE__ */ jsx12(
4608
+ /* @__PURE__ */ jsx11(
6378
4609
  PlaylistVisualization,
6379
4610
  {
6380
4611
  renderTrackControls,
@@ -6391,7 +4622,7 @@ var Waveform = ({
6391
4622
  recordingState
6392
4623
  }
6393
4624
  ),
6394
- annotations.length > 0 && /* @__PURE__ */ jsx12(
4625
+ annotations.length > 0 && /* @__PURE__ */ jsx11(
6395
4626
  PlaylistAnnotationList,
6396
4627
  {
6397
4628
  height: annotationTextHeight,
@@ -6406,7 +4637,7 @@ var Waveform = ({
6406
4637
  };
6407
4638
 
6408
4639
  // src/components/MediaElementPlaylist.tsx
6409
- import { useContext as useContext7, useRef as useRef23, useState as useState18, useCallback as useCallback24 } from "react";
4640
+ import { useContext as useContext7, useRef as useRef17, useState as useState12, useCallback as useCallback18 } from "react";
6410
4641
  import { DragDropProvider } from "@dnd-kit/react";
6411
4642
  import { RestrictToHorizontalAxis } from "@dnd-kit/abstract/modifiers";
6412
4643
  import {
@@ -6442,9 +4673,9 @@ var noDropAnimationPlugins = (defaults) => {
6442
4673
  };
6443
4674
 
6444
4675
  // src/components/AnimatedMediaElementPlayhead.tsx
6445
- import { useRef as useRef21, useEffect as useEffect15 } from "react";
4676
+ import { useRef as useRef15, useEffect as useEffect10 } from "react";
6446
4677
  import styled5 from "styled-components";
6447
- import { jsx as jsx13 } from "react/jsx-runtime";
4678
+ import { jsx as jsx12 } from "react/jsx-runtime";
6448
4679
  var PlayheadLine2 = styled5.div`
6449
4680
  position: absolute;
6450
4681
  top: 0;
@@ -6459,11 +4690,11 @@ var PlayheadLine2 = styled5.div`
6459
4690
  var AnimatedMediaElementPlayhead = ({
6460
4691
  color = "#ff0000"
6461
4692
  }) => {
6462
- const playheadRef = useRef21(null);
6463
- const animationFrameRef = useRef21(null);
4693
+ const playheadRef = useRef15(null);
4694
+ const animationFrameRef = useRef15(null);
6464
4695
  const { isPlaying, currentTimeRef } = useMediaElementAnimation();
6465
4696
  const { samplesPerPixel, sampleRate, progressBarWidth } = useMediaElementData();
6466
- useEffect15(() => {
4697
+ useEffect10(() => {
6467
4698
  const updatePosition = () => {
6468
4699
  var _a;
6469
4700
  if (playheadRef.current) {
@@ -6487,7 +4718,7 @@ var AnimatedMediaElementPlayhead = ({
6487
4718
  }
6488
4719
  };
6489
4720
  }, [isPlaying, sampleRate, samplesPerPixel, currentTimeRef]);
6490
- useEffect15(() => {
4721
+ useEffect10(() => {
6491
4722
  var _a;
6492
4723
  if (!isPlaying && playheadRef.current) {
6493
4724
  const time = (_a = currentTimeRef.current) != null ? _a : 0;
@@ -6495,11 +4726,11 @@ var AnimatedMediaElementPlayhead = ({
6495
4726
  playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;
6496
4727
  }
6497
4728
  });
6498
- return /* @__PURE__ */ jsx13(PlayheadLine2, { ref: playheadRef, $color: color, $width: progressBarWidth, "data-playhead": true });
4729
+ return /* @__PURE__ */ jsx12(PlayheadLine2, { ref: playheadRef, $color: color, $width: progressBarWidth, "data-playhead": true });
6499
4730
  };
6500
4731
 
6501
4732
  // src/components/ChannelWithMediaElementProgress.tsx
6502
- import { useRef as useRef22, useEffect as useEffect16 } from "react";
4733
+ import { useRef as useRef16, useEffect as useEffect11 } from "react";
6503
4734
  import styled6 from "styled-components";
6504
4735
  import {
6505
4736
  SmartChannel as SmartChannel2,
@@ -6507,7 +4738,7 @@ import {
6507
4738
  usePlaylistInfo as usePlaylistInfo2,
6508
4739
  waveformColorToCss as waveformColorToCss3
6509
4740
  } from "@waveform-playlist/ui-components";
6510
- import { jsx as jsx14, jsxs as jsxs4 } from "react/jsx-runtime";
4741
+ import { jsx as jsx13, jsxs as jsxs4 } from "react/jsx-runtime";
6511
4742
  var ChannelWrapper2 = styled6.div`
6512
4743
  position: relative;
6513
4744
  `;
@@ -6544,14 +4775,14 @@ var ChannelWithMediaElementProgress = (_a) => {
6544
4775
  "clipStartSample",
6545
4776
  "clipDurationSamples"
6546
4777
  ]);
6547
- const progressRef = useRef22(null);
6548
- const animationFrameRef = useRef22(null);
4778
+ const progressRef = useRef16(null);
4779
+ const animationFrameRef = useRef16(null);
6549
4780
  const theme = useTheme3();
6550
4781
  const { waveHeight } = usePlaylistInfo2();
6551
4782
  const { isPlaying, currentTimeRef } = useMediaElementAnimation();
6552
4783
  const { samplesPerPixel, sampleRate } = useMediaElementData();
6553
4784
  const progressColor = (theme == null ? void 0 : theme.waveProgressColor) || "rgba(0, 0, 0, 0.1)";
6554
- useEffect16(() => {
4785
+ useEffect11(() => {
6555
4786
  const updateProgress = () => {
6556
4787
  var _a2;
6557
4788
  if (progressRef.current) {
@@ -6593,7 +4824,7 @@ var ChannelWithMediaElementProgress = (_a) => {
6593
4824
  smartChannelProps.length,
6594
4825
  currentTimeRef
6595
4826
  ]);
6596
- useEffect16(() => {
4827
+ useEffect11(() => {
6597
4828
  var _a2;
6598
4829
  if (!isPlaying && progressRef.current) {
6599
4830
  const currentTime = (_a2 = currentTimeRef.current) != null ? _a2 : 0;
@@ -6620,7 +4851,7 @@ var ChannelWithMediaElementProgress = (_a) => {
6620
4851
  }
6621
4852
  const backgroundCss = waveformColorToCss3(backgroundColor);
6622
4853
  return /* @__PURE__ */ jsxs4(ChannelWrapper2, { children: [
6623
- /* @__PURE__ */ jsx14(
4854
+ /* @__PURE__ */ jsx13(
6624
4855
  Background2,
6625
4856
  {
6626
4857
  $color: backgroundCss,
@@ -6629,7 +4860,7 @@ var ChannelWithMediaElementProgress = (_a) => {
6629
4860
  $width: smartChannelProps.length
6630
4861
  }
6631
4862
  ),
6632
- /* @__PURE__ */ jsx14(
4863
+ /* @__PURE__ */ jsx13(
6633
4864
  ProgressOverlay2,
6634
4865
  {
6635
4866
  ref: progressRef,
@@ -6638,12 +4869,12 @@ var ChannelWithMediaElementProgress = (_a) => {
6638
4869
  $top: smartChannelProps.index * waveHeight
6639
4870
  }
6640
4871
  ),
6641
- /* @__PURE__ */ jsx14(ChannelContainer2, { children: /* @__PURE__ */ jsx14(SmartChannel2, __spreadProps(__spreadValues({}, smartChannelProps), { isSelected: true, transparentBackground: true })) })
4872
+ /* @__PURE__ */ jsx13(ChannelContainer2, { children: /* @__PURE__ */ jsx13(SmartChannel2, __spreadProps(__spreadValues({}, smartChannelProps), { isSelected: true, transparentBackground: true })) })
6642
4873
  ] });
6643
4874
  };
6644
4875
 
6645
4876
  // src/components/MediaElementPlaylist.tsx
6646
- import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs5 } from "react/jsx-runtime";
4877
+ import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs5 } from "react/jsx-runtime";
6647
4878
  var ZERO_REF = { current: 0 };
6648
4879
  var CustomMediaElementPlayhead = ({ renderPlayhead, color, samplesPerPixel, sampleRate }) => {
6649
4880
  var _a;
@@ -6688,11 +4919,11 @@ var MediaElementPlaylist = ({
6688
4919
  fadeIn,
6689
4920
  fadeOut
6690
4921
  } = useMediaElementData();
6691
- const [selectionStart, setSelectionStart] = useState18(0);
6692
- const [selectionEnd, setSelectionEnd] = useState18(0);
6693
- const [isSelecting, setIsSelecting] = useState18(false);
6694
- const scrollContainerRef = useRef23(null);
6695
- const handleScrollContainerRef = useCallback24(
4922
+ const [selectionStart, setSelectionStart] = useState12(0);
4923
+ const [selectionEnd, setSelectionEnd] = useState12(0);
4924
+ const [isSelecting, setIsSelecting] = useState12(false);
4925
+ const scrollContainerRef = useRef17(null);
4926
+ const handleScrollContainerRef = useCallback18(
6696
4927
  (el) => {
6697
4928
  scrollContainerRef.current = el;
6698
4929
  setScrollContainer(el);
@@ -6700,7 +4931,7 @@ var MediaElementPlaylist = ({
6700
4931
  [setScrollContainer]
6701
4932
  );
6702
4933
  const tracksFullWidth = Math.floor(duration * sampleRate / samplesPerPixel);
6703
- const handleAnnotationClick = useCallback24(
4934
+ const handleAnnotationClick = useCallback18(
6704
4935
  (annotation) => __async(null, null, function* () {
6705
4936
  setActiveAnnotationId(annotation.id);
6706
4937
  try {
@@ -6715,7 +4946,7 @@ var MediaElementPlaylist = ({
6715
4946
  }),
6716
4947
  [setActiveAnnotationId, play]
6717
4948
  );
6718
- const handleAnnotationUpdate = useCallback24(
4949
+ const handleAnnotationUpdate = useCallback18(
6719
4950
  (updatedAnnotations) => {
6720
4951
  setAnnotations(updatedAnnotations);
6721
4952
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6729,8 +4960,8 @@ var MediaElementPlaylist = ({
6729
4960
  duration,
6730
4961
  linkEndpoints: linkEndpointsProp
6731
4962
  });
6732
- const mouseDownTimeRef = useRef23(0);
6733
- const handleMouseDown = useCallback24(
4963
+ const mouseDownTimeRef = useRef17(0);
4964
+ const handleMouseDown = useCallback18(
6734
4965
  (e) => {
6735
4966
  const rect = e.currentTarget.getBoundingClientRect();
6736
4967
  const x = e.clientX - rect.left;
@@ -6742,7 +4973,7 @@ var MediaElementPlaylist = ({
6742
4973
  },
6743
4974
  [samplesPerPixel, sampleRate]
6744
4975
  );
6745
- const handleMouseMove = useCallback24(
4976
+ const handleMouseMove = useCallback18(
6746
4977
  (e) => {
6747
4978
  if (!isSelecting) return;
6748
4979
  const rect = e.currentTarget.getBoundingClientRect();
@@ -6752,7 +4983,7 @@ var MediaElementPlaylist = ({
6752
4983
  },
6753
4984
  [isSelecting, samplesPerPixel, sampleRate]
6754
4985
  );
6755
- const handleMouseUp = useCallback24(
4986
+ const handleMouseUp = useCallback18(
6756
4987
  (e) => {
6757
4988
  if (!isSelecting) return;
6758
4989
  setIsSelecting(false);
@@ -6782,9 +5013,9 @@ var MediaElementPlaylist = ({
6782
5013
  [isSelecting, selectionStart, samplesPerPixel, sampleRate, seekTo, isPlaying, playoutRef, play]
6783
5014
  );
6784
5015
  if (peaksDataArray.length === 0) {
6785
- return /* @__PURE__ */ jsx15("div", { className, children: "Loading waveform..." });
5016
+ return /* @__PURE__ */ jsx14("div", { className, children: "Loading waveform..." });
6786
5017
  }
6787
- return /* @__PURE__ */ jsx15(DevicePixelRatioProvider2, { children: /* @__PURE__ */ jsx15(
5018
+ return /* @__PURE__ */ jsx14(DevicePixelRatioProvider2, { children: /* @__PURE__ */ jsx14(
6788
5019
  PlaylistInfoContext2.Provider,
6789
5020
  {
6790
5021
  value: {
@@ -6798,7 +5029,7 @@ var MediaElementPlaylist = ({
6798
5029
  barWidth,
6799
5030
  barGap
6800
5031
  },
6801
- children: /* @__PURE__ */ jsx15(
5032
+ children: /* @__PURE__ */ jsx14(
6802
5033
  Playlist2,
6803
5034
  {
6804
5035
  theme,
@@ -6812,11 +5043,11 @@ var MediaElementPlaylist = ({
6812
5043
  onTracksMouseUp: handleMouseUp,
6813
5044
  scrollContainerRef: handleScrollContainerRef,
6814
5045
  isSelecting,
6815
- timescale: timeScaleHeight > 0 ? /* @__PURE__ */ jsx15(SmartScale2, {}) : void 0,
5046
+ timescale: timeScaleHeight > 0 ? /* @__PURE__ */ jsx14(SmartScale2, {}) : void 0,
6816
5047
  children: /* @__PURE__ */ jsxs5(Fragment4, { children: [
6817
5048
  peaksDataArray.map((trackClipPeaks, trackIndex) => {
6818
5049
  const maxChannels = trackClipPeaks.length > 0 ? Math.max(...trackClipPeaks.map((clip) => clip.peaks.data.length)) : 1;
6819
- return /* @__PURE__ */ jsx15(
5050
+ return /* @__PURE__ */ jsx14(
6820
5051
  TrackComponent2,
6821
5052
  {
6822
5053
  numChannels: maxChannels,
@@ -6844,7 +5075,7 @@ var MediaElementPlaylist = ({
6844
5075
  isSelected: true,
6845
5076
  trackId: `media-element-track-${trackIndex}`,
6846
5077
  children: [
6847
- peaksData.data.map((channelPeaks, channelIndex) => /* @__PURE__ */ jsx15(
5078
+ peaksData.data.map((channelPeaks, channelIndex) => /* @__PURE__ */ jsx14(
6848
5079
  ChannelWithMediaElementProgress,
6849
5080
  {
6850
5081
  index: channelIndex,
@@ -6856,7 +5087,7 @@ var MediaElementPlaylist = ({
6856
5087
  },
6857
5088
  `${trackIndex}-${clipIndex}-${channelIndex}`
6858
5089
  )),
6859
- showFades && fadeIn && fadeIn.duration > 0 && /* @__PURE__ */ jsx15(
5090
+ showFades && fadeIn && fadeIn.duration > 0 && /* @__PURE__ */ jsx14(
6860
5091
  FadeOverlay,
6861
5092
  {
6862
5093
  left: 0,
@@ -6865,7 +5096,7 @@ var MediaElementPlaylist = ({
6865
5096
  curveType: fadeIn.type
6866
5097
  }
6867
5098
  ),
6868
- showFades && fadeOut && fadeOut.duration > 0 && /* @__PURE__ */ jsx15(
5099
+ showFades && fadeOut && fadeOut.duration > 0 && /* @__PURE__ */ jsx14(
6869
5100
  FadeOverlay,
6870
5101
  {
6871
5102
  left: width - Math.floor(fadeOut.duration * sampleRate / samplesPerPixel),
@@ -6883,7 +5114,7 @@ var MediaElementPlaylist = ({
6883
5114
  trackIndex
6884
5115
  );
6885
5116
  }),
6886
- annotations.length > 0 && annotationIntegration && /* @__PURE__ */ jsx15(
5117
+ annotations.length > 0 && annotationIntegration && /* @__PURE__ */ jsx14(
6887
5118
  DragDropProvider,
6888
5119
  {
6889
5120
  onDragStart,
@@ -6891,11 +5122,11 @@ var MediaElementPlaylist = ({
6891
5122
  onDragEnd,
6892
5123
  modifiers: editable ? [RestrictToHorizontalAxis] : [],
6893
5124
  plugins: noDropAnimationPlugins,
6894
- children: /* @__PURE__ */ jsx15(annotationIntegration.AnnotationBoxesWrapper, { height: 30, width: tracksFullWidth, children: annotations.map((annotation, index) => {
5125
+ children: /* @__PURE__ */ jsx14(annotationIntegration.AnnotationBoxesWrapper, { height: 30, width: tracksFullWidth, children: annotations.map((annotation, index) => {
6895
5126
  const startPosition = annotation.start * sampleRate / samplesPerPixel;
6896
5127
  const endPosition = annotation.end * sampleRate / samplesPerPixel;
6897
5128
  const label = getAnnotationBoxLabel ? getAnnotationBoxLabel(annotation, index) : annotation.id;
6898
- return /* @__PURE__ */ jsx15(
5129
+ return /* @__PURE__ */ jsx14(
6899
5130
  annotationIntegration.AnnotationBox,
6900
5131
  {
6901
5132
  annotationId: annotation.id,
@@ -6913,7 +5144,7 @@ var MediaElementPlaylist = ({
6913
5144
  }) })
6914
5145
  }
6915
5146
  ),
6916
- selectionStart !== selectionEnd && /* @__PURE__ */ jsx15(
5147
+ selectionStart !== selectionEnd && /* @__PURE__ */ jsx14(
6917
5148
  Selection2,
6918
5149
  {
6919
5150
  startPosition: Math.min(selectionStart, selectionEnd) * sampleRate / samplesPerPixel,
@@ -6921,7 +5152,7 @@ var MediaElementPlaylist = ({
6921
5152
  color: theme.selectionColor
6922
5153
  }
6923
5154
  ),
6924
- renderPlayhead ? /* @__PURE__ */ jsx15(
5155
+ renderPlayhead ? /* @__PURE__ */ jsx14(
6925
5156
  CustomMediaElementPlayhead,
6926
5157
  {
6927
5158
  renderPlayhead,
@@ -6929,7 +5160,7 @@ var MediaElementPlaylist = ({
6929
5160
  samplesPerPixel,
6930
5161
  sampleRate
6931
5162
  }
6932
- ) : /* @__PURE__ */ jsx15(AnimatedMediaElementPlayhead, { color: theme.playheadColor })
5163
+ ) : /* @__PURE__ */ jsx14(AnimatedMediaElementPlayhead, { color: theme.playheadColor })
6933
5164
  ] })
6934
5165
  }
6935
5166
  )
@@ -6938,8 +5169,8 @@ var MediaElementPlaylist = ({
6938
5169
  };
6939
5170
 
6940
5171
  // src/components/MediaElementAnnotationList.tsx
6941
- import { useCallback as useCallback25 } from "react";
6942
- import { jsx as jsx16 } from "react/jsx-runtime";
5172
+ import { useCallback as useCallback19 } from "react";
5173
+ import { jsx as jsx15 } from "react/jsx-runtime";
6943
5174
  var MediaElementAnnotationList = ({
6944
5175
  height,
6945
5176
  renderAnnotationItem,
@@ -6954,7 +5185,7 @@ var MediaElementAnnotationList = ({
6954
5185
  const integration = useAnnotationIntegration();
6955
5186
  const { setAnnotations } = useMediaElementControls();
6956
5187
  const resolvedConfig = annotationListConfig != null ? annotationListConfig : { linkEndpoints: false, continuousPlay };
6957
- const handleAnnotationUpdate = useCallback25(
5188
+ const handleAnnotationUpdate = useCallback19(
6958
5189
  (updatedAnnotations) => {
6959
5190
  setAnnotations(updatedAnnotations);
6960
5191
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6962,7 +5193,7 @@ var MediaElementAnnotationList = ({
6962
5193
  [setAnnotations, onAnnotationUpdate]
6963
5194
  );
6964
5195
  const { AnnotationText } = integration;
6965
- return /* @__PURE__ */ jsx16(
5196
+ return /* @__PURE__ */ jsx15(
6966
5197
  AnnotationText,
6967
5198
  {
6968
5199
  annotations,
@@ -6981,7 +5212,7 @@ var MediaElementAnnotationList = ({
6981
5212
  };
6982
5213
 
6983
5214
  // src/components/MediaElementWaveform.tsx
6984
- import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs6 } from "react/jsx-runtime";
5215
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs6 } from "react/jsx-runtime";
6985
5216
  var MediaElementWaveform = ({
6986
5217
  annotationTextHeight,
6987
5218
  getAnnotationBoxLabel,
@@ -6997,7 +5228,7 @@ var MediaElementWaveform = ({
6997
5228
  }) => {
6998
5229
  const { annotations } = useMediaElementState();
6999
5230
  return /* @__PURE__ */ jsxs6(Fragment5, { children: [
7000
- /* @__PURE__ */ jsx17(
5231
+ /* @__PURE__ */ jsx16(
7001
5232
  MediaElementPlaylist,
7002
5233
  {
7003
5234
  getAnnotationBoxLabel,
@@ -7009,7 +5240,7 @@ var MediaElementWaveform = ({
7009
5240
  className
7010
5241
  }
7011
5242
  ),
7012
- annotations.length > 0 && /* @__PURE__ */ jsx17(
5243
+ annotations.length > 0 && /* @__PURE__ */ jsx16(
7013
5244
  MediaElementAnnotationList,
7014
5245
  {
7015
5246
  height: annotationTextHeight,
@@ -7084,7 +5315,7 @@ var KeyboardShortcuts = ({
7084
5315
  };
7085
5316
 
7086
5317
  // src/components/ClipInteractionProvider.tsx
7087
- import React16, { useEffect as useEffect17, useMemo as useMemo7 } from "react";
5318
+ import React16, { useEffect as useEffect12, useMemo as useMemo6 } from "react";
7088
5319
  import { DragDropProvider as DragDropProvider2 } from "@dnd-kit/react";
7089
5320
  import { RestrictToHorizontalAxis as RestrictToHorizontalAxis2 } from "@dnd-kit/abstract/modifiers";
7090
5321
  import {
@@ -7174,7 +5405,7 @@ _SnapToGridModifier.configure = configurator2(_SnapToGridModifier);
7174
5405
  var SnapToGridModifier = _SnapToGridModifier;
7175
5406
 
7176
5407
  // src/components/ClipInteractionProvider.tsx
7177
- import { jsx as jsx18 } from "react/jsx-runtime";
5408
+ import { jsx as jsx17 } from "react/jsx-runtime";
7178
5409
  var NOOP_TRACKS_CHANGE = () => {
7179
5410
  };
7180
5411
  var ClipInteractionProvider = ({
@@ -7187,14 +5418,14 @@ var ClipInteractionProvider = ({
7187
5418
  const beatsAndBars = useBeatsAndBars();
7188
5419
  const useBeatsSnap = snap && beatsAndBars != null && beatsAndBars.scaleMode === "beats" && beatsAndBars.snapTo !== "off";
7189
5420
  const useTimescaleSnap = snap && !useBeatsSnap;
7190
- useEffect17(() => {
5421
+ useEffect12(() => {
7191
5422
  if (onTracksChange == null) {
7192
5423
  console.warn(
7193
5424
  "[waveform-playlist] ClipInteractionProvider: onTracksChange is not set on WaveformPlaylistProvider. Drag and trim edits will not be persisted."
7194
5425
  );
7195
5426
  }
7196
5427
  }, [onTracksChange]);
7197
- const snapSamplePosition = useMemo7(() => {
5428
+ const snapSamplePosition = useMemo6(() => {
7198
5429
  if (useBeatsSnap && beatsAndBars) {
7199
5430
  const { bpm, timeSignature, snapTo } = beatsAndBars;
7200
5431
  const gridTicks = snapTo === "bar" ? ticksPerBar2(timeSignature) : ticksPerBeat2(timeSignature);
@@ -7234,7 +5465,7 @@ var ClipInteractionProvider = ({
7234
5465
  },
7235
5466
  [handleDragStart, tracks, setSelectedTrackId]
7236
5467
  );
7237
- const modifiers = useMemo7(() => {
5468
+ const modifiers = useMemo6(() => {
7238
5469
  const mods = [RestrictToHorizontalAxis2];
7239
5470
  if (useBeatsSnap && beatsAndBars) {
7240
5471
  mods.push(
@@ -7259,7 +5490,7 @@ var ClipInteractionProvider = ({
7259
5490
  mods.push(ClipCollisionModifier.configure({ tracks, samplesPerPixel }));
7260
5491
  return mods;
7261
5492
  }, [useBeatsSnap, useTimescaleSnap, beatsAndBars, tracks, samplesPerPixel, sampleRate]);
7262
- return /* @__PURE__ */ jsx18(ClipInteractionContextProvider, { value: true, children: /* @__PURE__ */ jsx18(
5493
+ return /* @__PURE__ */ jsx17(ClipInteractionContextProvider, { value: true, children: /* @__PURE__ */ jsx17(
7263
5494
  DragDropProvider2,
7264
5495
  {
7265
5496
  sensors,
@@ -7282,7 +5513,6 @@ export {
7282
5513
  ContinuousPlayCheckbox,
7283
5514
  DownloadAnnotationsButton,
7284
5515
  EditableCheckbox,
7285
- ExportWavButton,
7286
5516
  FastForwardButton,
7287
5517
  KeyboardShortcuts,
7288
5518
  LinkEndpointsCheckbox,
@@ -7305,17 +5535,10 @@ export {
7305
5535
  SpectrogramIntegrationProvider,
7306
5536
  StopButton,
7307
5537
  TimeFormatSelect,
7308
- Tone2 as Tone,
7309
5538
  Waveform,
7310
5539
  WaveformPlaylistProvider,
7311
5540
  ZoomInButton,
7312
5541
  ZoomOutButton,
7313
- createEffectChain,
7314
- createEffectInstance,
7315
- effectCategories,
7316
- effectDefinitions,
7317
- getEffectDefinition,
7318
- getEffectsByCategory,
7319
5542
  getShortcutLabel,
7320
5543
  getWaveformDataMetadata,
7321
5544
  loadPeaksFromWaveformData,
@@ -7324,22 +5547,16 @@ export {
7324
5547
  useAnnotationDragHandlers,
7325
5548
  useAnnotationIntegration,
7326
5549
  useAnnotationKeyboardControls,
7327
- useAudioTracks,
7328
5550
  useClipDragHandlers,
7329
5551
  useClipInteractionEnabled,
7330
5552
  useClipSplitting,
7331
5553
  useDragSensors,
7332
- useDynamicEffects,
7333
- useDynamicTracks,
7334
- useExportWav,
7335
5554
  useKeyboardShortcuts,
7336
- useMasterAnalyser,
7337
5555
  useMasterVolume,
7338
5556
  useMediaElementAnimation,
7339
5557
  useMediaElementControls,
7340
5558
  useMediaElementData,
7341
5559
  useMediaElementState,
7342
- useOutputMeter,
7343
5560
  usePlaybackAnimation,
7344
5561
  usePlaybackShortcuts,
7345
5562
  usePlaylistControls,
@@ -7348,7 +5565,6 @@ export {
7348
5565
  usePlaylistState,
7349
5566
  useSpectrogramIntegration,
7350
5567
  useTimeFormat,
7351
- useTrackDynamicEffects,
7352
5568
  useZoomControls,
7353
5569
  waveformDataToPeaks
7354
5570
  };