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