@waveform-playlist/browser 14.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/index.d.mts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +9 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -13
- package/dist/index.mjs.map +1 -1
- 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.js
CHANGED
|
@@ -68,12 +68,15 @@ var __async = (__this, __arguments, generator) => {
|
|
|
68
68
|
var tone_exports = {};
|
|
69
69
|
__export(tone_exports, {
|
|
70
70
|
ExportWavButton: () => ExportWavButton,
|
|
71
|
+
WamEffectGui: () => WamEffectGui,
|
|
71
72
|
createEffectChain: () => createEffectChain,
|
|
72
73
|
createEffectInstance: () => createEffectInstance,
|
|
74
|
+
createWamEffectInstance: () => createWamEffectInstance,
|
|
73
75
|
effectCategories: () => effectCategories,
|
|
74
76
|
effectDefinitions: () => effectDefinitions,
|
|
75
77
|
getEffectDefinition: () => getEffectDefinition,
|
|
76
78
|
getEffectsByCategory: () => getEffectsByCategory,
|
|
79
|
+
loadWamModule: () => loadWamModule,
|
|
77
80
|
useAudioTracks: () => useAudioTracks,
|
|
78
81
|
useDynamicEffects: () => useDynamicEffects,
|
|
79
82
|
useDynamicTracks: () => useDynamicTracks,
|
|
@@ -301,6 +304,7 @@ var useMasterAnalyser = (fftSize = 256) => {
|
|
|
301
304
|
|
|
302
305
|
// src/hooks/useDynamicEffects.ts
|
|
303
306
|
var import_react3 = require("react");
|
|
307
|
+
var import_playout = require("@waveform-playlist/playout");
|
|
304
308
|
|
|
305
309
|
// src/effects/effectDefinitions.ts
|
|
306
310
|
var effectDefinitions = [
|
|
@@ -903,8 +907,7 @@ function createEffectInstance(definition, initialParams) {
|
|
|
903
907
|
effect.dispose();
|
|
904
908
|
} catch (e) {
|
|
905
909
|
console.warn(
|
|
906
|
-
`[waveform-playlist] Error disposing effect "${definition.id}" (${instanceId})
|
|
907
|
-
e
|
|
910
|
+
`[waveform-playlist] Error disposing effect "${definition.id}" (${instanceId}): ${e instanceof Error ? e.message : String(e)}`
|
|
908
911
|
);
|
|
909
912
|
}
|
|
910
913
|
},
|
|
@@ -949,8 +952,7 @@ function createEffectInstance(definition, initialParams) {
|
|
|
949
952
|
effect.disconnect();
|
|
950
953
|
} catch (e) {
|
|
951
954
|
console.warn(
|
|
952
|
-
`[waveform-playlist] Error disconnecting effect "${definition.id}" (${instanceId})
|
|
953
|
-
e
|
|
955
|
+
`[waveform-playlist] Error disconnecting effect "${definition.id}" (${instanceId}): ${e instanceof Error ? e.message : String(e)}`
|
|
954
956
|
);
|
|
955
957
|
}
|
|
956
958
|
}
|
|
@@ -961,7 +963,7 @@ function createEffectChain(effects) {
|
|
|
961
963
|
throw new Error("Cannot create effect chain with no effects");
|
|
962
964
|
}
|
|
963
965
|
for (let i = 0; i < effects.length - 1; i++) {
|
|
964
|
-
effects[i].effect
|
|
966
|
+
(0, import_tone2.connect)(effects[i].effect, effects[i + 1].effect);
|
|
965
967
|
}
|
|
966
968
|
return {
|
|
967
969
|
input: effects[0].effect,
|
|
@@ -972,13 +974,105 @@ function createEffectChain(effects) {
|
|
|
972
974
|
};
|
|
973
975
|
}
|
|
974
976
|
|
|
975
|
-
// src/
|
|
977
|
+
// src/effects/loadWam.ts
|
|
978
|
+
function loadWamModule() {
|
|
979
|
+
return __async(this, null, function* () {
|
|
980
|
+
try {
|
|
981
|
+
return yield import("@dawcore/wam");
|
|
982
|
+
} catch (err) {
|
|
983
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
984
|
+
throw new Error(
|
|
985
|
+
"[waveform-playlist] WAM plugin support requires the optional '@dawcore/wam' package.\nInstall it with: npm install @dawcore/wam\nOriginal error: " + detail
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// src/effects/wamEffectFactory.ts
|
|
976
992
|
var import_tone3 = require("tone");
|
|
993
|
+
var wamInstanceCounter = 0;
|
|
994
|
+
function createWamEffectInstance(plugin) {
|
|
995
|
+
var _a, _b, _c;
|
|
996
|
+
const node = plugin.audioNode;
|
|
997
|
+
const instanceId = "wam_" + ++wamInstanceCounter;
|
|
998
|
+
return {
|
|
999
|
+
kind: "wam",
|
|
1000
|
+
plugin,
|
|
1001
|
+
url: plugin.url,
|
|
1002
|
+
effect: node,
|
|
1003
|
+
id: "wam:" + ((_c = (_b = (_a = plugin.descriptor) == null ? void 0 : _a.name) != null ? _b : plugin.url) != null ? _c : "plugin"),
|
|
1004
|
+
instanceId,
|
|
1005
|
+
dispose: () => {
|
|
1006
|
+
try {
|
|
1007
|
+
plugin.destroy();
|
|
1008
|
+
} catch (err) {
|
|
1009
|
+
console.warn(
|
|
1010
|
+
"[waveform-playlist] Error destroying WAM plugin: " + (err instanceof Error ? err.message : String(err))
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
},
|
|
1014
|
+
setParameter: (name, value) => {
|
|
1015
|
+
var _a2;
|
|
1016
|
+
if (typeof value !== "number") return;
|
|
1017
|
+
const target = plugin.audioNode;
|
|
1018
|
+
void ((_a2 = target.setParameterValues) == null ? void 0 : _a2.call(target, { [name]: { id: name, value, normalized: false } }));
|
|
1019
|
+
},
|
|
1020
|
+
getParameter: () => void 0,
|
|
1021
|
+
// WAM params are async; read via the plugin GUI
|
|
1022
|
+
connect: (destination) => (0, import_tone3.connect)(node, destination),
|
|
1023
|
+
disconnect: () => (0, import_tone3.disconnect)(node)
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// src/effects/offlineChain.ts
|
|
1028
|
+
var import_tone4 = require("tone");
|
|
1029
|
+
function buildOfflineChain(entries, getLivePlugin, rawContext) {
|
|
1030
|
+
return __async(this, null, function* () {
|
|
1031
|
+
const instances = [];
|
|
1032
|
+
const dispose = () => {
|
|
1033
|
+
instances.forEach((inst) => inst.dispose());
|
|
1034
|
+
};
|
|
1035
|
+
try {
|
|
1036
|
+
for (const entry of entries) {
|
|
1037
|
+
if (entry.kind === "native") {
|
|
1038
|
+
instances.push(createEffectInstance(entry.definition, entry.params));
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1041
|
+
const livePlugin = getLivePlugin(entry.instanceId);
|
|
1042
|
+
if (!livePlugin) {
|
|
1043
|
+
throw new Error(
|
|
1044
|
+
'[waveform-playlist] WAV export: no live WAM plugin found for "' + entry.definition.name + '" (' + entry.instanceId + ") \u2014 cannot re-instantiate it offline."
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
const wam = yield loadWamModule();
|
|
1048
|
+
const { hostGroupId } = yield wam.ensureWamHost(rawContext);
|
|
1049
|
+
const clone = yield wam.cloneInstanceInto(livePlugin, rawContext, hostGroupId);
|
|
1050
|
+
instances.push(createWamEffectInstance(clone));
|
|
1051
|
+
}
|
|
1052
|
+
} catch (err) {
|
|
1053
|
+
dispose();
|
|
1054
|
+
throw err;
|
|
1055
|
+
}
|
|
1056
|
+
return { instances, dispose };
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
function connectOfflineChain(from, instances, to) {
|
|
1060
|
+
let current = from;
|
|
1061
|
+
for (const inst of instances) {
|
|
1062
|
+
(0, import_tone4.connect)(current, inst.effect);
|
|
1063
|
+
current = inst.effect;
|
|
1064
|
+
}
|
|
1065
|
+
(0, import_tone4.connect)(current, to);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// src/hooks/useDynamicEffects.ts
|
|
1069
|
+
var import_tone5 = require("tone");
|
|
977
1070
|
function useDynamicEffects(fftSize = 256) {
|
|
978
1071
|
const [activeEffects, setActiveEffects] = (0, import_react3.useState)([]);
|
|
979
1072
|
const activeEffectsRef = (0, import_react3.useRef)(activeEffects);
|
|
980
1073
|
activeEffectsRef.current = activeEffects;
|
|
981
1074
|
const effectInstancesRef = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
|
|
1075
|
+
const isMountedRef = (0, import_react3.useRef)(true);
|
|
982
1076
|
const analyserRef = (0, import_react3.useRef)(null);
|
|
983
1077
|
const graphNodesRef = (0, import_react3.useRef)(null);
|
|
984
1078
|
const rebuildChain = (0, import_react3.useCallback)((effects) => {
|
|
@@ -990,7 +1084,8 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
990
1084
|
} catch (e) {
|
|
991
1085
|
console.warn("[waveform-playlist] Error disconnecting master effects chain:", e);
|
|
992
1086
|
}
|
|
993
|
-
const
|
|
1087
|
+
const audible = effects.filter((ae) => !(ae.kind === "wam" && ae.bypassed));
|
|
1088
|
+
const instances = audible.map((ae) => effectInstancesRef.current.get(ae.instanceId)).filter((inst) => inst !== void 0);
|
|
994
1089
|
if (instances.length === 0) {
|
|
995
1090
|
masterGainNode.connect(analyserNode);
|
|
996
1091
|
analyserNode.connect(destination);
|
|
@@ -1002,10 +1097,10 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1002
1097
|
} catch (e) {
|
|
1003
1098
|
console.warn(`[waveform-playlist] Error disconnecting effect "${inst.id}":`, e);
|
|
1004
1099
|
}
|
|
1005
|
-
|
|
1100
|
+
(0, import_tone5.connect)(currentNode, inst.effect);
|
|
1006
1101
|
currentNode = inst.effect;
|
|
1007
1102
|
});
|
|
1008
|
-
|
|
1103
|
+
(0, import_tone5.connect)(currentNode, analyserNode);
|
|
1009
1104
|
analyserNode.connect(destination);
|
|
1010
1105
|
}
|
|
1011
1106
|
}, []);
|
|
@@ -1024,12 +1119,68 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1024
1119
|
const newActiveEffect = {
|
|
1025
1120
|
instanceId: instance.instanceId,
|
|
1026
1121
|
effectId: definition.id,
|
|
1122
|
+
kind: "native",
|
|
1027
1123
|
definition,
|
|
1028
1124
|
params,
|
|
1029
1125
|
bypassed: false
|
|
1030
1126
|
};
|
|
1031
1127
|
setActiveEffects((prev) => [...prev, newActiveEffect]);
|
|
1032
1128
|
}, []);
|
|
1129
|
+
const addWamEffect = (0, import_react3.useCallback)((url, initialState) => __async(null, null, function* () {
|
|
1130
|
+
var _a, _b;
|
|
1131
|
+
if (!(0, import_playout.isNativeGlobalContext)()) {
|
|
1132
|
+
throw new Error(
|
|
1133
|
+
"[waveform-playlist] WAM plugins require a native AudioContext. Call configureGlobalContext({ nativeAudioContext: true }) from @waveform-playlist/playout before any audio initialization."
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
const wam = yield loadWamModule();
|
|
1137
|
+
const ctx = (0, import_playout.getGlobalAudioContext)();
|
|
1138
|
+
const { hostGroupId } = yield wam.ensureWamHost(ctx);
|
|
1139
|
+
const plugin = yield wam.createWamInstance(
|
|
1140
|
+
url,
|
|
1141
|
+
ctx,
|
|
1142
|
+
hostGroupId,
|
|
1143
|
+
initialState !== void 0 ? { initialState } : void 0
|
|
1144
|
+
);
|
|
1145
|
+
if (!isMountedRef.current) {
|
|
1146
|
+
try {
|
|
1147
|
+
plugin.destroy();
|
|
1148
|
+
} catch (err) {
|
|
1149
|
+
console.warn(
|
|
1150
|
+
"[waveform-playlist] Error destroying WAM plugin after unmount: " + (err instanceof Error ? err.message : String(err))
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
throw new Error(
|
|
1154
|
+
"[waveform-playlist] addWamEffect aborted: hook unmounted before the plugin finished loading."
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
const instance = createWamEffectInstance(plugin);
|
|
1158
|
+
effectInstancesRef.current.set(instance.instanceId, instance);
|
|
1159
|
+
const definition = {
|
|
1160
|
+
id: instance.id,
|
|
1161
|
+
name: (_b = (_a = plugin.descriptor) == null ? void 0 : _a.name) != null ? _b : url,
|
|
1162
|
+
category: "wam",
|
|
1163
|
+
description: "WAM plugin",
|
|
1164
|
+
parameters: []
|
|
1165
|
+
};
|
|
1166
|
+
setActiveEffects((prev) => [
|
|
1167
|
+
...prev,
|
|
1168
|
+
{
|
|
1169
|
+
instanceId: instance.instanceId,
|
|
1170
|
+
effectId: instance.id,
|
|
1171
|
+
kind: "wam",
|
|
1172
|
+
url,
|
|
1173
|
+
definition,
|
|
1174
|
+
params: {},
|
|
1175
|
+
bypassed: false
|
|
1176
|
+
}
|
|
1177
|
+
]);
|
|
1178
|
+
return instance.instanceId;
|
|
1179
|
+
}), []);
|
|
1180
|
+
const getWamPlugin = (0, import_react3.useCallback)((instanceId) => {
|
|
1181
|
+
const inst = effectInstancesRef.current.get(instanceId);
|
|
1182
|
+
return (inst == null ? void 0 : inst.kind) === "wam" ? inst.plugin : void 0;
|
|
1183
|
+
}, []);
|
|
1033
1184
|
const removeEffect = (0, import_react3.useCallback)((instanceId) => {
|
|
1034
1185
|
const instance = effectInstancesRef.current.get(instanceId);
|
|
1035
1186
|
if (instance) {
|
|
@@ -1057,6 +1208,12 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1057
1208
|
const effect = activeEffectsRef.current.find((e) => e.instanceId === instanceId);
|
|
1058
1209
|
if (!effect) return;
|
|
1059
1210
|
const newBypassed = !effect.bypassed;
|
|
1211
|
+
if (effect.kind === "wam") {
|
|
1212
|
+
setActiveEffects(
|
|
1213
|
+
(prev) => prev.map((e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { bypassed: newBypassed }) : e)
|
|
1214
|
+
);
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1060
1217
|
const instance = effectInstancesRef.current.get(instanceId);
|
|
1061
1218
|
if (instance) {
|
|
1062
1219
|
const originalWet = (_a = effect.params.wet) != null ? _a : 1;
|
|
@@ -1084,7 +1241,7 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1084
1241
|
}, [activeEffects, rebuildChain]);
|
|
1085
1242
|
const masterEffects = (0, import_react3.useCallback)(
|
|
1086
1243
|
(masterGainNode, destination, _isOffline) => {
|
|
1087
|
-
const analyserNode = new
|
|
1244
|
+
const analyserNode = new import_tone5.Analyser("fft", fftSize);
|
|
1088
1245
|
analyserRef.current = analyserNode;
|
|
1089
1246
|
graphNodesRef.current = {
|
|
1090
1247
|
masterGainNode,
|
|
@@ -1092,17 +1249,18 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1092
1249
|
analyserNode
|
|
1093
1250
|
};
|
|
1094
1251
|
const effects = activeEffectsRef.current;
|
|
1095
|
-
const
|
|
1252
|
+
const audible = effects.filter((ae) => !(ae.kind === "wam" && ae.bypassed));
|
|
1253
|
+
const instances = audible.map((ae) => effectInstancesRef.current.get(ae.instanceId)).filter((inst) => inst !== void 0);
|
|
1096
1254
|
if (instances.length === 0) {
|
|
1097
1255
|
masterGainNode.connect(analyserNode);
|
|
1098
1256
|
analyserNode.connect(destination);
|
|
1099
1257
|
} else {
|
|
1100
1258
|
let currentNode = masterGainNode;
|
|
1101
1259
|
instances.forEach((inst) => {
|
|
1102
|
-
|
|
1260
|
+
(0, import_tone5.connect)(currentNode, inst.effect);
|
|
1103
1261
|
currentNode = inst.effect;
|
|
1104
1262
|
});
|
|
1105
|
-
|
|
1263
|
+
(0, import_tone5.connect)(currentNode, analyserNode);
|
|
1106
1264
|
analyserNode.connect(destination);
|
|
1107
1265
|
}
|
|
1108
1266
|
return function cleanup() {
|
|
@@ -1115,42 +1273,45 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1115
1273
|
// Only fftSize - reads effects from ref
|
|
1116
1274
|
);
|
|
1117
1275
|
(0, import_react3.useEffect)(() => {
|
|
1276
|
+
isMountedRef.current = true;
|
|
1118
1277
|
const effectInstances = effectInstancesRef.current;
|
|
1119
1278
|
return () => {
|
|
1279
|
+
isMountedRef.current = false;
|
|
1120
1280
|
effectInstances.forEach((inst) => inst.dispose());
|
|
1121
1281
|
effectInstances.clear();
|
|
1122
1282
|
};
|
|
1123
1283
|
}, []);
|
|
1124
1284
|
const createOfflineEffectsFunction = (0, import_react3.useCallback)(() => {
|
|
1125
|
-
const
|
|
1126
|
-
if (
|
|
1285
|
+
const nonBypassed = activeEffects.filter((e) => !e.bypassed);
|
|
1286
|
+
if (nonBypassed.length === 0) {
|
|
1127
1287
|
return void 0;
|
|
1128
1288
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
if (offlineInstances.length === 0) {
|
|
1136
|
-
masterGainNode.connect(destination);
|
|
1137
|
-
} else {
|
|
1138
|
-
let currentNode = masterGainNode;
|
|
1139
|
-
offlineInstances.forEach((inst) => {
|
|
1140
|
-
currentNode.connect(inst.effect);
|
|
1141
|
-
currentNode = inst.effect;
|
|
1142
|
-
});
|
|
1143
|
-
currentNode.connect(destination);
|
|
1289
|
+
const hasWam = nonBypassed.some((e) => e.kind === "wam");
|
|
1290
|
+
return (masterGainNode, destination, _isOffline) => __async(null, null, function* () {
|
|
1291
|
+
if (hasWam && !(0, import_playout.isNativeGlobalContext)()) {
|
|
1292
|
+
throw new Error(
|
|
1293
|
+
"[waveform-playlist] WAV export with WAM effects requires a native AudioContext. Call configureGlobalContext({ nativeAudioContext: true }) from @waveform-playlist/playout before any audio initialization."
|
|
1294
|
+
);
|
|
1144
1295
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1296
|
+
const rawContext = masterGainNode.context.rawContext;
|
|
1297
|
+
const { instances, dispose } = yield buildOfflineChain(
|
|
1298
|
+
nonBypassed,
|
|
1299
|
+
(instanceId) => {
|
|
1300
|
+
const inst = effectInstancesRef.current.get(instanceId);
|
|
1301
|
+
return (inst == null ? void 0 : inst.kind) === "wam" ? inst.plugin : void 0;
|
|
1302
|
+
},
|
|
1303
|
+
rawContext
|
|
1304
|
+
);
|
|
1305
|
+
connectOfflineChain(masterGainNode, instances, destination);
|
|
1306
|
+
return dispose;
|
|
1307
|
+
});
|
|
1149
1308
|
}, [activeEffects]);
|
|
1150
1309
|
return {
|
|
1151
1310
|
activeEffects,
|
|
1152
1311
|
availableEffects: effectDefinitions,
|
|
1153
1312
|
addEffect,
|
|
1313
|
+
addWamEffect,
|
|
1314
|
+
getWamPlugin,
|
|
1154
1315
|
removeEffect,
|
|
1155
1316
|
updateParameter,
|
|
1156
1317
|
toggleBypass,
|
|
@@ -1164,11 +1325,14 @@ function useDynamicEffects(fftSize = 256) {
|
|
|
1164
1325
|
|
|
1165
1326
|
// src/hooks/useTrackDynamicEffects.ts
|
|
1166
1327
|
var import_react4 = require("react");
|
|
1328
|
+
var import_playout2 = require("@waveform-playlist/playout");
|
|
1329
|
+
var import_tone6 = require("tone");
|
|
1167
1330
|
function useTrackDynamicEffects() {
|
|
1168
1331
|
const [trackEffectsState, setTrackEffectsState] = (0, import_react4.useState)(
|
|
1169
1332
|
/* @__PURE__ */ new Map()
|
|
1170
1333
|
);
|
|
1171
1334
|
const trackEffectInstancesRef = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
|
|
1335
|
+
const isMountedRef = (0, import_react4.useRef)(true);
|
|
1172
1336
|
const trackGraphNodesRef = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
|
|
1173
1337
|
const rebuildTrackChain = (0, import_react4.useCallback)((trackId, trackEffects) => {
|
|
1174
1338
|
const nodes = trackGraphNodesRef.current.get(trackId);
|
|
@@ -1180,7 +1344,8 @@ function useTrackDynamicEffects() {
|
|
|
1180
1344
|
} catch (e) {
|
|
1181
1345
|
console.warn(`[waveform-playlist] Error disconnecting track "${trackId}" effect chain:`, e);
|
|
1182
1346
|
}
|
|
1183
|
-
const
|
|
1347
|
+
const audible = trackEffects.filter((ae) => !(ae.kind === "wam" && ae.bypassed));
|
|
1348
|
+
const instances = audible.map((ae) => instancesMap == null ? void 0 : instancesMap.get(ae.instanceId)).filter((inst) => inst !== void 0);
|
|
1184
1349
|
if (instances.length === 0) {
|
|
1185
1350
|
graphEnd.connect(masterGainNode);
|
|
1186
1351
|
} else {
|
|
@@ -1194,10 +1359,10 @@ function useTrackDynamicEffects() {
|
|
|
1194
1359
|
e
|
|
1195
1360
|
);
|
|
1196
1361
|
}
|
|
1197
|
-
|
|
1362
|
+
(0, import_tone6.connect)(currentNode, inst.effect);
|
|
1198
1363
|
currentNode = inst.effect;
|
|
1199
1364
|
});
|
|
1200
|
-
|
|
1365
|
+
(0, import_tone6.connect)(currentNode, masterGainNode);
|
|
1201
1366
|
}
|
|
1202
1367
|
}, []);
|
|
1203
1368
|
const addEffectToTrack = (0, import_react4.useCallback)((trackId, effectId) => {
|
|
@@ -1218,6 +1383,7 @@ function useTrackDynamicEffects() {
|
|
|
1218
1383
|
const newActiveEffect = {
|
|
1219
1384
|
instanceId: instance.instanceId,
|
|
1220
1385
|
effectId: definition.id,
|
|
1386
|
+
kind: "native",
|
|
1221
1387
|
definition,
|
|
1222
1388
|
params,
|
|
1223
1389
|
bypassed: false
|
|
@@ -1229,6 +1395,76 @@ function useTrackDynamicEffects() {
|
|
|
1229
1395
|
return newState;
|
|
1230
1396
|
});
|
|
1231
1397
|
}, []);
|
|
1398
|
+
const addWamEffectToTrack = (0, import_react4.useCallback)(
|
|
1399
|
+
(trackId, url, initialState) => __async(null, null, function* () {
|
|
1400
|
+
var _a, _b;
|
|
1401
|
+
if (!(0, import_playout2.isNativeGlobalContext)()) {
|
|
1402
|
+
throw new Error(
|
|
1403
|
+
"[waveform-playlist] WAM plugins require a native AudioContext. Call configureGlobalContext({ nativeAudioContext: true }) from @waveform-playlist/playout before any audio initialization."
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
const wam = yield loadWamModule();
|
|
1407
|
+
const ctx = (0, import_playout2.getGlobalAudioContext)();
|
|
1408
|
+
const { hostGroupId } = yield wam.ensureWamHost(ctx);
|
|
1409
|
+
const plugin = yield wam.createWamInstance(
|
|
1410
|
+
url,
|
|
1411
|
+
ctx,
|
|
1412
|
+
hostGroupId,
|
|
1413
|
+
initialState !== void 0 ? { initialState } : void 0
|
|
1414
|
+
);
|
|
1415
|
+
if (!isMountedRef.current) {
|
|
1416
|
+
try {
|
|
1417
|
+
plugin.destroy();
|
|
1418
|
+
} catch (err) {
|
|
1419
|
+
console.warn(
|
|
1420
|
+
"[waveform-playlist] Error destroying WAM plugin after unmount: " + (err instanceof Error ? err.message : String(err))
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1423
|
+
throw new Error(
|
|
1424
|
+
"[waveform-playlist] addWamEffectToTrack aborted: hook unmounted before the plugin finished loading."
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
const instance = createWamEffectInstance(plugin);
|
|
1428
|
+
if (!trackEffectInstancesRef.current.has(trackId)) {
|
|
1429
|
+
trackEffectInstancesRef.current.set(trackId, /* @__PURE__ */ new Map());
|
|
1430
|
+
}
|
|
1431
|
+
trackEffectInstancesRef.current.get(trackId).set(instance.instanceId, instance);
|
|
1432
|
+
const definition = {
|
|
1433
|
+
id: instance.id,
|
|
1434
|
+
name: (_b = (_a = plugin.descriptor) == null ? void 0 : _a.name) != null ? _b : url,
|
|
1435
|
+
category: "wam",
|
|
1436
|
+
description: "WAM plugin",
|
|
1437
|
+
parameters: []
|
|
1438
|
+
};
|
|
1439
|
+
setTrackEffectsState((prev) => {
|
|
1440
|
+
const newState = new Map(prev);
|
|
1441
|
+
const existing = newState.get(trackId) || [];
|
|
1442
|
+
newState.set(trackId, [
|
|
1443
|
+
...existing,
|
|
1444
|
+
{
|
|
1445
|
+
instanceId: instance.instanceId,
|
|
1446
|
+
effectId: instance.id,
|
|
1447
|
+
kind: "wam",
|
|
1448
|
+
url,
|
|
1449
|
+
definition,
|
|
1450
|
+
params: {},
|
|
1451
|
+
bypassed: false
|
|
1452
|
+
}
|
|
1453
|
+
]);
|
|
1454
|
+
return newState;
|
|
1455
|
+
});
|
|
1456
|
+
return instance.instanceId;
|
|
1457
|
+
}),
|
|
1458
|
+
[]
|
|
1459
|
+
);
|
|
1460
|
+
const getTrackWamPlugin = (0, import_react4.useCallback)(
|
|
1461
|
+
(trackId, instanceId) => {
|
|
1462
|
+
const instancesMap = trackEffectInstancesRef.current.get(trackId);
|
|
1463
|
+
const inst = instancesMap == null ? void 0 : instancesMap.get(instanceId);
|
|
1464
|
+
return (inst == null ? void 0 : inst.kind) === "wam" ? inst.plugin : void 0;
|
|
1465
|
+
},
|
|
1466
|
+
[]
|
|
1467
|
+
);
|
|
1232
1468
|
const removeEffectFromTrack = (0, import_react4.useCallback)((trackId, instanceId) => {
|
|
1233
1469
|
const instancesMap = trackEffectInstancesRef.current.get(trackId);
|
|
1234
1470
|
const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
|
|
@@ -1273,6 +1509,18 @@ function useTrackDynamicEffects() {
|
|
|
1273
1509
|
const effect = trackEffects.find((e) => e.instanceId === instanceId);
|
|
1274
1510
|
if (!effect) return;
|
|
1275
1511
|
const newBypassed = !effect.bypassed;
|
|
1512
|
+
if (effect.kind === "wam") {
|
|
1513
|
+
setTrackEffectsState((prev) => {
|
|
1514
|
+
const newState = new Map(prev);
|
|
1515
|
+
const existing = newState.get(trackId) || [];
|
|
1516
|
+
newState.set(
|
|
1517
|
+
trackId,
|
|
1518
|
+
existing.map((e) => e.instanceId === instanceId ? __spreadProps(__spreadValues({}, e), { bypassed: newBypassed }) : e)
|
|
1519
|
+
);
|
|
1520
|
+
return newState;
|
|
1521
|
+
});
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1276
1524
|
const instancesMap = trackEffectInstancesRef.current.get(trackId);
|
|
1277
1525
|
const instance = instancesMap == null ? void 0 : instancesMap.get(instanceId);
|
|
1278
1526
|
if (instance) {
|
|
@@ -1312,16 +1560,17 @@ function useTrackDynamicEffects() {
|
|
|
1312
1560
|
});
|
|
1313
1561
|
const trackEffects = trackEffectsStateRef.current.get(trackId) || [];
|
|
1314
1562
|
const instancesMap = trackEffectInstancesRef.current.get(trackId);
|
|
1315
|
-
const
|
|
1563
|
+
const audible = trackEffects.filter((ae) => !(ae.kind === "wam" && ae.bypassed));
|
|
1564
|
+
const instances = audible.map((ae) => instancesMap == null ? void 0 : instancesMap.get(ae.instanceId)).filter((inst) => inst !== void 0);
|
|
1316
1565
|
if (instances.length === 0) {
|
|
1317
1566
|
graphEnd.connect(masterGainNode);
|
|
1318
1567
|
} else {
|
|
1319
1568
|
let currentNode = graphEnd;
|
|
1320
1569
|
instances.forEach((inst) => {
|
|
1321
|
-
|
|
1570
|
+
(0, import_tone6.connect)(currentNode, inst.effect);
|
|
1322
1571
|
currentNode = inst.effect;
|
|
1323
1572
|
});
|
|
1324
|
-
|
|
1573
|
+
(0, import_tone6.connect)(currentNode, masterGainNode);
|
|
1325
1574
|
}
|
|
1326
1575
|
return function cleanup() {
|
|
1327
1576
|
trackGraphNodesRef.current.delete(trackId);
|
|
@@ -1337,8 +1586,10 @@ function useTrackDynamicEffects() {
|
|
|
1337
1586
|
});
|
|
1338
1587
|
}, [trackEffectsState, rebuildTrackChain]);
|
|
1339
1588
|
(0, import_react4.useEffect)(() => {
|
|
1589
|
+
isMountedRef.current = true;
|
|
1340
1590
|
const trackEffectInstances = trackEffectInstancesRef.current;
|
|
1341
1591
|
return () => {
|
|
1592
|
+
isMountedRef.current = false;
|
|
1342
1593
|
trackEffectInstances.forEach((instancesMap) => {
|
|
1343
1594
|
instancesMap.forEach((inst) => inst.dispose());
|
|
1344
1595
|
instancesMap.clear();
|
|
@@ -1349,36 +1600,38 @@ function useTrackDynamicEffects() {
|
|
|
1349
1600
|
const createOfflineTrackEffectsFunction = (0, import_react4.useCallback)(
|
|
1350
1601
|
(trackId) => {
|
|
1351
1602
|
const trackEffects = trackEffectsState.get(trackId) || [];
|
|
1352
|
-
const
|
|
1353
|
-
if (
|
|
1603
|
+
const nonBypassed = trackEffects.filter((e) => !e.bypassed);
|
|
1604
|
+
if (nonBypassed.length === 0) {
|
|
1354
1605
|
return void 0;
|
|
1355
1606
|
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
if (offlineInstances.length === 0) {
|
|
1363
|
-
graphEnd.connect(masterGainNode);
|
|
1364
|
-
} else {
|
|
1365
|
-
let currentNode = graphEnd;
|
|
1366
|
-
offlineInstances.forEach((inst) => {
|
|
1367
|
-
currentNode.connect(inst.effect);
|
|
1368
|
-
currentNode = inst.effect;
|
|
1369
|
-
});
|
|
1370
|
-
currentNode.connect(masterGainNode);
|
|
1607
|
+
const hasWam = nonBypassed.some((e) => e.kind === "wam");
|
|
1608
|
+
return (graphEnd, masterGainNode, _isOffline) => __async(null, null, function* () {
|
|
1609
|
+
if (hasWam && !(0, import_playout2.isNativeGlobalContext)()) {
|
|
1610
|
+
throw new Error(
|
|
1611
|
+
"[waveform-playlist] WAV export with WAM effects requires a native AudioContext. Call configureGlobalContext({ nativeAudioContext: true }) from @waveform-playlist/playout before any audio initialization."
|
|
1612
|
+
);
|
|
1371
1613
|
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1614
|
+
const rawContext = graphEnd.context.rawContext;
|
|
1615
|
+
const { instances, dispose } = yield buildOfflineChain(
|
|
1616
|
+
nonBypassed,
|
|
1617
|
+
(instanceId) => {
|
|
1618
|
+
var _a;
|
|
1619
|
+
const inst = (_a = trackEffectInstancesRef.current.get(trackId)) == null ? void 0 : _a.get(instanceId);
|
|
1620
|
+
return (inst == null ? void 0 : inst.kind) === "wam" ? inst.plugin : void 0;
|
|
1621
|
+
},
|
|
1622
|
+
rawContext
|
|
1623
|
+
);
|
|
1624
|
+
connectOfflineChain(graphEnd, instances, masterGainNode);
|
|
1625
|
+
return dispose;
|
|
1626
|
+
});
|
|
1376
1627
|
},
|
|
1377
1628
|
[trackEffectsState]
|
|
1378
1629
|
);
|
|
1379
1630
|
return {
|
|
1380
1631
|
trackEffectsState,
|
|
1381
1632
|
addEffectToTrack,
|
|
1633
|
+
addWamEffectToTrack,
|
|
1634
|
+
getTrackWamPlugin,
|
|
1382
1635
|
removeEffectFromTrack,
|
|
1383
1636
|
updateTrackEffectParameter,
|
|
1384
1637
|
toggleBypass,
|
|
@@ -1392,7 +1645,45 @@ function useTrackDynamicEffects() {
|
|
|
1392
1645
|
// src/hooks/useExportWav.ts
|
|
1393
1646
|
var import_react5 = require("react");
|
|
1394
1647
|
var import_core2 = require("@waveform-playlist/core");
|
|
1395
|
-
var
|
|
1648
|
+
var import_playout4 = require("@waveform-playlist/playout");
|
|
1649
|
+
|
|
1650
|
+
// src/utils/renderToneOffline.ts
|
|
1651
|
+
var import_tone7 = require("tone");
|
|
1652
|
+
var import_playout3 = require("@waveform-playlist/playout");
|
|
1653
|
+
var renderQueue = Promise.resolve();
|
|
1654
|
+
function renderToneOffline(build, duration, channels, sampleRate) {
|
|
1655
|
+
return __async(this, null, function* () {
|
|
1656
|
+
const run = renderQueue.then(
|
|
1657
|
+
() => renderToneOfflineSerial(build, duration, channels, sampleRate)
|
|
1658
|
+
);
|
|
1659
|
+
renderQueue = run.catch(() => void 0);
|
|
1660
|
+
return run;
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
function renderToneOfflineSerial(build, duration, channels, sampleRate) {
|
|
1664
|
+
return __async(this, null, function* () {
|
|
1665
|
+
const offlineContext = (0, import_playout3.isNativeGlobalContext)() ? new import_tone7.OfflineContext(
|
|
1666
|
+
new OfflineAudioContext({
|
|
1667
|
+
numberOfChannels: channels,
|
|
1668
|
+
length: Math.round(duration * sampleRate),
|
|
1669
|
+
sampleRate
|
|
1670
|
+
})
|
|
1671
|
+
) : new import_tone7.OfflineContext(channels, duration, sampleRate);
|
|
1672
|
+
const previousContext = (0, import_tone7.getContext)();
|
|
1673
|
+
(0, import_tone7.setContext)(offlineContext);
|
|
1674
|
+
try {
|
|
1675
|
+
yield build(offlineContext);
|
|
1676
|
+
} finally {
|
|
1677
|
+
(0, import_tone7.setContext)(previousContext);
|
|
1678
|
+
}
|
|
1679
|
+
const toneBuffer = yield offlineContext.render();
|
|
1680
|
+
const audioBuffer = toneBuffer.get();
|
|
1681
|
+
if (!audioBuffer) {
|
|
1682
|
+
throw new Error("Offline rendering produced no audio buffer");
|
|
1683
|
+
}
|
|
1684
|
+
return audioBuffer;
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1396
1687
|
|
|
1397
1688
|
// src/utils/wavEncoder.ts
|
|
1398
1689
|
function encodeWav(audioBuffer, options = {}) {
|
|
@@ -1491,7 +1782,7 @@ function useExportWav() {
|
|
|
1491
1782
|
if (mode === "individual" && (trackIndex === void 0 || trackIndex < 0 || trackIndex >= tracks.length)) {
|
|
1492
1783
|
throw new Error("Invalid track index for individual export");
|
|
1493
1784
|
}
|
|
1494
|
-
const sampleRate = (0,
|
|
1785
|
+
const sampleRate = (0, import_playout4.getGlobalAudioContext)().sampleRate;
|
|
1495
1786
|
let totalDurationSamples = 0;
|
|
1496
1787
|
for (const track of tracks) {
|
|
1497
1788
|
for (const clip of track.clips) {
|
|
@@ -1548,7 +1839,7 @@ function useExportWav() {
|
|
|
1548
1839
|
}
|
|
1549
1840
|
function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffects, effectsFunction, createOfflineTrackEffects, onProgress) {
|
|
1550
1841
|
return __async(this, null, function* () {
|
|
1551
|
-
const {
|
|
1842
|
+
const { Volume: Volume2, Gain: Gain2, Panner, Player, ToneAudioBuffer } = yield import("tone");
|
|
1552
1843
|
onProgress(0.1);
|
|
1553
1844
|
const audibleTracks = tracksToRender.filter(({ state }) => {
|
|
1554
1845
|
if (state.muted && !state.soloed) return false;
|
|
@@ -1559,23 +1850,25 @@ function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffec
|
|
|
1559
1850
|
(max, { track }) => Math.max(max, (0, import_core2.trackChannelCount)(track)),
|
|
1560
1851
|
1
|
|
1561
1852
|
);
|
|
1562
|
-
|
|
1853
|
+
const cleanups = [];
|
|
1563
1854
|
try {
|
|
1564
|
-
|
|
1565
|
-
(
|
|
1855
|
+
const audioBuffer = yield renderToneOffline(
|
|
1856
|
+
(context) => __async(null, null, function* () {
|
|
1566
1857
|
const masterVolume = new Volume2(0);
|
|
1567
1858
|
if (effectsFunction && applyEffects) {
|
|
1568
|
-
effectsFunction(masterVolume, destination, true);
|
|
1859
|
+
const cleanup = yield effectsFunction(masterVolume, context.destination, true);
|
|
1860
|
+
if (cleanup) cleanups.push(cleanup);
|
|
1569
1861
|
} else {
|
|
1570
|
-
masterVolume.connect(destination);
|
|
1862
|
+
masterVolume.connect(context.destination);
|
|
1571
1863
|
}
|
|
1572
1864
|
for (const { track, state } of audibleTracks) {
|
|
1573
1865
|
const trackVolume = new Volume2((0, import_core2.gainToDb)(state.volume));
|
|
1574
1866
|
const trackPan = new Panner({ pan: state.pan, channelCount: (0, import_core2.trackChannelCount)(track) });
|
|
1575
|
-
const trackMute = new
|
|
1867
|
+
const trackMute = new Gain2(state.muted ? 0 : 1);
|
|
1576
1868
|
const trackEffects = createOfflineTrackEffects == null ? void 0 : createOfflineTrackEffects(track.id);
|
|
1577
1869
|
if (trackEffects && applyEffects) {
|
|
1578
|
-
trackEffects(trackMute, masterVolume, true);
|
|
1870
|
+
const cleanup = yield trackEffects(trackMute, masterVolume, true);
|
|
1871
|
+
if (cleanup) cleanups.push(cleanup);
|
|
1579
1872
|
} else {
|
|
1580
1873
|
trackMute.connect(masterVolume);
|
|
1581
1874
|
}
|
|
@@ -1583,7 +1876,7 @@ function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffec
|
|
|
1583
1876
|
trackVolume.connect(trackPan);
|
|
1584
1877
|
for (const clip of track.clips) {
|
|
1585
1878
|
const {
|
|
1586
|
-
audioBuffer,
|
|
1879
|
+
audioBuffer: clipBuffer,
|
|
1587
1880
|
startSample,
|
|
1588
1881
|
durationSamples,
|
|
1589
1882
|
offsetSamples,
|
|
@@ -1591,7 +1884,7 @@ function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffec
|
|
|
1591
1884
|
fadeIn,
|
|
1592
1885
|
fadeOut
|
|
1593
1886
|
} = clip;
|
|
1594
|
-
if (!
|
|
1887
|
+
if (!clipBuffer) {
|
|
1595
1888
|
console.warn(
|
|
1596
1889
|
'[waveform-playlist] Skipping clip "' + (clip.name || clip.id) + '" - no audioBuffer for export'
|
|
1597
1890
|
);
|
|
@@ -1600,13 +1893,13 @@ function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffec
|
|
|
1600
1893
|
const startTime = startSample / sampleRate;
|
|
1601
1894
|
const clipDuration = durationSamples / sampleRate;
|
|
1602
1895
|
const offset = offsetSamples / sampleRate;
|
|
1603
|
-
const toneBuffer = new ToneAudioBuffer(
|
|
1896
|
+
const toneBuffer = new ToneAudioBuffer(clipBuffer);
|
|
1604
1897
|
const player = new Player(toneBuffer);
|
|
1605
|
-
const fadeGain = new
|
|
1898
|
+
const fadeGain = new Gain2(clipGain);
|
|
1606
1899
|
player.connect(fadeGain);
|
|
1607
1900
|
fadeGain.connect(trackVolume);
|
|
1608
1901
|
if (applyEffects) {
|
|
1609
|
-
const audioParam = (0,
|
|
1902
|
+
const audioParam = (0, import_playout4.getUnderlyingAudioParam)(fadeGain.gain);
|
|
1610
1903
|
if (audioParam) {
|
|
1611
1904
|
applyClipFades(audioParam, clipGain, startTime, clipDuration, fadeIn, fadeOut);
|
|
1612
1905
|
} else if (fadeIn || fadeOut) {
|
|
@@ -1618,25 +1911,29 @@ function renderOffline(tracksToRender, hasSolo, duration, sampleRate, applyEffec
|
|
|
1618
1911
|
player.start(startTime, offset, clipDuration);
|
|
1619
1912
|
}
|
|
1620
1913
|
}
|
|
1621
|
-
transport.start(0);
|
|
1914
|
+
context.transport.start(0);
|
|
1622
1915
|
}),
|
|
1623
1916
|
duration,
|
|
1624
1917
|
outputChannels,
|
|
1625
1918
|
sampleRate
|
|
1626
1919
|
);
|
|
1920
|
+
return audioBuffer;
|
|
1627
1921
|
} catch (err) {
|
|
1628
1922
|
if (err instanceof Error) {
|
|
1629
1923
|
throw err;
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1924
|
+
}
|
|
1925
|
+
throw new Error("Offline rendering failed: " + String(err));
|
|
1926
|
+
} finally {
|
|
1927
|
+
for (const cleanup of cleanups) {
|
|
1928
|
+
try {
|
|
1929
|
+
cleanup();
|
|
1930
|
+
} catch (err) {
|
|
1931
|
+
console.warn(
|
|
1932
|
+
"[waveform-playlist] Export cleanup error: " + (err instanceof Error ? err.message : String(err))
|
|
1933
|
+
);
|
|
1934
|
+
}
|
|
1632
1935
|
}
|
|
1633
1936
|
}
|
|
1634
|
-
onProgress(0.9);
|
|
1635
|
-
const result = buffer.get();
|
|
1636
|
-
if (!result) {
|
|
1637
|
-
throw new Error("Offline rendering produced no audio buffer");
|
|
1638
|
-
}
|
|
1639
|
-
return result;
|
|
1640
1937
|
});
|
|
1641
1938
|
}
|
|
1642
1939
|
function applyClipFades(gainParam, clipGain, startTime, clipDuration, fadeIn, fadeOut) {
|
|
@@ -1660,7 +1957,7 @@ function applyClipFades(gainParam, clipGain, startTime, clipDuration, fadeIn, fa
|
|
|
1660
1957
|
// src/hooks/useDynamicTracks.ts
|
|
1661
1958
|
var import_react6 = require("react");
|
|
1662
1959
|
var import_core3 = require("@waveform-playlist/core");
|
|
1663
|
-
var
|
|
1960
|
+
var import_playout5 = require("@waveform-playlist/playout");
|
|
1664
1961
|
function getSourceName(source) {
|
|
1665
1962
|
var _a, _b, _c, _d, _e;
|
|
1666
1963
|
if (source instanceof File) {
|
|
@@ -1711,7 +2008,7 @@ function useDynamicTracks() {
|
|
|
1711
2008
|
}, []);
|
|
1712
2009
|
const addTracks = (0, import_react6.useCallback)((sources) => {
|
|
1713
2010
|
if (sources.length === 0) return;
|
|
1714
|
-
const audioContext = (0,
|
|
2011
|
+
const audioContext = (0, import_playout5.getGlobalAudioContext)();
|
|
1715
2012
|
const placeholders = sources.map((source) => ({
|
|
1716
2013
|
track: (0, import_core3.createTrack)({ name: `${getSourceName(source)} (loading...)`, clips: [] }),
|
|
1717
2014
|
source
|
|
@@ -1782,7 +2079,7 @@ function useDynamicTracks() {
|
|
|
1782
2079
|
|
|
1783
2080
|
// src/hooks/useOutputMeter.ts
|
|
1784
2081
|
var import_react7 = require("react");
|
|
1785
|
-
var
|
|
2082
|
+
var import_playout6 = require("@waveform-playlist/playout");
|
|
1786
2083
|
var import_core4 = require("@waveform-playlist/core");
|
|
1787
2084
|
var import_worklets = require("@waveform-playlist/worklets");
|
|
1788
2085
|
var PEAK_DECAY = 0.98;
|
|
@@ -1810,7 +2107,7 @@ function useOutputMeter(options = {}) {
|
|
|
1810
2107
|
(0, import_react7.useEffect)(() => {
|
|
1811
2108
|
let isMounted = true;
|
|
1812
2109
|
const setup = () => __async(null, null, function* () {
|
|
1813
|
-
const context = (0,
|
|
2110
|
+
const context = (0, import_playout6.getGlobalContext)();
|
|
1814
2111
|
const rawCtx = context.rawContext;
|
|
1815
2112
|
yield rawCtx.audioWorklet.addModule(import_worklets.meterProcessorUrl);
|
|
1816
2113
|
if (!isMounted) return;
|
|
@@ -1859,7 +2156,7 @@ function useOutputMeter(options = {}) {
|
|
|
1859
2156
|
isMounted = false;
|
|
1860
2157
|
if (workletNodeRef.current) {
|
|
1861
2158
|
try {
|
|
1862
|
-
const context = (0,
|
|
2159
|
+
const context = (0, import_playout6.getGlobalContext)();
|
|
1863
2160
|
context.destination.chain();
|
|
1864
2161
|
} catch (err) {
|
|
1865
2162
|
console.warn("[waveform-playlist] Failed to restore destination chain:", String(err));
|
|
@@ -1943,15 +2240,78 @@ var ExportWavButton = ({
|
|
|
1943
2240
|
}
|
|
1944
2241
|
);
|
|
1945
2242
|
};
|
|
2243
|
+
|
|
2244
|
+
// src/components/WamEffectGui.tsx
|
|
2245
|
+
var import_react9 = require("react");
|
|
2246
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
2247
|
+
var WamEffectGui = ({ plugin, className }) => {
|
|
2248
|
+
const hostRef = (0, import_react9.useRef)(null);
|
|
2249
|
+
(0, import_react9.useEffect)(() => {
|
|
2250
|
+
const host = hostRef.current;
|
|
2251
|
+
if (!plugin || !host) return;
|
|
2252
|
+
let cancelled = false;
|
|
2253
|
+
let gui = null;
|
|
2254
|
+
(() => __async(null, null, function* () {
|
|
2255
|
+
var _a;
|
|
2256
|
+
try {
|
|
2257
|
+
if (plugin.createGui) {
|
|
2258
|
+
gui = yield plugin.createGui();
|
|
2259
|
+
} else {
|
|
2260
|
+
const wam = yield loadWamModule();
|
|
2261
|
+
gui = yield wam.createWamParameterPanel(
|
|
2262
|
+
plugin.audioNode
|
|
2263
|
+
);
|
|
2264
|
+
}
|
|
2265
|
+
if (cancelled) {
|
|
2266
|
+
if (gui) {
|
|
2267
|
+
try {
|
|
2268
|
+
(_a = plugin.destroyGui) == null ? void 0 : _a.call(plugin, gui);
|
|
2269
|
+
} catch (destroyErr) {
|
|
2270
|
+
console.warn(
|
|
2271
|
+
"[waveform-playlist] Error destroying WAM GUI: " + (destroyErr instanceof Error ? destroyErr.message : String(destroyErr))
|
|
2272
|
+
);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
gui = null;
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
host.innerHTML = "";
|
|
2279
|
+
host.appendChild(gui);
|
|
2280
|
+
} catch (err) {
|
|
2281
|
+
console.warn(
|
|
2282
|
+
"[waveform-playlist] Failed to create WAM GUI: " + (err instanceof Error ? err.message : String(err))
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2285
|
+
}))();
|
|
2286
|
+
return () => {
|
|
2287
|
+
var _a;
|
|
2288
|
+
cancelled = true;
|
|
2289
|
+
if (gui) {
|
|
2290
|
+
gui.remove();
|
|
2291
|
+
try {
|
|
2292
|
+
(_a = plugin.destroyGui) == null ? void 0 : _a.call(plugin, gui);
|
|
2293
|
+
} catch (destroyErr) {
|
|
2294
|
+
console.warn(
|
|
2295
|
+
"[waveform-playlist] Error destroying WAM GUI: " + (destroyErr instanceof Error ? destroyErr.message : String(destroyErr))
|
|
2296
|
+
);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
};
|
|
2300
|
+
}, [plugin]);
|
|
2301
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: hostRef, className: className != null ? className : "wam-effect-gui" });
|
|
2302
|
+
};
|
|
1946
2303
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1947
2304
|
0 && (module.exports = {
|
|
1948
2305
|
ExportWavButton,
|
|
2306
|
+
WamEffectGui,
|
|
1949
2307
|
createEffectChain,
|
|
1950
2308
|
createEffectInstance,
|
|
2309
|
+
createWamEffectInstance,
|
|
1951
2310
|
effectCategories,
|
|
1952
2311
|
effectDefinitions,
|
|
1953
2312
|
getEffectDefinition,
|
|
1954
2313
|
getEffectsByCategory,
|
|
2314
|
+
loadWamModule,
|
|
1955
2315
|
useAudioTracks,
|
|
1956
2316
|
useDynamicEffects,
|
|
1957
2317
|
useDynamicTracks,
|