@waveform-playlist/browser 13.1.3 → 15.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.js CHANGED
@@ -88,7 +88,6 @@ __export(index_exports, {
88
88
  ContinuousPlayCheckbox: () => ContinuousPlayCheckbox,
89
89
  DownloadAnnotationsButton: () => DownloadAnnotationsButton,
90
90
  EditableCheckbox: () => EditableCheckbox,
91
- ExportWavButton: () => ExportWavButton,
92
91
  FastForwardButton: () => FastForwardButton,
93
92
  KeyboardShortcuts: () => KeyboardShortcuts,
94
93
  LinkEndpointsCheckbox: () => LinkEndpointsCheckbox,
@@ -111,18 +110,11 @@ __export(index_exports, {
111
110
  SpectrogramIntegrationProvider: () => SpectrogramIntegrationProvider,
112
111
  StopButton: () => StopButton,
113
112
  TimeFormatSelect: () => TimeFormatSelect,
114
- Tone: () => Tone2,
115
113
  Waveform: () => Waveform,
116
114
  WaveformPlaylistProvider: () => WaveformPlaylistProvider,
117
115
  ZoomInButton: () => ZoomInButton,
118
116
  ZoomOutButton: () => ZoomOutButton,
119
- createEffectChain: () => createEffectChain,
120
- createEffectInstance: () => createEffectInstance,
121
- effectCategories: () => effectCategories,
122
- effectDefinitions: () => effectDefinitions,
123
- getEffectDefinition: () => getEffectDefinition,
124
- getEffectsByCategory: () => getEffectsByCategory,
125
- getShortcutLabel: () => import_core3.getShortcutLabel,
117
+ getShortcutLabel: () => import_core2.getShortcutLabel,
126
118
  getWaveformDataMetadata: () => getWaveformDataMetadata,
127
119
  loadPeaksFromWaveformData: () => loadPeaksFromWaveformData,
128
120
  loadWaveformData: () => loadWaveformData,
@@ -130,22 +122,16 @@ __export(index_exports, {
130
122
  useAnnotationDragHandlers: () => useAnnotationDragHandlers,
131
123
  useAnnotationIntegration: () => useAnnotationIntegration,
132
124
  useAnnotationKeyboardControls: () => useAnnotationKeyboardControls,
133
- useAudioTracks: () => useAudioTracks,
134
125
  useClipDragHandlers: () => useClipDragHandlers,
135
126
  useClipInteractionEnabled: () => useClipInteractionEnabled,
136
127
  useClipSplitting: () => useClipSplitting,
137
128
  useDragSensors: () => useDragSensors,
138
- useDynamicEffects: () => useDynamicEffects,
139
- useDynamicTracks: () => useDynamicTracks,
140
- useExportWav: () => useExportWav,
141
129
  useKeyboardShortcuts: () => useKeyboardShortcuts,
142
- useMasterAnalyser: () => useMasterAnalyser,
143
130
  useMasterVolume: () => useMasterVolume,
144
131
  useMediaElementAnimation: () => useMediaElementAnimation,
145
132
  useMediaElementControls: () => useMediaElementControls,
146
133
  useMediaElementData: () => useMediaElementData,
147
134
  useMediaElementState: () => useMediaElementState,
148
- useOutputMeter: () => useOutputMeter,
149
135
  usePlaybackAnimation: () => usePlaybackAnimation,
150
136
  usePlaybackShortcuts: () => usePlaybackShortcuts,
151
137
  usePlaylistControls: () => usePlaylistControls,
@@ -154,20 +140,47 @@ __export(index_exports, {
154
140
  usePlaylistState: () => usePlaylistState,
155
141
  useSpectrogramIntegration: () => useSpectrogramIntegration,
156
142
  useTimeFormat: () => useTimeFormat,
157
- useTrackDynamicEffects: () => useTrackDynamicEffects,
158
143
  useZoomControls: () => useZoomControls,
159
144
  waveformDataToPeaks: () => waveformDataToPeaks
160
145
  });
161
146
  module.exports = __toCommonJS(index_exports);
162
- var Tone2 = __toESM(require("tone"));
163
147
 
164
148
  // src/WaveformPlaylistContext.tsx
165
- var import_react24 = require("react");
149
+ var import_react17 = require("react");
166
150
  var import_styled_components = require("styled-components");
167
- var import_playout6 = require("@waveform-playlist/playout");
151
+
152
+ // src/playout/resolvePlayoutAdapter.ts
153
+ 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`.";
154
+ function resolvePlayoutAdapter(opts) {
155
+ return __async(this, null, function* () {
156
+ if (opts.createAdapter) {
157
+ return opts.createAdapter();
158
+ }
159
+ let mod;
160
+ try {
161
+ mod = yield import("@waveform-playlist/playout");
162
+ } catch (originalErr) {
163
+ console.warn(
164
+ "[waveform-playlist] @waveform-playlist/playout dynamic import failed: " + String(originalErr)
165
+ );
166
+ throw new Error(INSTALL_HINT);
167
+ }
168
+ if (opts.sampleRate !== void 0) {
169
+ try {
170
+ mod.configureGlobalContext({ sampleRate: opts.sampleRate });
171
+ } catch (ctxErr) {
172
+ console.warn(
173
+ "[waveform-playlist] configureGlobalContext failed (continuing with default rate): " + String(ctxErr)
174
+ );
175
+ }
176
+ }
177
+ return mod.createToneAdapter({ effects: opts.effects, soundFontCache: opts.soundFontCache });
178
+ });
179
+ }
180
+
181
+ // src/WaveformPlaylistContext.tsx
168
182
  var import_engine3 = require("@waveform-playlist/engine");
169
183
  var import_ui_components2 = require("@waveform-playlist/ui-components");
170
- var import_tone4 = require("tone");
171
184
 
172
185
  // src/waveformDataLoader.ts
173
186
  var import_waveform_data = __toESM(require("waveform-data"));
@@ -333,9 +346,11 @@ function extractPeaksFromWaveformDataFull(waveformData, samplesPerPixel, isMono,
333
346
  }
334
347
 
335
348
  // src/soundFontSync.ts
336
- var import_playout = require("@waveform-playlist/playout");
349
+ function supportsSoundFont(adapter) {
350
+ return adapter != null && typeof adapter.setSoundFontCache === "function";
351
+ }
337
352
  function syncSoundFontCacheToAdapter(adapter, cache) {
338
- if (!(0, import_playout.isToneAdapter)(adapter)) return;
353
+ if (!supportsSoundFont(adapter)) return;
339
354
  adapter.setSoundFontCache(cache);
340
355
  }
341
356
 
@@ -592,223 +607,8 @@ function useUndoState({ engineRef }) {
592
607
  };
593
608
  }
594
609
 
595
- // src/hooks/useAudioEffects.ts
596
- var import_react8 = require("react");
597
- var import_tone = require("tone");
598
- var useMasterAnalyser = (fftSize = 256) => {
599
- const analyserRef = (0, import_react8.useRef)(null);
600
- const masterEffects = (0, import_react8.useCallback)(
601
- (masterGainNode, destination, _isOffline) => {
602
- const analyserNode = new import_tone.Analyser("fft", fftSize);
603
- masterGainNode.connect(analyserNode);
604
- masterGainNode.connect(destination);
605
- analyserRef.current = analyserNode;
606
- return function cleanup() {
607
- analyserNode.dispose();
608
- analyserRef.current = null;
609
- };
610
- },
611
- [fftSize]
612
- );
613
- return { analyserRef, masterEffects };
614
- };
615
-
616
- // src/hooks/useAudioTracks.ts
617
- var import_react9 = require("react");
618
- var import_core = require("@waveform-playlist/core");
619
- var Tone = __toESM(require("tone"));
620
- function buildTrackFromConfig(config, index, audioBuffer, stableIds, contextSampleRate = 48e3) {
621
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
622
- const buffer = audioBuffer != null ? audioBuffer : config.audioBuffer;
623
- const sampleRate = (_c = (_b = buffer == null ? void 0 : buffer.sampleRate) != null ? _b : (_a = config.waveformData) == null ? void 0 : _a.sample_rate) != null ? _c : contextSampleRate;
624
- 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;
625
- if (sourceDuration === void 0) {
626
- console.warn(
627
- `[waveform-playlist] Track ${index + 1} ("${(_h = config.name) != null ? _h : "unnamed"}"): Cannot create track \u2014 provide duration, audioBuffer, or waveformData with duration.`
628
- );
629
- return null;
630
- }
631
- const clip = (0, import_core.createClipFromSeconds)({
632
- audioBuffer: buffer,
633
- sampleRate,
634
- sourceDuration,
635
- startTime: (_i = config.startTime) != null ? _i : 0,
636
- duration: (_j = config.duration) != null ? _j : sourceDuration,
637
- offset: (_k = config.offset) != null ? _k : 0,
638
- name: config.name || `Track ${index + 1}`,
639
- fadeIn: config.fadeIn,
640
- fadeOut: config.fadeOut,
641
- waveformData: config.waveformData
642
- });
643
- if (isNaN(clip.startSample) || isNaN(clip.durationSamples) || isNaN(clip.offsetSamples)) {
644
- console.error(
645
- `[waveform-playlist] Invalid clip values for track ${index + 1} ("${(_l = config.name) != null ? _l : "unnamed"}"): startSample=${clip.startSample}, durationSamples=${clip.durationSamples}, offsetSamples=${clip.offsetSamples}`
646
- );
647
- return null;
648
- }
649
- const track = __spreadProps(__spreadValues({}, (0, import_core.createTrack)({
650
- name: config.name || `Track ${index + 1}`,
651
- clips: [clip],
652
- muted: (_m = config.muted) != null ? _m : false,
653
- soloed: (_n = config.soloed) != null ? _n : false,
654
- volume: (_o = config.volume) != null ? _o : 1,
655
- pan: (_p = config.pan) != null ? _p : 0,
656
- color: config.color
657
- })), {
658
- effects: config.effects,
659
- renderMode: config.renderMode,
660
- spectrogramConfig: config.spectrogramConfig,
661
- spectrogramColorMap: config.spectrogramColorMap
662
- });
663
- const existingIds = stableIds.get(index);
664
- if (existingIds) {
665
- track.id = existingIds.trackId;
666
- track.clips[0] = __spreadProps(__spreadValues({}, track.clips[0]), { id: existingIds.clipId });
667
- } else {
668
- stableIds.set(index, { trackId: track.id, clipId: track.clips[0].id });
669
- }
670
- return track;
671
- }
672
- function useAudioTracks(configs, options = {}) {
673
- const { immediate = false, progressive = false } = options;
674
- const isImmediate = immediate || progressive;
675
- const [loading, setLoading] = (0, import_react9.useState)(true);
676
- const [error, setError] = (0, import_react9.useState)(null);
677
- const [loadedCount, setLoadedCount] = (0, import_react9.useState)(0);
678
- const totalCount = configs.length;
679
- const [loadedBuffers, setLoadedBuffers] = (0, import_react9.useState)(/* @__PURE__ */ new Map());
680
- const stableIdsRef = (0, import_react9.useRef)(/* @__PURE__ */ new Map());
681
- const contextSampleRateRef = (0, import_react9.useRef)(48e3);
682
- const derivedTracks = (0, import_react9.useMemo)(() => {
683
- if (!isImmediate) return null;
684
- const result = [];
685
- for (let i = 0; i < configs.length; i++) {
686
- const track = buildTrackFromConfig(
687
- configs[i],
688
- i,
689
- loadedBuffers.get(i),
690
- stableIdsRef.current,
691
- contextSampleRateRef.current
692
- );
693
- if (track) result.push(track);
694
- }
695
- return result;
696
- }, [isImmediate, configs, loadedBuffers]);
697
- const [tracks, setTracks] = (0, import_react9.useState)(derivedTracks != null ? derivedTracks : []);
698
- const prevDerivedRef = (0, import_react9.useRef)(derivedTracks);
699
- if (derivedTracks !== prevDerivedRef.current) {
700
- prevDerivedRef.current = derivedTracks;
701
- if (derivedTracks) setTracks(derivedTracks);
702
- }
703
- (0, import_react9.useEffect)(() => {
704
- if (configs.length === 0) {
705
- setTracks([]);
706
- setLoading(false);
707
- setLoadedCount(0);
708
- return;
709
- }
710
- let cancelled = false;
711
- const abortController = new AbortController();
712
- const loadTracks = () => __async(null, null, function* () {
713
- try {
714
- setLoading(true);
715
- setError(null);
716
- setLoadedCount(0);
717
- if (isImmediate) {
718
- setLoadedBuffers(/* @__PURE__ */ new Map());
719
- }
720
- const audioContext = Tone.getContext().rawContext;
721
- contextSampleRateRef.current = audioContext.sampleRate;
722
- const loadPromises = configs.map((config, index) => __async(null, null, function* () {
723
- if (config.audioBuffer) {
724
- if (isImmediate && !cancelled) {
725
- setLoadedBuffers((prev) => {
726
- const next = new Map(prev);
727
- next.set(index, config.audioBuffer);
728
- return next;
729
- });
730
- setLoadedCount((prev) => prev + 1);
731
- return;
732
- }
733
- return buildTrackFromConfig(
734
- config,
735
- index,
736
- config.audioBuffer,
737
- stableIdsRef.current,
738
- audioContext.sampleRate
739
- );
740
- }
741
- if (!config.src && config.waveformData) {
742
- if (isImmediate && !cancelled) {
743
- setLoadedCount((prev) => prev + 1);
744
- return;
745
- }
746
- return buildTrackFromConfig(
747
- config,
748
- index,
749
- void 0,
750
- stableIdsRef.current,
751
- audioContext.sampleRate
752
- );
753
- }
754
- if (!config.src) {
755
- throw new Error(`Track ${index + 1}: Must provide src, audioBuffer, or waveformData`);
756
- }
757
- const response = yield fetch(config.src, { signal: abortController.signal });
758
- if (!response.ok) {
759
- throw new Error(`Failed to fetch ${config.src}: ${response.statusText}`);
760
- }
761
- const arrayBuffer = yield response.arrayBuffer();
762
- const audioBuffer = yield audioContext.decodeAudioData(arrayBuffer);
763
- if (!audioBuffer || !audioBuffer.sampleRate || !audioBuffer.duration) {
764
- throw new Error(`Invalid audio buffer for ${config.src}`);
765
- }
766
- if (isImmediate && !cancelled) {
767
- setLoadedBuffers((prev) => {
768
- const next = new Map(prev);
769
- next.set(index, audioBuffer);
770
- return next;
771
- });
772
- setLoadedCount((prev) => prev + 1);
773
- return;
774
- }
775
- return buildTrackFromConfig(
776
- config,
777
- index,
778
- audioBuffer,
779
- stableIdsRef.current,
780
- audioContext.sampleRate
781
- );
782
- }));
783
- const loadedTracks = yield Promise.all(loadPromises);
784
- if (!cancelled) {
785
- if (!isImmediate) {
786
- const validTracks = loadedTracks.filter((t) => t != null);
787
- setTracks(validTracks);
788
- setLoadedCount(validTracks.length);
789
- }
790
- setLoading(false);
791
- }
792
- } catch (err) {
793
- if (!cancelled) {
794
- const errorMessage = err instanceof Error ? err.message : "Unknown error loading audio";
795
- setError(errorMessage);
796
- setLoading(false);
797
- console.error(`[waveform-playlist] Error loading audio tracks: ${errorMessage}`);
798
- }
799
- }
800
- });
801
- loadTracks();
802
- return () => {
803
- cancelled = true;
804
- abortController.abort();
805
- };
806
- }, [configs, isImmediate]);
807
- return { tracks, loading, error, loadedCount, totalCount };
808
- }
809
-
810
610
  // src/hooks/useClipDragHandlers.ts
811
- var import_react10 = __toESM(require("react"));
611
+ var import_react8 = __toESM(require("react"));
812
612
 
813
613
  // src/utils/boundaryTrim.ts
814
614
  var import_engine = require("@waveform-playlist/engine");
@@ -862,11 +662,11 @@ function useClipDragHandlers({
862
662
  snapSamplePosition
863
663
  }) {
864
664
  const { sampleRate } = usePlaylistData();
865
- const snapSamplePositionRef = import_react10.default.useRef(snapSamplePosition);
665
+ const snapSamplePositionRef = import_react8.default.useRef(snapSamplePosition);
866
666
  snapSamplePositionRef.current = snapSamplePosition;
867
- const originalClipStateRef = import_react10.default.useRef(null);
868
- const lastBoundaryDeltaRef = import_react10.default.useRef(0);
869
- const onDragStart = import_react10.default.useCallback(
667
+ const originalClipStateRef = import_react8.default.useRef(null);
668
+ const lastBoundaryDeltaRef = import_react8.default.useRef(0);
669
+ const onDragStart = import_react8.default.useCallback(
870
670
  (event) => {
871
671
  var _a;
872
672
  const data = (_a = event.operation.source) == null ? void 0 : _a.data;
@@ -902,7 +702,7 @@ function useClipDragHandlers({
902
702
  },
903
703
  [tracks, isDraggingRef, engineRef]
904
704
  );
905
- const onDragMove = import_react10.default.useCallback(
705
+ const onDragMove = import_react8.default.useCallback(
906
706
  (event) => {
907
707
  var _a, _b, _c;
908
708
  const data = (_a = event.operation.source) == null ? void 0 : _a.data;
@@ -964,7 +764,7 @@ function useClipDragHandlers({
964
764
  },
965
765
  [tracks, onTracksChange, samplesPerPixel, sampleRate]
966
766
  );
967
- const onDragEnd = import_react10.default.useCallback(
767
+ const onDragEnd = import_react8.default.useCallback(
968
768
  (event) => {
969
769
  var _a, _b, _c, _d, _e, _f, _g;
970
770
  if (event.canceled) {
@@ -1042,19 +842,19 @@ function useClipDragHandlers({
1042
842
  }
1043
843
 
1044
844
  // src/hooks/useAnnotationDragHandlers.ts
1045
- var import_react11 = __toESM(require("react"));
1046
- var import_playout2 = require("@waveform-playlist/playout");
845
+ var import_react9 = __toESM(require("react"));
1047
846
  var LINK_THRESHOLD = 0.01;
1048
847
  function useAnnotationDragHandlers({
1049
848
  annotations,
1050
849
  onAnnotationsChange,
1051
850
  samplesPerPixel,
1052
- sampleRate = (0, import_playout2.getGlobalAudioContext)().sampleRate,
851
+ // Default mirrors the engine default; the providers always pass the real rate from context (#510).
852
+ sampleRate = 48e3,
1053
853
  duration,
1054
854
  linkEndpoints
1055
855
  }) {
1056
- const originalAnnotationStateRef = import_react11.default.useRef(null);
1057
- const onDragStart = import_react11.default.useCallback(
856
+ const originalAnnotationStateRef = import_react9.default.useRef(null);
857
+ const onDragStart = import_react9.default.useCallback(
1058
858
  (event) => {
1059
859
  var _a;
1060
860
  const data = (_a = event.operation.source) == null ? void 0 : _a.data;
@@ -1073,7 +873,7 @@ function useAnnotationDragHandlers({
1073
873
  },
1074
874
  [annotations]
1075
875
  );
1076
- const onDragMove = import_react11.default.useCallback(
876
+ const onDragMove = import_react9.default.useCallback(
1077
877
  (event) => {
1078
878
  var _a, _b, _c;
1079
879
  if (!originalAnnotationStateRef.current) {
@@ -1099,7 +899,7 @@ function useAnnotationDragHandlers({
1099
899
  },
1100
900
  [annotations, onAnnotationsChange, samplesPerPixel, sampleRate, duration, linkEndpoints]
1101
901
  );
1102
- const onDragEnd = import_react11.default.useCallback(
902
+ const onDragEnd = import_react9.default.useCallback(
1103
903
  (event) => {
1104
904
  if (event.canceled && originalAnnotationStateRef.current) {
1105
905
  const { annotationIndex, start, end } = originalAnnotationStateRef.current;
@@ -1207,7 +1007,7 @@ function updateAnnotationBoundaries({
1207
1007
  }
1208
1008
 
1209
1009
  // src/hooks/useDragSensors.ts
1210
- var import_react12 = require("react");
1010
+ var import_react10 = require("react");
1211
1011
  var import_dom = require("@dnd-kit/dom");
1212
1012
  function useDragSensors(options = {}) {
1213
1013
  const {
@@ -1216,7 +1016,7 @@ function useDragSensors(options = {}) {
1216
1016
  touchTolerance = 5,
1217
1017
  mouseDistance = 1
1218
1018
  } = options;
1219
- return (0, import_react12.useMemo)(() => {
1019
+ return (0, import_react10.useMemo)(() => {
1220
1020
  if (touchOptimized) {
1221
1021
  return [
1222
1022
  import_dom.PointerSensor.configure({
@@ -1245,14 +1045,14 @@ function useDragSensors(options = {}) {
1245
1045
  }
1246
1046
 
1247
1047
  // src/hooks/useClipSplitting.ts
1248
- var import_react13 = require("react");
1048
+ var import_react11 = require("react");
1249
1049
  var import_engine2 = require("@waveform-playlist/engine");
1250
1050
  var useClipSplitting = (options) => {
1251
1051
  const { tracks, engineRef } = options;
1252
1052
  const { sampleRate } = usePlaylistData();
1253
1053
  const { currentTimeRef } = usePlaybackAnimation();
1254
1054
  const { selectedTrackId } = usePlaylistState();
1255
- const splitClipAt = (0, import_react13.useCallback)(
1055
+ const splitClipAt = (0, import_react11.useCallback)(
1256
1056
  (trackIndex, clipIndex, splitTime) => {
1257
1057
  const { samplesPerPixel } = options;
1258
1058
  const track = tracks[trackIndex];
@@ -1276,7 +1076,7 @@ var useClipSplitting = (options) => {
1276
1076
  },
1277
1077
  [tracks, options, engineRef, sampleRate]
1278
1078
  );
1279
- const splitClipAtPlayhead = (0, import_react13.useCallback)(() => {
1079
+ const splitClipAtPlayhead = (0, import_react11.useCallback)(() => {
1280
1080
  var _a;
1281
1081
  if (!selectedTrackId) {
1282
1082
  console.warn("[waveform-playlist] No track selected \u2014 click a clip to select a track first");
@@ -1307,16 +1107,16 @@ var useClipSplitting = (options) => {
1307
1107
  };
1308
1108
 
1309
1109
  // src/hooks/useKeyboardShortcuts.ts
1310
- var import_react14 = require("react");
1110
+ var import_react12 = require("react");
1111
+ var import_core = require("@waveform-playlist/core");
1311
1112
  var import_core2 = require("@waveform-playlist/core");
1312
- var import_core3 = require("@waveform-playlist/core");
1313
1113
  var useKeyboardShortcuts = (options) => {
1314
1114
  const { shortcuts, enabled = true } = options;
1315
- const handleKeyDown = (0, import_react14.useCallback)(
1316
- (event) => (0, import_core2.handleKeyboardEvent)(event, shortcuts, enabled),
1115
+ const handleKeyDown = (0, import_react12.useCallback)(
1116
+ (event) => (0, import_core.handleKeyboardEvent)(event, shortcuts, enabled),
1317
1117
  [shortcuts, enabled]
1318
1118
  );
1319
- (0, import_react14.useEffect)(() => {
1119
+ (0, import_react12.useEffect)(() => {
1320
1120
  if (!enabled) return;
1321
1121
  window.addEventListener("keydown", handleKeyDown);
1322
1122
  return () => {
@@ -1326,22 +1126,22 @@ var useKeyboardShortcuts = (options) => {
1326
1126
  };
1327
1127
 
1328
1128
  // src/hooks/usePlaybackShortcuts.ts
1329
- var import_react15 = require("react");
1129
+ var import_react13 = require("react");
1330
1130
  var usePlaybackShortcuts = (options = {}) => {
1331
1131
  const { enabled = true, additionalShortcuts = [], shortcuts: overrideShortcuts } = options;
1332
1132
  const { isPlaying } = usePlaybackAnimation();
1333
1133
  const { setCurrentTime, play, pause, stop } = usePlaylistControls();
1334
- const togglePlayPause = (0, import_react15.useCallback)(() => {
1134
+ const togglePlayPause = (0, import_react13.useCallback)(() => {
1335
1135
  if (isPlaying) {
1336
1136
  pause();
1337
1137
  } else {
1338
1138
  play();
1339
1139
  }
1340
1140
  }, [isPlaying, play, pause]);
1341
- const stopPlayback = (0, import_react15.useCallback)(() => {
1141
+ const stopPlayback = (0, import_react13.useCallback)(() => {
1342
1142
  stop();
1343
1143
  }, [stop]);
1344
- const rewindToStart = (0, import_react15.useCallback)(() => {
1144
+ const rewindToStart = (0, import_react13.useCallback)(() => {
1345
1145
  setCurrentTime(0);
1346
1146
  if (isPlaying) {
1347
1147
  play(0);
@@ -1381,7 +1181,7 @@ var usePlaybackShortcuts = (options = {}) => {
1381
1181
  };
1382
1182
 
1383
1183
  // src/hooks/useAnnotationKeyboardControls.ts
1384
- var import_react16 = require("react");
1184
+ var import_react14 = require("react");
1385
1185
  var LINK_THRESHOLD2 = 0.01;
1386
1186
  var TIME_DELTA = 0.01;
1387
1187
  function useAnnotationKeyboardControls({
@@ -1401,11 +1201,11 @@ function useAnnotationKeyboardControls({
1401
1201
  const playlistData = usePlaylistDataOptional();
1402
1202
  const samplesPerPixel = samplesPerPixelProp != null ? samplesPerPixelProp : playlistData == null ? void 0 : playlistData.samplesPerPixel;
1403
1203
  const sampleRate = sampleRateProp != null ? sampleRateProp : playlistData == null ? void 0 : playlistData.sampleRate;
1404
- const activeIndex = (0, import_react16.useMemo)(() => {
1204
+ const activeIndex = (0, import_react14.useMemo)(() => {
1405
1205
  if (!activeAnnotationId) return -1;
1406
1206
  return annotations.findIndex((a) => a.id === activeAnnotationId);
1407
1207
  }, [annotations, activeAnnotationId]);
1408
- const scrollToAnnotation = (0, import_react16.useCallback)(
1208
+ const scrollToAnnotation = (0, import_react14.useCallback)(
1409
1209
  (annotationId) => {
1410
1210
  if (!(scrollContainerRef == null ? void 0 : scrollContainerRef.current) || !samplesPerPixel || !sampleRate) return;
1411
1211
  const annotation = annotations.find((a) => a.id === annotationId);
@@ -1428,12 +1228,12 @@ function useAnnotationKeyboardControls({
1428
1228
  },
1429
1229
  [annotations, scrollContainerRef, samplesPerPixel, sampleRate]
1430
1230
  );
1431
- (0, import_react16.useEffect)(() => {
1231
+ (0, import_react14.useEffect)(() => {
1432
1232
  if (activeAnnotationId && (scrollContainerRef == null ? void 0 : scrollContainerRef.current) && samplesPerPixel && sampleRate) {
1433
1233
  scrollToAnnotation(activeAnnotationId);
1434
1234
  }
1435
1235
  }, [activeAnnotationId, scrollToAnnotation, scrollContainerRef, samplesPerPixel, sampleRate]);
1436
- const moveStartBoundary = (0, import_react16.useCallback)(
1236
+ const moveStartBoundary = (0, import_react14.useCallback)(
1437
1237
  (delta) => {
1438
1238
  if (activeIndex < 0) return;
1439
1239
  const annotation = annotations[activeIndex];
@@ -1462,7 +1262,7 @@ function useAnnotationKeyboardControls({
1462
1262
  },
1463
1263
  [annotations, activeIndex, linkEndpoints, onAnnotationsChange]
1464
1264
  );
1465
- const moveEndBoundary = (0, import_react16.useCallback)(
1265
+ const moveEndBoundary = (0, import_react14.useCallback)(
1466
1266
  (delta) => {
1467
1267
  if (activeIndex < 0) return;
1468
1268
  const annotation = annotations[activeIndex];
@@ -1522,7 +1322,7 @@ function useAnnotationKeyboardControls({
1522
1322
  },
1523
1323
  [annotations, activeIndex, duration, linkEndpoints, onAnnotationsChange]
1524
1324
  );
1525
- const selectPrevious = (0, import_react16.useCallback)(() => {
1325
+ const selectPrevious = (0, import_react14.useCallback)(() => {
1526
1326
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1527
1327
  if (activeIndex <= 0) {
1528
1328
  onActiveAnnotationChange(annotations[annotations.length - 1].id);
@@ -1530,7 +1330,7 @@ function useAnnotationKeyboardControls({
1530
1330
  onActiveAnnotationChange(annotations[activeIndex - 1].id);
1531
1331
  }
1532
1332
  }, [annotations, activeIndex, onActiveAnnotationChange]);
1533
- const selectNext = (0, import_react16.useCallback)(() => {
1333
+ const selectNext = (0, import_react14.useCallback)(() => {
1534
1334
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1535
1335
  if (activeIndex < 0 || activeIndex >= annotations.length - 1) {
1536
1336
  onActiveAnnotationChange(annotations[0].id);
@@ -1538,25 +1338,25 @@ function useAnnotationKeyboardControls({
1538
1338
  onActiveAnnotationChange(annotations[activeIndex + 1].id);
1539
1339
  }
1540
1340
  }, [annotations, activeIndex, onActiveAnnotationChange]);
1541
- const selectFirst = (0, import_react16.useCallback)(() => {
1341
+ const selectFirst = (0, import_react14.useCallback)(() => {
1542
1342
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1543
1343
  onActiveAnnotationChange(annotations[0].id);
1544
1344
  }, [annotations, onActiveAnnotationChange]);
1545
- const selectLast = (0, import_react16.useCallback)(() => {
1345
+ const selectLast = (0, import_react14.useCallback)(() => {
1546
1346
  if (!onActiveAnnotationChange || annotations.length === 0) return;
1547
1347
  onActiveAnnotationChange(annotations[annotations.length - 1].id);
1548
1348
  }, [annotations, onActiveAnnotationChange]);
1549
- const clearSelection = (0, import_react16.useCallback)(() => {
1349
+ const clearSelection = (0, import_react14.useCallback)(() => {
1550
1350
  if (!onActiveAnnotationChange) return;
1551
1351
  onActiveAnnotationChange(null);
1552
1352
  }, [onActiveAnnotationChange]);
1553
- const playActiveAnnotation = (0, import_react16.useCallback)(() => {
1353
+ const playActiveAnnotation = (0, import_react14.useCallback)(() => {
1554
1354
  if (activeIndex < 0 || !onPlay) return;
1555
1355
  const annotation = annotations[activeIndex];
1556
1356
  const playDuration = !continuousPlay ? annotation.end - annotation.start : void 0;
1557
1357
  onPlay(annotation.start, playDuration);
1558
1358
  }, [annotations, activeIndex, continuousPlay, onPlay]);
1559
- const activeAnnotationShortcuts = (0, import_react16.useMemo)(
1359
+ const activeAnnotationShortcuts = (0, import_react14.useMemo)(
1560
1360
  () => [
1561
1361
  {
1562
1362
  key: "[",
@@ -1593,7 +1393,7 @@ function useAnnotationKeyboardControls({
1593
1393
  ],
1594
1394
  [moveStartBoundary, moveEndBoundary, playActiveAnnotation]
1595
1395
  );
1596
- const navigationShortcuts = (0, import_react16.useMemo)(
1396
+ const navigationShortcuts = (0, import_react14.useMemo)(
1597
1397
  () => [
1598
1398
  {
1599
1399
  key: "ArrowUp",
@@ -1646,1397 +1446,39 @@ function useAnnotationKeyboardControls({
1646
1446
  });
1647
1447
  useKeyboardShortcuts({
1648
1448
  shortcuts: navigationShortcuts,
1649
- enabled: enabled && annotations.length > 0 && !!onActiveAnnotationChange
1650
- });
1651
- return {
1652
- moveStartBoundary,
1653
- moveEndBoundary,
1654
- selectPrevious,
1655
- selectNext,
1656
- selectFirst,
1657
- selectLast,
1658
- clearSelection,
1659
- scrollToAnnotation,
1660
- playActiveAnnotation
1661
- };
1662
- }
1663
-
1664
- // src/hooks/useDynamicEffects.ts
1665
- var import_react17 = require("react");
1666
-
1667
- // src/effects/effectDefinitions.ts
1668
- var effectDefinitions = [
1669
- // === REVERB EFFECTS ===
1670
- {
1671
- id: "reverb",
1672
- name: "Reverb",
1673
- category: "reverb",
1674
- description: "Simple convolution reverb with adjustable decay time",
1675
- parameters: [
1676
- {
1677
- name: "decay",
1678
- label: "Decay",
1679
- type: "number",
1680
- min: 0.1,
1681
- max: 10,
1682
- step: 0.1,
1683
- default: 1.5,
1684
- unit: "s"
1685
- },
1686
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1687
- ]
1688
- },
1689
- {
1690
- id: "freeverb",
1691
- name: "Freeverb",
1692
- category: "reverb",
1693
- description: "Classic Schroeder/Moorer reverb with room size and dampening",
1694
- parameters: [
1695
- {
1696
- name: "roomSize",
1697
- label: "Room Size",
1698
- type: "number",
1699
- min: 0,
1700
- max: 1,
1701
- step: 0.01,
1702
- default: 0.7
1703
- },
1704
- {
1705
- name: "dampening",
1706
- label: "Dampening",
1707
- type: "number",
1708
- min: 0,
1709
- max: 1e4,
1710
- step: 100,
1711
- default: 3e3,
1712
- unit: "Hz"
1713
- },
1714
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1715
- ]
1716
- },
1717
- {
1718
- id: "jcReverb",
1719
- name: "JC Reverb",
1720
- category: "reverb",
1721
- description: "Attempt at Roland JC-120 chorus reverb emulation",
1722
- parameters: [
1723
- {
1724
- name: "roomSize",
1725
- label: "Room Size",
1726
- type: "number",
1727
- min: 0,
1728
- max: 1,
1729
- step: 0.01,
1730
- default: 0.5
1731
- },
1732
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1733
- ]
1734
- },
1735
- // === DELAY EFFECTS ===
1736
- {
1737
- id: "feedbackDelay",
1738
- name: "Feedback Delay",
1739
- category: "delay",
1740
- description: "Delay line with feedback for echo effects",
1741
- parameters: [
1742
- {
1743
- name: "delayTime",
1744
- label: "Delay Time",
1745
- type: "number",
1746
- min: 0,
1747
- max: 1,
1748
- step: 0.01,
1749
- default: 0.25,
1750
- unit: "s"
1751
- },
1752
- {
1753
- name: "feedback",
1754
- label: "Feedback",
1755
- type: "number",
1756
- min: 0,
1757
- max: 0.95,
1758
- step: 0.01,
1759
- default: 0.5
1760
- },
1761
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1762
- ]
1763
- },
1764
- {
1765
- id: "pingPongDelay",
1766
- name: "Ping Pong Delay",
1767
- category: "delay",
1768
- description: "Stereo delay bouncing between left and right channels",
1769
- parameters: [
1770
- {
1771
- name: "delayTime",
1772
- label: "Delay Time",
1773
- type: "number",
1774
- min: 0,
1775
- max: 1,
1776
- step: 0.01,
1777
- default: 0.25,
1778
- unit: "s"
1779
- },
1780
- {
1781
- name: "feedback",
1782
- label: "Feedback",
1783
- type: "number",
1784
- min: 0,
1785
- max: 0.95,
1786
- step: 0.01,
1787
- default: 0.5
1788
- },
1789
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1790
- ]
1791
- },
1792
- // === MODULATION EFFECTS ===
1793
- {
1794
- id: "chorus",
1795
- name: "Chorus",
1796
- category: "modulation",
1797
- description: "Creates thickness by layering detuned copies of the signal",
1798
- parameters: [
1799
- {
1800
- name: "frequency",
1801
- label: "Rate",
1802
- type: "number",
1803
- min: 0.1,
1804
- max: 10,
1805
- step: 0.1,
1806
- default: 1.5,
1807
- unit: "Hz"
1808
- },
1809
- {
1810
- name: "delayTime",
1811
- label: "Delay",
1812
- type: "number",
1813
- min: 0,
1814
- max: 20,
1815
- step: 0.5,
1816
- default: 3.5,
1817
- unit: "ms"
1818
- },
1819
- { name: "depth", label: "Depth", type: "number", min: 0, max: 1, step: 0.01, default: 0.7 },
1820
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1821
- ]
1822
- },
1823
- {
1824
- id: "phaser",
1825
- name: "Phaser",
1826
- category: "modulation",
1827
- description: "Classic phaser effect using allpass filters",
1828
- parameters: [
1829
- {
1830
- name: "frequency",
1831
- label: "Rate",
1832
- type: "number",
1833
- min: 0.1,
1834
- max: 10,
1835
- step: 0.1,
1836
- default: 0.5,
1837
- unit: "Hz"
1838
- },
1839
- { name: "octaves", label: "Octaves", type: "number", min: 1, max: 6, step: 1, default: 3 },
1840
- {
1841
- name: "baseFrequency",
1842
- label: "Base Freq",
1843
- type: "number",
1844
- min: 100,
1845
- max: 2e3,
1846
- step: 10,
1847
- default: 350,
1848
- unit: "Hz"
1849
- },
1850
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
1851
- ]
1852
- },
1853
- {
1854
- id: "tremolo",
1855
- name: "Tremolo",
1856
- category: "modulation",
1857
- description: "Rhythmic volume modulation",
1858
- parameters: [
1859
- {
1860
- name: "frequency",
1861
- label: "Rate",
1862
- type: "number",
1863
- min: 0.1,
1864
- max: 20,
1865
- step: 0.1,
1866
- default: 4,
1867
- unit: "Hz"
1868
- },
1869
- { name: "depth", label: "Depth", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 },
1870
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1871
- ]
1872
- },
1873
- {
1874
- id: "vibrato",
1875
- name: "Vibrato",
1876
- category: "modulation",
1877
- description: "Pitch modulation effect",
1878
- parameters: [
1879
- {
1880
- name: "frequency",
1881
- label: "Rate",
1882
- type: "number",
1883
- min: 0.1,
1884
- max: 20,
1885
- step: 0.1,
1886
- default: 5,
1887
- unit: "Hz"
1888
- },
1889
- { name: "depth", label: "Depth", type: "number", min: 0, max: 1, step: 0.01, default: 0.1 },
1890
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1891
- ]
1892
- },
1893
- {
1894
- id: "autoPanner",
1895
- name: "Auto Panner",
1896
- category: "modulation",
1897
- description: "Automatic left-right panning",
1898
- parameters: [
1899
- {
1900
- name: "frequency",
1901
- label: "Rate",
1902
- type: "number",
1903
- min: 0.1,
1904
- max: 10,
1905
- step: 0.1,
1906
- default: 1,
1907
- unit: "Hz"
1908
- },
1909
- { name: "depth", label: "Depth", type: "number", min: 0, max: 1, step: 0.01, default: 1 },
1910
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1911
- ]
1912
- },
1913
- // === FILTER EFFECTS ===
1914
- {
1915
- id: "autoFilter",
1916
- name: "Auto Filter",
1917
- category: "filter",
1918
- description: "Automated filter sweep with LFO",
1919
- parameters: [
1920
- {
1921
- name: "frequency",
1922
- label: "Rate",
1923
- type: "number",
1924
- min: 0.1,
1925
- max: 10,
1926
- step: 0.1,
1927
- default: 1,
1928
- unit: "Hz"
1929
- },
1930
- {
1931
- name: "baseFrequency",
1932
- label: "Base Freq",
1933
- type: "number",
1934
- min: 20,
1935
- max: 2e3,
1936
- step: 10,
1937
- default: 200,
1938
- unit: "Hz"
1939
- },
1940
- {
1941
- name: "octaves",
1942
- label: "Octaves",
1943
- type: "number",
1944
- min: 0.5,
1945
- max: 8,
1946
- step: 0.5,
1947
- default: 2.6
1948
- },
1949
- { name: "depth", label: "Depth", type: "number", min: 0, max: 1, step: 0.01, default: 1 },
1950
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1951
- ]
1952
- },
1953
- {
1954
- id: "autoWah",
1955
- name: "Auto Wah",
1956
- category: "filter",
1957
- description: "Envelope follower filter effect",
1958
- parameters: [
1959
- {
1960
- name: "baseFrequency",
1961
- label: "Base Freq",
1962
- type: "number",
1963
- min: 20,
1964
- max: 500,
1965
- step: 10,
1966
- default: 100,
1967
- unit: "Hz"
1968
- },
1969
- { name: "octaves", label: "Octaves", type: "number", min: 1, max: 8, step: 1, default: 6 },
1970
- {
1971
- name: "sensitivity",
1972
- label: "Sensitivity",
1973
- type: "number",
1974
- min: -40,
1975
- max: 0,
1976
- step: 1,
1977
- default: 0,
1978
- unit: "dB"
1979
- },
1980
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
1981
- ]
1982
- },
1983
- {
1984
- id: "eq3",
1985
- name: "3-Band EQ",
1986
- category: "filter",
1987
- description: "Three band equalizer with low, mid, and high controls",
1988
- parameters: [
1989
- {
1990
- name: "low",
1991
- label: "Low",
1992
- type: "number",
1993
- min: -24,
1994
- max: 24,
1995
- step: 0.5,
1996
- default: 0,
1997
- unit: "dB"
1998
- },
1999
- {
2000
- name: "mid",
2001
- label: "Mid",
2002
- type: "number",
2003
- min: -24,
2004
- max: 24,
2005
- step: 0.5,
2006
- default: 0,
2007
- unit: "dB"
2008
- },
2009
- {
2010
- name: "high",
2011
- label: "High",
2012
- type: "number",
2013
- min: -24,
2014
- max: 24,
2015
- step: 0.5,
2016
- default: 0,
2017
- unit: "dB"
2018
- },
2019
- {
2020
- name: "lowFrequency",
2021
- label: "Low Freq",
2022
- type: "number",
2023
- min: 20,
2024
- max: 500,
2025
- step: 10,
2026
- default: 400,
2027
- unit: "Hz"
2028
- },
2029
- {
2030
- name: "highFrequency",
2031
- label: "High Freq",
2032
- type: "number",
2033
- min: 1e3,
2034
- max: 1e4,
2035
- step: 100,
2036
- default: 2500,
2037
- unit: "Hz"
2038
- }
2039
- ]
2040
- },
2041
- // === DISTORTION EFFECTS ===
2042
- {
2043
- id: "distortion",
2044
- name: "Distortion",
2045
- category: "distortion",
2046
- description: "Wave shaping distortion effect",
2047
- parameters: [
2048
- {
2049
- name: "distortion",
2050
- label: "Drive",
2051
- type: "number",
2052
- min: 0,
2053
- max: 1,
2054
- step: 0.01,
2055
- default: 0.4
2056
- },
2057
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
2058
- ]
2059
- },
2060
- {
2061
- id: "bitCrusher",
2062
- name: "Bit Crusher",
2063
- category: "distortion",
2064
- description: "Reduces bit depth for lo-fi digital texture",
2065
- parameters: [
2066
- { name: "bits", label: "Bits", type: "number", min: 1, max: 16, step: 1, default: 4 },
2067
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
2068
- ]
2069
- },
2070
- {
2071
- id: "chebyshev",
2072
- name: "Chebyshev",
2073
- category: "distortion",
2074
- description: "Waveshaping distortion using Chebyshev polynomials",
2075
- parameters: [
2076
- { name: "order", label: "Order", type: "number", min: 1, max: 100, step: 1, default: 50 },
2077
- { name: "wet", label: "Mix", type: "number", min: 0, max: 1, step: 0.01, default: 1 }
2078
- ]
2079
- },
2080
- // === DYNAMICS EFFECTS ===
2081
- {
2082
- id: "compressor",
2083
- name: "Compressor",
2084
- category: "dynamics",
2085
- description: "Dynamic range compressor",
2086
- parameters: [
2087
- {
2088
- name: "threshold",
2089
- label: "Threshold",
2090
- type: "number",
2091
- min: -60,
2092
- max: 0,
2093
- step: 1,
2094
- default: -24,
2095
- unit: "dB"
2096
- },
2097
- { name: "ratio", label: "Ratio", type: "number", min: 1, max: 20, step: 0.5, default: 4 },
2098
- {
2099
- name: "attack",
2100
- label: "Attack",
2101
- type: "number",
2102
- min: 0,
2103
- max: 1,
2104
- step: 1e-3,
2105
- default: 3e-3,
2106
- unit: "s"
2107
- },
2108
- {
2109
- name: "release",
2110
- label: "Release",
2111
- type: "number",
2112
- min: 0,
2113
- max: 1,
2114
- step: 0.01,
2115
- default: 0.25,
2116
- unit: "s"
2117
- },
2118
- {
2119
- name: "knee",
2120
- label: "Knee",
2121
- type: "number",
2122
- min: 0,
2123
- max: 40,
2124
- step: 1,
2125
- default: 30,
2126
- unit: "dB"
2127
- }
2128
- ]
2129
- },
2130
- {
2131
- id: "limiter",
2132
- name: "Limiter",
2133
- category: "dynamics",
2134
- description: "Hard limiter to prevent clipping",
2135
- parameters: [
2136
- {
2137
- name: "threshold",
2138
- label: "Threshold",
2139
- type: "number",
2140
- min: -12,
2141
- max: 0,
2142
- step: 0.5,
2143
- default: -6,
2144
- unit: "dB"
2145
- }
2146
- ]
2147
- },
2148
- {
2149
- id: "gate",
2150
- name: "Gate",
2151
- category: "dynamics",
2152
- description: "Noise gate to silence signal below threshold",
2153
- parameters: [
2154
- {
2155
- name: "threshold",
2156
- label: "Threshold",
2157
- type: "number",
2158
- min: -100,
2159
- max: 0,
2160
- step: 1,
2161
- default: -40,
2162
- unit: "dB"
2163
- },
2164
- {
2165
- name: "attack",
2166
- label: "Attack",
2167
- type: "number",
2168
- min: 0,
2169
- max: 0.3,
2170
- step: 1e-3,
2171
- default: 1e-3,
2172
- unit: "s"
2173
- },
2174
- {
2175
- name: "release",
2176
- label: "Release",
2177
- type: "number",
2178
- min: 0,
2179
- max: 0.5,
2180
- step: 0.01,
2181
- default: 0.1,
2182
- unit: "s"
2183
- }
2184
- ]
2185
- },
2186
- // === SPATIAL EFFECTS ===
2187
- {
2188
- id: "stereoWidener",
2189
- name: "Stereo Widener",
2190
- category: "spatial",
2191
- description: "Expands or narrows the stereo image",
2192
- parameters: [
2193
- { name: "width", label: "Width", type: "number", min: 0, max: 1, step: 0.01, default: 0.5 }
2194
- ]
2195
- }
2196
- ];
2197
- var getEffectDefinition = (id) => {
2198
- return effectDefinitions.find((def) => def.id === id);
2199
- };
2200
- var getEffectsByCategory = (category) => {
2201
- return effectDefinitions.filter((def) => def.category === category);
2202
- };
2203
- var effectCategories = [
2204
- { id: "reverb", name: "Reverb" },
2205
- { id: "delay", name: "Delay" },
2206
- { id: "modulation", name: "Modulation" },
2207
- { id: "filter", name: "Filter" },
2208
- { id: "distortion", name: "Distortion" },
2209
- { id: "dynamics", name: "Dynamics" },
2210
- { id: "spatial", name: "Spatial" }
2211
- ];
2212
-
2213
- // src/effects/effectFactory.ts
2214
- var import_tone2 = require("tone");
2215
- function asEffectConstructor(ctor) {
2216
- return ctor;
2217
- }
2218
- var effectConstructors = {
2219
- reverb: asEffectConstructor(import_tone2.Reverb),
2220
- freeverb: asEffectConstructor(import_tone2.Freeverb),
2221
- jcReverb: asEffectConstructor(import_tone2.JCReverb),
2222
- feedbackDelay: asEffectConstructor(import_tone2.FeedbackDelay),
2223
- pingPongDelay: asEffectConstructor(import_tone2.PingPongDelay),
2224
- chorus: asEffectConstructor(import_tone2.Chorus),
2225
- phaser: asEffectConstructor(import_tone2.Phaser),
2226
- tremolo: asEffectConstructor(import_tone2.Tremolo),
2227
- vibrato: asEffectConstructor(import_tone2.Vibrato),
2228
- autoPanner: asEffectConstructor(import_tone2.AutoPanner),
2229
- autoFilter: asEffectConstructor(import_tone2.AutoFilter),
2230
- autoWah: asEffectConstructor(import_tone2.AutoWah),
2231
- eq3: asEffectConstructor(import_tone2.EQ3),
2232
- distortion: asEffectConstructor(import_tone2.Distortion),
2233
- bitCrusher: asEffectConstructor(import_tone2.BitCrusher),
2234
- chebyshev: asEffectConstructor(import_tone2.Chebyshev),
2235
- compressor: asEffectConstructor(import_tone2.Compressor),
2236
- limiter: asEffectConstructor(import_tone2.Limiter),
2237
- gate: asEffectConstructor(import_tone2.Gate),
2238
- stereoWidener: asEffectConstructor(import_tone2.StereoWidener)
2239
- };
2240
- var instanceCounter = 0;
2241
- var generateInstanceId = () => {
2242
- return `effect_${Date.now()}_${++instanceCounter}`;
2243
- };
2244
- function createEffectInstance(definition, initialParams) {
2245
- const Constructor = effectConstructors[definition.id];
2246
- if (!Constructor) {
2247
- throw new Error(`Unknown effect type: ${definition.id}`);
2248
- }
2249
- const options = {};
2250
- definition.parameters.forEach((param) => {
2251
- var _a;
2252
- const value = (_a = initialParams == null ? void 0 : initialParams[param.name]) != null ? _a : param.default;
2253
- options[param.name] = value;
2254
- });
2255
- const effect = new Constructor(options);
2256
- const instanceId = generateInstanceId();
2257
- const effectRecord = effect;
2258
- return {
2259
- effect,
2260
- id: definition.id,
2261
- instanceId,
2262
- dispose() {
2263
- try {
2264
- effect.disconnect();
2265
- effect.dispose();
2266
- } catch (e) {
2267
- console.warn(
2268
- `[waveform-playlist] Error disposing effect "${definition.id}" (${instanceId}):`,
2269
- e
2270
- );
2271
- }
2272
- },
2273
- setParameter(name, value) {
2274
- const prop = effectRecord[name];
2275
- if (name === "wet") {
2276
- const wetProp = effectRecord["wet"];
2277
- if (wetProp && typeof wetProp === "object" && "value" in wetProp) {
2278
- wetProp.value = value;
2279
- return;
2280
- }
2281
- }
2282
- if (prop !== void 0) {
2283
- if (prop && typeof prop === "object" && "value" in prop) {
2284
- prop.value = value;
2285
- } else {
2286
- effectRecord[name] = value;
2287
- }
2288
- }
2289
- },
2290
- getParameter(name) {
2291
- if (name === "wet") {
2292
- const wetProp = effectRecord["wet"];
2293
- if (wetProp && typeof wetProp === "object" && "value" in wetProp) {
2294
- return wetProp.value;
2295
- }
2296
- }
2297
- const prop = effectRecord[name];
2298
- if (prop !== void 0) {
2299
- if (prop && typeof prop === "object" && "value" in prop) {
2300
- return prop.value;
2301
- }
2302
- return prop;
2303
- }
2304
- return void 0;
2305
- },
2306
- connect(destination) {
2307
- effect.connect(destination);
2308
- },
2309
- disconnect() {
2310
- try {
2311
- effect.disconnect();
2312
- } catch (e) {
2313
- console.warn(
2314
- `[waveform-playlist] Error disconnecting effect "${definition.id}" (${instanceId}):`,
2315
- e
2316
- );
2317
- }
2318
- }
2319
- };
2320
- }
2321
- function createEffectChain(effects) {
2322
- if (effects.length === 0) {
2323
- throw new Error("Cannot create effect chain with no effects");
2324
- }
2325
- for (let i = 0; i < effects.length - 1; i++) {
2326
- effects[i].effect.connect(effects[i + 1].effect);
2327
- }
2328
- return {
2329
- input: effects[0].effect,
2330
- output: effects[effects.length - 1].effect,
2331
- dispose() {
2332
- effects.forEach((e) => e.dispose());
2333
- }
2334
- };
2335
- }
2336
-
2337
- // src/hooks/useDynamicEffects.ts
2338
- var import_tone3 = require("tone");
2339
- function useDynamicEffects(fftSize = 256) {
2340
- const [activeEffects, setActiveEffects] = (0, import_react17.useState)([]);
2341
- const activeEffectsRef = (0, import_react17.useRef)(activeEffects);
2342
- activeEffectsRef.current = activeEffects;
2343
- const effectInstancesRef = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
2344
- const analyserRef = (0, import_react17.useRef)(null);
2345
- const graphNodesRef = (0, import_react17.useRef)(null);
2346
- const rebuildChain = (0, import_react17.useCallback)((effects) => {
2347
- const nodes = graphNodesRef.current;
2348
- if (!nodes) return;
2349
- const { masterGainNode, destination, analyserNode } = nodes;
2350
- try {
2351
- masterGainNode.disconnect();
2352
- } catch (e) {
2353
- console.warn("[waveform-playlist] Error disconnecting master effects chain:", e);
2354
- }
2355
- const instances = effects.map((ae) => effectInstancesRef.current.get(ae.instanceId)).filter((inst) => inst !== void 0);
2356
- if (instances.length === 0) {
2357
- masterGainNode.connect(analyserNode);
2358
- analyserNode.connect(destination);
2359
- } else {
2360
- let currentNode = masterGainNode;
2361
- instances.forEach((inst) => {
2362
- try {
2363
- inst.disconnect();
2364
- } catch (e) {
2365
- console.warn(`[waveform-playlist] Error disconnecting effect "${inst.id}":`, e);
2366
- }
2367
- currentNode.connect(inst.effect);
2368
- currentNode = inst.effect;
2369
- });
2370
- currentNode.connect(analyserNode);
2371
- analyserNode.connect(destination);
2372
- }
2373
- }, []);
2374
- const addEffect = (0, import_react17.useCallback)((effectId) => {
2375
- const definition = getEffectDefinition(effectId);
2376
- if (!definition) {
2377
- console.error(`Unknown effect: ${effectId}`);
2378
- return;
2379
- }
2380
- const params = {};
2381
- definition.parameters.forEach((p) => {
2382
- params[p.name] = p.default;
2383
- });
2384
- const instance = createEffectInstance(definition, params);
2385
- effectInstancesRef.current.set(instance.instanceId, instance);
2386
- const newActiveEffect = {
2387
- instanceId: instance.instanceId,
2388
- effectId: definition.id,
2389
- definition,
2390
- params,
2391
- bypassed: false
2392
- };
2393
- setActiveEffects((prev) => [...prev, newActiveEffect]);
2394
- }, []);
2395
- const removeEffect = (0, import_react17.useCallback)((instanceId) => {
2396
- const instance = effectInstancesRef.current.get(instanceId);
2397
- if (instance) {
2398
- instance.dispose();
2399
- effectInstancesRef.current.delete(instanceId);
2400
- }
2401
- setActiveEffects((prev) => prev.filter((e) => e.instanceId !== instanceId));
2402
- }, []);
2403
- const updateParameter = (0, import_react17.useCallback)(
2404
- (instanceId, paramName, value) => {
2405
- const instance = effectInstancesRef.current.get(instanceId);
2406
- if (instance) {
2407
- instance.setParameter(paramName, value);
2408
- }
2409
- setActiveEffects(
2410
- (prev) => prev.map(
2411
- (e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { params: __spreadProps(__spreadValues({}, e.params), { [paramName]: value }) }) : e
2412
- )
2413
- );
2414
- },
2415
- []
2416
- );
2417
- const toggleBypass = (0, import_react17.useCallback)((instanceId) => {
2418
- var _a;
2419
- const effect = activeEffectsRef.current.find((e) => e.instanceId === instanceId);
2420
- if (!effect) return;
2421
- const newBypassed = !effect.bypassed;
2422
- const instance = effectInstancesRef.current.get(instanceId);
2423
- if (instance) {
2424
- const originalWet = (_a = effect.params.wet) != null ? _a : 1;
2425
- instance.setParameter("wet", newBypassed ? 0 : originalWet);
2426
- }
2427
- setActiveEffects(
2428
- (prev) => prev.map((e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { bypassed: newBypassed }) : e)
2429
- );
2430
- }, []);
2431
- const reorderEffects = (0, import_react17.useCallback)((fromIndex, toIndex) => {
2432
- setActiveEffects((prev) => {
2433
- const newEffects = [...prev];
2434
- const [removed] = newEffects.splice(fromIndex, 1);
2435
- newEffects.splice(toIndex, 0, removed);
2436
- return newEffects;
2437
- });
2438
- }, []);
2439
- const clearAllEffects = (0, import_react17.useCallback)(() => {
2440
- effectInstancesRef.current.forEach((inst) => inst.dispose());
2441
- effectInstancesRef.current.clear();
2442
- setActiveEffects([]);
2443
- }, []);
2444
- (0, import_react17.useEffect)(() => {
2445
- rebuildChain(activeEffects);
2446
- }, [activeEffects, rebuildChain]);
2447
- const masterEffects = (0, import_react17.useCallback)(
2448
- (masterGainNode, destination, _isOffline) => {
2449
- const analyserNode = new import_tone3.Analyser("fft", fftSize);
2450
- analyserRef.current = analyserNode;
2451
- graphNodesRef.current = {
2452
- masterGainNode,
2453
- destination,
2454
- analyserNode
2455
- };
2456
- const effects = activeEffectsRef.current;
2457
- const instances = effects.map((ae) => effectInstancesRef.current.get(ae.instanceId)).filter((inst) => inst !== void 0);
2458
- if (instances.length === 0) {
2459
- masterGainNode.connect(analyserNode);
2460
- analyserNode.connect(destination);
2461
- } else {
2462
- let currentNode = masterGainNode;
2463
- instances.forEach((inst) => {
2464
- currentNode.connect(inst.effect);
2465
- currentNode = inst.effect;
2466
- });
2467
- currentNode.connect(analyserNode);
2468
- analyserNode.connect(destination);
2469
- }
2470
- return function cleanup() {
2471
- analyserNode.dispose();
2472
- analyserRef.current = null;
2473
- graphNodesRef.current = null;
2474
- };
2475
- },
2476
- [fftSize]
2477
- // Only fftSize - reads effects from ref
2478
- );
2479
- (0, import_react17.useEffect)(() => {
2480
- const effectInstances = effectInstancesRef.current;
2481
- return () => {
2482
- effectInstances.forEach((inst) => inst.dispose());
2483
- effectInstances.clear();
2484
- };
2485
- }, []);
2486
- const createOfflineEffectsFunction = (0, import_react17.useCallback)(() => {
2487
- const nonBypassedEffects = activeEffects.filter((e) => !e.bypassed);
2488
- if (nonBypassedEffects.length === 0) {
2489
- return void 0;
2490
- }
2491
- return (masterGainNode, destination, _isOffline) => {
2492
- const offlineInstances = [];
2493
- for (const activeEffect of nonBypassedEffects) {
2494
- const instance = createEffectInstance(activeEffect.definition, activeEffect.params);
2495
- offlineInstances.push(instance);
2496
- }
2497
- if (offlineInstances.length === 0) {
2498
- masterGainNode.connect(destination);
2499
- } else {
2500
- let currentNode = masterGainNode;
2501
- offlineInstances.forEach((inst) => {
2502
- currentNode.connect(inst.effect);
2503
- currentNode = inst.effect;
2504
- });
2505
- currentNode.connect(destination);
2506
- }
2507
- return function cleanup() {
2508
- offlineInstances.forEach((inst) => inst.dispose());
2509
- };
2510
- };
2511
- }, [activeEffects]);
2512
- return {
2513
- activeEffects,
2514
- availableEffects: effectDefinitions,
2515
- addEffect,
2516
- removeEffect,
2517
- updateParameter,
2518
- toggleBypass,
2519
- reorderEffects,
2520
- clearAllEffects,
2521
- masterEffects,
2522
- createOfflineEffectsFunction,
2523
- analyserRef
2524
- };
2525
- }
2526
-
2527
- // src/hooks/useTrackDynamicEffects.ts
2528
- var import_react18 = require("react");
2529
- function useTrackDynamicEffects() {
2530
- const [trackEffectsState, setTrackEffectsState] = (0, import_react18.useState)(
2531
- /* @__PURE__ */ new Map()
2532
- );
2533
- const trackEffectInstancesRef = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
2534
- const trackGraphNodesRef = (0, import_react18.useRef)(/* @__PURE__ */ new Map());
2535
- const rebuildTrackChain = (0, import_react18.useCallback)((trackId, trackEffects) => {
2536
- const nodes = trackGraphNodesRef.current.get(trackId);
2537
- if (!nodes) return;
2538
- const { graphEnd, masterGainNode } = nodes;
2539
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2540
- try {
2541
- graphEnd.disconnect();
2542
- } catch (e) {
2543
- console.warn(`[waveform-playlist] Error disconnecting track "${trackId}" effect chain:`, e);
2544
- }
2545
- const instances = trackEffects.map((ae) => instancesMap == null ? void 0 : instancesMap.get(ae.instanceId)).filter((inst) => inst !== void 0);
2546
- if (instances.length === 0) {
2547
- graphEnd.connect(masterGainNode);
2548
- } else {
2549
- let currentNode = graphEnd;
2550
- instances.forEach((inst) => {
2551
- try {
2552
- inst.disconnect();
2553
- } catch (e) {
2554
- console.warn(
2555
- `[waveform-playlist] Error disconnecting effect "${inst.id}" on track "${trackId}":`,
2556
- e
2557
- );
2558
- }
2559
- currentNode.connect(inst.effect);
2560
- currentNode = inst.effect;
2561
- });
2562
- currentNode.connect(masterGainNode);
2563
- }
2564
- }, []);
2565
- const addEffectToTrack = (0, import_react18.useCallback)((trackId, effectId) => {
2566
- const definition = getEffectDefinition(effectId);
2567
- if (!definition) {
2568
- console.error(`Unknown effect: ${effectId}`);
2569
- return;
2570
- }
2571
- const params = {};
2572
- definition.parameters.forEach((p) => {
2573
- params[p.name] = p.default;
2574
- });
2575
- const instance = createEffectInstance(definition, params);
2576
- if (!trackEffectInstancesRef.current.has(trackId)) {
2577
- trackEffectInstancesRef.current.set(trackId, /* @__PURE__ */ new Map());
2578
- }
2579
- trackEffectInstancesRef.current.get(trackId).set(instance.instanceId, instance);
2580
- const newActiveEffect = {
2581
- instanceId: instance.instanceId,
2582
- effectId: definition.id,
2583
- definition,
2584
- params,
2585
- bypassed: false
2586
- };
2587
- setTrackEffectsState((prev) => {
2588
- const newState = new Map(prev);
2589
- const existing = newState.get(trackId) || [];
2590
- newState.set(trackId, [...existing, newActiveEffect]);
2591
- return newState;
2592
- });
2593
- }, []);
2594
- const removeEffectFromTrack = (0, import_react18.useCallback)((trackId, instanceId) => {
2595
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2596
- const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
2597
- if (instance) {
2598
- instance.dispose();
2599
- instancesMap == null ? void 0 : instancesMap.delete(instanceId);
2600
- }
2601
- setTrackEffectsState((prev) => {
2602
- const newState = new Map(prev);
2603
- const existing = newState.get(trackId) || [];
2604
- newState.set(
2605
- trackId,
2606
- existing.filter((e) => e.instanceId !== instanceId)
2607
- );
2608
- return newState;
2609
- });
2610
- }, []);
2611
- const updateTrackEffectParameter = (0, import_react18.useCallback)(
2612
- (trackId, instanceId, paramName, value) => {
2613
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2614
- const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
2615
- if (instance) {
2616
- instance.setParameter(paramName, value);
2617
- }
2618
- setTrackEffectsState((prev) => {
2619
- const newState = new Map(prev);
2620
- const existing = newState.get(trackId) || [];
2621
- newState.set(
2622
- trackId,
2623
- existing.map(
2624
- (e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { params: __spreadProps(__spreadValues({}, e.params), { [paramName]: value }) }) : e
2625
- )
2626
- );
2627
- return newState;
2628
- });
2629
- },
2630
- []
2631
- );
2632
- const toggleBypass = (0, import_react18.useCallback)((trackId, instanceId) => {
2633
- var _a;
2634
- const trackEffects = trackEffectsStateRef.current.get(trackId) || [];
2635
- const effect = trackEffects.find((e) => e.instanceId === instanceId);
2636
- if (!effect) return;
2637
- const newBypassed = !effect.bypassed;
2638
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2639
- const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
2640
- if (instance) {
2641
- const originalWet = (_a = effect.params.wet) != null ? _a : 1;
2642
- instance.setParameter("wet", newBypassed ? 0 : originalWet);
2643
- }
2644
- setTrackEffectsState((prev) => {
2645
- const newState = new Map(prev);
2646
- const existing = newState.get(trackId) || [];
2647
- newState.set(
2648
- trackId,
2649
- existing.map((e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { bypassed: newBypassed }) : e)
2650
- );
2651
- return newState;
2652
- });
2653
- }, []);
2654
- const clearTrackEffects = (0, import_react18.useCallback)((trackId) => {
2655
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2656
- if (instancesMap) {
2657
- instancesMap.forEach((inst) => inst.dispose());
2658
- instancesMap.clear();
2659
- }
2660
- setTrackEffectsState((prev) => {
2661
- const newState = new Map(prev);
2662
- newState.set(trackId, []);
2663
- return newState;
2664
- });
2665
- }, []);
2666
- const trackEffectsStateRef = (0, import_react18.useRef)(trackEffectsState);
2667
- trackEffectsStateRef.current = trackEffectsState;
2668
- const getTrackEffectsFunction = (0, import_react18.useCallback)(
2669
- (trackId) => {
2670
- return (graphEnd, masterGainNode, _isOffline) => {
2671
- trackGraphNodesRef.current.set(trackId, {
2672
- graphEnd,
2673
- masterGainNode
2674
- });
2675
- const trackEffects = trackEffectsStateRef.current.get(trackId) || [];
2676
- const instancesMap = trackEffectInstancesRef.current.get(trackId);
2677
- const instances = trackEffects.map((ae) => instancesMap == null ? void 0 : instancesMap.get(ae.instanceId)).filter((inst) => inst !== void 0);
2678
- if (instances.length === 0) {
2679
- graphEnd.connect(masterGainNode);
2680
- } else {
2681
- let currentNode = graphEnd;
2682
- instances.forEach((inst) => {
2683
- currentNode.connect(inst.effect);
2684
- currentNode = inst.effect;
2685
- });
2686
- currentNode.connect(masterGainNode);
2687
- }
2688
- return function cleanup() {
2689
- trackGraphNodesRef.current.delete(trackId);
2690
- };
2691
- };
2692
- },
2693
- []
2694
- // No dependencies - stable function that reads from refs
2695
- );
2696
- (0, import_react18.useEffect)(() => {
2697
- trackEffectsState.forEach((effects, trackId) => {
2698
- rebuildTrackChain(trackId, effects);
2699
- });
2700
- }, [trackEffectsState, rebuildTrackChain]);
2701
- (0, import_react18.useEffect)(() => {
2702
- const trackEffectInstances = trackEffectInstancesRef.current;
2703
- return () => {
2704
- trackEffectInstances.forEach((instancesMap) => {
2705
- instancesMap.forEach((inst) => inst.dispose());
2706
- instancesMap.clear();
2707
- });
2708
- trackEffectInstances.clear();
2709
- };
2710
- }, []);
2711
- const createOfflineTrackEffectsFunction = (0, import_react18.useCallback)(
2712
- (trackId) => {
2713
- const trackEffects = trackEffectsState.get(trackId) || [];
2714
- const nonBypassedEffects = trackEffects.filter((e) => !e.bypassed);
2715
- if (nonBypassedEffects.length === 0) {
2716
- return void 0;
2717
- }
2718
- return (graphEnd, masterGainNode, _isOffline) => {
2719
- const offlineInstances = [];
2720
- for (const activeEffect of nonBypassedEffects) {
2721
- const instance = createEffectInstance(activeEffect.definition, activeEffect.params);
2722
- offlineInstances.push(instance);
2723
- }
2724
- if (offlineInstances.length === 0) {
2725
- graphEnd.connect(masterGainNode);
2726
- } else {
2727
- let currentNode = graphEnd;
2728
- offlineInstances.forEach((inst) => {
2729
- currentNode.connect(inst.effect);
2730
- currentNode = inst.effect;
2731
- });
2732
- currentNode.connect(masterGainNode);
2733
- }
2734
- return function cleanup() {
2735
- offlineInstances.forEach((inst) => inst.dispose());
2736
- };
2737
- };
2738
- },
2739
- [trackEffectsState]
2740
- );
2741
- return {
2742
- trackEffectsState,
2743
- addEffectToTrack,
2744
- removeEffectFromTrack,
2745
- updateTrackEffectParameter,
2746
- toggleBypass,
2747
- clearTrackEffects,
2748
- getTrackEffectsFunction,
2749
- createOfflineTrackEffectsFunction,
2750
- availableEffects: effectDefinitions
2751
- };
2752
- }
2753
-
2754
- // src/hooks/useExportWav.ts
2755
- var import_react19 = require("react");
2756
- var import_core4 = require("@waveform-playlist/core");
2757
- var import_playout3 = require("@waveform-playlist/playout");
2758
-
2759
- // src/utils/wavEncoder.ts
2760
- function encodeWav(audioBuffer, options = {}) {
2761
- const { bitDepth = 16 } = options;
2762
- const numChannels = audioBuffer.numberOfChannels;
2763
- const sampleRate = audioBuffer.sampleRate;
2764
- const numSamples = audioBuffer.length;
2765
- const bytesPerSample = bitDepth / 8;
2766
- const blockAlign = numChannels * bytesPerSample;
2767
- const byteRate = sampleRate * blockAlign;
2768
- const dataSize = numSamples * blockAlign;
2769
- const headerSize = 44;
2770
- const totalSize = headerSize + dataSize;
2771
- const buffer = new ArrayBuffer(totalSize);
2772
- const view = new DataView(buffer);
2773
- writeString(view, 0, "RIFF");
2774
- view.setUint32(4, totalSize - 8, true);
2775
- writeString(view, 8, "WAVE");
2776
- writeString(view, 12, "fmt ");
2777
- view.setUint32(16, 16, true);
2778
- view.setUint16(20, bitDepth === 32 ? 3 : 1, true);
2779
- view.setUint16(22, numChannels, true);
2780
- view.setUint32(24, sampleRate, true);
2781
- view.setUint32(28, byteRate, true);
2782
- view.setUint16(32, blockAlign, true);
2783
- view.setUint16(34, bitDepth, true);
2784
- writeString(view, 36, "data");
2785
- view.setUint32(40, dataSize, true);
2786
- const channelData = [];
2787
- for (let ch = 0; ch < numChannels; ch++) {
2788
- channelData.push(audioBuffer.getChannelData(ch));
2789
- }
2790
- let offset = headerSize;
2791
- if (bitDepth === 16) {
2792
- for (let i = 0; i < numSamples; i++) {
2793
- for (let ch = 0; ch < numChannels; ch++) {
2794
- const sample = channelData[ch][i];
2795
- const clampedSample = Math.max(-1, Math.min(1, sample));
2796
- const intSample = clampedSample < 0 ? clampedSample * 32768 : clampedSample * 32767;
2797
- view.setInt16(offset, intSample, true);
2798
- offset += 2;
2799
- }
2800
- }
2801
- } else {
2802
- for (let i = 0; i < numSamples; i++) {
2803
- for (let ch = 0; ch < numChannels; ch++) {
2804
- view.setFloat32(offset, channelData[ch][i], true);
2805
- offset += 4;
2806
- }
2807
- }
2808
- }
2809
- return new Blob([buffer], { type: "audio/wav" });
2810
- }
2811
- function writeString(view, offset, str) {
2812
- for (let i = 0; i < str.length; i++) {
2813
- view.setUint8(offset + i, str.charCodeAt(i));
2814
- }
2815
- }
2816
- function downloadBlob(blob, filename) {
2817
- const url = URL.createObjectURL(blob);
2818
- const a = document.createElement("a");
2819
- a.href = url;
2820
- a.download = filename;
2821
- a.style.display = "none";
2822
- document.body.appendChild(a);
2823
- a.click();
2824
- document.body.removeChild(a);
2825
- URL.revokeObjectURL(url);
2826
- }
2827
-
2828
- // src/hooks/useExportWav.ts
2829
- function useExportWav() {
2830
- const [isExporting, setIsExporting] = (0, import_react19.useState)(false);
2831
- const [progress, setProgress] = (0, import_react19.useState)(0);
2832
- const [error, setError] = (0, import_react19.useState)(null);
2833
- const exportWav = (0, import_react19.useCallback)(
2834
- (_0, _1, ..._2) => __async(null, [_0, _1, ..._2], function* (tracks, trackStates, options = {}) {
2835
- const {
2836
- filename = "export",
2837
- mode = "master",
2838
- trackIndex,
2839
- autoDownload = true,
2840
- applyEffects = true,
2841
- effectsFunction,
2842
- createOfflineTrackEffects,
2843
- bitDepth = 16,
2844
- onProgress
2845
- } = options;
2846
- setIsExporting(true);
2847
- setProgress(0);
2848
- setError(null);
2849
- try {
2850
- if (tracks.length === 0) {
2851
- throw new Error("No tracks to export");
2852
- }
2853
- if (mode === "individual" && (trackIndex === void 0 || trackIndex < 0 || trackIndex >= tracks.length)) {
2854
- throw new Error("Invalid track index for individual export");
2855
- }
2856
- const sampleRate = (0, import_playout3.getGlobalAudioContext)().sampleRate;
2857
- let totalDurationSamples = 0;
2858
- for (const track of tracks) {
2859
- for (const clip of track.clips) {
2860
- const clipEndSample = clip.startSample + clip.durationSamples;
2861
- totalDurationSamples = Math.max(totalDurationSamples, clipEndSample);
2862
- }
2863
- }
2864
- totalDurationSamples += Math.round(sampleRate * 0.1);
2865
- const duration = totalDurationSamples / sampleRate;
2866
- const tracksToRender = mode === "individual" ? [{ track: tracks[trackIndex], state: trackStates[trackIndex], index: trackIndex }] : tracks.map((track, index) => ({ track, state: trackStates[index], index }));
2867
- const hasSolo = mode === "master" && trackStates.some((state) => state.soloed);
2868
- const reportProgress = (p) => {
2869
- setProgress(p);
2870
- onProgress == null ? void 0 : onProgress(p);
2871
- };
2872
- const renderedBuffer = yield renderOffline(
2873
- tracksToRender,
2874
- hasSolo,
2875
- duration,
2876
- sampleRate,
2877
- applyEffects,
2878
- effectsFunction,
2879
- createOfflineTrackEffects,
2880
- reportProgress
2881
- );
2882
- reportProgress(0.9);
2883
- const blob = encodeWav(renderedBuffer, { bitDepth });
2884
- reportProgress(1);
2885
- if (autoDownload) {
2886
- const exportFilename = mode === "individual" ? `${filename}_${tracks[trackIndex].name}` : filename;
2887
- downloadBlob(blob, `${exportFilename}.wav`);
2888
- }
2889
- return {
2890
- audioBuffer: renderedBuffer,
2891
- blob,
2892
- duration
2893
- };
2894
- } catch (err) {
2895
- const message = err instanceof Error ? err.message : "Export failed";
2896
- setError(message);
2897
- throw err;
2898
- } finally {
2899
- setIsExporting(false);
2900
- }
2901
- }),
2902
- []
2903
- );
2904
- return {
2905
- exportWav,
2906
- isExporting,
2907
- progress,
2908
- error
2909
- };
2910
- }
2911
- function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffects, effectsFunction, createOfflineTrackEffects, onProgress) {
2912
- return __async(this, null, function* () {
2913
- const { Offline, Volume: Volume2, Gain, Panner, Player, ToneAudioBuffer } = yield import("tone");
2914
- onProgress(0.1);
2915
- const audibleTracks = tracksToRender.filter(({ state }) => {
2916
- if (state.muted && !state.soloed) return false;
2917
- if (hasSolo && !state.soloed) return false;
2918
- return true;
2919
- });
2920
- const outputChannels = audibleTracks.reduce(
2921
- (max, { track }) => Math.max(max, (0, import_core4.trackChannelCount)(track)),
2922
- 1
2923
- );
2924
- let buffer;
2925
- try {
2926
- buffer = yield Offline(
2927
- (_0) => __async(null, [_0], function* ({ transport, destination }) {
2928
- const masterVolume = new Volume2(0);
2929
- if (effectsFunction && applyEffects) {
2930
- effectsFunction(masterVolume, destination, true);
2931
- } else {
2932
- masterVolume.connect(destination);
2933
- }
2934
- for (const { track, state } of audibleTracks) {
2935
- const trackVolume = new Volume2((0, import_core4.gainToDb)(state.volume));
2936
- const trackPan = new Panner({ pan: state.pan, channelCount: (0, import_core4.trackChannelCount)(track) });
2937
- const trackMute = new Gain(state.muted ? 0 : 1);
2938
- const trackEffects = createOfflineTrackEffects == null ? void 0 : createOfflineTrackEffects(track.id);
2939
- if (trackEffects && applyEffects) {
2940
- trackEffects(trackMute, masterVolume, true);
2941
- } else {
2942
- trackMute.connect(masterVolume);
2943
- }
2944
- trackPan.connect(trackMute);
2945
- trackVolume.connect(trackPan);
2946
- for (const clip of track.clips) {
2947
- const {
2948
- audioBuffer,
2949
- startSample,
2950
- durationSamples,
2951
- offsetSamples,
2952
- gain: clipGain,
2953
- fadeIn,
2954
- fadeOut
2955
- } = clip;
2956
- if (!audioBuffer) {
2957
- console.warn(
2958
- '[waveform-playlist] Skipping clip "' + (clip.name || clip.id) + '" - no audioBuffer for export'
2959
- );
2960
- continue;
2961
- }
2962
- const startTime = startSample / sampleRate;
2963
- const clipDuration = durationSamples / sampleRate;
2964
- const offset = offsetSamples / sampleRate;
2965
- const toneBuffer = new ToneAudioBuffer(audioBuffer);
2966
- const player = new Player(toneBuffer);
2967
- const fadeGain = new Gain(clipGain);
2968
- player.connect(fadeGain);
2969
- fadeGain.connect(trackVolume);
2970
- if (applyEffects) {
2971
- const audioParam = (0, import_playout3.getUnderlyingAudioParam)(fadeGain.gain);
2972
- if (audioParam) {
2973
- applyClipFades(audioParam, clipGain, startTime, clipDuration, fadeIn, fadeOut);
2974
- } else if (fadeIn || fadeOut) {
2975
- console.warn(
2976
- '[waveform-playlist] Cannot apply fades for clip "' + (clip.name || clip.id) + '" - AudioParam not accessible'
2977
- );
2978
- }
2979
- }
2980
- player.start(startTime, offset, clipDuration);
2981
- }
2982
- }
2983
- transport.start(0);
2984
- }),
2985
- duration,
2986
- outputChannels,
2987
- sampleRate
2988
- );
2989
- } catch (err) {
2990
- if (err instanceof Error) {
2991
- throw err;
2992
- } else {
2993
- throw new Error("Tone.Offline rendering failed: " + String(err));
2994
- }
2995
- }
2996
- onProgress(0.9);
2997
- const result = buffer.get();
2998
- if (!result) {
2999
- throw new Error("Offline rendering produced no audio buffer");
3000
- }
3001
- return result;
1449
+ enabled: enabled && annotations.length > 0 && !!onActiveAnnotationChange
3002
1450
  });
3003
- }
3004
- function applyClipFades(gainParam, clipGain, startTime, clipDuration, fadeIn, fadeOut) {
3005
- if (fadeIn) {
3006
- gainParam.setValueAtTime(0, startTime);
3007
- } else {
3008
- gainParam.setValueAtTime(clipGain, startTime);
3009
- }
3010
- if (fadeIn) {
3011
- (0, import_core4.applyFadeIn)(gainParam, startTime, fadeIn.duration, fadeIn.type || "linear", 0, clipGain);
3012
- }
3013
- if (fadeOut) {
3014
- const fadeOutStart = startTime + clipDuration - fadeOut.duration;
3015
- if (!fadeIn || fadeIn.duration < clipDuration - fadeOut.duration) {
3016
- gainParam.setValueAtTime(clipGain, fadeOutStart);
3017
- }
3018
- (0, import_core4.applyFadeOut)(gainParam, fadeOutStart, fadeOut.duration, fadeOut.type || "linear", clipGain, 0);
3019
- }
1451
+ return {
1452
+ moveStartBoundary,
1453
+ moveEndBoundary,
1454
+ selectPrevious,
1455
+ selectNext,
1456
+ selectFirst,
1457
+ selectLast,
1458
+ clearSelection,
1459
+ scrollToAnnotation,
1460
+ playActiveAnnotation
1461
+ };
3020
1462
  }
3021
1463
 
3022
1464
  // src/hooks/useAnimationFrameLoop.ts
3023
- var import_react20 = require("react");
1465
+ var import_react15 = require("react");
3024
1466
  var useAnimationFrameLoop = () => {
3025
- const animationFrameRef = (0, import_react20.useRef)(null);
3026
- const stopAnimationFrameLoop = (0, import_react20.useCallback)(() => {
1467
+ const animationFrameRef = (0, import_react15.useRef)(null);
1468
+ const stopAnimationFrameLoop = (0, import_react15.useCallback)(() => {
3027
1469
  if (animationFrameRef.current !== null) {
3028
1470
  cancelAnimationFrame(animationFrameRef.current);
3029
1471
  animationFrameRef.current = null;
3030
1472
  }
3031
1473
  }, []);
3032
- const startAnimationFrameLoop = (0, import_react20.useCallback)(
1474
+ const startAnimationFrameLoop = (0, import_react15.useCallback)(
3033
1475
  (callback) => {
3034
1476
  stopAnimationFrameLoop();
3035
1477
  animationFrameRef.current = requestAnimationFrame(callback);
3036
1478
  },
3037
1479
  [stopAnimationFrameLoop]
3038
1480
  );
3039
- (0, import_react20.useEffect)(() => {
1481
+ (0, import_react15.useEffect)(() => {
3040
1482
  return () => {
3041
1483
  stopAnimationFrameLoop();
3042
1484
  };
@@ -3049,7 +1491,7 @@ var useAnimationFrameLoop = () => {
3049
1491
  };
3050
1492
 
3051
1493
  // src/hooks/useWaveformDataCache.ts
3052
- var import_react21 = require("react");
1494
+ var import_react16 = require("react");
3053
1495
 
3054
1496
  // src/workers/peaksWorker.ts
3055
1497
  var import_waveform_data2 = __toESM(require("waveform-data"));
@@ -3281,20 +1723,20 @@ function createPeaksWorker() {
3281
1723
 
3282
1724
  // src/hooks/useWaveformDataCache.ts
3283
1725
  function useWaveformDataCache(tracks, baseScale) {
3284
- const [cache, setCache] = (0, import_react21.useState)(() => /* @__PURE__ */ new Map());
3285
- const [isGenerating, setIsGenerating] = (0, import_react21.useState)(false);
3286
- const workerRef = (0, import_react21.useRef)(null);
3287
- const generatedByBufferRef = (0, import_react21.useRef)(/* @__PURE__ */ new WeakMap());
3288
- const inflightByBufferRef = (0, import_react21.useRef)(/* @__PURE__ */ new WeakMap());
3289
- const subscribersByBufferRef = (0, import_react21.useRef)(/* @__PURE__ */ new WeakMap());
3290
- const pendingCountRef = (0, import_react21.useRef)(0);
3291
- const getWorker = (0, import_react21.useCallback)(() => {
1726
+ const [cache, setCache] = (0, import_react16.useState)(() => /* @__PURE__ */ new Map());
1727
+ const [isGenerating, setIsGenerating] = (0, import_react16.useState)(false);
1728
+ const workerRef = (0, import_react16.useRef)(null);
1729
+ const generatedByBufferRef = (0, import_react16.useRef)(/* @__PURE__ */ new WeakMap());
1730
+ const inflightByBufferRef = (0, import_react16.useRef)(/* @__PURE__ */ new WeakMap());
1731
+ const subscribersByBufferRef = (0, import_react16.useRef)(/* @__PURE__ */ new WeakMap());
1732
+ const pendingCountRef = (0, import_react16.useRef)(0);
1733
+ const getWorker = (0, import_react16.useCallback)(() => {
3292
1734
  if (!workerRef.current) {
3293
1735
  workerRef.current = createPeaksWorker();
3294
1736
  }
3295
1737
  return workerRef.current;
3296
1738
  }, []);
3297
- (0, import_react21.useEffect)(() => {
1739
+ (0, import_react16.useEffect)(() => {
3298
1740
  let cancelled = false;
3299
1741
  const generatedByBuffer = generatedByBufferRef.current;
3300
1742
  const inflightByBuffer = inflightByBufferRef.current;
@@ -3399,7 +1841,7 @@ function useWaveformDataCache(tracks, baseScale) {
3399
1841
  setIsGenerating(false);
3400
1842
  };
3401
1843
  }, [tracks, baseScale, getWorker]);
3402
- (0, import_react21.useEffect)(() => {
1844
+ (0, import_react16.useEffect)(() => {
3403
1845
  return () => {
3404
1846
  var _a;
3405
1847
  (_a = workerRef.current) == null ? void 0 : _a.terminate();
@@ -3409,232 +1851,12 @@ function useWaveformDataCache(tracks, baseScale) {
3409
1851
  return { cache, isGenerating };
3410
1852
  }
3411
1853
 
3412
- // src/hooks/useDynamicTracks.ts
3413
- var import_react22 = require("react");
3414
- var import_core5 = require("@waveform-playlist/core");
3415
- var import_playout4 = require("@waveform-playlist/playout");
3416
- function getSourceName(source) {
3417
- var _a, _b, _c, _d, _e;
3418
- if (source instanceof File) {
3419
- return source.name.replace(/\.[^/.]+$/, "");
3420
- }
3421
- if (source instanceof Blob) {
3422
- return "Untitled";
3423
- }
3424
- if (typeof source === "string") {
3425
- return (_b = (_a = source.split("/").pop()) == null ? void 0 : _a.replace(/\.[^/.]+$/, "")) != null ? _b : "Untitled";
3426
- }
3427
- return (_e = (_d = source.name) != null ? _d : (_c = source.src.split("/").pop()) == null ? void 0 : _c.replace(/\.[^/.]+$/, "")) != null ? _e : "Untitled";
3428
- }
3429
- function decodeSource(source, audioContext, signal) {
3430
- return __async(this, null, function* () {
3431
- const name = getSourceName(source);
3432
- if (source instanceof Blob) {
3433
- const arrayBuffer2 = yield source.arrayBuffer();
3434
- signal == null ? void 0 : signal.throwIfAborted();
3435
- const audioBuffer2 = yield audioContext.decodeAudioData(arrayBuffer2);
3436
- return { audioBuffer: audioBuffer2, name };
3437
- }
3438
- const url = typeof source === "string" ? source : source.src;
3439
- const response = yield fetch(url, { signal });
3440
- if (!response.ok) throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
3441
- const arrayBuffer = yield response.arrayBuffer();
3442
- signal == null ? void 0 : signal.throwIfAborted();
3443
- const audioBuffer = yield audioContext.decodeAudioData(arrayBuffer);
3444
- return { audioBuffer, name };
3445
- });
3446
- }
3447
- function useDynamicTracks() {
3448
- const [tracks, setTracks] = (0, import_react22.useState)([]);
3449
- const [loadingCount, setLoadingCount] = (0, import_react22.useState)(0);
3450
- const [errors, setErrors] = (0, import_react22.useState)([]);
3451
- const cancelledRef = (0, import_react22.useRef)(false);
3452
- const loadingIdsRef = (0, import_react22.useRef)(/* @__PURE__ */ new Set());
3453
- const abortControllersRef = (0, import_react22.useRef)(/* @__PURE__ */ new Map());
3454
- (0, import_react22.useEffect)(() => {
3455
- const controllers = abortControllersRef.current;
3456
- return () => {
3457
- cancelledRef.current = true;
3458
- for (const controller of controllers.values()) {
3459
- controller.abort();
3460
- }
3461
- controllers.clear();
3462
- };
3463
- }, []);
3464
- const addTracks = (0, import_react22.useCallback)((sources) => {
3465
- if (sources.length === 0) return;
3466
- const audioContext = (0, import_playout4.getGlobalAudioContext)();
3467
- const placeholders = sources.map((source) => ({
3468
- track: (0, import_core5.createTrack)({ name: `${getSourceName(source)} (loading...)`, clips: [] }),
3469
- source
3470
- }));
3471
- setTracks((prev) => [...prev, ...placeholders.map((p) => p.track)]);
3472
- setLoadingCount((prev) => prev + sources.length);
3473
- for (const { track, source } of placeholders) {
3474
- loadingIdsRef.current.add(track.id);
3475
- const controller = new AbortController();
3476
- abortControllersRef.current.set(track.id, controller);
3477
- (() => __async(null, null, function* () {
3478
- try {
3479
- const { audioBuffer, name } = yield decodeSource(source, audioContext, controller.signal);
3480
- const clip = (0, import_core5.createClipFromSeconds)({
3481
- audioBuffer,
3482
- startTime: 0,
3483
- duration: audioBuffer.duration,
3484
- offset: 0,
3485
- name
3486
- });
3487
- if (!cancelledRef.current && loadingIdsRef.current.has(track.id)) {
3488
- setTracks(
3489
- (prev) => prev.map((t) => t.id === track.id ? __spreadProps(__spreadValues({}, t), { name, clips: [clip] }) : t)
3490
- );
3491
- }
3492
- } catch (error) {
3493
- if (error instanceof DOMException && error.name === "AbortError") return;
3494
- console.warn("[waveform-playlist] Error loading audio:", error);
3495
- if (!cancelledRef.current && loadingIdsRef.current.has(track.id)) {
3496
- setTracks((prev) => prev.filter((t) => t.id !== track.id));
3497
- setErrors((prev) => [
3498
- ...prev,
3499
- {
3500
- name: getSourceName(source),
3501
- error: error instanceof Error ? error : new Error(String(error))
3502
- }
3503
- ]);
3504
- }
3505
- } finally {
3506
- abortControllersRef.current.delete(track.id);
3507
- if (!cancelledRef.current && loadingIdsRef.current.delete(track.id)) {
3508
- setLoadingCount((prev) => prev - 1);
3509
- }
3510
- }
3511
- }))();
3512
- }
3513
- }, []);
3514
- const removeTrack = (0, import_react22.useCallback)((trackId) => {
3515
- setTracks((prev) => prev.filter((t) => t.id !== trackId));
3516
- const controller = abortControllersRef.current.get(trackId);
3517
- if (controller) {
3518
- controller.abort();
3519
- abortControllersRef.current.delete(trackId);
3520
- }
3521
- if (loadingIdsRef.current.delete(trackId)) {
3522
- setLoadingCount((prev) => prev - 1);
3523
- }
3524
- }, []);
3525
- return {
3526
- tracks,
3527
- addTracks,
3528
- removeTrack,
3529
- loadingCount,
3530
- isLoading: loadingCount > 0,
3531
- errors
3532
- };
3533
- }
3534
-
3535
- // src/hooks/useOutputMeter.ts
3536
- var import_react23 = require("react");
3537
- var import_playout5 = require("@waveform-playlist/playout");
3538
- var import_core6 = require("@waveform-playlist/core");
3539
- var import_worklets = require("@waveform-playlist/worklets");
3540
- var PEAK_DECAY = 0.98;
3541
- function useOutputMeter(options = {}) {
3542
- const { channelCount = 2, updateRate = 60, isPlaying = false } = options;
3543
- const [levels, setLevels] = (0, import_react23.useState)(() => new Array(channelCount).fill(0));
3544
- const [peakLevels, setPeakLevels] = (0, import_react23.useState)(() => new Array(channelCount).fill(0));
3545
- const [rmsLevels, setRmsLevels] = (0, import_react23.useState)(() => new Array(channelCount).fill(0));
3546
- const workletNodeRef = (0, import_react23.useRef)(null);
3547
- const smoothedPeakRef = (0, import_react23.useRef)(new Array(channelCount).fill(0));
3548
- const [meterError, setMeterError] = (0, import_react23.useState)(null);
3549
- const resetPeak = (0, import_react23.useCallback)(
3550
- () => setPeakLevels(new Array(channelCount).fill(0)),
3551
- [channelCount]
3552
- );
3553
- (0, import_react23.useEffect)(() => {
3554
- if (!isPlaying) {
3555
- const zeros = new Array(channelCount).fill(0);
3556
- smoothedPeakRef.current = new Array(channelCount).fill(0);
3557
- setLevels(zeros);
3558
- setRmsLevels(zeros);
3559
- setPeakLevels(zeros);
3560
- }
3561
- }, [isPlaying, channelCount]);
3562
- (0, import_react23.useEffect)(() => {
3563
- let isMounted = true;
3564
- const setup = () => __async(null, null, function* () {
3565
- const context = (0, import_playout5.getGlobalContext)();
3566
- const rawCtx = context.rawContext;
3567
- yield rawCtx.audioWorklet.addModule(import_worklets.meterProcessorUrl);
3568
- if (!isMounted) return;
3569
- const workletNode = context.createAudioWorkletNode("meter-processor", {
3570
- channelCount,
3571
- channelCountMode: "explicit",
3572
- processorOptions: {
3573
- numberOfChannels: channelCount,
3574
- updateRate
3575
- }
3576
- });
3577
- workletNodeRef.current = workletNode;
3578
- workletNode.onprocessorerror = (event) => {
3579
- console.warn("[waveform-playlist] Output meter worklet processor error:", String(event));
3580
- };
3581
- const destination = context.destination;
3582
- destination.chain(workletNode);
3583
- smoothedPeakRef.current = new Array(channelCount).fill(0);
3584
- workletNode.port.onmessage = (event) => {
3585
- var _a;
3586
- if (!isMounted) return;
3587
- const { peak, rms } = event.data;
3588
- const smoothed = smoothedPeakRef.current;
3589
- const peakValues = [];
3590
- const rmsValues = [];
3591
- for (let ch = 0; ch < peak.length; ch++) {
3592
- smoothed[ch] = Math.max(peak[ch], ((_a = smoothed[ch]) != null ? _a : 0) * PEAK_DECAY);
3593
- peakValues.push((0, import_core6.gainToNormalized)(smoothed[ch]));
3594
- rmsValues.push((0, import_core6.gainToNormalized)(rms[ch]));
3595
- }
3596
- setLevels(peakValues);
3597
- setRmsLevels(rmsValues);
3598
- setPeakLevels((prev) => peakValues.map((val, i) => {
3599
- var _a2;
3600
- return Math.max((_a2 = prev[i]) != null ? _a2 : 0, val);
3601
- }));
3602
- };
3603
- });
3604
- setup().catch((err) => {
3605
- console.warn("[waveform-playlist] Failed to set up output meter:", String(err));
3606
- if (isMounted) {
3607
- setMeterError(err instanceof Error ? err : new Error(String(err)));
3608
- }
3609
- });
3610
- return () => {
3611
- isMounted = false;
3612
- if (workletNodeRef.current) {
3613
- try {
3614
- const context = (0, import_playout5.getGlobalContext)();
3615
- context.destination.chain();
3616
- } catch (err) {
3617
- console.warn("[waveform-playlist] Failed to restore destination chain:", String(err));
3618
- }
3619
- try {
3620
- workletNodeRef.current.disconnect();
3621
- workletNodeRef.current.port.close();
3622
- } catch (err) {
3623
- console.warn("[waveform-playlist] Output meter disconnect during cleanup:", String(err));
3624
- }
3625
- workletNodeRef.current = null;
3626
- }
3627
- };
3628
- }, [channelCount, updateRate]);
3629
- return { levels, peakLevels, rmsLevels, resetPeak, error: meterError };
3630
- }
3631
-
3632
1854
  // src/WaveformPlaylistContext.tsx
3633
1855
  var import_jsx_runtime = require("react/jsx-runtime");
3634
- var PlaybackAnimationContext = (0, import_react24.createContext)(null);
3635
- var PlaylistStateContext = (0, import_react24.createContext)(null);
3636
- var PlaylistControlsContext = (0, import_react24.createContext)(null);
3637
- var PlaylistDataContext = (0, import_react24.createContext)(null);
1856
+ var PlaybackAnimationContext = (0, import_react17.createContext)(null);
1857
+ var PlaylistStateContext = (0, import_react17.createContext)(null);
1858
+ var PlaylistControlsContext = (0, import_react17.createContext)(null);
1859
+ var PlaylistDataContext = (0, import_react17.createContext)(null);
3638
1860
  var WaveformPlaylistProvider = ({
3639
1861
  tracks,
3640
1862
  timescale = false,
@@ -3648,6 +1870,7 @@ var WaveformPlaylistProvider = ({
3648
1870
  annotationList,
3649
1871
  effects,
3650
1872
  onReady,
1873
+ onError,
3651
1874
  onAnnotationUpdate: _onAnnotationUpdate,
3652
1875
  onAnnotationsChange,
3653
1876
  barWidth = 1,
@@ -3658,18 +1881,19 @@ var WaveformPlaylistProvider = ({
3658
1881
  deferEngineRebuild = false,
3659
1882
  indefinitePlayback = false,
3660
1883
  sampleRate: sampleRateProp,
1884
+ createAdapter,
3661
1885
  children
3662
1886
  }) => {
3663
1887
  var _a, _b, _c, _d;
3664
1888
  const progressBarWidth = progressBarWidthProp != null ? progressBarWidthProp : barWidth + barGap;
3665
- const indefinitePlaybackRef = (0, import_react24.useRef)(indefinitePlayback);
1889
+ const indefinitePlaybackRef = (0, import_react17.useRef)(indefinitePlayback);
3666
1890
  indefinitePlaybackRef.current = indefinitePlayback;
3667
- const stableZoomLevels = (0, import_react24.useMemo)(
1891
+ const stableZoomLevels = (0, import_react17.useMemo)(
3668
1892
  () => zoomLevels,
3669
1893
  // eslint-disable-next-line react-hooks/exhaustive-deps
3670
1894
  [zoomLevels == null ? void 0 : zoomLevels.join(",")]
3671
1895
  );
3672
- const annotations = (0, import_react24.useMemo)(() => {
1896
+ const annotations = (0, import_react17.useMemo)(() => {
3673
1897
  if (!(annotationList == null ? void 0 : annotationList.annotations)) return [];
3674
1898
  if (process.env.NODE_ENV !== "production" && annotationList.annotations.length > 0) {
3675
1899
  const first = annotationList.annotations[0];
@@ -3682,63 +1906,50 @@ var WaveformPlaylistProvider = ({
3682
1906
  }
3683
1907
  return annotationList.annotations;
3684
1908
  }, [annotationList == null ? void 0 : annotationList.annotations]);
3685
- const annotationsRef = (0, import_react24.useRef)(annotations);
1909
+ const annotationsRef = (0, import_react17.useRef)(annotations);
3686
1910
  annotationsRef.current = annotations;
3687
- const [activeAnnotationId, setActiveAnnotationIdState] = (0, import_react24.useState)(null);
3688
- const [isPlaying, setIsPlaying] = (0, import_react24.useState)(false);
3689
- const [currentTime, setCurrentTime] = (0, import_react24.useState)(0);
3690
- const [duration, setDuration] = (0, import_react24.useState)(0);
3691
- const [audioBuffers, setAudioBuffers] = (0, import_react24.useState)([]);
3692
- const [peaksDataArray, setPeaksDataArray] = (0, import_react24.useState)([]);
3693
- const [trackStates, setTrackStates] = (0, import_react24.useState)([]);
3694
- const [isAutomaticScroll, setIsAutomaticScroll] = (0, import_react24.useState)(automaticScroll);
3695
- const [continuousPlay, setContinuousPlayState] = (0, import_react24.useState)(
1911
+ const [activeAnnotationId, setActiveAnnotationIdState] = (0, import_react17.useState)(null);
1912
+ const [isPlaying, setIsPlaying] = (0, import_react17.useState)(false);
1913
+ const [currentTime, setCurrentTime] = (0, import_react17.useState)(0);
1914
+ const [duration, setDuration] = (0, import_react17.useState)(0);
1915
+ const [audioBuffers, setAudioBuffers] = (0, import_react17.useState)([]);
1916
+ const [peaksDataArray, setPeaksDataArray] = (0, import_react17.useState)([]);
1917
+ const [trackStates, setTrackStates] = (0, import_react17.useState)([]);
1918
+ const [isAutomaticScroll, setIsAutomaticScroll] = (0, import_react17.useState)(automaticScroll);
1919
+ const [continuousPlay, setContinuousPlayState] = (0, import_react17.useState)(
3696
1920
  (_a = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _a : false
3697
1921
  );
3698
- const [linkEndpoints, setLinkEndpoints] = (0, import_react24.useState)((_b = annotationList == null ? void 0 : annotationList.linkEndpoints) != null ? _b : false);
3699
- const [annotationsEditable, setAnnotationsEditable] = (0, import_react24.useState)((_c = annotationList == null ? void 0 : annotationList.editable) != null ? _c : false);
3700
- const [isReady, setIsReady] = (0, import_react24.useState)(false);
3701
- const engineRef = (0, import_react24.useRef)(null);
3702
- const adapterRef = (0, import_react24.useRef)(null);
3703
- const audioInitializedRef = (0, import_react24.useRef)(false);
3704
- const isPlayingRef = (0, import_react24.useRef)(false);
1922
+ const [linkEndpoints, setLinkEndpoints] = (0, import_react17.useState)((_b = annotationList == null ? void 0 : annotationList.linkEndpoints) != null ? _b : false);
1923
+ const [annotationsEditable, setAnnotationsEditable] = (0, import_react17.useState)((_c = annotationList == null ? void 0 : annotationList.editable) != null ? _c : false);
1924
+ const [isReady, setIsReady] = (0, import_react17.useState)(false);
1925
+ const engineRef = (0, import_react17.useRef)(null);
1926
+ const adapterRef = (0, import_react17.useRef)(null);
1927
+ const audioInitializedRef = (0, import_react17.useRef)(false);
1928
+ const isPlayingRef = (0, import_react17.useRef)(false);
3705
1929
  isPlayingRef.current = isPlaying;
3706
- const playStartPositionRef = (0, import_react24.useRef)(0);
3707
- const currentTimeRef = (0, import_react24.useRef)(0);
3708
- const visualTimeRef = (0, import_react24.useRef)(0);
3709
- const tracksRef = (0, import_react24.useRef)(tracks);
3710
- const soundFontCacheRef = (0, import_react24.useRef)(soundFontCache);
1930
+ const playStartPositionRef = (0, import_react17.useRef)(0);
1931
+ const currentTimeRef = (0, import_react17.useRef)(0);
1932
+ const visualTimeRef = (0, import_react17.useRef)(0);
1933
+ const tracksRef = (0, import_react17.useRef)(tracks);
1934
+ const soundFontCacheRef = (0, import_react17.useRef)(soundFontCache);
3711
1935
  soundFontCacheRef.current = soundFontCache;
3712
- const trackStatesRef = (0, import_react24.useRef)(trackStates);
3713
- const playbackStartTimeRef = (0, import_react24.useRef)(0);
3714
- const audioStartPositionRef = (0, import_react24.useRef)(0);
3715
- const playbackEndTimeRef = (0, import_react24.useRef)(null);
3716
- const scrollContainerRef = (0, import_react24.useRef)(null);
3717
- const isAutomaticScrollRef = (0, import_react24.useRef)(false);
3718
- const frameCallbacksRef = (0, import_react24.useRef)(/* @__PURE__ */ new Map());
3719
- const continuousPlayRef = (0, import_react24.useRef)((_d = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _d : false);
3720
- const activeAnnotationIdRef = (0, import_react24.useRef)(null);
3721
- const engineTracksRef = (0, import_react24.useRef)(null);
3722
- const lastTracksVersionRef = (0, import_react24.useRef)(0);
3723
- const skipEngineDisposeRef = (0, import_react24.useRef)(false);
3724
- const isDraggingRef = (0, import_react24.useRef)(false);
3725
- const prevTracksRef = (0, import_react24.useRef)([]);
3726
- const samplesPerPixelRef = (0, import_react24.useRef)(initialSamplesPerPixel);
3727
- const [initialSampleRate] = (0, import_react24.useState)(() => {
3728
- if (typeof AudioContext === "undefined") return sampleRateProp != null ? sampleRateProp : 48e3;
3729
- try {
3730
- if (sampleRateProp !== void 0) {
3731
- return (0, import_playout6.configureGlobalContext)({ sampleRate: sampleRateProp });
3732
- }
3733
- return (0, import_playout6.getGlobalAudioContext)().sampleRate;
3734
- } catch (err) {
3735
- console.warn(
3736
- "[waveform-playlist] Failed to configure AudioContext: " + String(err) + " \u2014 falling back to " + (sampleRateProp != null ? sampleRateProp : 48e3) + " Hz"
3737
- );
3738
- return sampleRateProp != null ? sampleRateProp : 48e3;
3739
- }
3740
- });
3741
- const sampleRateRef = (0, import_react24.useRef)(initialSampleRate);
1936
+ const trackStatesRef = (0, import_react17.useRef)(trackStates);
1937
+ const playbackStartTimeRef = (0, import_react17.useRef)(0);
1938
+ const audioStartPositionRef = (0, import_react17.useRef)(0);
1939
+ const playbackEndTimeRef = (0, import_react17.useRef)(null);
1940
+ const scrollContainerRef = (0, import_react17.useRef)(null);
1941
+ const isAutomaticScrollRef = (0, import_react17.useRef)(false);
1942
+ const frameCallbacksRef = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
1943
+ const continuousPlayRef = (0, import_react17.useRef)((_d = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _d : false);
1944
+ const activeAnnotationIdRef = (0, import_react17.useRef)(null);
1945
+ const engineTracksRef = (0, import_react17.useRef)(null);
1946
+ const lastTracksVersionRef = (0, import_react17.useRef)(0);
1947
+ const skipEngineDisposeRef = (0, import_react17.useRef)(false);
1948
+ const isDraggingRef = (0, import_react17.useRef)(false);
1949
+ const prevTracksRef = (0, import_react17.useRef)([]);
1950
+ const samplesPerPixelRef = (0, import_react17.useRef)(initialSamplesPerPixel);
1951
+ const [initialSampleRate] = (0, import_react17.useState)(() => sampleRateProp != null ? sampleRateProp : 48e3);
1952
+ const sampleRateRef = (0, import_react17.useRef)(initialSampleRate);
3742
1953
  const { timeFormat, setTimeFormat, formatTime: formatTime2 } = useTimeFormat();
3743
1954
  const zoom = useZoomControls({ engineRef, initialSamplesPerPixel });
3744
1955
  const { samplesPerPixel, onEngineState: onZoomEngineState } = zoom;
@@ -3783,20 +1994,20 @@ var WaveformPlaylistProvider = ({
3783
1994
  onEngineState: onUndoEngineState
3784
1995
  } = useUndoState({ engineRef });
3785
1996
  const { animationFrameRef, startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();
3786
- const baseScale = (0, import_react24.useMemo)(
1997
+ const baseScale = (0, import_react17.useMemo)(
3787
1998
  () => Math.min(...stableZoomLevels != null ? stableZoomLevels : [256, 512, 1024, 2048, 4096, 8192]),
3788
1999
  [stableZoomLevels]
3789
2000
  );
3790
2001
  const { cache: waveformDataCache } = useWaveformDataCache(tracks, baseScale);
3791
- const setContinuousPlay = (0, import_react24.useCallback)((value) => {
2002
+ const setContinuousPlay = (0, import_react17.useCallback)((value) => {
3792
2003
  continuousPlayRef.current = value;
3793
2004
  setContinuousPlayState(value);
3794
2005
  }, []);
3795
- const setActiveAnnotationId = (0, import_react24.useCallback)((value) => {
2006
+ const setActiveAnnotationId = (0, import_react17.useCallback)((value) => {
3796
2007
  activeAnnotationIdRef.current = value;
3797
2008
  setActiveAnnotationIdState(value);
3798
2009
  }, []);
3799
- const setLoopRegionFromSelection = (0, import_react24.useCallback)(() => {
2010
+ const setLoopRegionFromSelection = (0, import_react17.useCallback)(() => {
3800
2011
  var _a2, _b2;
3801
2012
  const start = (_a2 = selectionStartRef.current) != null ? _a2 : 0;
3802
2013
  const end = (_b2 = selectionEndRef.current) != null ? _b2 : 0;
@@ -3804,21 +2015,21 @@ var WaveformPlaylistProvider = ({
3804
2015
  setLoopRegion(start, end);
3805
2016
  }
3806
2017
  }, [setLoopRegion, selectionStartRef, selectionEndRef]);
3807
- (0, import_react24.useEffect)(() => {
2018
+ (0, import_react17.useEffect)(() => {
3808
2019
  isAutomaticScrollRef.current = isAutomaticScroll;
3809
2020
  }, [isAutomaticScroll]);
3810
- (0, import_react24.useEffect)(() => {
2021
+ (0, import_react17.useEffect)(() => {
3811
2022
  trackStatesRef.current = trackStates;
3812
2023
  }, [trackStates]);
3813
2024
  tracksRef.current = tracks;
3814
2025
  const isEngineTracks = tracks === engineTracksRef.current;
3815
2026
  const prevTracks = prevTracksRef.current;
3816
- const isIncrementalAdd = engineRef.current !== null && prevTracks.length > 0 && tracks.length > prevTracks.length && prevTracks.every((pt) => {
2027
+ const isIncrementalAdd = engineRef.current !== null && tracks.length > prevTracks.length && prevTracks.every((pt) => {
3817
2028
  const current = tracks.find((t) => t.id === pt.id);
3818
2029
  return current === pt;
3819
2030
  });
3820
2031
  skipEngineDisposeRef.current = isEngineTracks || isDraggingRef.current || isIncrementalAdd;
3821
- (0, import_react24.useEffect)(() => {
2032
+ (0, import_react17.useEffect)(() => {
3822
2033
  if (!scrollContainerRef.current || duration === 0) return;
3823
2034
  const container = scrollContainerRef.current;
3824
2035
  const oldSamplesPerPixel = samplesPerPixelRef.current;
@@ -3834,8 +2045,8 @@ var WaveformPlaylistProvider = ({
3834
2045
  container.scrollLeft = newScrollLeft;
3835
2046
  samplesPerPixelRef.current = newSamplesPerPixel;
3836
2047
  }, [samplesPerPixel, duration]);
3837
- const pendingResumeRef = (0, import_react24.useRef)(null);
3838
- (0, import_react24.useEffect)(() => {
2048
+ const pendingResumeRef = (0, import_react17.useRef)(null);
2049
+ (0, import_react17.useEffect)(() => {
3839
2050
  var _a2, _b2, _c2, _d2;
3840
2051
  if (isEngineTracks || isDraggingRef.current) {
3841
2052
  if (isEngineTracks) {
@@ -3926,12 +2137,10 @@ var WaveformPlaylistProvider = ({
3926
2137
  setTrackStates([]);
3927
2138
  setPeaksDataArray([]);
3928
2139
  if (engineRef.current) {
3929
- engineRef.current.dispose();
3930
- engineRef.current = null;
3931
- adapterRef.current = null;
2140
+ engineRef.current.setTracks([]);
2141
+ prevTracksRef.current = tracks;
2142
+ return;
3932
2143
  }
3933
- prevTracksRef.current = tracks;
3934
- return;
3935
2144
  }
3936
2145
  const wasPlaying = isPlayingRef.current;
3937
2146
  const resumePosition = currentTimeRef.current;
@@ -3940,6 +2149,7 @@ var WaveformPlaylistProvider = ({
3940
2149
  stopAnimationFrameLoop();
3941
2150
  pendingResumeRef.current = { position: resumePosition };
3942
2151
  }
2152
+ let cancelled = false;
3943
2153
  const loadAudio = () => __async(null, null, function* () {
3944
2154
  var _a3, _b3, _c3, _d3, _e;
3945
2155
  try {
@@ -3982,7 +2192,17 @@ var WaveformPlaylistProvider = ({
3982
2192
  lastTracksVersionRef.current = 0;
3983
2193
  engineTracksRef.current = null;
3984
2194
  audioInitializedRef.current = false;
3985
- const adapter = (0, import_playout6.createToneAdapter)({ effects, soundFontCache: soundFontCacheRef.current });
2195
+ const adapter = yield resolvePlayoutAdapter({
2196
+ createAdapter,
2197
+ effects,
2198
+ soundFontCache: soundFontCacheRef.current,
2199
+ sampleRate: sampleRateProp
2200
+ });
2201
+ if (cancelled) {
2202
+ adapter.dispose();
2203
+ return;
2204
+ }
2205
+ sampleRateRef.current = adapter.audioContext.sampleRate;
3986
2206
  adapterRef.current = adapter;
3987
2207
  const engine = new import_engine3.PlaylistEngine({
3988
2208
  adapter,
@@ -4040,11 +2260,23 @@ var WaveformPlaylistProvider = ({
4040
2260
  prevTracksRef.current = tracks;
4041
2261
  onReady == null ? void 0 : onReady();
4042
2262
  } catch (error) {
4043
- console.warn("[waveform-playlist] Error loading audio:", String(error));
2263
+ if (!engineRef.current && adapterRef.current) {
2264
+ try {
2265
+ adapterRef.current.dispose();
2266
+ } catch (disposeErr) {
2267
+ console.warn(
2268
+ "[waveform-playlist] adapter dispose after load failure threw: " + String(disposeErr)
2269
+ );
2270
+ }
2271
+ adapterRef.current = null;
2272
+ }
2273
+ console.warn("[waveform-playlist] Error loading audio: " + String(error));
2274
+ onError == null ? void 0 : onError(error instanceof Error ? error : new Error(String(error)));
4044
2275
  }
4045
2276
  });
4046
2277
  loadAudio();
4047
2278
  return () => {
2279
+ cancelled = true;
4048
2280
  if (skipEngineDisposeRef.current) {
4049
2281
  skipEngineDisposeRef.current = false;
4050
2282
  return;
@@ -4067,6 +2299,7 @@ var WaveformPlaylistProvider = ({
4067
2299
  // to the live adapter by the sync effect below; only adapter creation reads it
4068
2300
  // (via soundFontCacheRef).
4069
2301
  onReady,
2302
+ onError,
4070
2303
  effects,
4071
2304
  stopAnimationFrameLoop,
4072
2305
  onSelectionEngineState,
@@ -4085,10 +2318,10 @@ var WaveformPlaylistProvider = ({
4085
2318
  stableZoomLevels,
4086
2319
  deferEngineRebuild
4087
2320
  ]);
4088
- (0, import_react24.useEffect)(() => {
2321
+ (0, import_react17.useEffect)(() => {
4089
2322
  syncSoundFontCacheToAdapter(adapterRef.current, soundFontCache);
4090
2323
  }, [soundFontCache]);
4091
- (0, import_react24.useEffect)(() => {
2324
+ (0, import_react17.useEffect)(() => {
4092
2325
  if (tracks.length === 0) return;
4093
2326
  const allTrackPeaks = tracks.map((track) => {
4094
2327
  const clipPeaks = track.clips.map((clip) => {
@@ -4169,8 +2402,15 @@ var WaveformPlaylistProvider = ({
4169
2402
  });
4170
2403
  setPeaksDataArray(allTrackPeaks);
4171
2404
  }, [tracks, samplesPerPixel, mono, waveformDataCache, deferEngineRebuild]);
4172
- const getPlaybackTimeFallbackWarnedRef = (0, import_react24.useRef)(false);
4173
- const getPlaybackTime = (0, import_react24.useCallback)(() => {
2405
+ const getAudioContextTime = (0, import_react17.useCallback)(
2406
+ () => {
2407
+ var _a2, _b2;
2408
+ return (_b2 = (_a2 = adapterRef.current) == null ? void 0 : _a2.audioContext.currentTime) != null ? _b2 : 0;
2409
+ },
2410
+ []
2411
+ );
2412
+ const getPlaybackTimeFallbackWarnedRef = (0, import_react17.useRef)(false);
2413
+ const getPlaybackTime = (0, import_react17.useCallback)(() => {
4174
2414
  var _a2, _b2;
4175
2415
  if (engineRef.current) {
4176
2416
  return engineRef.current.getCurrentTime();
@@ -4181,30 +2421,35 @@ var WaveformPlaylistProvider = ({
4181
2421
  "[waveform-playlist] getPlaybackTime called without engine. Falling back to manual elapsed time (loop wrapping will not work)."
4182
2422
  );
4183
2423
  }
4184
- const elapsed = (0, import_tone4.getContext)().currentTime - ((_a2 = playbackStartTimeRef.current) != null ? _a2 : 0);
2424
+ const elapsed = getAudioContextTime() - ((_a2 = playbackStartTimeRef.current) != null ? _a2 : 0);
4185
2425
  return ((_b2 = audioStartPositionRef.current) != null ? _b2 : 0) + elapsed;
4186
- }, []);
4187
- const toVisualTime = (0, import_react24.useCallback)((rawTime) => {
2426
+ }, [getAudioContextTime]);
2427
+ const toVisualTime = (0, import_react17.useCallback)((rawTime) => {
4188
2428
  return Number.isFinite(rawTime) ? Math.max(0, rawTime) : 0;
4189
2429
  }, []);
4190
- const setCurrentTimeRefs = (0, import_react24.useCallback)(
2430
+ const setCurrentTimeRefs = (0, import_react17.useCallback)(
4191
2431
  (rawTime) => {
4192
2432
  currentTimeRef.current = rawTime;
4193
2433
  visualTimeRef.current = toVisualTime(rawTime);
4194
2434
  },
4195
2435
  [toVisualTime]
4196
2436
  );
4197
- const getLookAhead = (0, import_react24.useCallback)(() => {
2437
+ const getLookAhead = (0, import_react17.useCallback)(() => {
4198
2438
  var _a2, _b2;
4199
2439
  return (_b2 = (_a2 = engineRef.current) == null ? void 0 : _a2.lookAhead) != null ? _b2 : 0;
4200
2440
  }, []);
4201
- const registerFrameCallback = (0, import_react24.useCallback)((id, cb) => {
2441
+ const getOutputLatency = (0, import_react17.useCallback)(() => {
2442
+ var _a2;
2443
+ const audioCtx = (_a2 = adapterRef.current) == null ? void 0 : _a2.audioContext;
2444
+ return audioCtx && "outputLatency" in audioCtx ? audioCtx.outputLatency : 0;
2445
+ }, []);
2446
+ const registerFrameCallback = (0, import_react17.useCallback)((id, cb) => {
4202
2447
  frameCallbacksRef.current.set(id, cb);
4203
2448
  }, []);
4204
- const unregisterFrameCallback = (0, import_react24.useCallback)((id) => {
2449
+ const unregisterFrameCallback = (0, import_react17.useCallback)((id) => {
4205
2450
  frameCallbacksRef.current.delete(id);
4206
2451
  }, []);
4207
- const startAnimationLoop = (0, import_react24.useCallback)(() => {
2452
+ const startAnimationLoop = (0, import_react17.useCallback)(() => {
4208
2453
  const updateTime = () => {
4209
2454
  const time = getPlaybackTime();
4210
2455
  currentTimeRef.current = time;
@@ -4292,15 +2537,14 @@ var WaveformPlaylistProvider = ({
4292
2537
  setCurrentTimeRefs
4293
2538
  ]);
4294
2539
  const stopAnimationLoop = stopAnimationFrameLoop;
4295
- (0, import_react24.useEffect)(() => {
2540
+ (0, import_react17.useEffect)(() => {
4296
2541
  const reschedulePlayback = () => __async(null, null, function* () {
4297
2542
  if (isPlaying && animationFrameRef.current && engineRef.current) {
4298
2543
  if (continuousPlay) {
4299
2544
  const currentPos = currentTimeRef.current;
4300
2545
  engineRef.current.stop();
4301
2546
  stopAnimationLoop();
4302
- const context = (0, import_tone4.getContext)();
4303
- const timeNow = context.currentTime;
2547
+ const timeNow = getAudioContextTime();
4304
2548
  playbackStartTimeRef.current = timeNow;
4305
2549
  audioStartPositionRef.current = currentPos;
4306
2550
  engineRef.current.play(currentPos);
@@ -4316,14 +2560,20 @@ var WaveformPlaylistProvider = ({
4316
2560
  setIsPlaying(false);
4317
2561
  stopAnimationLoop();
4318
2562
  });
4319
- }, [continuousPlay, isPlaying, startAnimationLoop, stopAnimationLoop, animationFrameRef]);
4320
- (0, import_react24.useEffect)(() => {
2563
+ }, [
2564
+ continuousPlay,
2565
+ isPlaying,
2566
+ startAnimationLoop,
2567
+ stopAnimationLoop,
2568
+ animationFrameRef,
2569
+ getAudioContextTime
2570
+ ]);
2571
+ (0, import_react17.useEffect)(() => {
4321
2572
  const resumePlayback = () => __async(null, null, function* () {
4322
2573
  if (pendingResumeRef.current && engineRef.current) {
4323
2574
  const { position } = pendingResumeRef.current;
4324
2575
  pendingResumeRef.current = null;
4325
- const context = (0, import_tone4.getContext)();
4326
- const timeNow = context.currentTime;
2576
+ const timeNow = getAudioContextTime();
4327
2577
  playbackStartTimeRef.current = timeNow;
4328
2578
  audioStartPositionRef.current = position;
4329
2579
  if (!audioInitializedRef.current) {
@@ -4340,8 +2590,8 @@ var WaveformPlaylistProvider = ({
4340
2590
  setIsPlaying(false);
4341
2591
  stopAnimationLoop();
4342
2592
  });
4343
- }, [tracks, startAnimationLoop, stopAnimationLoop]);
4344
- const play = (0, import_react24.useCallback)(
2593
+ }, [tracks, startAnimationLoop, stopAnimationLoop, getAudioContextTime]);
2594
+ const play = (0, import_react17.useCallback)(
4345
2595
  (startTime, playDuration) => __async(null, null, function* () {
4346
2596
  if (!engineRef.current) return;
4347
2597
  const actualStartTime = startTime != null ? startTime : currentTimeRef.current;
@@ -4350,8 +2600,7 @@ var WaveformPlaylistProvider = ({
4350
2600
  engineRef.current.stop();
4351
2601
  engineRef.current.seek(actualStartTime);
4352
2602
  stopAnimationLoop();
4353
- const context = (0, import_tone4.getContext)();
4354
- const startTimeNow = context.currentTime;
2603
+ const startTimeNow = getAudioContextTime();
4355
2604
  playbackStartTimeRef.current = startTimeNow;
4356
2605
  audioStartPositionRef.current = actualStartTime;
4357
2606
  playbackEndTimeRef.current = playDuration !== void 0 ? actualStartTime + playDuration : null;
@@ -4370,9 +2619,9 @@ var WaveformPlaylistProvider = ({
4370
2619
  setIsPlaying(true);
4371
2620
  startAnimationLoop();
4372
2621
  }),
4373
- [startAnimationLoop, stopAnimationLoop, setCurrentTimeRefs]
2622
+ [startAnimationLoop, stopAnimationLoop, setCurrentTimeRefs, getAudioContextTime]
4374
2623
  );
4375
- const pause = (0, import_react24.useCallback)(() => {
2624
+ const pause = (0, import_react17.useCallback)(() => {
4376
2625
  if (!engineRef.current) return;
4377
2626
  const pauseTime = getPlaybackTime();
4378
2627
  engineRef.current.pause();
@@ -4381,7 +2630,7 @@ var WaveformPlaylistProvider = ({
4381
2630
  setCurrentTimeRefs(pauseTime);
4382
2631
  setCurrentTime(pauseTime);
4383
2632
  }, [stopAnimationLoop, getPlaybackTime, setCurrentTimeRefs]);
4384
- const stop = (0, import_react24.useCallback)(() => {
2633
+ const stop = (0, import_react17.useCallback)(() => {
4385
2634
  if (!engineRef.current) return;
4386
2635
  engineRef.current.stop();
4387
2636
  setIsPlaying(false);
@@ -4390,7 +2639,7 @@ var WaveformPlaylistProvider = ({
4390
2639
  setCurrentTime(playStartPositionRef.current);
4391
2640
  setActiveAnnotationId(null);
4392
2641
  }, [stopAnimationLoop, setActiveAnnotationId, setCurrentTimeRefs]);
4393
- const seekTo = (0, import_react24.useCallback)(
2642
+ const seekTo = (0, import_react17.useCallback)(
4394
2643
  (time) => {
4395
2644
  const clampedTime = Math.max(0, Math.min(time, duration));
4396
2645
  setCurrentTimeRefs(clampedTime);
@@ -4401,7 +2650,7 @@ var WaveformPlaylistProvider = ({
4401
2650
  },
4402
2651
  [duration, isPlaying, play, setCurrentTimeRefs]
4403
2652
  );
4404
- const setTrackMute = (0, import_react24.useCallback)(
2653
+ const setTrackMute = (0, import_react17.useCallback)(
4405
2654
  (trackIndex, muted) => {
4406
2655
  var _a2;
4407
2656
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4415,7 +2664,7 @@ var WaveformPlaylistProvider = ({
4415
2664
  },
4416
2665
  [trackStates]
4417
2666
  );
4418
- const setTrackSolo = (0, import_react24.useCallback)(
2667
+ const setTrackSolo = (0, import_react17.useCallback)(
4419
2668
  (trackIndex, soloed) => {
4420
2669
  var _a2;
4421
2670
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4429,7 +2678,7 @@ var WaveformPlaylistProvider = ({
4429
2678
  },
4430
2679
  [trackStates]
4431
2680
  );
4432
- const setTrackVolume = (0, import_react24.useCallback)(
2681
+ const setTrackVolume = (0, import_react17.useCallback)(
4433
2682
  (trackIndex, volume2) => {
4434
2683
  var _a2;
4435
2684
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4443,7 +2692,7 @@ var WaveformPlaylistProvider = ({
4443
2692
  },
4444
2693
  [trackStates]
4445
2694
  );
4446
- const setTrackPan = (0, import_react24.useCallback)(
2695
+ const setTrackPan = (0, import_react17.useCallback)(
4447
2696
  (trackIndex, pan) => {
4448
2697
  var _a2;
4449
2698
  const trackId = (_a2 = tracksRef.current[trackIndex]) == null ? void 0 : _a2.id;
@@ -4457,7 +2706,7 @@ var WaveformPlaylistProvider = ({
4457
2706
  },
4458
2707
  [trackStates]
4459
2708
  );
4460
- const setSelection = (0, import_react24.useCallback)(
2709
+ const setSelection = (0, import_react17.useCallback)(
4461
2710
  (start, end) => {
4462
2711
  setSelectionEngine(start, end);
4463
2712
  setCurrentTimeRefs(start);
@@ -4470,12 +2719,12 @@ var WaveformPlaylistProvider = ({
4470
2719
  },
4471
2720
  [isPlaying, setSelectionEngine, setCurrentTimeRefs]
4472
2721
  );
4473
- const setScrollContainer = (0, import_react24.useCallback)((element) => {
2722
+ const setScrollContainer = (0, import_react17.useCallback)((element) => {
4474
2723
  scrollContainerRef.current = element;
4475
2724
  }, []);
4476
- const onAnnotationsChangeRef = (0, import_react24.useRef)(onAnnotationsChange);
2725
+ const onAnnotationsChangeRef = (0, import_react17.useRef)(onAnnotationsChange);
4477
2726
  onAnnotationsChangeRef.current = onAnnotationsChange;
4478
- const setAnnotations = (0, import_react24.useCallback)(
2727
+ const setAnnotations = (0, import_react17.useCallback)(
4479
2728
  (action) => {
4480
2729
  const updated = typeof action === "function" ? action(annotationsRef.current) : action;
4481
2730
  if (!onAnnotationsChangeRef.current) {
@@ -4493,7 +2742,7 @@ var WaveformPlaylistProvider = ({
4493
2742
  const sampleRate = sampleRateRef.current;
4494
2743
  const timeScaleHeight = timescale ? 30 : 0;
4495
2744
  const minimumPlaylistHeight = tracks.length * waveHeight + timeScaleHeight;
4496
- const animationValue = (0, import_react24.useMemo)(
2745
+ const animationValue = (0, import_react17.useMemo)(
4497
2746
  () => ({
4498
2747
  isPlaying,
4499
2748
  currentTime,
@@ -4502,7 +2751,9 @@ var WaveformPlaylistProvider = ({
4502
2751
  playbackStartTimeRef,
4503
2752
  audioStartPositionRef,
4504
2753
  getPlaybackTime,
2754
+ getAudioContextTime,
4505
2755
  getLookAhead,
2756
+ getOutputLatency,
4506
2757
  registerFrameCallback,
4507
2758
  unregisterFrameCallback
4508
2759
  }),
@@ -4514,12 +2765,14 @@ var WaveformPlaylistProvider = ({
4514
2765
  playbackStartTimeRef,
4515
2766
  audioStartPositionRef,
4516
2767
  getPlaybackTime,
2768
+ getAudioContextTime,
4517
2769
  getLookAhead,
2770
+ getOutputLatency,
4518
2771
  registerFrameCallback,
4519
2772
  unregisterFrameCallback
4520
2773
  ]
4521
2774
  );
4522
- const stateValue = (0, import_react24.useMemo)(
2775
+ const stateValue = (0, import_react17.useMemo)(
4523
2776
  () => ({
4524
2777
  continuousPlay,
4525
2778
  linkEndpoints,
@@ -4555,17 +2808,17 @@ var WaveformPlaylistProvider = ({
4555
2808
  canRedo
4556
2809
  ]
4557
2810
  );
4558
- const setCurrentTimeControl = (0, import_react24.useCallback)(
2811
+ const setCurrentTimeControl = (0, import_react17.useCallback)(
4559
2812
  (time) => {
4560
2813
  setCurrentTimeRefs(time);
4561
2814
  setCurrentTime(time);
4562
2815
  },
4563
2816
  [setCurrentTimeRefs]
4564
2817
  );
4565
- const setAutomaticScrollControl = (0, import_react24.useCallback)((enabled) => {
2818
+ const setAutomaticScrollControl = (0, import_react17.useCallback)((enabled) => {
4566
2819
  setIsAutomaticScroll(enabled);
4567
2820
  }, []);
4568
- const controlsValue = (0, import_react24.useMemo)(
2821
+ const controlsValue = (0, import_react17.useMemo)(
4569
2822
  () => ({
4570
2823
  // Playback controls
4571
2824
  play,
@@ -4641,7 +2894,7 @@ var WaveformPlaylistProvider = ({
4641
2894
  redo
4642
2895
  ]
4643
2896
  );
4644
- const dataValue = (0, import_react24.useMemo)(
2897
+ const dataValue = (0, import_react17.useMemo)(
4645
2898
  () => ({
4646
2899
  duration,
4647
2900
  audioBuffers,
@@ -4697,45 +2950,69 @@ var WaveformPlaylistProvider = ({
4697
2950
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_styled_components.ThemeProvider, { theme: mergedTheme, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlaybackAnimationContext.Provider, { value: animationValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlaylistStateContext.Provider, { value: stateValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlaylistControlsContext.Provider, { value: controlsValue, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PlaylistDataContext.Provider, { value: dataValue, children }) }) }) }) });
4698
2951
  };
4699
2952
  var usePlaybackAnimation = () => {
4700
- const context = (0, import_react24.useContext)(PlaybackAnimationContext);
2953
+ const context = (0, import_react17.useContext)(PlaybackAnimationContext);
4701
2954
  if (!context) {
4702
2955
  throw new Error("usePlaybackAnimation must be used within WaveformPlaylistProvider");
4703
2956
  }
4704
2957
  return context;
4705
2958
  };
4706
2959
  var usePlaylistState = () => {
4707
- const context = (0, import_react24.useContext)(PlaylistStateContext);
2960
+ const context = (0, import_react17.useContext)(PlaylistStateContext);
4708
2961
  if (!context) {
4709
2962
  throw new Error("usePlaylistState must be used within WaveformPlaylistProvider");
4710
2963
  }
4711
2964
  return context;
4712
2965
  };
4713
2966
  var usePlaylistControls = () => {
4714
- const context = (0, import_react24.useContext)(PlaylistControlsContext);
2967
+ const context = (0, import_react17.useContext)(PlaylistControlsContext);
4715
2968
  if (!context) {
4716
2969
  throw new Error("usePlaylistControls must be used within WaveformPlaylistProvider");
4717
2970
  }
4718
2971
  return context;
4719
2972
  };
4720
2973
  var usePlaylistData = () => {
4721
- const context = (0, import_react24.useContext)(PlaylistDataContext);
2974
+ const context = (0, import_react17.useContext)(PlaylistDataContext);
4722
2975
  if (!context) {
4723
2976
  throw new Error("usePlaylistData must be used within WaveformPlaylistProvider");
4724
2977
  }
4725
2978
  return context;
4726
2979
  };
4727
- var usePlaylistDataOptional = () => (0, import_react24.useContext)(PlaylistDataContext);
2980
+ var usePlaylistDataOptional = () => (0, import_react17.useContext)(PlaylistDataContext);
4728
2981
 
4729
2982
  // src/MediaElementPlaylistContext.tsx
4730
- var import_react25 = require("react");
2983
+ var import_react18 = require("react");
4731
2984
  var import_styled_components2 = require("styled-components");
4732
- var import_media_element_playout = require("@waveform-playlist/media-element-playout");
2985
+
2986
+ // src/playout/resolveMediaElementPlayout.ts
2987
+ 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`.";
2988
+ function resolveMediaElementPlayout(opts) {
2989
+ return __async(this, null, function* () {
2990
+ if (opts.createPlayout) {
2991
+ return opts.createPlayout();
2992
+ }
2993
+ let mod;
2994
+ try {
2995
+ mod = yield import("@waveform-playlist/media-element-playout");
2996
+ } catch (originalErr) {
2997
+ console.warn(
2998
+ "[waveform-playlist] @waveform-playlist/media-element-playout dynamic import failed: " + String(originalErr)
2999
+ );
3000
+ throw new Error(INSTALL_HINT2);
3001
+ }
3002
+ return new mod.MediaElementPlayout({
3003
+ playbackRate: opts.playbackRate,
3004
+ preservesPitch: opts.preservesPitch
3005
+ });
3006
+ });
3007
+ }
3008
+
3009
+ // src/MediaElementPlaylistContext.tsx
4733
3010
  var import_ui_components3 = require("@waveform-playlist/ui-components");
4734
3011
  var import_jsx_runtime2 = require("react/jsx-runtime");
4735
- var MediaElementAnimationContext = (0, import_react25.createContext)(null);
4736
- var MediaElementStateContext = (0, import_react25.createContext)(null);
4737
- var MediaElementControlsContext = (0, import_react25.createContext)(null);
4738
- var MediaElementDataContext = (0, import_react25.createContext)(null);
3012
+ var MediaElementAnimationContext = (0, import_react18.createContext)(null);
3013
+ var MediaElementStateContext = (0, import_react18.createContext)(null);
3014
+ var MediaElementControlsContext = (0, import_react18.createContext)(null);
3015
+ var MediaElementDataContext = (0, import_react18.createContext)(null);
4739
3016
  var MediaElementPlaylistProvider = ({
4740
3017
  track,
4741
3018
  samplesPerPixel: initialSamplesPerPixel = 1024,
@@ -4753,16 +3030,18 @@ var MediaElementPlaylistProvider = ({
4753
3030
  audioContext,
4754
3031
  onAnnotationsChange,
4755
3032
  onReady,
3033
+ onError,
3034
+ createPlayout,
4756
3035
  children
4757
3036
  }) => {
4758
3037
  var _a;
4759
3038
  const progressBarWidth = progressBarWidthProp != null ? progressBarWidthProp : barWidth + barGap;
4760
- const [isPlaying, setIsPlaying] = (0, import_react25.useState)(false);
4761
- const [currentTime, setCurrentTime] = (0, import_react25.useState)(0);
4762
- const [duration, setDuration] = (0, import_react25.useState)(0);
4763
- const [peaksDataArray, setPeaksDataArray] = (0, import_react25.useState)([]);
4764
- const [playbackRate, setPlaybackRateState] = (0, import_react25.useState)(initialPlaybackRate);
4765
- const annotations = (0, import_react25.useMemo)(() => {
3039
+ const [isPlaying, setIsPlaying] = (0, import_react18.useState)(false);
3040
+ const [currentTime, setCurrentTime] = (0, import_react18.useState)(0);
3041
+ const [duration, setDuration] = (0, import_react18.useState)(0);
3042
+ const [peaksDataArray, setPeaksDataArray] = (0, import_react18.useState)([]);
3043
+ const [playbackRate, setPlaybackRateState] = (0, import_react18.useState)(initialPlaybackRate);
3044
+ const annotations = (0, import_react18.useMemo)(() => {
4766
3045
  if (!(annotationList == null ? void 0 : annotationList.annotations)) return [];
4767
3046
  if (process.env.NODE_ENV !== "production" && annotationList.annotations.length > 0) {
4768
3047
  const first = annotationList.annotations[0];
@@ -4775,73 +3054,92 @@ var MediaElementPlaylistProvider = ({
4775
3054
  }
4776
3055
  return annotationList.annotations;
4777
3056
  }, [annotationList == null ? void 0 : annotationList.annotations]);
4778
- const annotationsRef = (0, import_react25.useRef)(annotations);
3057
+ const annotationsRef = (0, import_react18.useRef)(annotations);
4779
3058
  annotationsRef.current = annotations;
4780
- const [activeAnnotationId, setActiveAnnotationIdState] = (0, import_react25.useState)(null);
4781
- const [continuousPlay, setContinuousPlayState] = (0, import_react25.useState)(
3059
+ const [activeAnnotationId, setActiveAnnotationIdState] = (0, import_react18.useState)(null);
3060
+ const [continuousPlay, setContinuousPlayState] = (0, import_react18.useState)(
4782
3061
  (_a = annotationList == null ? void 0 : annotationList.isContinuousPlay) != null ? _a : false
4783
3062
  );
4784
- const [samplesPerPixel] = (0, import_react25.useState)(initialSamplesPerPixel);
4785
- const [isAutomaticScroll, setIsAutomaticScroll] = (0, import_react25.useState)(automaticScroll);
4786
- const playoutRef = (0, import_react25.useRef)(null);
4787
- const currentTimeRef = (0, import_react25.useRef)(0);
4788
- const continuousPlayRef = (0, import_react25.useRef)(continuousPlay);
4789
- const activeAnnotationIdRef = (0, import_react25.useRef)(null);
4790
- const scrollContainerRef = (0, import_react25.useRef)(null);
4791
- const isAutomaticScrollRef = (0, import_react25.useRef)(automaticScroll);
4792
- const samplesPerPixelRef = (0, import_react25.useRef)(initialSamplesPerPixel);
3063
+ const [samplesPerPixel] = (0, import_react18.useState)(initialSamplesPerPixel);
3064
+ const [isAutomaticScroll, setIsAutomaticScroll] = (0, import_react18.useState)(automaticScroll);
3065
+ const playoutRef = (0, import_react18.useRef)(null);
3066
+ const currentTimeRef = (0, import_react18.useRef)(0);
3067
+ const continuousPlayRef = (0, import_react18.useRef)(continuousPlay);
3068
+ const activeAnnotationIdRef = (0, import_react18.useRef)(null);
3069
+ const scrollContainerRef = (0, import_react18.useRef)(null);
3070
+ const isAutomaticScrollRef = (0, import_react18.useRef)(automaticScroll);
3071
+ const samplesPerPixelRef = (0, import_react18.useRef)(initialSamplesPerPixel);
4793
3072
  const { startAnimationFrameLoop, stopAnimationFrameLoop } = useAnimationFrameLoop();
4794
- (0, import_react25.useEffect)(() => {
3073
+ (0, import_react18.useEffect)(() => {
4795
3074
  continuousPlayRef.current = continuousPlay;
4796
3075
  }, [continuousPlay]);
4797
- (0, import_react25.useEffect)(() => {
3076
+ (0, import_react18.useEffect)(() => {
4798
3077
  isAutomaticScrollRef.current = isAutomaticScroll;
4799
3078
  }, [isAutomaticScroll]);
4800
- const setActiveAnnotationId = (0, import_react25.useCallback)((value) => {
3079
+ const setActiveAnnotationId = (0, import_react18.useCallback)((value) => {
4801
3080
  activeAnnotationIdRef.current = value;
4802
3081
  setActiveAnnotationIdState(value);
4803
3082
  }, []);
4804
- const setContinuousPlay = (0, import_react25.useCallback)((value) => {
3083
+ const setContinuousPlay = (0, import_react18.useCallback)((value) => {
4805
3084
  continuousPlayRef.current = value;
4806
3085
  setContinuousPlayState(value);
4807
3086
  }, []);
4808
- const setScrollContainer = (0, import_react25.useCallback)((element) => {
3087
+ const setScrollContainer = (0, import_react18.useCallback)((element) => {
4809
3088
  scrollContainerRef.current = element;
4810
3089
  }, []);
4811
3090
  const sampleRate = track.waveformData.sample_rate;
4812
- (0, import_react25.useEffect)(() => {
4813
- var _a2, _b;
4814
- const playout = new import_media_element_playout.MediaElementPlayout({
4815
- playbackRate: initialPlaybackRate,
4816
- preservesPitch
4817
- });
4818
- playout.addTrack({
4819
- source: track.source,
4820
- peaks: track.waveformData,
4821
- name: track.name,
4822
- audioContext,
4823
- fadeIn: track.fadeIn,
4824
- fadeOut: track.fadeOut
4825
- });
4826
- const mediaTrack = playout.getTrack((_b = (_a2 = playout["track"]) == null ? void 0 : _a2.id) != null ? _b : "");
4827
- if (mediaTrack) {
4828
- mediaTrack.setOnTimeUpdateCallback((time) => {
4829
- currentTimeRef.current = time;
4830
- });
4831
- }
4832
- playout.setOnPlaybackComplete(() => {
4833
- stopAnimationFrameLoop();
4834
- setIsPlaying(false);
4835
- setActiveAnnotationId(null);
4836
- currentTimeRef.current = 0;
4837
- setCurrentTime(0);
4838
- });
4839
- playoutRef.current = playout;
4840
- setDuration(track.waveformData.duration);
4841
- onReady == null ? void 0 : onReady();
3091
+ (0, import_react18.useEffect)(() => {
3092
+ let cancelled = false;
3093
+ let createdPlayout = null;
3094
+ (() => __async(null, null, function* () {
3095
+ var _a2, _b;
3096
+ try {
3097
+ const playout = yield resolveMediaElementPlayout({
3098
+ createPlayout,
3099
+ playbackRate: initialPlaybackRate,
3100
+ preservesPitch
3101
+ });
3102
+ if (cancelled) {
3103
+ playout.dispose();
3104
+ return;
3105
+ }
3106
+ createdPlayout = playout;
3107
+ playout.addTrack({
3108
+ source: track.source,
3109
+ peaks: track.waveformData,
3110
+ name: track.name,
3111
+ audioContext,
3112
+ fadeIn: track.fadeIn,
3113
+ fadeOut: track.fadeOut
3114
+ });
3115
+ const mediaTrack = playout.getTrack((_b = (_a2 = playout["track"]) == null ? void 0 : _a2.id) != null ? _b : "");
3116
+ if (mediaTrack) {
3117
+ mediaTrack.setOnTimeUpdateCallback((time) => {
3118
+ currentTimeRef.current = time;
3119
+ });
3120
+ }
3121
+ playout.setOnPlaybackComplete(() => {
3122
+ stopAnimationFrameLoop();
3123
+ setIsPlaying(false);
3124
+ setActiveAnnotationId(null);
3125
+ currentTimeRef.current = 0;
3126
+ setCurrentTime(0);
3127
+ });
3128
+ playoutRef.current = playout;
3129
+ setDuration(track.waveformData.duration);
3130
+ onReady == null ? void 0 : onReady();
3131
+ } catch (err) {
3132
+ console.warn("[waveform-playlist] MediaElement playout init failed: " + String(err));
3133
+ onError == null ? void 0 : onError(err instanceof Error ? err : new Error(String(err)));
3134
+ }
3135
+ }))();
4842
3136
  return () => {
3137
+ cancelled = true;
4843
3138
  stopAnimationFrameLoop();
4844
- playout.dispose();
3139
+ if (createdPlayout) {
3140
+ createdPlayout.dispose();
3141
+ }
3142
+ playoutRef.current = null;
4845
3143
  };
4846
3144
  }, [
4847
3145
  track.source,
@@ -4853,10 +3151,12 @@ var MediaElementPlaylistProvider = ({
4853
3151
  initialPlaybackRate,
4854
3152
  preservesPitch,
4855
3153
  onReady,
3154
+ onError,
4856
3155
  stopAnimationFrameLoop,
4857
- setActiveAnnotationId
3156
+ setActiveAnnotationId,
3157
+ createPlayout
4858
3158
  ]);
4859
- (0, import_react25.useEffect)(() => {
3159
+ (0, import_react18.useEffect)(() => {
4860
3160
  var _a2;
4861
3161
  try {
4862
3162
  const extractedPeaks = extractPeaksFromWaveformData(
@@ -4885,7 +3185,7 @@ var MediaElementPlaylistProvider = ({
4885
3185
  console.warn("[waveform-playlist] Failed to extract peaks from waveform data:", err);
4886
3186
  }
4887
3187
  }, [track.waveformData, track.name, samplesPerPixel, sampleRate]);
4888
- const startAnimationLoop = (0, import_react25.useCallback)(() => {
3188
+ const startAnimationLoop = (0, import_react18.useCallback)(() => {
4889
3189
  const updateTime = () => {
4890
3190
  var _a2, _b, _c;
4891
3191
  const time = (_b = (_a2 = playoutRef.current) == null ? void 0 : _a2.getCurrentTime()) != null ? _b : 0;
@@ -4928,7 +3228,7 @@ var MediaElementPlaylistProvider = ({
4928
3228
  startAnimationFrameLoop(updateTime);
4929
3229
  }, [setActiveAnnotationId, sampleRate, startAnimationFrameLoop]);
4930
3230
  const stopAnimationLoop = stopAnimationFrameLoop;
4931
- const play = (0, import_react25.useCallback)(
3231
+ const play = (0, import_react18.useCallback)(
4932
3232
  (startTime) => {
4933
3233
  if (!playoutRef.current) return;
4934
3234
  const actualStartTime = startTime != null ? startTime : currentTimeRef.current;
@@ -4938,14 +3238,14 @@ var MediaElementPlaylistProvider = ({
4938
3238
  },
4939
3239
  [startAnimationLoop]
4940
3240
  );
4941
- const pause = (0, import_react25.useCallback)(() => {
3241
+ const pause = (0, import_react18.useCallback)(() => {
4942
3242
  if (!playoutRef.current) return;
4943
3243
  playoutRef.current.pause();
4944
3244
  setIsPlaying(false);
4945
3245
  stopAnimationLoop();
4946
3246
  setCurrentTime(playoutRef.current.getCurrentTime());
4947
3247
  }, [stopAnimationLoop]);
4948
- const stop = (0, import_react25.useCallback)(() => {
3248
+ const stop = (0, import_react18.useCallback)(() => {
4949
3249
  if (!playoutRef.current) return;
4950
3250
  playoutRef.current.stop();
4951
3251
  setIsPlaying(false);
@@ -4954,7 +3254,7 @@ var MediaElementPlaylistProvider = ({
4954
3254
  setCurrentTime(0);
4955
3255
  setActiveAnnotationId(null);
4956
3256
  }, [stopAnimationLoop, setActiveAnnotationId]);
4957
- const seekTo = (0, import_react25.useCallback)(
3257
+ const seekTo = (0, import_react18.useCallback)(
4958
3258
  (time) => {
4959
3259
  const clampedTime = Math.max(0, Math.min(time, duration));
4960
3260
  currentTimeRef.current = clampedTime;
@@ -4965,7 +3265,7 @@ var MediaElementPlaylistProvider = ({
4965
3265
  },
4966
3266
  [duration]
4967
3267
  );
4968
- const setPlaybackRate = (0, import_react25.useCallback)((rate) => {
3268
+ const setPlaybackRate = (0, import_react18.useCallback)((rate) => {
4969
3269
  const clampedRate = Math.max(0.5, Math.min(2, rate));
4970
3270
  setPlaybackRateState(clampedRate);
4971
3271
  if (playoutRef.current) {
@@ -4973,7 +3273,7 @@ var MediaElementPlaylistProvider = ({
4973
3273
  }
4974
3274
  }, []);
4975
3275
  const timeScaleHeight = timescale ? 30 : 0;
4976
- const animationValue = (0, import_react25.useMemo)(
3276
+ const animationValue = (0, import_react18.useMemo)(
4977
3277
  () => ({
4978
3278
  isPlaying,
4979
3279
  currentTime,
@@ -4981,7 +3281,7 @@ var MediaElementPlaylistProvider = ({
4981
3281
  }),
4982
3282
  [isPlaying, currentTime]
4983
3283
  );
4984
- const stateValue = (0, import_react25.useMemo)(
3284
+ const stateValue = (0, import_react18.useMemo)(
4985
3285
  () => ({
4986
3286
  continuousPlay,
4987
3287
  annotations,
@@ -4991,9 +3291,9 @@ var MediaElementPlaylistProvider = ({
4991
3291
  }),
4992
3292
  [continuousPlay, annotations, activeAnnotationId, playbackRate, isAutomaticScroll]
4993
3293
  );
4994
- const onAnnotationsChangeRef = (0, import_react25.useRef)(onAnnotationsChange);
3294
+ const onAnnotationsChangeRef = (0, import_react18.useRef)(onAnnotationsChange);
4995
3295
  onAnnotationsChangeRef.current = onAnnotationsChange;
4996
- const setAnnotations = (0, import_react25.useCallback)(
3296
+ const setAnnotations = (0, import_react18.useCallback)(
4997
3297
  (action) => {
4998
3298
  const updated = typeof action === "function" ? action(annotationsRef.current) : action;
4999
3299
  if (!onAnnotationsChangeRef.current) {
@@ -5008,7 +3308,7 @@ var MediaElementPlaylistProvider = ({
5008
3308
  },
5009
3309
  []
5010
3310
  );
5011
- const controlsValue = (0, import_react25.useMemo)(
3311
+ const controlsValue = (0, import_react18.useMemo)(
5012
3312
  () => ({
5013
3313
  play,
5014
3314
  pause,
@@ -5036,7 +3336,7 @@ var MediaElementPlaylistProvider = ({
5036
3336
  setScrollContainer
5037
3337
  ]
5038
3338
  );
5039
- const dataValue = (0, import_react25.useMemo)(
3339
+ const dataValue = (0, import_react18.useMemo)(
5040
3340
  () => ({
5041
3341
  duration,
5042
3342
  peaksDataArray,
@@ -5071,28 +3371,28 @@ var MediaElementPlaylistProvider = ({
5071
3371
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_styled_components2.ThemeProvider, { theme: mergedTheme, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MediaElementAnimationContext.Provider, { value: animationValue, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MediaElementStateContext.Provider, { value: stateValue, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MediaElementControlsContext.Provider, { value: controlsValue, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MediaElementDataContext.Provider, { value: dataValue, children }) }) }) }) });
5072
3372
  };
5073
3373
  var useMediaElementAnimation = () => {
5074
- const context = (0, import_react25.useContext)(MediaElementAnimationContext);
3374
+ const context = (0, import_react18.useContext)(MediaElementAnimationContext);
5075
3375
  if (!context) {
5076
3376
  throw new Error("useMediaElementAnimation must be used within MediaElementPlaylistProvider");
5077
3377
  }
5078
3378
  return context;
5079
3379
  };
5080
3380
  var useMediaElementState = () => {
5081
- const context = (0, import_react25.useContext)(MediaElementStateContext);
3381
+ const context = (0, import_react18.useContext)(MediaElementStateContext);
5082
3382
  if (!context) {
5083
3383
  throw new Error("useMediaElementState must be used within MediaElementPlaylistProvider");
5084
3384
  }
5085
3385
  return context;
5086
3386
  };
5087
3387
  var useMediaElementControls = () => {
5088
- const context = (0, import_react25.useContext)(MediaElementControlsContext);
3388
+ const context = (0, import_react18.useContext)(MediaElementControlsContext);
5089
3389
  if (!context) {
5090
3390
  throw new Error("useMediaElementControls must be used within MediaElementPlaylistProvider");
5091
3391
  }
5092
3392
  return context;
5093
3393
  };
5094
3394
  var useMediaElementData = () => {
5095
- const context = (0, import_react25.useContext)(MediaElementDataContext);
3395
+ const context = (0, import_react18.useContext)(MediaElementDataContext);
5096
3396
  if (!context) {
5097
3397
  throw new Error("useMediaElementData must be used within MediaElementPlaylistProvider");
5098
3398
  }
@@ -5100,7 +3400,7 @@ var useMediaElementData = () => {
5100
3400
  };
5101
3401
 
5102
3402
  // src/components/PlaybackControls.tsx
5103
- var import_react26 = require("react");
3403
+ var import_react19 = require("react");
5104
3404
  var import_ui_components4 = require("@waveform-playlist/ui-components");
5105
3405
  var import_jsx_runtime3 = require("react/jsx-runtime");
5106
3406
  var PlayButton = ({ className }) => {
@@ -5236,7 +3536,7 @@ var ClearAllButton = ({
5236
3536
  className
5237
3537
  }) => {
5238
3538
  const { stop } = usePlaylistControls();
5239
- const handleClick = (0, import_react26.useCallback)(() => {
3539
+ const handleClick = (0, import_react19.useCallback)(() => {
5240
3540
  stop();
5241
3541
  onClearAll();
5242
3542
  }, [stop, onClearAll]);
@@ -5264,7 +3564,7 @@ var ZoomOutButton = ({
5264
3564
  };
5265
3565
 
5266
3566
  // src/components/ContextualControls.tsx
5267
- var import_react27 = require("react");
3567
+ var import_react20 = require("react");
5268
3568
  var import_ui_components6 = require("@waveform-playlist/ui-components");
5269
3569
  var import_styled_components3 = __toESM(require("styled-components"));
5270
3570
  var import_jsx_runtime5 = require("react/jsx-runtime");
@@ -5297,10 +3597,10 @@ var PositionDisplay = import_styled_components3.default.span`
5297
3597
  `;
5298
3598
  var AudioPosition = ({ className }) => {
5299
3599
  var _a;
5300
- const timeRef = (0, import_react27.useRef)(null);
3600
+ const timeRef = (0, import_react20.useRef)(null);
5301
3601
  const { isPlaying, currentTimeRef, registerFrameCallback, unregisterFrameCallback } = usePlaybackAnimation();
5302
3602
  const { timeFormat: format } = usePlaylistData();
5303
- (0, import_react27.useEffect)(() => {
3603
+ (0, import_react20.useEffect)(() => {
5304
3604
  const id = "audio-position";
5305
3605
  if (isPlaying) {
5306
3606
  registerFrameCallback(id, ({ time }) => {
@@ -5311,7 +3611,7 @@ var AudioPosition = ({ className }) => {
5311
3611
  }
5312
3612
  return () => unregisterFrameCallback(id);
5313
3613
  }, [isPlaying, format, registerFrameCallback, unregisterFrameCallback]);
5314
- (0, import_react27.useEffect)(() => {
3614
+ (0, import_react20.useEffect)(() => {
5315
3615
  var _a2;
5316
3616
  if (!isPlaying && timeRef.current) {
5317
3617
  timeRef.current.textContent = (0, import_ui_components6.formatTime)((_a2 = currentTimeRef.current) != null ? _a2 : 0, format);
@@ -5346,11 +3646,11 @@ var AutomaticScrollCheckbox = ({ className }) => {
5346
3646
  };
5347
3647
 
5348
3648
  // src/AnnotationIntegrationContext.tsx
5349
- var import_react28 = require("react");
5350
- var AnnotationIntegrationContext = (0, import_react28.createContext)(null);
3649
+ var import_react21 = require("react");
3650
+ var AnnotationIntegrationContext = (0, import_react21.createContext)(null);
5351
3651
  var AnnotationIntegrationProvider = AnnotationIntegrationContext.Provider;
5352
3652
  function useAnnotationIntegration() {
5353
- const context = (0, import_react28.useContext)(AnnotationIntegrationContext);
3653
+ const context = (0, import_react21.useContext)(AnnotationIntegrationContext);
5354
3654
  if (!context) {
5355
3655
  throw new Error(
5356
3656
  "useAnnotationIntegration must be used within <AnnotationProvider>. Install @waveform-playlist/annotations and wrap your app with <AnnotationProvider>. See: https://waveform-playlist.naomiaro.com/docs/guides/annotations"
@@ -5388,73 +3688,25 @@ var DownloadAnnotationsButton = ({
5388
3688
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Base, { annotations, filename, className });
5389
3689
  };
5390
3690
 
5391
- // src/components/ExportControls.tsx
5392
- var import_ui_components7 = require("@waveform-playlist/ui-components");
5393
- var import_jsx_runtime7 = require("react/jsx-runtime");
5394
- var ExportWavButton = ({
5395
- label = "Export WAV",
5396
- filename = "export",
5397
- mode = "master",
5398
- trackIndex,
5399
- bitDepth = 16,
5400
- applyEffects = true,
5401
- effectsFunction,
5402
- createOfflineTrackEffects,
5403
- className,
5404
- onExportComplete,
5405
- onExportError
5406
- }) => {
5407
- const { tracks, trackStates } = usePlaylistData();
5408
- const { exportWav, isExporting, progress } = useExportWav();
5409
- const handleExport = () => __async(null, null, function* () {
5410
- try {
5411
- const result = yield exportWav(tracks, trackStates, {
5412
- filename,
5413
- mode,
5414
- trackIndex,
5415
- bitDepth,
5416
- applyEffects,
5417
- effectsFunction,
5418
- createOfflineTrackEffects,
5419
- autoDownload: true
5420
- });
5421
- onExportComplete == null ? void 0 : onExportComplete(result.blob);
5422
- } catch (error) {
5423
- onExportError == null ? void 0 : onExportError(error instanceof Error ? error : new Error("Export failed"));
5424
- }
5425
- });
5426
- const buttonLabel = isExporting ? `Exporting ${Math.round(progress * 100)}%` : label;
5427
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
5428
- import_ui_components7.BaseControlButton,
5429
- {
5430
- onClick: handleExport,
5431
- disabled: isExporting || tracks.length === 0,
5432
- className,
5433
- children: buttonLabel
5434
- }
5435
- );
5436
- };
5437
-
5438
3691
  // src/contexts/ClipInteractionContext.tsx
5439
- var import_react29 = require("react");
5440
- var ClipInteractionContext = (0, import_react29.createContext)(false);
3692
+ var import_react22 = require("react");
3693
+ var ClipInteractionContext = (0, import_react22.createContext)(false);
5441
3694
  var ClipInteractionContextProvider = ClipInteractionContext.Provider;
5442
3695
  function useClipInteractionEnabled() {
5443
- return (0, import_react29.useContext)(ClipInteractionContext);
3696
+ return (0, import_react22.useContext)(ClipInteractionContext);
5444
3697
  }
5445
3698
 
5446
3699
  // src/components/PlaylistVisualization.tsx
5447
- var import_react33 = require("react");
3700
+ var import_react26 = require("react");
5448
3701
  var import_react_dom = require("react-dom");
5449
3702
  var import_styled_components6 = __toESM(require("styled-components"));
5450
- var import_playout7 = require("@waveform-playlist/playout");
5451
- var import_ui_components9 = require("@waveform-playlist/ui-components");
5452
- var import_core8 = require("@waveform-playlist/core");
3703
+ var import_ui_components8 = require("@waveform-playlist/ui-components");
3704
+ var import_core4 = require("@waveform-playlist/core");
5453
3705
 
5454
3706
  // src/components/AnimatedPlayhead.tsx
5455
- var import_react30 = require("react");
3707
+ var import_react23 = require("react");
5456
3708
  var import_styled_components4 = __toESM(require("styled-components"));
5457
- var import_jsx_runtime8 = require("react/jsx-runtime");
3709
+ var import_jsx_runtime7 = require("react/jsx-runtime");
5458
3710
  var PlayheadLine = import_styled_components4.default.div.attrs((props) => ({
5459
3711
  style: {
5460
3712
  width: `${props.$width}px`,
@@ -5470,7 +3722,7 @@ var PlayheadLine = import_styled_components4.default.div.attrs((props) => ({
5470
3722
  will-change: transform;
5471
3723
  `;
5472
3724
  var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5473
- const playheadRef = (0, import_react30.useRef)(null);
3725
+ const playheadRef = (0, import_react23.useRef)(null);
5474
3726
  const {
5475
3727
  isPlaying,
5476
3728
  currentTimeRef,
@@ -5479,7 +3731,7 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5479
3731
  unregisterFrameCallback
5480
3732
  } = usePlaybackAnimation();
5481
3733
  const { samplesPerPixel, sampleRate, progressBarWidth } = usePlaylistData();
5482
- (0, import_react30.useEffect)(() => {
3734
+ (0, import_react23.useEffect)(() => {
5483
3735
  const id = "playhead";
5484
3736
  if (isPlaying) {
5485
3737
  registerFrameCallback(id, ({ visualTime, sampleRate: sr, samplesPerPixel: spp }) => {
@@ -5491,7 +3743,7 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5491
3743
  }
5492
3744
  return () => unregisterFrameCallback(id);
5493
3745
  }, [isPlaying, registerFrameCallback, unregisterFrameCallback]);
5494
- (0, import_react30.useEffect)(() => {
3746
+ (0, import_react23.useEffect)(() => {
5495
3747
  var _a, _b;
5496
3748
  if (!isPlaying && playheadRef.current) {
5497
3749
  const time = (_b = (_a = visualTimeRef.current) != null ? _a : currentTimeRef.current) != null ? _b : 0;
@@ -5499,15 +3751,15 @@ var AnimatedPlayhead = ({ color = "#ff0000" }) => {
5499
3751
  playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;
5500
3752
  }
5501
3753
  });
5502
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PlayheadLine, { ref: playheadRef, $color: color, $width: progressBarWidth, "data-playhead": true });
3754
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PlayheadLine, { ref: playheadRef, $color: color, $width: progressBarWidth, "data-playhead": true });
5503
3755
  };
5504
3756
 
5505
3757
  // src/components/ChannelWithProgress.tsx
5506
- var import_react31 = require("react");
3758
+ var import_react24 = require("react");
5507
3759
  var import_styled_components5 = __toESM(require("styled-components"));
5508
- var import_core7 = require("@waveform-playlist/core");
5509
- var import_ui_components8 = require("@waveform-playlist/ui-components");
5510
- var import_jsx_runtime9 = require("react/jsx-runtime");
3760
+ var import_core3 = require("@waveform-playlist/core");
3761
+ var import_ui_components7 = require("@waveform-playlist/ui-components");
3762
+ var import_jsx_runtime8 = require("react/jsx-runtime");
5511
3763
  var ChannelWrapper = import_styled_components5.default.div`
5512
3764
  position: relative;
5513
3765
  `;
@@ -5561,10 +3813,10 @@ var ChannelWithProgress = (_a) => {
5561
3813
  "clipSampleRate",
5562
3814
  "clipOffsetSeconds"
5563
3815
  ]);
5564
- const progressRef = (0, import_react31.useRef)(null);
5565
- const callbackId = (0, import_react31.useId)();
5566
- const theme = (0, import_ui_components8.useTheme)();
5567
- const { waveHeight } = (0, import_ui_components8.usePlaylistInfo)();
3816
+ const progressRef = (0, import_react24.useRef)(null);
3817
+ const callbackId = (0, import_react24.useId)();
3818
+ const theme = (0, import_ui_components7.useTheme)();
3819
+ const { waveHeight } = (0, import_ui_components7.usePlaylistInfo)();
5568
3820
  const {
5569
3821
  isPlaying,
5570
3822
  currentTimeRef,
@@ -5574,12 +3826,12 @@ var ChannelWithProgress = (_a) => {
5574
3826
  } = usePlaybackAnimation();
5575
3827
  const { samplesPerPixel, sampleRate } = usePlaylistData();
5576
3828
  const progressColor = (theme == null ? void 0 : theme.waveProgressColor) || "rgba(0, 0, 0, 0.1)";
5577
- const clipPixelWidth = (0, import_core7.clipPixelWidth)(
3829
+ const clipPixelWidth = (0, import_core3.clipPixelWidth)(
5578
3830
  clipStartSample,
5579
3831
  clipDurationSamples,
5580
3832
  samplesPerPixel
5581
3833
  );
5582
- (0, import_react31.useEffect)(() => {
3834
+ (0, import_react24.useEffect)(() => {
5583
3835
  if (isPlaying) {
5584
3836
  registerFrameCallback(callbackId, ({ visualTime, sampleRate: sr }) => {
5585
3837
  if (progressRef.current) {
@@ -5607,7 +3859,7 @@ var ChannelWithProgress = (_a) => {
5607
3859
  registerFrameCallback,
5608
3860
  unregisterFrameCallback
5609
3861
  ]);
5610
- (0, import_react31.useEffect)(() => {
3862
+ (0, import_react24.useEffect)(() => {
5611
3863
  var _a2, _b2;
5612
3864
  if (!isPlaying && progressRef.current) {
5613
3865
  const currentTime = (_b2 = (_a2 = visualTimeRef.current) != null ? _a2 : currentTimeRef.current) != null ? _b2 : 0;
@@ -5635,14 +3887,14 @@ var ChannelWithProgress = (_a) => {
5635
3887
  const isSpectrogramMode = smartChannelProps.renderMode === "spectrogram" || smartChannelProps.renderMode === "both";
5636
3888
  const isPianoRollMode = smartChannelProps.renderMode === "piano-roll";
5637
3889
  const isBothMode = smartChannelProps.renderMode === "both";
5638
- const backgroundCss = isSpectrogramMode ? "#000" : isPianoRollMode ? (theme == null ? void 0 : theme.pianoRollBackgroundColor) || "#1a1a2e" : (0, import_ui_components8.waveformColorToCss)(backgroundColor);
3890
+ const backgroundCss = isSpectrogramMode ? "#000" : isPianoRollMode ? (theme == null ? void 0 : theme.pianoRollBackgroundColor) || "#1a1a2e" : (0, import_ui_components7.waveformColorToCss)(backgroundColor);
5639
3891
  const halfHeight = Math.floor(waveHeight / 2);
5640
3892
  const effectiveHeight = waveHeight;
5641
3893
  const effectiveTop = isBothMode ? smartChannelProps.index * waveHeight : smartChannelProps.index * waveHeight;
5642
- const waveformBackgroundCss = (0, import_ui_components8.waveformColorToCss)(backgroundColor);
5643
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(ChannelWrapper, { children: [
5644
- isBothMode ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
5645
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3894
+ const waveformBackgroundCss = (0, import_ui_components7.waveformColorToCss)(backgroundColor);
3895
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(ChannelWrapper, { children: [
3896
+ isBothMode ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3897
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5646
3898
  Background,
5647
3899
  {
5648
3900
  $color: "#000",
@@ -5651,7 +3903,7 @@ var ChannelWithProgress = (_a) => {
5651
3903
  $width: smartChannelProps.length
5652
3904
  }
5653
3905
  ),
5654
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3906
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5655
3907
  Background,
5656
3908
  {
5657
3909
  $color: waveformBackgroundCss,
@@ -5660,7 +3912,7 @@ var ChannelWithProgress = (_a) => {
5660
3912
  $width: smartChannelProps.length
5661
3913
  }
5662
3914
  )
5663
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3915
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5664
3916
  Background,
5665
3917
  {
5666
3918
  $color: backgroundCss,
@@ -5669,7 +3921,7 @@ var ChannelWithProgress = (_a) => {
5669
3921
  $width: smartChannelProps.length
5670
3922
  }
5671
3923
  ),
5672
- !isPianoRollMode && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3924
+ !isPianoRollMode && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5673
3925
  ProgressOverlay,
5674
3926
  {
5675
3927
  ref: progressRef,
@@ -5679,8 +3931,8 @@ var ChannelWithProgress = (_a) => {
5679
3931
  $width: clipPixelWidth
5680
3932
  }
5681
3933
  ),
5682
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChannelContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5683
- import_ui_components8.SmartChannel,
3934
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChannelContainer, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3935
+ import_ui_components7.SmartChannel,
5684
3936
  __spreadProps(__spreadValues({}, smartChannelProps), {
5685
3937
  transparentBackground: true,
5686
3938
  midiNotes,
@@ -5692,11 +3944,11 @@ var ChannelWithProgress = (_a) => {
5692
3944
  };
5693
3945
 
5694
3946
  // src/SpectrogramIntegrationContext.tsx
5695
- var import_react32 = require("react");
5696
- var SpectrogramIntegrationContext = (0, import_react32.createContext)(null);
3947
+ var import_react25 = require("react");
3948
+ var SpectrogramIntegrationContext = (0, import_react25.createContext)(null);
5697
3949
  var SpectrogramIntegrationProvider = SpectrogramIntegrationContext.Provider;
5698
3950
  function useSpectrogramIntegration() {
5699
- const context = (0, import_react32.useContext)(SpectrogramIntegrationContext);
3951
+ const context = (0, import_react25.useContext)(SpectrogramIntegrationContext);
5700
3952
  if (!context) {
5701
3953
  throw new Error(
5702
3954
  "useSpectrogramIntegration must be used within <SpectrogramProvider>. Install @waveform-playlist/spectrogram and wrap your app with <SpectrogramProvider>."
@@ -5706,7 +3958,7 @@ function useSpectrogramIntegration() {
5706
3958
  }
5707
3959
 
5708
3960
  // src/components/PlaylistVisualization.tsx
5709
- var import_jsx_runtime10 = require("react/jsx-runtime");
3961
+ var import_jsx_runtime9 = require("react/jsx-runtime");
5710
3962
  var DEFAULT_EMPTY_TRACK_DURATION = 60;
5711
3963
  var ControlSlot = import_styled_components6.default.div.attrs((props) => ({
5712
3964
  style: { height: `${props.$height}px` }
@@ -5725,7 +3977,8 @@ var CustomPlayhead = ({ renderPlayhead, color, samplesPerPixel, sampleRate }) =>
5725
3977
  visualTimeRef,
5726
3978
  playbackStartTimeRef,
5727
3979
  audioStartPositionRef,
5728
- getPlaybackTime
3980
+ getPlaybackTime,
3981
+ getAudioContextTime
5729
3982
  } = usePlaybackAnimation();
5730
3983
  const visualTime = (_b = (_a = visualTimeRef.current) != null ? _a : currentTimeRef.current) != null ? _b : 0;
5731
3984
  return renderPlayhead({
@@ -5739,7 +3992,7 @@ var CustomPlayhead = ({ renderPlayhead, color, samplesPerPixel, sampleRate }) =>
5739
3992
  samplesPerPixel,
5740
3993
  sampleRate,
5741
3994
  controlsOffset: 0,
5742
- getAudioContextTime: () => (0, import_playout7.getGlobalAudioContext)().currentTime,
3995
+ getAudioContextTime,
5743
3996
  getPlaybackTime
5744
3997
  });
5745
3998
  };
@@ -5763,8 +4016,8 @@ var PlaylistVisualization = ({
5763
4016
  recordingState
5764
4017
  }) => {
5765
4018
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
5766
- const theme = (0, import_ui_components9.useTheme)();
5767
- const { isPlaying, getLookAhead } = usePlaybackAnimation();
4019
+ const theme = (0, import_ui_components8.useTheme)();
4020
+ const { isPlaying, getLookAhead, getOutputLatency } = usePlaybackAnimation();
5768
4021
  const {
5769
4022
  selectionStart,
5770
4023
  selectionEnd,
@@ -5779,7 +4032,7 @@ var PlaylistVisualization = ({
5779
4032
  isLoopEnabled,
5780
4033
  indefinitePlayback
5781
4034
  } = usePlaylistState();
5782
- const annotationIntegration = (0, import_react33.useContext)(AnnotationIntegrationContext);
4035
+ const annotationIntegration = (0, import_react26.useContext)(AnnotationIntegrationContext);
5783
4036
  const {
5784
4037
  setAnnotations: _setAnnotations,
5785
4038
  setActiveAnnotationId,
@@ -5809,8 +4062,8 @@ var PlaylistVisualization = ({
5809
4062
  isReady,
5810
4063
  mono
5811
4064
  } = usePlaylistData();
5812
- const spectrogram = (0, import_react33.useContext)(SpectrogramIntegrationContext);
5813
- const perTrackSpectrogramHelpers = (0, import_react33.useMemo)(() => {
4065
+ const spectrogram = (0, import_react26.useContext)(SpectrogramIntegrationContext);
4066
+ const perTrackSpectrogramHelpers = (0, import_react26.useMemo)(() => {
5814
4067
  if (!spectrogram)
5815
4068
  return /* @__PURE__ */ new Map();
5816
4069
  const helpers = /* @__PURE__ */ new Map();
@@ -5829,11 +4082,11 @@ var PlaylistVisualization = ({
5829
4082
  });
5830
4083
  return helpers;
5831
4084
  }, [tracks, spectrogram]);
5832
- const [settingsModalTrackId, setSettingsModalTrackId] = (0, import_react33.useState)(null);
5833
- const [isSelecting, setIsSelecting] = (0, import_react33.useState)(false);
5834
- const mouseDownTimeRef = (0, import_react33.useRef)(0);
5835
- const scrollContainerRef = (0, import_react33.useRef)(null);
5836
- const handleScrollContainerRef = (0, import_react33.useCallback)(
4085
+ const [settingsModalTrackId, setSettingsModalTrackId] = (0, import_react26.useState)(null);
4086
+ const [isSelecting, setIsSelecting] = (0, import_react26.useState)(false);
4087
+ const mouseDownTimeRef = (0, import_react26.useRef)(0);
4088
+ const scrollContainerRef = (0, import_react26.useRef)(null);
4089
+ const handleScrollContainerRef = (0, import_react26.useCallback)(
5837
4090
  (element) => {
5838
4091
  scrollContainerRef.current = element;
5839
4092
  setScrollContainer(element);
@@ -5871,7 +4124,7 @@ var PlaylistVisualization = ({
5871
4124
  );
5872
4125
  }
5873
4126
  });
5874
- const selectTrack = (0, import_react33.useCallback)(
4127
+ const selectTrack = (0, import_react26.useCallback)(
5875
4128
  (trackIndex) => {
5876
4129
  if (trackIndex >= 0 && trackIndex < tracks.length) {
5877
4130
  const track = tracks[trackIndex];
@@ -5941,7 +4194,7 @@ var PlaylistVisualization = ({
5941
4194
  };
5942
4195
  const hasClips = tracks.some((track) => track.clips.length > 0);
5943
4196
  if (hasClips && peaksDataArray.length === 0) {
5944
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className, children: "Loading waveform..." });
4197
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className, children: "Loading waveform..." });
5945
4198
  }
5946
4199
  const trackControlsSlots = controls.show ? peaksDataArray.map((trackClipPeaks, trackIndex) => {
5947
4200
  var _a2, _b2, _c2;
@@ -5957,11 +4210,11 @@ var PlaylistVisualization = ({
5957
4210
  const hasMidiNotes = track.clips.some((c) => c.midiNotes && c.midiNotes.length > 0);
5958
4211
  const effectiveRenderMode = (_c2 = (_b2 = (_a2 = spectrogram == null ? void 0 : spectrogram.trackSpectrogramOverrides.get(track.id)) == null ? void 0 : _a2.renderMode) != null ? _b2 : track.renderMode) != null ? _c2 : hasMidiNotes ? "piano-roll" : "waveform";
5959
4212
  const maxChannels = getTrackChannelCount(trackClipPeaks, recordingState, track.id, mono);
5960
- const slotHeight = waveHeight * maxChannels + (showClipHeaders ? import_ui_components9.CLIP_HEADER_HEIGHT : 0);
5961
- const trackControlContent = renderTrackControls ? renderTrackControls(trackIndex) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ui_components9.Controls, { onClick: () => selectTrack(trackIndex), children: [
5962
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ui_components9.Header, { style: { justifyContent: "center", position: "relative" }, children: [
5963
- onRemoveTrack && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5964
- import_ui_components9.CloseButton,
4213
+ const slotHeight = waveHeight * maxChannels + (showClipHeaders ? import_ui_components8.CLIP_HEADER_HEIGHT : 0);
4214
+ const trackControlContent = renderTrackControls ? renderTrackControls(trackIndex) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ui_components8.Controls, { onClick: () => selectTrack(trackIndex), children: [
4215
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ui_components8.Header, { style: { justifyContent: "center", position: "relative" }, children: [
4216
+ onRemoveTrack && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4217
+ import_ui_components8.CloseButton,
5965
4218
  {
5966
4219
  onClick: (e) => {
5967
4220
  e.stopPropagation();
@@ -5969,7 +4222,7 @@ var PlaylistVisualization = ({
5969
4222
  }
5970
4223
  }
5971
4224
  ),
5972
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4225
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5973
4226
  "span",
5974
4227
  {
5975
4228
  style: {
@@ -5982,8 +4235,8 @@ var PlaylistVisualization = ({
5982
4235
  children: trackState.name || `Track ${trackIndex + 1}`
5983
4236
  }
5984
4237
  ),
5985
- (spectrogram == null ? void 0 : spectrogram.renderMenuItems) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { position: "absolute", right: 0, top: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5986
- import_ui_components9.TrackMenu,
4238
+ (spectrogram == null ? void 0 : spectrogram.renderMenuItems) && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { position: "absolute", right: 0, top: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4239
+ import_ui_components8.TrackMenu,
5987
4240
  {
5988
4241
  items: (onClose) => spectrogram.renderMenuItems({
5989
4242
  renderMode: effectiveRenderMode,
@@ -5994,17 +4247,17 @@ var PlaylistVisualization = ({
5994
4247
  }
5995
4248
  ) })
5996
4249
  ] }),
5997
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ui_components9.ButtonGroup, { children: [
5998
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5999
- import_ui_components9.Button,
4250
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ui_components8.ButtonGroup, { children: [
4251
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4252
+ import_ui_components8.Button,
6000
4253
  {
6001
4254
  $variant: trackState.muted ? "danger" : "outline",
6002
4255
  onClick: () => setTrackMute(trackIndex, !trackState.muted),
6003
4256
  children: "Mute"
6004
4257
  }
6005
4258
  ),
6006
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6007
- import_ui_components9.Button,
4259
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4260
+ import_ui_components8.Button,
6008
4261
  {
6009
4262
  $variant: trackState.soloed ? "info" : "outline",
6010
4263
  onClick: () => setTrackSolo(trackIndex, !trackState.soloed),
@@ -6012,10 +4265,10 @@ var PlaylistVisualization = ({
6012
4265
  }
6013
4266
  )
6014
4267
  ] }),
6015
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ui_components9.SliderWrapper, { children: [
6016
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ui_components9.VolumeDownIcon, {}),
6017
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6018
- import_ui_components9.Slider,
4268
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ui_components8.SliderWrapper, { children: [
4269
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ui_components8.VolumeDownIcon, {}),
4270
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4271
+ import_ui_components8.Slider,
6019
4272
  {
6020
4273
  min: "0",
6021
4274
  max: "1",
@@ -6024,12 +4277,12 @@ var PlaylistVisualization = ({
6024
4277
  onChange: (e) => setTrackVolume(trackIndex, parseFloat(e.target.value))
6025
4278
  }
6026
4279
  ),
6027
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ui_components9.VolumeUpIcon, {})
4280
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ui_components8.VolumeUpIcon, {})
6028
4281
  ] }),
6029
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ui_components9.SliderWrapper, { children: [
6030
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "L" }),
6031
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6032
- import_ui_components9.Slider,
4282
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ui_components8.SliderWrapper, { children: [
4283
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "L" }),
4284
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4285
+ import_ui_components8.Slider,
6033
4286
  {
6034
4287
  min: "-1",
6035
4288
  max: "1",
@@ -6038,10 +4291,10 @@ var PlaylistVisualization = ({
6038
4291
  onChange: (e) => setTrackPan(trackIndex, parseFloat(e.target.value))
6039
4292
  }
6040
4293
  ),
6041
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: "R" })
4294
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "R" })
6042
4295
  ] })
6043
4296
  ] });
6044
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4297
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6045
4298
  ControlSlot,
6046
4299
  {
6047
4300
  $height: slotHeight,
@@ -6051,9 +4304,9 @@ var PlaylistVisualization = ({
6051
4304
  track.id
6052
4305
  );
6053
4306
  }) : void 0;
6054
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_ui_components9.DevicePixelRatioProvider, { children: [
6055
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6056
- import_ui_components9.PlaylistInfoContext.Provider,
4307
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_ui_components8.DevicePixelRatioProvider, { children: [
4308
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4309
+ import_ui_components8.PlaylistInfoContext.Provider,
6057
4310
  {
6058
4311
  value: {
6059
4312
  samplesPerPixel,
@@ -6066,11 +4319,11 @@ var PlaylistVisualization = ({
6066
4319
  barWidth,
6067
4320
  barGap
6068
4321
  },
6069
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6070
- import_ui_components9.Playlist,
4322
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4323
+ import_ui_components8.Playlist,
6071
4324
  {
6072
4325
  theme,
6073
- backgroundColor: theme.playlistBackgroundColor || (0, import_ui_components9.waveformColorToCss)(theme.waveOutlineColor),
4326
+ backgroundColor: theme.playlistBackgroundColor || (0, import_ui_components8.waveformColorToCss)(theme.waveOutlineColor),
6074
4327
  timescaleBackgroundColor: theme.timescaleBackgroundColor,
6075
4328
  timescaleWidth: tracksFullWidth,
6076
4329
  tracksWidth: tracksFullWidth,
@@ -6083,10 +4336,10 @@ var PlaylistVisualization = ({
6083
4336
  "data-playlist-state": isReady ? "ready" : "loading",
6084
4337
  trackControlsSlots,
6085
4338
  timescaleGapHeight: timeScaleHeight > 0 ? timeScaleHeight + 1 : 0,
6086
- timescale: timeScaleHeight > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
6087
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_ui_components9.SmartScale, { renderTick }),
6088
- isLoopEnabled && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6089
- import_ui_components9.TimescaleLoopRegion,
4339
+ timescale: timeScaleHeight > 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
4340
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_ui_components8.SmartScale, { renderTick }),
4341
+ isLoopEnabled && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4342
+ import_ui_components8.TimescaleLoopRegion,
6090
4343
  {
6091
4344
  startPosition: Math.min(loopStart, loopEnd) * sampleRate / samplesPerPixel,
6092
4345
  endPosition: Math.max(loopStart, loopEnd) * sampleRate / samplesPerPixel,
@@ -6102,7 +4355,7 @@ var PlaylistVisualization = ({
6102
4355
  }
6103
4356
  )
6104
4357
  ] }) : void 0,
6105
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
4358
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
6106
4359
  peaksDataArray.map((trackClipPeaks, trackIndex) => {
6107
4360
  var _a2, _b2, _c2, _d2;
6108
4361
  const track = tracks[trackIndex];
@@ -6115,11 +4368,11 @@ var PlaylistVisualization = ({
6115
4368
  track.id,
6116
4369
  mono
6117
4370
  );
6118
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
6119
- import_ui_components9.Track,
4371
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
4372
+ import_ui_components8.Track,
6120
4373
  {
6121
4374
  numChannels: maxChannels,
6122
- backgroundColor: effectiveRenderMode === "piano-roll" ? theme.pianoRollBackgroundColor || "#1a1a2e" : (0, import_ui_components9.waveformColorToCss)(theme.waveOutlineColor),
4375
+ backgroundColor: effectiveRenderMode === "piano-roll" ? theme.pianoRollBackgroundColor || "#1a1a2e" : (0, import_ui_components8.waveformColorToCss)(theme.waveOutlineColor),
6123
4376
  offset: 0,
6124
4377
  width: tracksFullWidth,
6125
4378
  hasClipHeaders: showClipHeaders,
@@ -6131,8 +4384,8 @@ var PlaylistVisualization = ({
6131
4384
  const helpers = perTrackSpectrogramHelpers.get(track.id);
6132
4385
  const trackCfg = helpers == null ? void 0 : helpers.config;
6133
4386
  if (!(trackCfg == null ? void 0 : trackCfg.labels) || !helpers) return null;
6134
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6135
- import_ui_components9.SpectrogramLabels,
4387
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4388
+ import_ui_components8.SpectrogramLabels,
6136
4389
  {
6137
4390
  waveHeight,
6138
4391
  numChannels: maxChannels,
@@ -6149,8 +4402,8 @@ var PlaylistVisualization = ({
6149
4402
  trackClipPeaks.map((clip, clipIndex) => {
6150
4403
  const peaksData = clip.peaks;
6151
4404
  const width = peaksData.length;
6152
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6153
- import_ui_components9.Clip,
4405
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4406
+ import_ui_components8.Clip,
6154
4407
  {
6155
4408
  clipId: clip.clipId,
6156
4409
  trackIndex,
@@ -6179,7 +4432,7 @@ var PlaylistVisualization = ({
6179
4432
  selectTrack(trackIndex);
6180
4433
  },
6181
4434
  children: peaksData.data.map((channelPeaks, channelIndex) => {
6182
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4435
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6183
4436
  ChannelWithProgress,
6184
4437
  {
6185
4438
  index: channelIndex,
@@ -6206,14 +4459,12 @@ var PlaylistVisualization = ({
6206
4459
  );
6207
4460
  }),
6208
4461
  (recordingState == null ? void 0 : recordingState.isRecording) && recordingState.trackId === track.id && ((_d2 = recordingState.peaks[0]) == null ? void 0 : _d2.length) > 0 && (() => {
6209
- const audioCtx = (0, import_playout7.getGlobalAudioContext)();
6210
- const outputLatency = "outputLatency" in audioCtx ? audioCtx.outputLatency : 0;
6211
- const lookAhead = getLookAhead();
6212
- const latencyOffsetSamples = (0, import_core8.audibleLatencySamples)(
6213
- outputLatency,
6214
- lookAhead,
4462
+ const latencyOffsetSamples = (0, import_core4.resolveRecordingOffsetSamples)({
4463
+ overrideSeconds: recordingState.latencyOffset,
4464
+ outputLatency: getOutputLatency(),
4465
+ lookAhead: getLookAhead(),
6215
4466
  sampleRate
6216
- );
4467
+ });
6217
4468
  const latencyPixels = Math.floor(latencyOffsetSamples / samplesPerPixel);
6218
4469
  const skipPeakElements = latencyPixels * 2;
6219
4470
  const previewDuration = Math.max(
@@ -6223,8 +4474,8 @@ var PlaylistVisualization = ({
6223
4474
  const previewChannels = (mono ? recordingState.peaks.slice(0, 1) : recordingState.peaks).map(
6224
4475
  (channelPeaks) => skipPeakElements > 0 && skipPeakElements < channelPeaks.length ? channelPeaks.subarray(skipPeakElements) : channelPeaks
6225
4476
  );
6226
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6227
- import_ui_components9.Clip,
4477
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4478
+ import_ui_components8.Clip,
6228
4479
  {
6229
4480
  clipId: "recording-preview",
6230
4481
  trackIndex,
@@ -6237,7 +4488,7 @@ var PlaylistVisualization = ({
6237
4488
  disableHeaderDrag: true,
6238
4489
  isSelected: track.id === selectedTrackId,
6239
4490
  trackId: track.id,
6240
- children: previewChannels.map((channelPeaks, chIdx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4491
+ children: previewChannels.map((channelPeaks, chIdx) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6241
4492
  ChannelWithProgress,
6242
4493
  {
6243
4494
  index: chIdx,
@@ -6259,11 +4510,11 @@ var PlaylistVisualization = ({
6259
4510
  track.id
6260
4511
  );
6261
4512
  }),
6262
- annotations.length > 0 && annotationIntegration && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(annotationIntegration.AnnotationBoxesWrapper, { height: 30, width: tracksFullWidth, children: annotations.map((annotation, index) => {
4513
+ annotations.length > 0 && annotationIntegration && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(annotationIntegration.AnnotationBoxesWrapper, { height: 30, width: tracksFullWidth, children: annotations.map((annotation, index) => {
6263
4514
  const startPosition = annotation.start * sampleRate / samplesPerPixel;
6264
4515
  const endPosition = annotation.end * sampleRate / samplesPerPixel;
6265
4516
  const label = getAnnotationBoxLabel ? getAnnotationBoxLabel(annotation, index) : annotation.id;
6266
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4517
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6267
4518
  annotationIntegration.AnnotationBox,
6268
4519
  {
6269
4520
  annotationId: annotation.id,
@@ -6279,15 +4530,15 @@ var PlaylistVisualization = ({
6279
4530
  annotation.id
6280
4531
  );
6281
4532
  }) }),
6282
- selectionStart !== selectionEnd && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6283
- import_ui_components9.Selection,
4533
+ selectionStart !== selectionEnd && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
4534
+ import_ui_components8.Selection,
6284
4535
  {
6285
4536
  startPosition: Math.min(selectionStart, selectionEnd) * sampleRate / samplesPerPixel,
6286
4537
  endPosition: Math.max(selectionStart, selectionEnd) * sampleRate / samplesPerPixel,
6287
4538
  color: theme.selectionColor
6288
4539
  }
6289
4540
  ),
6290
- (isPlaying || selectionStart === selectionEnd) && (renderPlayhead ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4541
+ (isPlaying || selectionStart === selectionEnd) && (renderPlayhead ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6291
4542
  CustomPlayhead,
6292
4543
  {
6293
4544
  renderPlayhead,
@@ -6295,14 +4546,14 @@ var PlaylistVisualization = ({
6295
4546
  samplesPerPixel,
6296
4547
  sampleRate
6297
4548
  }
6298
- ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AnimatedPlayhead, { color: theme.playheadColor }))
4549
+ ) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AnimatedPlayhead, { color: theme.playheadColor }))
6299
4550
  ] })
6300
4551
  }
6301
4552
  )
6302
4553
  }
6303
4554
  ),
6304
4555
  (spectrogram == null ? void 0 : spectrogram.SettingsModal) && typeof document !== "undefined" && (0, import_react_dom.createPortal)(
6305
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
4556
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6306
4557
  spectrogram.SettingsModal,
6307
4558
  {
6308
4559
  open: settingsModalTrackId !== null,
@@ -6322,8 +4573,8 @@ var PlaylistVisualization = ({
6322
4573
  };
6323
4574
 
6324
4575
  // src/components/PlaylistAnnotationList.tsx
6325
- var import_react34 = require("react");
6326
- var import_jsx_runtime11 = require("react/jsx-runtime");
4576
+ var import_react27 = require("react");
4577
+ var import_jsx_runtime10 = require("react/jsx-runtime");
6327
4578
  var PlaylistAnnotationList = ({
6328
4579
  height,
6329
4580
  renderAnnotationItem,
@@ -6337,7 +4588,7 @@ var PlaylistAnnotationList = ({
6337
4588
  const integration = useAnnotationIntegration();
6338
4589
  const { setAnnotations } = usePlaylistControls();
6339
4590
  const resolvedConfig = annotationListConfig != null ? annotationListConfig : { linkEndpoints, continuousPlay };
6340
- const handleAnnotationUpdate = (0, import_react34.useCallback)(
4591
+ const handleAnnotationUpdate = (0, import_react27.useCallback)(
6341
4592
  (updatedAnnotations) => {
6342
4593
  setAnnotations(updatedAnnotations);
6343
4594
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6345,7 +4596,7 @@ var PlaylistAnnotationList = ({
6345
4596
  [setAnnotations, onAnnotationUpdate]
6346
4597
  );
6347
4598
  const { AnnotationText } = integration;
6348
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
4599
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
6349
4600
  AnnotationText,
6350
4601
  {
6351
4602
  annotations,
@@ -6364,7 +4615,7 @@ var PlaylistAnnotationList = ({
6364
4615
  };
6365
4616
 
6366
4617
  // src/components/Waveform.tsx
6367
- var import_jsx_runtime12 = require("react/jsx-runtime");
4618
+ var import_jsx_runtime11 = require("react/jsx-runtime");
6368
4619
  var Waveform = ({
6369
4620
  renderTrackControls,
6370
4621
  renderTick,
@@ -6388,8 +4639,8 @@ var Waveform = ({
6388
4639
  const { annotations } = usePlaylistState();
6389
4640
  const clipInteractionEnabled = useClipInteractionEnabled();
6390
4641
  const effectiveInteractiveClips = interactiveClips || clipInteractionEnabled;
6391
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
6392
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4642
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
4643
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
6393
4644
  PlaylistVisualization,
6394
4645
  {
6395
4646
  renderTrackControls,
@@ -6406,7 +4657,7 @@ var Waveform = ({
6406
4657
  recordingState
6407
4658
  }
6408
4659
  ),
6409
- annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
4660
+ annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
6410
4661
  PlaylistAnnotationList,
6411
4662
  {
6412
4663
  height: annotationTextHeight,
@@ -6421,10 +4672,10 @@ var Waveform = ({
6421
4672
  };
6422
4673
 
6423
4674
  // src/components/MediaElementPlaylist.tsx
6424
- var import_react37 = require("react");
6425
- var import_react38 = require("@dnd-kit/react");
4675
+ var import_react30 = require("react");
4676
+ var import_react31 = require("@dnd-kit/react");
6426
4677
  var import_modifiers = require("@dnd-kit/abstract/modifiers");
6427
- var import_ui_components11 = require("@waveform-playlist/ui-components");
4678
+ var import_ui_components10 = require("@waveform-playlist/ui-components");
6428
4679
 
6429
4680
  // src/plugins/noDropAnimationPlugins.ts
6430
4681
  var import_dom2 = require("@dnd-kit/dom");
@@ -6446,9 +4697,9 @@ var noDropAnimationPlugins = (defaults) => {
6446
4697
  };
6447
4698
 
6448
4699
  // src/components/AnimatedMediaElementPlayhead.tsx
6449
- var import_react35 = require("react");
4700
+ var import_react28 = require("react");
6450
4701
  var import_styled_components7 = __toESM(require("styled-components"));
6451
- var import_jsx_runtime13 = require("react/jsx-runtime");
4702
+ var import_jsx_runtime12 = require("react/jsx-runtime");
6452
4703
  var PlayheadLine2 = import_styled_components7.default.div`
6453
4704
  position: absolute;
6454
4705
  top: 0;
@@ -6463,11 +4714,11 @@ var PlayheadLine2 = import_styled_components7.default.div`
6463
4714
  var AnimatedMediaElementPlayhead = ({
6464
4715
  color = "#ff0000"
6465
4716
  }) => {
6466
- const playheadRef = (0, import_react35.useRef)(null);
6467
- const animationFrameRef = (0, import_react35.useRef)(null);
4717
+ const playheadRef = (0, import_react28.useRef)(null);
4718
+ const animationFrameRef = (0, import_react28.useRef)(null);
6468
4719
  const { isPlaying, currentTimeRef } = useMediaElementAnimation();
6469
4720
  const { samplesPerPixel, sampleRate, progressBarWidth } = useMediaElementData();
6470
- (0, import_react35.useEffect)(() => {
4721
+ (0, import_react28.useEffect)(() => {
6471
4722
  const updatePosition = () => {
6472
4723
  var _a;
6473
4724
  if (playheadRef.current) {
@@ -6491,7 +4742,7 @@ var AnimatedMediaElementPlayhead = ({
6491
4742
  }
6492
4743
  };
6493
4744
  }, [isPlaying, sampleRate, samplesPerPixel, currentTimeRef]);
6494
- (0, import_react35.useEffect)(() => {
4745
+ (0, import_react28.useEffect)(() => {
6495
4746
  var _a;
6496
4747
  if (!isPlaying && playheadRef.current) {
6497
4748
  const time = (_a = currentTimeRef.current) != null ? _a : 0;
@@ -6499,14 +4750,14 @@ var AnimatedMediaElementPlayhead = ({
6499
4750
  playheadRef.current.style.transform = `translate3d(${position}px, 0, 0)`;
6500
4751
  }
6501
4752
  });
6502
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PlayheadLine2, { ref: playheadRef, $color: color, $width: progressBarWidth, "data-playhead": true });
4753
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PlayheadLine2, { ref: playheadRef, $color: color, $width: progressBarWidth, "data-playhead": true });
6503
4754
  };
6504
4755
 
6505
4756
  // src/components/ChannelWithMediaElementProgress.tsx
6506
- var import_react36 = require("react");
4757
+ var import_react29 = require("react");
6507
4758
  var import_styled_components8 = __toESM(require("styled-components"));
6508
- var import_ui_components10 = require("@waveform-playlist/ui-components");
6509
- var import_jsx_runtime14 = require("react/jsx-runtime");
4759
+ var import_ui_components9 = require("@waveform-playlist/ui-components");
4760
+ var import_jsx_runtime13 = require("react/jsx-runtime");
6510
4761
  var ChannelWrapper2 = import_styled_components8.default.div`
6511
4762
  position: relative;
6512
4763
  `;
@@ -6543,14 +4794,14 @@ var ChannelWithMediaElementProgress = (_a) => {
6543
4794
  "clipStartSample",
6544
4795
  "clipDurationSamples"
6545
4796
  ]);
6546
- const progressRef = (0, import_react36.useRef)(null);
6547
- const animationFrameRef = (0, import_react36.useRef)(null);
6548
- const theme = (0, import_ui_components10.useTheme)();
6549
- const { waveHeight } = (0, import_ui_components10.usePlaylistInfo)();
4797
+ const progressRef = (0, import_react29.useRef)(null);
4798
+ const animationFrameRef = (0, import_react29.useRef)(null);
4799
+ const theme = (0, import_ui_components9.useTheme)();
4800
+ const { waveHeight } = (0, import_ui_components9.usePlaylistInfo)();
6550
4801
  const { isPlaying, currentTimeRef } = useMediaElementAnimation();
6551
4802
  const { samplesPerPixel, sampleRate } = useMediaElementData();
6552
4803
  const progressColor = (theme == null ? void 0 : theme.waveProgressColor) || "rgba(0, 0, 0, 0.1)";
6553
- (0, import_react36.useEffect)(() => {
4804
+ (0, import_react29.useEffect)(() => {
6554
4805
  const updateProgress = () => {
6555
4806
  var _a2;
6556
4807
  if (progressRef.current) {
@@ -6592,7 +4843,7 @@ var ChannelWithMediaElementProgress = (_a) => {
6592
4843
  smartChannelProps.length,
6593
4844
  currentTimeRef
6594
4845
  ]);
6595
- (0, import_react36.useEffect)(() => {
4846
+ (0, import_react29.useEffect)(() => {
6596
4847
  var _a2;
6597
4848
  if (!isPlaying && progressRef.current) {
6598
4849
  const currentTime = (_a2 = currentTimeRef.current) != null ? _a2 : 0;
@@ -6617,9 +4868,9 @@ var ChannelWithMediaElementProgress = (_a) => {
6617
4868
  } else {
6618
4869
  backgroundColor = (theme == null ? void 0 : theme.selectedWaveOutlineColor) || (theme == null ? void 0 : theme.waveOutlineColor) || "grey";
6619
4870
  }
6620
- const backgroundCss = (0, import_ui_components10.waveformColorToCss)(backgroundColor);
6621
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(ChannelWrapper2, { children: [
6622
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4871
+ const backgroundCss = (0, import_ui_components9.waveformColorToCss)(backgroundColor);
4872
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(ChannelWrapper2, { children: [
4873
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
6623
4874
  Background2,
6624
4875
  {
6625
4876
  $color: backgroundCss,
@@ -6628,7 +4879,7 @@ var ChannelWithMediaElementProgress = (_a) => {
6628
4879
  $width: smartChannelProps.length
6629
4880
  }
6630
4881
  ),
6631
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4882
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
6632
4883
  ProgressOverlay2,
6633
4884
  {
6634
4885
  ref: progressRef,
@@ -6637,12 +4888,12 @@ var ChannelWithMediaElementProgress = (_a) => {
6637
4888
  $top: smartChannelProps.index * waveHeight
6638
4889
  }
6639
4890
  ),
6640
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChannelContainer2, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ui_components10.SmartChannel, __spreadProps(__spreadValues({}, smartChannelProps), { isSelected: true, transparentBackground: true })) })
4891
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ChannelContainer2, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_ui_components9.SmartChannel, __spreadProps(__spreadValues({}, smartChannelProps), { isSelected: true, transparentBackground: true })) })
6641
4892
  ] });
6642
4893
  };
6643
4894
 
6644
4895
  // src/components/MediaElementPlaylist.tsx
6645
- var import_jsx_runtime15 = require("react/jsx-runtime");
4896
+ var import_jsx_runtime14 = require("react/jsx-runtime");
6646
4897
  var ZERO_REF = { current: 0 };
6647
4898
  var CustomMediaElementPlayhead = ({ renderPlayhead, color, samplesPerPixel, sampleRate }) => {
6648
4899
  var _a;
@@ -6668,10 +4919,10 @@ var MediaElementPlaylist = ({
6668
4919
  showFades = false,
6669
4920
  className
6670
4921
  }) => {
6671
- const theme = (0, import_ui_components11.useTheme)();
4922
+ const theme = (0, import_ui_components10.useTheme)();
6672
4923
  const { isPlaying } = useMediaElementAnimation();
6673
4924
  const { annotations, activeAnnotationId } = useMediaElementState();
6674
- const annotationIntegration = (0, import_react37.useContext)(AnnotationIntegrationContext);
4925
+ const annotationIntegration = (0, import_react30.useContext)(AnnotationIntegrationContext);
6675
4926
  const { play, seekTo, setActiveAnnotationId, setAnnotations, setScrollContainer } = useMediaElementControls();
6676
4927
  const {
6677
4928
  duration,
@@ -6687,11 +4938,11 @@ var MediaElementPlaylist = ({
6687
4938
  fadeIn,
6688
4939
  fadeOut
6689
4940
  } = useMediaElementData();
6690
- const [selectionStart, setSelectionStart] = (0, import_react37.useState)(0);
6691
- const [selectionEnd, setSelectionEnd] = (0, import_react37.useState)(0);
6692
- const [isSelecting, setIsSelecting] = (0, import_react37.useState)(false);
6693
- const scrollContainerRef = (0, import_react37.useRef)(null);
6694
- const handleScrollContainerRef = (0, import_react37.useCallback)(
4941
+ const [selectionStart, setSelectionStart] = (0, import_react30.useState)(0);
4942
+ const [selectionEnd, setSelectionEnd] = (0, import_react30.useState)(0);
4943
+ const [isSelecting, setIsSelecting] = (0, import_react30.useState)(false);
4944
+ const scrollContainerRef = (0, import_react30.useRef)(null);
4945
+ const handleScrollContainerRef = (0, import_react30.useCallback)(
6695
4946
  (el) => {
6696
4947
  scrollContainerRef.current = el;
6697
4948
  setScrollContainer(el);
@@ -6699,7 +4950,7 @@ var MediaElementPlaylist = ({
6699
4950
  [setScrollContainer]
6700
4951
  );
6701
4952
  const tracksFullWidth = Math.floor(duration * sampleRate / samplesPerPixel);
6702
- const handleAnnotationClick = (0, import_react37.useCallback)(
4953
+ const handleAnnotationClick = (0, import_react30.useCallback)(
6703
4954
  (annotation) => __async(null, null, function* () {
6704
4955
  setActiveAnnotationId(annotation.id);
6705
4956
  try {
@@ -6714,7 +4965,7 @@ var MediaElementPlaylist = ({
6714
4965
  }),
6715
4966
  [setActiveAnnotationId, play]
6716
4967
  );
6717
- const handleAnnotationUpdate = (0, import_react37.useCallback)(
4968
+ const handleAnnotationUpdate = (0, import_react30.useCallback)(
6718
4969
  (updatedAnnotations) => {
6719
4970
  setAnnotations(updatedAnnotations);
6720
4971
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6728,8 +4979,8 @@ var MediaElementPlaylist = ({
6728
4979
  duration,
6729
4980
  linkEndpoints: linkEndpointsProp
6730
4981
  });
6731
- const mouseDownTimeRef = (0, import_react37.useRef)(0);
6732
- const handleMouseDown = (0, import_react37.useCallback)(
4982
+ const mouseDownTimeRef = (0, import_react30.useRef)(0);
4983
+ const handleMouseDown = (0, import_react30.useCallback)(
6733
4984
  (e) => {
6734
4985
  const rect = e.currentTarget.getBoundingClientRect();
6735
4986
  const x = e.clientX - rect.left;
@@ -6741,7 +4992,7 @@ var MediaElementPlaylist = ({
6741
4992
  },
6742
4993
  [samplesPerPixel, sampleRate]
6743
4994
  );
6744
- const handleMouseMove = (0, import_react37.useCallback)(
4995
+ const handleMouseMove = (0, import_react30.useCallback)(
6745
4996
  (e) => {
6746
4997
  if (!isSelecting) return;
6747
4998
  const rect = e.currentTarget.getBoundingClientRect();
@@ -6751,7 +5002,7 @@ var MediaElementPlaylist = ({
6751
5002
  },
6752
5003
  [isSelecting, samplesPerPixel, sampleRate]
6753
5004
  );
6754
- const handleMouseUp = (0, import_react37.useCallback)(
5005
+ const handleMouseUp = (0, import_react30.useCallback)(
6755
5006
  (e) => {
6756
5007
  if (!isSelecting) return;
6757
5008
  setIsSelecting(false);
@@ -6781,10 +5032,10 @@ var MediaElementPlaylist = ({
6781
5032
  [isSelecting, selectionStart, samplesPerPixel, sampleRate, seekTo, isPlaying, playoutRef, play]
6782
5033
  );
6783
5034
  if (peaksDataArray.length === 0) {
6784
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className, children: "Loading waveform..." });
5035
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className, children: "Loading waveform..." });
6785
5036
  }
6786
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ui_components11.DevicePixelRatioProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6787
- import_ui_components11.PlaylistInfoContext.Provider,
5037
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ui_components10.DevicePixelRatioProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5038
+ import_ui_components10.PlaylistInfoContext.Provider,
6788
5039
  {
6789
5040
  value: {
6790
5041
  samplesPerPixel,
@@ -6797,11 +5048,11 @@ var MediaElementPlaylist = ({
6797
5048
  barWidth,
6798
5049
  barGap
6799
5050
  },
6800
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6801
- import_ui_components11.Playlist,
5051
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5052
+ import_ui_components10.Playlist,
6802
5053
  {
6803
5054
  theme,
6804
- backgroundColor: (0, import_ui_components11.waveformColorToCss)(theme.waveOutlineColor),
5055
+ backgroundColor: (0, import_ui_components10.waveformColorToCss)(theme.waveOutlineColor),
6805
5056
  timescaleBackgroundColor: theme.timescaleBackgroundColor,
6806
5057
  timescaleWidth: tracksFullWidth,
6807
5058
  tracksWidth: tracksFullWidth,
@@ -6811,15 +5062,15 @@ var MediaElementPlaylist = ({
6811
5062
  onTracksMouseUp: handleMouseUp,
6812
5063
  scrollContainerRef: handleScrollContainerRef,
6813
5064
  isSelecting,
6814
- timescale: timeScaleHeight > 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_ui_components11.SmartScale, {}) : void 0,
6815
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
5065
+ timescale: timeScaleHeight > 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_ui_components10.SmartScale, {}) : void 0,
5066
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
6816
5067
  peaksDataArray.map((trackClipPeaks, trackIndex) => {
6817
5068
  const maxChannels = trackClipPeaks.length > 0 ? Math.max(...trackClipPeaks.map((clip) => clip.peaks.data.length)) : 1;
6818
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6819
- import_ui_components11.Track,
5069
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5070
+ import_ui_components10.Track,
6820
5071
  {
6821
5072
  numChannels: maxChannels,
6822
- backgroundColor: (0, import_ui_components11.waveformColorToCss)(theme.waveOutlineColor),
5073
+ backgroundColor: (0, import_ui_components10.waveformColorToCss)(theme.waveOutlineColor),
6823
5074
  offset: 0,
6824
5075
  width: tracksFullWidth,
6825
5076
  hasClipHeaders: false,
@@ -6828,8 +5079,8 @@ var MediaElementPlaylist = ({
6828
5079
  children: trackClipPeaks.map((clip, clipIndex) => {
6829
5080
  const peaksData = clip.peaks;
6830
5081
  const width = peaksData.length;
6831
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
6832
- import_ui_components11.Clip,
5082
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
5083
+ import_ui_components10.Clip,
6833
5084
  {
6834
5085
  clipId: clip.clipId,
6835
5086
  trackIndex,
@@ -6843,7 +5094,7 @@ var MediaElementPlaylist = ({
6843
5094
  isSelected: true,
6844
5095
  trackId: `media-element-track-${trackIndex}`,
6845
5096
  children: [
6846
- peaksData.data.map((channelPeaks, channelIndex) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5097
+ peaksData.data.map((channelPeaks, channelIndex) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6847
5098
  ChannelWithMediaElementProgress,
6848
5099
  {
6849
5100
  index: channelIndex,
@@ -6855,8 +5106,8 @@ var MediaElementPlaylist = ({
6855
5106
  },
6856
5107
  `${trackIndex}-${clipIndex}-${channelIndex}`
6857
5108
  )),
6858
- showFades && fadeIn && fadeIn.duration > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6859
- import_ui_components11.FadeOverlay,
5109
+ showFades && fadeIn && fadeIn.duration > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5110
+ import_ui_components10.FadeOverlay,
6860
5111
  {
6861
5112
  left: 0,
6862
5113
  width: Math.floor(fadeIn.duration * sampleRate / samplesPerPixel),
@@ -6864,8 +5115,8 @@ var MediaElementPlaylist = ({
6864
5115
  curveType: fadeIn.type
6865
5116
  }
6866
5117
  ),
6867
- showFades && fadeOut && fadeOut.duration > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6868
- import_ui_components11.FadeOverlay,
5118
+ showFades && fadeOut && fadeOut.duration > 0 && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5119
+ import_ui_components10.FadeOverlay,
6869
5120
  {
6870
5121
  left: width - Math.floor(fadeOut.duration * sampleRate / samplesPerPixel),
6871
5122
  width: Math.floor(fadeOut.duration * sampleRate / samplesPerPixel),
@@ -6882,19 +5133,19 @@ var MediaElementPlaylist = ({
6882
5133
  trackIndex
6883
5134
  );
6884
5135
  }),
6885
- annotations.length > 0 && annotationIntegration && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6886
- import_react38.DragDropProvider,
5136
+ annotations.length > 0 && annotationIntegration && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5137
+ import_react31.DragDropProvider,
6887
5138
  {
6888
5139
  onDragStart,
6889
5140
  onDragMove,
6890
5141
  onDragEnd,
6891
5142
  modifiers: editable ? [import_modifiers.RestrictToHorizontalAxis] : [],
6892
5143
  plugins: noDropAnimationPlugins,
6893
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(annotationIntegration.AnnotationBoxesWrapper, { height: 30, width: tracksFullWidth, children: annotations.map((annotation, index) => {
5144
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(annotationIntegration.AnnotationBoxesWrapper, { height: 30, width: tracksFullWidth, children: annotations.map((annotation, index) => {
6894
5145
  const startPosition = annotation.start * sampleRate / samplesPerPixel;
6895
5146
  const endPosition = annotation.end * sampleRate / samplesPerPixel;
6896
5147
  const label = getAnnotationBoxLabel ? getAnnotationBoxLabel(annotation, index) : annotation.id;
6897
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5148
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6898
5149
  annotationIntegration.AnnotationBox,
6899
5150
  {
6900
5151
  annotationId: annotation.id,
@@ -6912,15 +5163,15 @@ var MediaElementPlaylist = ({
6912
5163
  }) })
6913
5164
  }
6914
5165
  ),
6915
- selectionStart !== selectionEnd && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6916
- import_ui_components11.Selection,
5166
+ selectionStart !== selectionEnd && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5167
+ import_ui_components10.Selection,
6917
5168
  {
6918
5169
  startPosition: Math.min(selectionStart, selectionEnd) * sampleRate / samplesPerPixel,
6919
5170
  endPosition: Math.max(selectionStart, selectionEnd) * sampleRate / samplesPerPixel,
6920
5171
  color: theme.selectionColor
6921
5172
  }
6922
5173
  ),
6923
- renderPlayhead ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5174
+ renderPlayhead ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
6924
5175
  CustomMediaElementPlayhead,
6925
5176
  {
6926
5177
  renderPlayhead,
@@ -6928,7 +5179,7 @@ var MediaElementPlaylist = ({
6928
5179
  samplesPerPixel,
6929
5180
  sampleRate
6930
5181
  }
6931
- ) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(AnimatedMediaElementPlayhead, { color: theme.playheadColor })
5182
+ ) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AnimatedMediaElementPlayhead, { color: theme.playheadColor })
6932
5183
  ] })
6933
5184
  }
6934
5185
  )
@@ -6937,8 +5188,8 @@ var MediaElementPlaylist = ({
6937
5188
  };
6938
5189
 
6939
5190
  // src/components/MediaElementAnnotationList.tsx
6940
- var import_react39 = require("react");
6941
- var import_jsx_runtime16 = require("react/jsx-runtime");
5191
+ var import_react32 = require("react");
5192
+ var import_jsx_runtime15 = require("react/jsx-runtime");
6942
5193
  var MediaElementAnnotationList = ({
6943
5194
  height,
6944
5195
  renderAnnotationItem,
@@ -6953,7 +5204,7 @@ var MediaElementAnnotationList = ({
6953
5204
  const integration = useAnnotationIntegration();
6954
5205
  const { setAnnotations } = useMediaElementControls();
6955
5206
  const resolvedConfig = annotationListConfig != null ? annotationListConfig : { linkEndpoints: false, continuousPlay };
6956
- const handleAnnotationUpdate = (0, import_react39.useCallback)(
5207
+ const handleAnnotationUpdate = (0, import_react32.useCallback)(
6957
5208
  (updatedAnnotations) => {
6958
5209
  setAnnotations(updatedAnnotations);
6959
5210
  onAnnotationUpdate == null ? void 0 : onAnnotationUpdate(updatedAnnotations);
@@ -6961,7 +5212,7 @@ var MediaElementAnnotationList = ({
6961
5212
  [setAnnotations, onAnnotationUpdate]
6962
5213
  );
6963
5214
  const { AnnotationText } = integration;
6964
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5215
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
6965
5216
  AnnotationText,
6966
5217
  {
6967
5218
  annotations,
@@ -6980,7 +5231,7 @@ var MediaElementAnnotationList = ({
6980
5231
  };
6981
5232
 
6982
5233
  // src/components/MediaElementWaveform.tsx
6983
- var import_jsx_runtime17 = require("react/jsx-runtime");
5234
+ var import_jsx_runtime16 = require("react/jsx-runtime");
6984
5235
  var MediaElementWaveform = ({
6985
5236
  annotationTextHeight,
6986
5237
  getAnnotationBoxLabel,
@@ -6995,8 +5246,8 @@ var MediaElementWaveform = ({
6995
5246
  className
6996
5247
  }) => {
6997
5248
  const { annotations } = useMediaElementState();
6998
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
6999
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5249
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
5250
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7000
5251
  MediaElementPlaylist,
7001
5252
  {
7002
5253
  getAnnotationBoxLabel,
@@ -7008,7 +5259,7 @@ var MediaElementWaveform = ({
7008
5259
  className
7009
5260
  }
7010
5261
  ),
7011
- annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5262
+ annotations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7012
5263
  MediaElementAnnotationList,
7013
5264
  {
7014
5265
  height: annotationTextHeight,
@@ -7083,11 +5334,11 @@ var KeyboardShortcuts = ({
7083
5334
  };
7084
5335
 
7085
5336
  // src/components/ClipInteractionProvider.tsx
7086
- var import_react40 = __toESM(require("react"));
7087
- var import_react41 = require("@dnd-kit/react");
5337
+ var import_react33 = __toESM(require("react"));
5338
+ var import_react34 = require("@dnd-kit/react");
7088
5339
  var import_modifiers2 = require("@dnd-kit/abstract/modifiers");
7089
- var import_core10 = require("@waveform-playlist/core");
7090
- var import_ui_components12 = require("@waveform-playlist/ui-components");
5340
+ var import_core6 = require("@waveform-playlist/core");
5341
+ var import_ui_components11 = require("@waveform-playlist/ui-components");
7091
5342
 
7092
5343
  // src/modifiers/ClipCollisionModifier.ts
7093
5344
  var import_abstract = require("@dnd-kit/abstract");
@@ -7115,7 +5366,7 @@ var ClipCollisionModifier = _ClipCollisionModifier;
7115
5366
 
7116
5367
  // src/modifiers/SnapToGridModifier.ts
7117
5368
  var import_abstract2 = require("@dnd-kit/abstract");
7118
- var import_core9 = require("@waveform-playlist/core");
5369
+ var import_core5 = require("@waveform-playlist/core");
7119
5370
  var _SnapToGridModifier = class _SnapToGridModifier extends import_abstract2.Modifier {
7120
5371
  apply(operation) {
7121
5372
  const { transform, source } = operation;
@@ -7136,18 +5387,18 @@ var _SnapToGridModifier = class _SnapToGridModifier extends import_abstract2.Mod
7136
5387
  }
7137
5388
  const { snapTo, bpm, timeSignature, sampleRate } = this.options;
7138
5389
  if (snapTo === "off") return transform;
7139
- const gridTicks = snapTo === "bar" ? (0, import_core9.ticksPerBar)(timeSignature) : (0, import_core9.ticksPerBeat)(timeSignature);
5390
+ const gridTicks = snapTo === "bar" ? (0, import_core5.ticksPerBar)(timeSignature) : (0, import_core5.ticksPerBeat)(timeSignature);
7140
5391
  if (startSample !== void 0) {
7141
5392
  const proposedSamples = startSample + transform.x * samplesPerPixel;
7142
- const proposedTicks = (0, import_core9.samplesToTicks)(proposedSamples, bpm, sampleRate);
7143
- const snappedTicks2 = (0, import_core9.snapToGrid)(proposedTicks, gridTicks);
7144
- const snappedSamples2 = (0, import_core9.ticksToSamples)(snappedTicks2, bpm, sampleRate);
5393
+ const proposedTicks = (0, import_core5.samplesToTicks)(proposedSamples, bpm, sampleRate);
5394
+ const snappedTicks2 = (0, import_core5.snapToGrid)(proposedTicks, gridTicks);
5395
+ const snappedSamples2 = (0, import_core5.ticksToSamples)(snappedTicks2, bpm, sampleRate);
7145
5396
  return { x: (snappedSamples2 - startSample) / samplesPerPixel, y: 0 };
7146
5397
  }
7147
5398
  const deltaSamples = transform.x * samplesPerPixel;
7148
- const deltaTicks = (0, import_core9.samplesToTicks)(deltaSamples, bpm, sampleRate);
7149
- const snappedTicks = (0, import_core9.snapToGrid)(deltaTicks, gridTicks);
7150
- const snappedSamples = (0, import_core9.ticksToSamples)(snappedTicks, bpm, sampleRate);
5399
+ const deltaTicks = (0, import_core5.samplesToTicks)(deltaSamples, bpm, sampleRate);
5400
+ const snappedTicks = (0, import_core5.snapToGrid)(deltaTicks, gridTicks);
5401
+ const snappedSamples = (0, import_core5.ticksToSamples)(snappedTicks, bpm, sampleRate);
7151
5402
  return { x: snappedSamples / samplesPerPixel, y: 0 };
7152
5403
  }
7153
5404
  };
@@ -7155,7 +5406,7 @@ _SnapToGridModifier.configure = (0, import_abstract2.configurator)(_SnapToGridMo
7155
5406
  var SnapToGridModifier = _SnapToGridModifier;
7156
5407
 
7157
5408
  // src/components/ClipInteractionProvider.tsx
7158
- var import_jsx_runtime18 = require("react/jsx-runtime");
5409
+ var import_jsx_runtime17 = require("react/jsx-runtime");
7159
5410
  var NOOP_TRACKS_CHANGE = () => {
7160
5411
  };
7161
5412
  var ClipInteractionProvider = ({
@@ -7165,28 +5416,28 @@ var ClipInteractionProvider = ({
7165
5416
  }) => {
7166
5417
  const { tracks, samplesPerPixel, sampleRate, playoutRef, isDraggingRef, onTracksChange } = usePlaylistData();
7167
5418
  const { setSelectedTrackId } = usePlaylistControls();
7168
- const beatsAndBars = (0, import_ui_components12.useBeatsAndBars)();
5419
+ const beatsAndBars = (0, import_ui_components11.useBeatsAndBars)();
7169
5420
  const useBeatsSnap = snap && beatsAndBars != null && beatsAndBars.scaleMode === "beats" && beatsAndBars.snapTo !== "off";
7170
5421
  const useTimescaleSnap = snap && !useBeatsSnap;
7171
- (0, import_react40.useEffect)(() => {
5422
+ (0, import_react33.useEffect)(() => {
7172
5423
  if (onTracksChange == null) {
7173
5424
  console.warn(
7174
5425
  "[waveform-playlist] ClipInteractionProvider: onTracksChange is not set on WaveformPlaylistProvider. Drag and trim edits will not be persisted."
7175
5426
  );
7176
5427
  }
7177
5428
  }, [onTracksChange]);
7178
- const snapSamplePosition = (0, import_react40.useMemo)(() => {
5429
+ const snapSamplePosition = (0, import_react33.useMemo)(() => {
7179
5430
  if (useBeatsSnap && beatsAndBars) {
7180
5431
  const { bpm, timeSignature, snapTo } = beatsAndBars;
7181
- const gridTicks = snapTo === "bar" ? (0, import_core10.ticksPerBar)(timeSignature) : (0, import_core10.ticksPerBeat)(timeSignature);
5432
+ const gridTicks = snapTo === "bar" ? (0, import_core6.ticksPerBar)(timeSignature) : (0, import_core6.ticksPerBeat)(timeSignature);
7182
5433
  return (samplePos) => {
7183
- const ticks = (0, import_core10.samplesToTicks)(samplePos, bpm, sampleRate);
7184
- const snapped = (0, import_core10.snapToGrid)(ticks, gridTicks);
7185
- return (0, import_core10.ticksToSamples)(snapped, bpm, sampleRate);
5434
+ const ticks = (0, import_core6.samplesToTicks)(samplePos, bpm, sampleRate);
5435
+ const snapped = (0, import_core6.snapToGrid)(ticks, gridTicks);
5436
+ return (0, import_core6.ticksToSamples)(snapped, bpm, sampleRate);
7186
5437
  };
7187
5438
  }
7188
5439
  if (useTimescaleSnap) {
7189
- const gridSamples = Math.round((0, import_ui_components12.getScaleInfo)(samplesPerPixel).smallStep / 1e3 * sampleRate);
5440
+ const gridSamples = Math.round((0, import_ui_components11.getScaleInfo)(samplesPerPixel).smallStep / 1e3 * sampleRate);
7190
5441
  return (samplePos) => Math.round(samplePos / gridSamples) * gridSamples;
7191
5442
  }
7192
5443
  return void 0;
@@ -7204,7 +5455,7 @@ var ClipInteractionProvider = ({
7204
5455
  isDraggingRef,
7205
5456
  snapSamplePosition
7206
5457
  });
7207
- const onDragStart = import_react40.default.useCallback(
5458
+ const onDragStart = import_react33.default.useCallback(
7208
5459
  (event) => {
7209
5460
  var _a, _b, _c;
7210
5461
  const trackIndex = (_c = (_b = (_a = event.operation) == null ? void 0 : _a.source) == null ? void 0 : _b.data) == null ? void 0 : _c.trackIndex;
@@ -7215,7 +5466,7 @@ var ClipInteractionProvider = ({
7215
5466
  },
7216
5467
  [handleDragStart, tracks, setSelectedTrackId]
7217
5468
  );
7218
- const modifiers = (0, import_react40.useMemo)(() => {
5469
+ const modifiers = (0, import_react33.useMemo)(() => {
7219
5470
  const mods = [import_modifiers2.RestrictToHorizontalAxis];
7220
5471
  if (useBeatsSnap && beatsAndBars) {
7221
5472
  mods.push(
@@ -7232,7 +5483,7 @@ var ClipInteractionProvider = ({
7232
5483
  mods.push(
7233
5484
  SnapToGridModifier.configure({
7234
5485
  mode: "timescale",
7235
- gridSamples: Math.round((0, import_ui_components12.getScaleInfo)(samplesPerPixel).smallStep / 1e3 * sampleRate),
5486
+ gridSamples: Math.round((0, import_ui_components11.getScaleInfo)(samplesPerPixel).smallStep / 1e3 * sampleRate),
7236
5487
  samplesPerPixel
7237
5488
  })
7238
5489
  );
@@ -7240,8 +5491,8 @@ var ClipInteractionProvider = ({
7240
5491
  mods.push(ClipCollisionModifier.configure({ tracks, samplesPerPixel }));
7241
5492
  return mods;
7242
5493
  }, [useBeatsSnap, useTimescaleSnap, beatsAndBars, tracks, samplesPerPixel, sampleRate]);
7243
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ClipInteractionContextProvider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
7244
- import_react41.DragDropProvider,
5494
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ClipInteractionContextProvider, { value: true, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5495
+ import_react34.DragDropProvider,
7245
5496
  {
7246
5497
  sensors,
7247
5498
  onDragStart,
@@ -7264,7 +5515,6 @@ var ClipInteractionProvider = ({
7264
5515
  ContinuousPlayCheckbox,
7265
5516
  DownloadAnnotationsButton,
7266
5517
  EditableCheckbox,
7267
- ExportWavButton,
7268
5518
  FastForwardButton,
7269
5519
  KeyboardShortcuts,
7270
5520
  LinkEndpointsCheckbox,
@@ -7287,17 +5537,10 @@ var ClipInteractionProvider = ({
7287
5537
  SpectrogramIntegrationProvider,
7288
5538
  StopButton,
7289
5539
  TimeFormatSelect,
7290
- Tone,
7291
5540
  Waveform,
7292
5541
  WaveformPlaylistProvider,
7293
5542
  ZoomInButton,
7294
5543
  ZoomOutButton,
7295
- createEffectChain,
7296
- createEffectInstance,
7297
- effectCategories,
7298
- effectDefinitions,
7299
- getEffectDefinition,
7300
- getEffectsByCategory,
7301
5544
  getShortcutLabel,
7302
5545
  getWaveformDataMetadata,
7303
5546
  loadPeaksFromWaveformData,
@@ -7306,22 +5549,16 @@ var ClipInteractionProvider = ({
7306
5549
  useAnnotationDragHandlers,
7307
5550
  useAnnotationIntegration,
7308
5551
  useAnnotationKeyboardControls,
7309
- useAudioTracks,
7310
5552
  useClipDragHandlers,
7311
5553
  useClipInteractionEnabled,
7312
5554
  useClipSplitting,
7313
5555
  useDragSensors,
7314
- useDynamicEffects,
7315
- useDynamicTracks,
7316
- useExportWav,
7317
5556
  useKeyboardShortcuts,
7318
- useMasterAnalyser,
7319
5557
  useMasterVolume,
7320
5558
  useMediaElementAnimation,
7321
5559
  useMediaElementControls,
7322
5560
  useMediaElementData,
7323
5561
  useMediaElementState,
7324
- useOutputMeter,
7325
5562
  usePlaybackAnimation,
7326
5563
  usePlaybackShortcuts,
7327
5564
  usePlaylistControls,
@@ -7330,7 +5567,6 @@ var ClipInteractionProvider = ({
7330
5567
  usePlaylistState,
7331
5568
  useSpectrogramIntegration,
7332
5569
  useTimeFormat,
7333
- useTrackDynamicEffects,
7334
5570
  useZoomControls,
7335
5571
  waveformDataToPeaks
7336
5572
  });