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