@waveform-playlist/browser 15.0.0 → 15.1.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/tone.d.mts +156 -82
- package/dist/tone.d.ts +156 -82
- package/dist/tone.js +450 -90
- package/dist/tone.js.map +1 -1
- package/dist/tone.mjs +447 -89
- package/dist/tone.mjs.map +1 -1
- package/package.json +15 -9
package/dist/tone.mjs
CHANGED
|
@@ -258,6 +258,10 @@ var useMasterAnalyser = (fftSize = 256) => {
|
|
|
258
258
|
|
|
259
259
|
// src/hooks/useDynamicEffects.ts
|
|
260
260
|
import { useState as useState2, useCallback as useCallback2, useRef as useRef3, useEffect as useEffect2 } from "react";
|
|
261
|
+
import {
|
|
262
|
+
isNativeGlobalContext,
|
|
263
|
+
getGlobalAudioContext
|
|
264
|
+
} from "@waveform-playlist/playout";
|
|
261
265
|
|
|
262
266
|
// src/effects/effectDefinitions.ts
|
|
263
267
|
var effectDefinitions = [
|
|
@@ -826,7 +830,8 @@ import {
|
|
|
826
830
|
Compressor,
|
|
827
831
|
Limiter,
|
|
828
832
|
Gate,
|
|
829
|
-
StereoWidener
|
|
833
|
+
StereoWidener,
|
|
834
|
+
connect
|
|
830
835
|
} from "tone";
|
|
831
836
|
function asEffectConstructor(ctor) {
|
|
832
837
|
return ctor;
|
|
@@ -881,8 +886,7 @@ function createEffectInstance(definition, initialParams) {
|
|
|
881
886
|
effect.dispose();
|
|
882
887
|
} catch (e) {
|
|
883
888
|
console.warn(
|
|
884
|
-
`[waveform-playlist] Error disposing effect "${definition.id}" (${instanceId})
|
|
885
|
-
e
|
|
889
|
+
`[waveform-playlist] Error disposing effect "${definition.id}" (${instanceId}): ${e instanceof Error ? e.message : String(e)}`
|
|
886
890
|
);
|
|
887
891
|
}
|
|
888
892
|
},
|
|
@@ -927,8 +931,7 @@ function createEffectInstance(definition, initialParams) {
|
|
|
927
931
|
effect.disconnect();
|
|
928
932
|
} catch (e) {
|
|
929
933
|
console.warn(
|
|
930
|
-
`[waveform-playlist] Error disconnecting effect "${definition.id}" (${instanceId})
|
|
931
|
-
e
|
|
934
|
+
`[waveform-playlist] Error disconnecting effect "${definition.id}" (${instanceId}): ${e instanceof Error ? e.message : String(e)}`
|
|
932
935
|
);
|
|
933
936
|
}
|
|
934
937
|
}
|
|
@@ -939,7 +942,7 @@ function createEffectChain(effects) {
|
|
|
939
942
|
throw new Error("Cannot create effect chain with no effects");
|
|
940
943
|
}
|
|
941
944
|
for (let i = 0; i < effects.length - 1; i++) {
|
|
942
|
-
effects[i].effect
|
|
945
|
+
connect(effects[i].effect, effects[i + 1].effect);
|
|
943
946
|
}
|
|
944
947
|
return {
|
|
945
948
|
input: effects[0].effect,
|
|
@@ -950,13 +953,105 @@ function createEffectChain(effects) {
|
|
|
950
953
|
};
|
|
951
954
|
}
|
|
952
955
|
|
|
956
|
+
// src/effects/loadWam.ts
|
|
957
|
+
function loadWamModule() {
|
|
958
|
+
return __async(this, null, function* () {
|
|
959
|
+
try {
|
|
960
|
+
return yield import("@dawcore/wam");
|
|
961
|
+
} catch (err) {
|
|
962
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
963
|
+
throw new Error(
|
|
964
|
+
"[waveform-playlist] WAM plugin support requires the optional '@dawcore/wam' package.\nInstall it with: npm install @dawcore/wam\nOriginal error: " + detail
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// src/effects/wamEffectFactory.ts
|
|
971
|
+
import { connect as connect2, disconnect } from "tone";
|
|
972
|
+
var wamInstanceCounter = 0;
|
|
973
|
+
function createWamEffectInstance(plugin) {
|
|
974
|
+
var _a, _b, _c;
|
|
975
|
+
const node = plugin.audioNode;
|
|
976
|
+
const instanceId = "wam_" + ++wamInstanceCounter;
|
|
977
|
+
return {
|
|
978
|
+
kind: "wam",
|
|
979
|
+
plugin,
|
|
980
|
+
url: plugin.url,
|
|
981
|
+
effect: node,
|
|
982
|
+
id: "wam:" + ((_c = (_b = (_a = plugin.descriptor) == null ? void 0 : _a.name) != null ? _b : plugin.url) != null ? _c : "plugin"),
|
|
983
|
+
instanceId,
|
|
984
|
+
dispose: () => {
|
|
985
|
+
try {
|
|
986
|
+
plugin.destroy();
|
|
987
|
+
} catch (err) {
|
|
988
|
+
console.warn(
|
|
989
|
+
"[waveform-playlist] Error destroying WAM plugin: " + (err instanceof Error ? err.message : String(err))
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
},
|
|
993
|
+
setParameter: (name, value) => {
|
|
994
|
+
var _a2;
|
|
995
|
+
if (typeof value !== "number") return;
|
|
996
|
+
const target = plugin.audioNode;
|
|
997
|
+
void ((_a2 = target.setParameterValues) == null ? void 0 : _a2.call(target, { [name]: { id: name, value, normalized: false } }));
|
|
998
|
+
},
|
|
999
|
+
getParameter: () => void 0,
|
|
1000
|
+
// WAM params are async; read via the plugin GUI
|
|
1001
|
+
connect: (destination) => connect2(node, destination),
|
|
1002
|
+
disconnect: () => disconnect(node)
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// src/effects/offlineChain.ts
|
|
1007
|
+
import { connect as connect3 } from "tone";
|
|
1008
|
+
function buildOfflineChain(entries, getLivePlugin, rawContext) {
|
|
1009
|
+
return __async(this, null, function* () {
|
|
1010
|
+
const instances = [];
|
|
1011
|
+
const dispose = () => {
|
|
1012
|
+
instances.forEach((inst) => inst.dispose());
|
|
1013
|
+
};
|
|
1014
|
+
try {
|
|
1015
|
+
for (const entry of entries) {
|
|
1016
|
+
if (entry.kind === "native") {
|
|
1017
|
+
instances.push(createEffectInstance(entry.definition, entry.params));
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
const livePlugin = getLivePlugin(entry.instanceId);
|
|
1021
|
+
if (!livePlugin) {
|
|
1022
|
+
throw new Error(
|
|
1023
|
+
'[waveform-playlist] WAV export: no live WAM plugin found for "' + entry.definition.name + '" (' + entry.instanceId + ") \u2014 cannot re-instantiate it offline."
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
const wam = yield loadWamModule();
|
|
1027
|
+
const { hostGroupId } = yield wam.ensureWamHost(rawContext);
|
|
1028
|
+
const clone = yield wam.cloneInstanceInto(livePlugin, rawContext, hostGroupId);
|
|
1029
|
+
instances.push(createWamEffectInstance(clone));
|
|
1030
|
+
}
|
|
1031
|
+
} catch (err) {
|
|
1032
|
+
dispose();
|
|
1033
|
+
throw err;
|
|
1034
|
+
}
|
|
1035
|
+
return { instances, dispose };
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
function connectOfflineChain(from, instances, to) {
|
|
1039
|
+
let current = from;
|
|
1040
|
+
for (const inst of instances) {
|
|
1041
|
+
connect3(current, inst.effect);
|
|
1042
|
+
current = inst.effect;
|
|
1043
|
+
}
|
|
1044
|
+
connect3(current, to);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
953
1047
|
// src/hooks/useDynamicEffects.ts
|
|
954
|
-
import { Analyser as Analyser2 } from "tone";
|
|
1048
|
+
import { Analyser as Analyser2, connect as connect4 } from "tone";
|
|
955
1049
|
function useDynamicEffects(fftSize = 256) {
|
|
956
1050
|
const [activeEffects, setActiveEffects] = useState2([]);
|
|
957
1051
|
const activeEffectsRef = useRef3(activeEffects);
|
|
958
1052
|
activeEffectsRef.current = activeEffects;
|
|
959
1053
|
const effectInstancesRef = useRef3(/* @__PURE__ */ new Map());
|
|
1054
|
+
const isMountedRef = useRef3(true);
|
|
960
1055
|
const analyserRef = useRef3(null);
|
|
961
1056
|
const graphNodesRef = useRef3(null);
|
|
962
1057
|
const rebuildChain = useCallback2((effects) => {
|
|
@@ -968,7 +1063,8 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
968
1063
|
} catch (e) {
|
|
969
1064
|
console.warn("[waveform-playlist] Error disconnecting master effects chain:", e);
|
|
970
1065
|
}
|
|
971
|
-
const
|
|
1066
|
+
const audible = effects.filter((ae) => !(ae.kind === "wam" && ae.bypassed));
|
|
1067
|
+
const instances = audible.map((ae) => effectInstancesRef.current.get(ae.instanceId)).filter((inst) => inst !== void 0);
|
|
972
1068
|
if (instances.length === 0) {
|
|
973
1069
|
masterGainNode.connect(analyserNode);
|
|
974
1070
|
analyserNode.connect(destination);
|
|
@@ -980,10 +1076,10 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
980
1076
|
} catch (e) {
|
|
981
1077
|
console.warn(`[waveform-playlist] Error disconnecting effect "${inst.id}":`, e);
|
|
982
1078
|
}
|
|
983
|
-
currentNode
|
|
1079
|
+
connect4(currentNode, inst.effect);
|
|
984
1080
|
currentNode = inst.effect;
|
|
985
1081
|
});
|
|
986
|
-
currentNode
|
|
1082
|
+
connect4(currentNode, analyserNode);
|
|
987
1083
|
analyserNode.connect(destination);
|
|
988
1084
|
}
|
|
989
1085
|
}, []);
|
|
@@ -1002,12 +1098,68 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1002
1098
|
const newActiveEffect = {
|
|
1003
1099
|
instanceId: instance.instanceId,
|
|
1004
1100
|
effectId: definition.id,
|
|
1101
|
+
kind: "native",
|
|
1005
1102
|
definition,
|
|
1006
1103
|
params,
|
|
1007
1104
|
bypassed: false
|
|
1008
1105
|
};
|
|
1009
1106
|
setActiveEffects((prev) => [...prev, newActiveEffect]);
|
|
1010
1107
|
}, []);
|
|
1108
|
+
const addWamEffect = useCallback2((url, initialState) => __async(null, null, function* () {
|
|
1109
|
+
var _a, _b;
|
|
1110
|
+
if (!isNativeGlobalContext()) {
|
|
1111
|
+
throw new Error(
|
|
1112
|
+
"[waveform-playlist] WAM plugins require a native AudioContext. Call configureGlobalContext({ nativeAudioContext: true }) from @waveform-playlist/playout before any audio initialization."
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
const wam = yield loadWamModule();
|
|
1116
|
+
const ctx = getGlobalAudioContext();
|
|
1117
|
+
const { hostGroupId } = yield wam.ensureWamHost(ctx);
|
|
1118
|
+
const plugin = yield wam.createWamInstance(
|
|
1119
|
+
url,
|
|
1120
|
+
ctx,
|
|
1121
|
+
hostGroupId,
|
|
1122
|
+
initialState !== void 0 ? { initialState } : void 0
|
|
1123
|
+
);
|
|
1124
|
+
if (!isMountedRef.current) {
|
|
1125
|
+
try {
|
|
1126
|
+
plugin.destroy();
|
|
1127
|
+
} catch (err) {
|
|
1128
|
+
console.warn(
|
|
1129
|
+
"[waveform-playlist] Error destroying WAM plugin after unmount: " + (err instanceof Error ? err.message : String(err))
|
|
1130
|
+
);
|
|
1131
|
+
}
|
|
1132
|
+
throw new Error(
|
|
1133
|
+
"[waveform-playlist] addWamEffect aborted: hook unmounted before the plugin finished loading."
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
const instance = createWamEffectInstance(plugin);
|
|
1137
|
+
effectInstancesRef.current.set(instance.instanceId, instance);
|
|
1138
|
+
const definition = {
|
|
1139
|
+
id: instance.id,
|
|
1140
|
+
name: (_b = (_a = plugin.descriptor) == null ? void 0 : _a.name) != null ? _b : url,
|
|
1141
|
+
category: "wam",
|
|
1142
|
+
description: "WAM plugin",
|
|
1143
|
+
parameters: []
|
|
1144
|
+
};
|
|
1145
|
+
setActiveEffects((prev) => [
|
|
1146
|
+
...prev,
|
|
1147
|
+
{
|
|
1148
|
+
instanceId: instance.instanceId,
|
|
1149
|
+
effectId: instance.id,
|
|
1150
|
+
kind: "wam",
|
|
1151
|
+
url,
|
|
1152
|
+
definition,
|
|
1153
|
+
params: {},
|
|
1154
|
+
bypassed: false
|
|
1155
|
+
}
|
|
1156
|
+
]);
|
|
1157
|
+
return instance.instanceId;
|
|
1158
|
+
}), []);
|
|
1159
|
+
const getWamPlugin = useCallback2((instanceId) => {
|
|
1160
|
+
const inst = effectInstancesRef.current.get(instanceId);
|
|
1161
|
+
return (inst == null ? void 0 : inst.kind) === "wam" ? inst.plugin : void 0;
|
|
1162
|
+
}, []);
|
|
1011
1163
|
const removeEffect = useCallback2((instanceId) => {
|
|
1012
1164
|
const instance = effectInstancesRef.current.get(instanceId);
|
|
1013
1165
|
if (instance) {
|
|
@@ -1035,6 +1187,12 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1035
1187
|
const effect = activeEffectsRef.current.find((e) => e.instanceId === instanceId);
|
|
1036
1188
|
if (!effect) return;
|
|
1037
1189
|
const newBypassed = !effect.bypassed;
|
|
1190
|
+
if (effect.kind === "wam") {
|
|
1191
|
+
setActiveEffects(
|
|
1192
|
+
(prev) => prev.map((e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { bypassed: newBypassed }) : e)
|
|
1193
|
+
);
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1038
1196
|
const instance = effectInstancesRef.current.get(instanceId);
|
|
1039
1197
|
if (instance) {
|
|
1040
1198
|
const originalWet = (_a = effect.params.wet) != null ? _a : 1;
|
|
@@ -1070,17 +1228,18 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1070
1228
|
analyserNode
|
|
1071
1229
|
};
|
|
1072
1230
|
const effects = activeEffectsRef.current;
|
|
1073
|
-
const
|
|
1231
|
+
const audible = effects.filter((ae) => !(ae.kind === "wam" && ae.bypassed));
|
|
1232
|
+
const instances = audible.map((ae) => effectInstancesRef.current.get(ae.instanceId)).filter((inst) => inst !== void 0);
|
|
1074
1233
|
if (instances.length === 0) {
|
|
1075
1234
|
masterGainNode.connect(analyserNode);
|
|
1076
1235
|
analyserNode.connect(destination);
|
|
1077
1236
|
} else {
|
|
1078
1237
|
let currentNode = masterGainNode;
|
|
1079
1238
|
instances.forEach((inst) => {
|
|
1080
|
-
currentNode
|
|
1239
|
+
connect4(currentNode, inst.effect);
|
|
1081
1240
|
currentNode = inst.effect;
|
|
1082
1241
|
});
|
|
1083
|
-
currentNode
|
|
1242
|
+
connect4(currentNode, analyserNode);
|
|
1084
1243
|
analyserNode.connect(destination);
|
|
1085
1244
|
}
|
|
1086
1245
|
return function cleanup() {
|
|
@@ -1093,42 +1252,45 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1093
1252
|
// Only fftSize - reads effects from ref
|
|
1094
1253
|
);
|
|
1095
1254
|
useEffect2(() => {
|
|
1255
|
+
isMountedRef.current = true;
|
|
1096
1256
|
const effectInstances = effectInstancesRef.current;
|
|
1097
1257
|
return () => {
|
|
1258
|
+
isMountedRef.current = false;
|
|
1098
1259
|
effectInstances.forEach((inst) => inst.dispose());
|
|
1099
1260
|
effectInstances.clear();
|
|
1100
1261
|
};
|
|
1101
1262
|
}, []);
|
|
1102
1263
|
const createOfflineEffectsFunction = useCallback2(() => {
|
|
1103
|
-
const
|
|
1104
|
-
if (
|
|
1264
|
+
const nonBypassed = activeEffects.filter((e) => !e.bypassed);
|
|
1265
|
+
if (nonBypassed.length === 0) {
|
|
1105
1266
|
return void 0;
|
|
1106
1267
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
if (offlineInstances.length === 0) {
|
|
1114
|
-
masterGainNode.connect(destination);
|
|
1115
|
-
} else {
|
|
1116
|
-
let currentNode = masterGainNode;
|
|
1117
|
-
offlineInstances.forEach((inst) => {
|
|
1118
|
-
currentNode.connect(inst.effect);
|
|
1119
|
-
currentNode = inst.effect;
|
|
1120
|
-
});
|
|
1121
|
-
currentNode.connect(destination);
|
|
1268
|
+
const hasWam = nonBypassed.some((e) => e.kind === "wam");
|
|
1269
|
+
return (masterGainNode, destination, _isOffline) => __async(null, null, function* () {
|
|
1270
|
+
if (hasWam && !isNativeGlobalContext()) {
|
|
1271
|
+
throw new Error(
|
|
1272
|
+
"[waveform-playlist] WAV export with WAM effects requires a native AudioContext. Call configureGlobalContext({ nativeAudioContext: true }) from @waveform-playlist/playout before any audio initialization."
|
|
1273
|
+
);
|
|
1122
1274
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1275
|
+
const rawContext = masterGainNode.context.rawContext;
|
|
1276
|
+
const { instances, dispose } = yield buildOfflineChain(
|
|
1277
|
+
nonBypassed,
|
|
1278
|
+
(instanceId) => {
|
|
1279
|
+
const inst = effectInstancesRef.current.get(instanceId);
|
|
1280
|
+
return (inst == null ? void 0 : inst.kind) === "wam" ? inst.plugin : void 0;
|
|
1281
|
+
},
|
|
1282
|
+
rawContext
|
|
1283
|
+
);
|
|
1284
|
+
connectOfflineChain(masterGainNode, instances, destination);
|
|
1285
|
+
return dispose;
|
|
1286
|
+
});
|
|
1127
1287
|
}, [activeEffects]);
|
|
1128
1288
|
return {
|
|
1129
1289
|
activeEffects,
|
|
1130
1290
|
availableEffects: effectDefinitions,
|
|
1131
1291
|
addEffect,
|
|
1292
|
+
addWamEffect,
|
|
1293
|
+
getWamPlugin,
|
|
1132
1294
|
removeEffect,
|
|
1133
1295
|
updateParameter,
|
|
1134
1296
|
toggleBypass,
|
|
@@ -1142,11 +1304,14 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1142
1304
|
|
|
1143
1305
|
// src/hooks/useTrackDynamicEffects.ts
|
|
1144
1306
|
import { useState as useState3, useCallback as useCallback3, useRef as useRef4, useEffect as useEffect3 } from "react";
|
|
1307
|
+
import { isNativeGlobalContext as isNativeGlobalContext2, getGlobalAudioContext as getGlobalAudioContext2 } from "@waveform-playlist/playout";
|
|
1308
|
+
import { connect as connect5 } from "tone";
|
|
1145
1309
|
function useTrackDynamicEffects() {
|
|
1146
1310
|
const [trackEffectsState, setTrackEffectsState] = useState3(
|
|
1147
1311
|
/* @__PURE__ */ new Map()
|
|
1148
1312
|
);
|
|
1149
1313
|
const trackEffectInstancesRef = useRef4(/* @__PURE__ */ new Map());
|
|
1314
|
+
const isMountedRef = useRef4(true);
|
|
1150
1315
|
const trackGraphNodesRef = useRef4(/* @__PURE__ */ new Map());
|
|
1151
1316
|
const rebuildTrackChain = useCallback3((trackId, trackEffects) => {
|
|
1152
1317
|
const nodes = trackGraphNodesRef.current.get(trackId);
|
|
@@ -1158,7 +1323,8 @@ function useTrackDynamicEffects() {
|
|
|
1158
1323
|
} catch (e) {
|
|
1159
1324
|
console.warn(`[waveform-playlist] Error disconnecting track "${trackId}" effect chain:`, e);
|
|
1160
1325
|
}
|
|
1161
|
-
const
|
|
1326
|
+
const audible = trackEffects.filter((ae) => !(ae.kind === "wam" && ae.bypassed));
|
|
1327
|
+
const instances = audible.map((ae) => instancesMap == null ? void 0 : instancesMap.get(ae.instanceId)).filter((inst) => inst !== void 0);
|
|
1162
1328
|
if (instances.length === 0) {
|
|
1163
1329
|
graphEnd.connect(masterGainNode);
|
|
1164
1330
|
} else {
|
|
@@ -1172,10 +1338,10 @@ function useTrackDynamicEffects() {
|
|
|
1172
1338
|
e
|
|
1173
1339
|
);
|
|
1174
1340
|
}
|
|
1175
|
-
currentNode
|
|
1341
|
+
connect5(currentNode, inst.effect);
|
|
1176
1342
|
currentNode = inst.effect;
|
|
1177
1343
|
});
|
|
1178
|
-
currentNode
|
|
1344
|
+
connect5(currentNode, masterGainNode);
|
|
1179
1345
|
}
|
|
1180
1346
|
}, []);
|
|
1181
1347
|
const addEffectToTrack = useCallback3((trackId, effectId) => {
|
|
@@ -1196,6 +1362,7 @@ function useTrackDynamicEffects() {
|
|
|
1196
1362
|
const newActiveEffect = {
|
|
1197
1363
|
instanceId: instance.instanceId,
|
|
1198
1364
|
effectId: definition.id,
|
|
1365
|
+
kind: "native",
|
|
1199
1366
|
definition,
|
|
1200
1367
|
params,
|
|
1201
1368
|
bypassed: false
|
|
@@ -1207,6 +1374,76 @@ function useTrackDynamicEffects() {
|
|
|
1207
1374
|
return newState;
|
|
1208
1375
|
});
|
|
1209
1376
|
}, []);
|
|
1377
|
+
const addWamEffectToTrack = useCallback3(
|
|
1378
|
+
(trackId, url, initialState) => __async(null, null, function* () {
|
|
1379
|
+
var _a, _b;
|
|
1380
|
+
if (!isNativeGlobalContext2()) {
|
|
1381
|
+
throw new Error(
|
|
1382
|
+
"[waveform-playlist] WAM plugins require a native AudioContext. Call configureGlobalContext({ nativeAudioContext: true }) from @waveform-playlist/playout before any audio initialization."
|
|
1383
|
+
);
|
|
1384
|
+
}
|
|
1385
|
+
const wam = yield loadWamModule();
|
|
1386
|
+
const ctx = getGlobalAudioContext2();
|
|
1387
|
+
const { hostGroupId } = yield wam.ensureWamHost(ctx);
|
|
1388
|
+
const plugin = yield wam.createWamInstance(
|
|
1389
|
+
url,
|
|
1390
|
+
ctx,
|
|
1391
|
+
hostGroupId,
|
|
1392
|
+
initialState !== void 0 ? { initialState } : void 0
|
|
1393
|
+
);
|
|
1394
|
+
if (!isMountedRef.current) {
|
|
1395
|
+
try {
|
|
1396
|
+
plugin.destroy();
|
|
1397
|
+
} catch (err) {
|
|
1398
|
+
console.warn(
|
|
1399
|
+
"[waveform-playlist] Error destroying WAM plugin after unmount: " + (err instanceof Error ? err.message : String(err))
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
throw new Error(
|
|
1403
|
+
"[waveform-playlist] addWamEffectToTrack aborted: hook unmounted before the plugin finished loading."
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
const instance = createWamEffectInstance(plugin);
|
|
1407
|
+
if (!trackEffectInstancesRef.current.has(trackId)) {
|
|
1408
|
+
trackEffectInstancesRef.current.set(trackId, /* @__PURE__ */ new Map());
|
|
1409
|
+
}
|
|
1410
|
+
trackEffectInstancesRef.current.get(trackId).set(instance.instanceId, instance);
|
|
1411
|
+
const definition = {
|
|
1412
|
+
id: instance.id,
|
|
1413
|
+
name: (_b = (_a = plugin.descriptor) == null ? void 0 : _a.name) != null ? _b : url,
|
|
1414
|
+
category: "wam",
|
|
1415
|
+
description: "WAM plugin",
|
|
1416
|
+
parameters: []
|
|
1417
|
+
};
|
|
1418
|
+
setTrackEffectsState((prev) => {
|
|
1419
|
+
const newState = new Map(prev);
|
|
1420
|
+
const existing = newState.get(trackId) || [];
|
|
1421
|
+
newState.set(trackId, [
|
|
1422
|
+
...existing,
|
|
1423
|
+
{
|
|
1424
|
+
instanceId: instance.instanceId,
|
|
1425
|
+
effectId: instance.id,
|
|
1426
|
+
kind: "wam",
|
|
1427
|
+
url,
|
|
1428
|
+
definition,
|
|
1429
|
+
params: {},
|
|
1430
|
+
bypassed: false
|
|
1431
|
+
}
|
|
1432
|
+
]);
|
|
1433
|
+
return newState;
|
|
1434
|
+
});
|
|
1435
|
+
return instance.instanceId;
|
|
1436
|
+
}),
|
|
1437
|
+
[]
|
|
1438
|
+
);
|
|
1439
|
+
const getTrackWamPlugin = useCallback3(
|
|
1440
|
+
(trackId, instanceId) => {
|
|
1441
|
+
const instancesMap = trackEffectInstancesRef.current.get(trackId);
|
|
1442
|
+
const inst = instancesMap == null ? void 0 : instancesMap.get(instanceId);
|
|
1443
|
+
return (inst == null ? void 0 : inst.kind) === "wam" ? inst.plugin : void 0;
|
|
1444
|
+
},
|
|
1445
|
+
[]
|
|
1446
|
+
);
|
|
1210
1447
|
const removeEffectFromTrack = useCallback3((trackId, instanceId) => {
|
|
1211
1448
|
const instancesMap = trackEffectInstancesRef.current.get(trackId);
|
|
1212
1449
|
const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
|
|
@@ -1251,6 +1488,18 @@ function useTrackDynamicEffects() {
|
|
|
1251
1488
|
const effect = trackEffects.find((e) => e.instanceId === instanceId);
|
|
1252
1489
|
if (!effect) return;
|
|
1253
1490
|
const newBypassed = !effect.bypassed;
|
|
1491
|
+
if (effect.kind === "wam") {
|
|
1492
|
+
setTrackEffectsState((prev) => {
|
|
1493
|
+
const newState = new Map(prev);
|
|
1494
|
+
const existing = newState.get(trackId) || [];
|
|
1495
|
+
newState.set(
|
|
1496
|
+
trackId,
|
|
1497
|
+
existing.map((e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { bypassed: newBypassed }) : e)
|
|
1498
|
+
);
|
|
1499
|
+
return newState;
|
|
1500
|
+
});
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1254
1503
|
const instancesMap = trackEffectInstancesRef.current.get(trackId);
|
|
1255
1504
|
const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
|
|
1256
1505
|
if (instance) {
|
|
@@ -1290,16 +1539,17 @@ function useTrackDynamicEffects() {
|
|
|
1290
1539
|
});
|
|
1291
1540
|
const trackEffects = trackEffectsStateRef.current.get(trackId) || [];
|
|
1292
1541
|
const instancesMap = trackEffectInstancesRef.current.get(trackId);
|
|
1293
|
-
const
|
|
1542
|
+
const audible = trackEffects.filter((ae) => !(ae.kind === "wam" && ae.bypassed));
|
|
1543
|
+
const instances = audible.map((ae) => instancesMap == null ? void 0 : instancesMap.get(ae.instanceId)).filter((inst) => inst !== void 0);
|
|
1294
1544
|
if (instances.length === 0) {
|
|
1295
1545
|
graphEnd.connect(masterGainNode);
|
|
1296
1546
|
} else {
|
|
1297
1547
|
let currentNode = graphEnd;
|
|
1298
1548
|
instances.forEach((inst) => {
|
|
1299
|
-
currentNode
|
|
1549
|
+
connect5(currentNode, inst.effect);
|
|
1300
1550
|
currentNode = inst.effect;
|
|
1301
1551
|
});
|
|
1302
|
-
currentNode
|
|
1552
|
+
connect5(currentNode, masterGainNode);
|
|
1303
1553
|
}
|
|
1304
1554
|
return function cleanup() {
|
|
1305
1555
|
trackGraphNodesRef.current.delete(trackId);
|
|
@@ -1315,8 +1565,10 @@ function useTrackDynamicEffects() {
|
|
|
1315
1565
|
});
|
|
1316
1566
|
}, [trackEffectsState, rebuildTrackChain]);
|
|
1317
1567
|
useEffect3(() => {
|
|
1568
|
+
isMountedRef.current = true;
|
|
1318
1569
|
const trackEffectInstances = trackEffectInstancesRef.current;
|
|
1319
1570
|
return () => {
|
|
1571
|
+
isMountedRef.current = false;
|
|
1320
1572
|
trackEffectInstances.forEach((instancesMap) => {
|
|
1321
1573
|
instancesMap.forEach((inst) => inst.dispose());
|
|
1322
1574
|
instancesMap.clear();
|
|
@@ -1327,36 +1579,38 @@ function useTrackDynamicEffects() {
|
|
|
1327
1579
|
const createOfflineTrackEffectsFunction = useCallback3(
|
|
1328
1580
|
(trackId) => {
|
|
1329
1581
|
const trackEffects = trackEffectsState.get(trackId) || [];
|
|
1330
|
-
const
|
|
1331
|
-
if (
|
|
1582
|
+
const nonBypassed = trackEffects.filter((e) => !e.bypassed);
|
|
1583
|
+
if (nonBypassed.length === 0) {
|
|
1332
1584
|
return void 0;
|
|
1333
1585
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
if (offlineInstances.length === 0) {
|
|
1341
|
-
graphEnd.connect(masterGainNode);
|
|
1342
|
-
} else {
|
|
1343
|
-
let currentNode = graphEnd;
|
|
1344
|
-
offlineInstances.forEach((inst) => {
|
|
1345
|
-
currentNode.connect(inst.effect);
|
|
1346
|
-
currentNode = inst.effect;
|
|
1347
|
-
});
|
|
1348
|
-
currentNode.connect(masterGainNode);
|
|
1586
|
+
const hasWam = nonBypassed.some((e) => e.kind === "wam");
|
|
1587
|
+
return (graphEnd, masterGainNode, _isOffline) => __async(null, null, function* () {
|
|
1588
|
+
if (hasWam && !isNativeGlobalContext2()) {
|
|
1589
|
+
throw new Error(
|
|
1590
|
+
"[waveform-playlist] WAV export with WAM effects requires a native AudioContext. Call configureGlobalContext({ nativeAudioContext: true }) from @waveform-playlist/playout before any audio initialization."
|
|
1591
|
+
);
|
|
1349
1592
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1593
|
+
const rawContext = graphEnd.context.rawContext;
|
|
1594
|
+
const { instances, dispose } = yield buildOfflineChain(
|
|
1595
|
+
nonBypassed,
|
|
1596
|
+
(instanceId) => {
|
|
1597
|
+
var _a;
|
|
1598
|
+
const inst = (_a = trackEffectInstancesRef.current.get(trackId)) == null ? void 0 : _a.get(instanceId);
|
|
1599
|
+
return (inst == null ? void 0 : inst.kind) === "wam" ? inst.plugin : void 0;
|
|
1600
|
+
},
|
|
1601
|
+
rawContext
|
|
1602
|
+
);
|
|
1603
|
+
connectOfflineChain(graphEnd, instances, masterGainNode);
|
|
1604
|
+
return dispose;
|
|
1605
|
+
});
|
|
1354
1606
|
},
|
|
1355
1607
|
[trackEffectsState]
|
|
1356
1608
|
);
|
|
1357
1609
|
return {
|
|
1358
1610
|
trackEffectsState,
|
|
1359
1611
|
addEffectToTrack,
|
|
1612
|
+
addWamEffectToTrack,
|
|
1613
|
+
getTrackWamPlugin,
|
|
1360
1614
|
removeEffectFromTrack,
|
|
1361
1615
|
updateTrackEffectParameter,
|
|
1362
1616
|
toggleBypass,
|
|
@@ -1375,10 +1629,45 @@ import {
|
|
|
1375
1629
|
applyFadeIn,
|
|
1376
1630
|
applyFadeOut
|
|
1377
1631
|
} from "@waveform-playlist/core";
|
|
1378
|
-
import {
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
} from "
|
|
1632
|
+
import { getUnderlyingAudioParam, getGlobalAudioContext as getGlobalAudioContext3 } from "@waveform-playlist/playout";
|
|
1633
|
+
|
|
1634
|
+
// src/utils/renderToneOffline.ts
|
|
1635
|
+
import { getContext as getContext2, setContext, OfflineContext } from "tone";
|
|
1636
|
+
import { isNativeGlobalContext as isNativeGlobalContext3 } from "@waveform-playlist/playout";
|
|
1637
|
+
var renderQueue = Promise.resolve();
|
|
1638
|
+
function renderToneOffline(build, duration, channels, sampleRate) {
|
|
1639
|
+
return __async(this, null, function* () {
|
|
1640
|
+
const run = renderQueue.then(
|
|
1641
|
+
() => renderToneOfflineSerial(build, duration, channels, sampleRate)
|
|
1642
|
+
);
|
|
1643
|
+
renderQueue = run.catch(() => void 0);
|
|
1644
|
+
return run;
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
function renderToneOfflineSerial(build, duration, channels, sampleRate) {
|
|
1648
|
+
return __async(this, null, function* () {
|
|
1649
|
+
const offlineContext = isNativeGlobalContext3() ? new OfflineContext(
|
|
1650
|
+
new OfflineAudioContext({
|
|
1651
|
+
numberOfChannels: channels,
|
|
1652
|
+
length: Math.round(duration * sampleRate),
|
|
1653
|
+
sampleRate
|
|
1654
|
+
})
|
|
1655
|
+
) : new OfflineContext(channels, duration, sampleRate);
|
|
1656
|
+
const previousContext = getContext2();
|
|
1657
|
+
setContext(offlineContext);
|
|
1658
|
+
try {
|
|
1659
|
+
yield build(offlineContext);
|
|
1660
|
+
} finally {
|
|
1661
|
+
setContext(previousContext);
|
|
1662
|
+
}
|
|
1663
|
+
const toneBuffer = yield offlineContext.render();
|
|
1664
|
+
const audioBuffer = toneBuffer.get();
|
|
1665
|
+
if (!audioBuffer) {
|
|
1666
|
+
throw new Error("Offline rendering produced no audio buffer");
|
|
1667
|
+
}
|
|
1668
|
+
return audioBuffer;
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1382
1671
|
|
|
1383
1672
|
// src/utils/wavEncoder.ts
|
|
1384
1673
|
function encodeWav(audioBuffer, options = {}) {
|
|
@@ -1477,7 +1766,7 @@ function useExportWav() {
|
|
|
1477
1766
|
if (mode === "individual" && (trackIndex === void 0 || trackIndex < 0 || trackIndex >= tracks.length)) {
|
|
1478
1767
|
throw new Error("Invalid track index for individual export");
|
|
1479
1768
|
}
|
|
1480
|
-
const sampleRate =
|
|
1769
|
+
const sampleRate = getGlobalAudioContext3().sampleRate;
|
|
1481
1770
|
let totalDurationSamples = 0;
|
|
1482
1771
|
for (const track of tracks) {
|
|
1483
1772
|
for (const clip of track.clips) {
|
|
@@ -1534,7 +1823,7 @@ function useExportWav() {
|
|
|
1534
1823
|
}
|
|
1535
1824
|
function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffects, effectsFunction, createOfflineTrackEffects, onProgress) {
|
|
1536
1825
|
return __async(this, null, function* () {
|
|
1537
|
-
const {
|
|
1826
|
+
const { Volume: Volume2, Gain: Gain2, Panner, Player, ToneAudioBuffer } = yield import("tone");
|
|
1538
1827
|
onProgress(0.1);
|
|
1539
1828
|
const audibleTracks = tracksToRender.filter(({ state }) => {
|
|
1540
1829
|
if (state.muted && !state.soloed) return false;
|
|
@@ -1545,23 +1834,25 @@ function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffec
|
|
|
1545
1834
|
(max, { track }) => Math.max(max, trackChannelCount(track)),
|
|
1546
1835
|
1
|
|
1547
1836
|
);
|
|
1548
|
-
|
|
1837
|
+
const cleanups = [];
|
|
1549
1838
|
try {
|
|
1550
|
-
|
|
1551
|
-
(
|
|
1839
|
+
const audioBuffer = yield renderToneOffline(
|
|
1840
|
+
(context) => __async(null, null, function* () {
|
|
1552
1841
|
const masterVolume = new Volume2(0);
|
|
1553
1842
|
if (effectsFunction && applyEffects) {
|
|
1554
|
-
effectsFunction(masterVolume, destination, true);
|
|
1843
|
+
const cleanup = yield effectsFunction(masterVolume, context.destination, true);
|
|
1844
|
+
if (cleanup) cleanups.push(cleanup);
|
|
1555
1845
|
} else {
|
|
1556
|
-
masterVolume.connect(destination);
|
|
1846
|
+
masterVolume.connect(context.destination);
|
|
1557
1847
|
}
|
|
1558
1848
|
for (const { track, state } of audibleTracks) {
|
|
1559
1849
|
const trackVolume = new Volume2(gainToDb(state.volume));
|
|
1560
1850
|
const trackPan = new Panner({ pan: state.pan, channelCount: trackChannelCount(track) });
|
|
1561
|
-
const trackMute = new
|
|
1851
|
+
const trackMute = new Gain2(state.muted ? 0 : 1);
|
|
1562
1852
|
const trackEffects = createOfflineTrackEffects == null ? void 0 : createOfflineTrackEffects(track.id);
|
|
1563
1853
|
if (trackEffects && applyEffects) {
|
|
1564
|
-
trackEffects(trackMute, masterVolume, true);
|
|
1854
|
+
const cleanup = yield trackEffects(trackMute, masterVolume, true);
|
|
1855
|
+
if (cleanup) cleanups.push(cleanup);
|
|
1565
1856
|
} else {
|
|
1566
1857
|
trackMute.connect(masterVolume);
|
|
1567
1858
|
}
|
|
@@ -1569,7 +1860,7 @@ function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffec
|
|
|
1569
1860
|
trackVolume.connect(trackPan);
|
|
1570
1861
|
for (const clip of track.clips) {
|
|
1571
1862
|
const {
|
|
1572
|
-
audioBuffer,
|
|
1863
|
+
audioBuffer: clipBuffer,
|
|
1573
1864
|
startSample,
|
|
1574
1865
|
durationSamples,
|
|
1575
1866
|
offsetSamples,
|
|
@@ -1577,7 +1868,7 @@ function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffec
|
|
|
1577
1868
|
fadeIn,
|
|
1578
1869
|
fadeOut
|
|
1579
1870
|
} = clip;
|
|
1580
|
-
if (!
|
|
1871
|
+
if (!clipBuffer) {
|
|
1581
1872
|
console.warn(
|
|
1582
1873
|
'[waveform-playlist] Skipping clip "' + (clip.name || clip.id) + '" - no audioBuffer for export'
|
|
1583
1874
|
);
|
|
@@ -1586,9 +1877,9 @@ function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffec
|
|
|
1586
1877
|
const startTime = startSample / sampleRate;
|
|
1587
1878
|
const clipDuration = durationSamples / sampleRate;
|
|
1588
1879
|
const offset = offsetSamples / sampleRate;
|
|
1589
|
-
const toneBuffer = new ToneAudioBuffer(
|
|
1880
|
+
const toneBuffer = new ToneAudioBuffer(clipBuffer);
|
|
1590
1881
|
const player = new Player(toneBuffer);
|
|
1591
|
-
const fadeGain = new
|
|
1882
|
+
const fadeGain = new Gain2(clipGain);
|
|
1592
1883
|
player.connect(fadeGain);
|
|
1593
1884
|
fadeGain.connect(trackVolume);
|
|
1594
1885
|
if (applyEffects) {
|
|
@@ -1604,25 +1895,29 @@ function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffec
|
|
|
1604
1895
|
player.start(startTime, offset, clipDuration);
|
|
1605
1896
|
}
|
|
1606
1897
|
}
|
|
1607
|
-
transport.start(0);
|
|
1898
|
+
context.transport.start(0);
|
|
1608
1899
|
}),
|
|
1609
1900
|
duration,
|
|
1610
1901
|
outputChannels,
|
|
1611
1902
|
sampleRate
|
|
1612
1903
|
);
|
|
1904
|
+
return audioBuffer;
|
|
1613
1905
|
} catch (err) {
|
|
1614
1906
|
if (err instanceof Error) {
|
|
1615
1907
|
throw err;
|
|
1616
|
-
}
|
|
1617
|
-
|
|
1908
|
+
}
|
|
1909
|
+
throw new Error("Offline rendering failed: " + String(err));
|
|
1910
|
+
} finally {
|
|
1911
|
+
for (const cleanup of cleanups) {
|
|
1912
|
+
try {
|
|
1913
|
+
cleanup();
|
|
1914
|
+
} catch (err) {
|
|
1915
|
+
console.warn(
|
|
1916
|
+
"[waveform-playlist] Export cleanup error: " + (err instanceof Error ? err.message : String(err))
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1618
1919
|
}
|
|
1619
1920
|
}
|
|
1620
|
-
onProgress(0.9);
|
|
1621
|
-
const result = buffer.get();
|
|
1622
|
-
if (!result) {
|
|
1623
|
-
throw new Error("Offline rendering produced no audio buffer");
|
|
1624
|
-
}
|
|
1625
|
-
return result;
|
|
1626
1921
|
});
|
|
1627
1922
|
}
|
|
1628
1923
|
function applyClipFades(gainParam, clipGain, startTime, clipDuration, fadeIn, fadeOut) {
|
|
@@ -1646,7 +1941,7 @@ function applyClipFades(gainParam, clipGain, startTime, clipDuration, fadeIn, fa
|
|
|
1646
1941
|
// src/hooks/useDynamicTracks.ts
|
|
1647
1942
|
import { useState as useState5, useCallback as useCallback5, useRef as useRef5, useEffect as useEffect4 } from "react";
|
|
1648
1943
|
import { createTrack as createTrack2, createClipFromSeconds as createClipFromSeconds2 } from "@waveform-playlist/core";
|
|
1649
|
-
import { getGlobalAudioContext as
|
|
1944
|
+
import { getGlobalAudioContext as getGlobalAudioContext4 } from "@waveform-playlist/playout";
|
|
1650
1945
|
function getSourceName(source) {
|
|
1651
1946
|
var _a, _b, _c, _d, _e;
|
|
1652
1947
|
if (source instanceof File) {
|
|
@@ -1697,7 +1992,7 @@ function useDynamicTracks() {
|
|
|
1697
1992
|
}, []);
|
|
1698
1993
|
const addTracks = useCallback5((sources) => {
|
|
1699
1994
|
if (sources.length === 0) return;
|
|
1700
|
-
const audioContext =
|
|
1995
|
+
const audioContext = getGlobalAudioContext4();
|
|
1701
1996
|
const placeholders = sources.map((source) => ({
|
|
1702
1997
|
track: createTrack2({ name: `${getSourceName(source)} (loading...)`, clips: [] }),
|
|
1703
1998
|
source
|
|
@@ -1939,14 +2234,77 @@ var ExportWavButton = ({
|
|
|
1939
2234
|
}
|
|
1940
2235
|
);
|
|
1941
2236
|
};
|
|
2237
|
+
|
|
2238
|
+
// src/components/WamEffectGui.tsx
|
|
2239
|
+
import { useEffect as useEffect7, useRef as useRef8 } from "react";
|
|
2240
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
2241
|
+
var WamEffectGui = ({ plugin, className }) => {
|
|
2242
|
+
const hostRef = useRef8(null);
|
|
2243
|
+
useEffect7(() => {
|
|
2244
|
+
const host = hostRef.current;
|
|
2245
|
+
if (!plugin || !host) return;
|
|
2246
|
+
let cancelled = false;
|
|
2247
|
+
let gui = null;
|
|
2248
|
+
(() => __async(null, null, function* () {
|
|
2249
|
+
var _a;
|
|
2250
|
+
try {
|
|
2251
|
+
if (plugin.createGui) {
|
|
2252
|
+
gui = yield plugin.createGui();
|
|
2253
|
+
} else {
|
|
2254
|
+
const wam = yield loadWamModule();
|
|
2255
|
+
gui = yield wam.createWamParameterPanel(
|
|
2256
|
+
plugin.audioNode
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
if (cancelled) {
|
|
2260
|
+
if (gui) {
|
|
2261
|
+
try {
|
|
2262
|
+
(_a = plugin.destroyGui) == null ? void 0 : _a.call(plugin, gui);
|
|
2263
|
+
} catch (destroyErr) {
|
|
2264
|
+
console.warn(
|
|
2265
|
+
"[waveform-playlist] Error destroying WAM GUI: " + (destroyErr instanceof Error ? destroyErr.message : String(destroyErr))
|
|
2266
|
+
);
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
gui = null;
|
|
2270
|
+
return;
|
|
2271
|
+
}
|
|
2272
|
+
host.innerHTML = "";
|
|
2273
|
+
host.appendChild(gui);
|
|
2274
|
+
} catch (err) {
|
|
2275
|
+
console.warn(
|
|
2276
|
+
"[waveform-playlist] Failed to create WAM GUI: " + (err instanceof Error ? err.message : String(err))
|
|
2277
|
+
);
|
|
2278
|
+
}
|
|
2279
|
+
}))();
|
|
2280
|
+
return () => {
|
|
2281
|
+
var _a;
|
|
2282
|
+
cancelled = true;
|
|
2283
|
+
if (gui) {
|
|
2284
|
+
gui.remove();
|
|
2285
|
+
try {
|
|
2286
|
+
(_a = plugin.destroyGui) == null ? void 0 : _a.call(plugin, gui);
|
|
2287
|
+
} catch (destroyErr) {
|
|
2288
|
+
console.warn(
|
|
2289
|
+
"[waveform-playlist] Error destroying WAM GUI: " + (destroyErr instanceof Error ? destroyErr.message : String(destroyErr))
|
|
2290
|
+
);
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
};
|
|
2294
|
+
}, [plugin]);
|
|
2295
|
+
return /* @__PURE__ */ jsx3("div", { ref: hostRef, className: className != null ? className : "wam-effect-gui" });
|
|
2296
|
+
};
|
|
1942
2297
|
export {
|
|
1943
2298
|
ExportWavButton,
|
|
2299
|
+
WamEffectGui,
|
|
1944
2300
|
createEffectChain,
|
|
1945
2301
|
createEffectInstance,
|
|
2302
|
+
createWamEffectInstance,
|
|
1946
2303
|
effectCategories,
|
|
1947
2304
|
effectDefinitions,
|
|
1948
2305
|
getEffectDefinition,
|
|
1949
2306
|
getEffectsByCategory,
|
|
2307
|
+
loadWamModule,
|
|
1950
2308
|
useAudioTracks,
|
|
1951
2309
|
useDynamicEffects,
|
|
1952
2310
|
useDynamicTracks,
|