hyperframes 0.8.186 → 0.8.188
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/{add-UGK7AZI6.js → add-UORF6235.js} +12 -12
- package/dist/{animatedGifPrep-HPZPSB43.js → animatedGifPrep-23MLSEXF.js} +7 -7
- package/dist/api.js +15 -15
- package/dist/{asset-X2GTFWOY.js → asset-DOKDVHCW.js} +2 -2
- package/dist/{assetCataloger-5NWEG4IU.js → assetCataloger-CD27MHEY.js} +7 -7
- package/dist/{audio-7PBM5F3H.js → audio-3NMZHNNU.js} +15 -15
- package/dist/{auth-WABSMO46.js → auth-AWKTSKGC.js} +1 -1
- package/dist/{autoUpdate-6SCMLTSC.js → autoUpdate-VBOI4CNU.js} +2 -2
- package/dist/{backgroundChecks-XOGPAAR3.js → backgroundChecks-S4R454YU.js} +3 -3
- package/dist/backgroundChecksWorker.js +3 -3
- package/dist/{batchRender-VR6IA77K.js → batchRender-EGPYNDHK.js} +4 -4
- package/dist/{beats-3IWXLMSC.js → beats-YENEQUOA.js} +4 -4
- package/dist/{benchmark-2SSYATCX.js → benchmark-THSJIQG7.js} +9 -9
- package/dist/{browser-PMRN345D.js → browser-BDMVQ6WJ.js} +3 -3
- package/dist/{bundleWithLocalizedFonts-TT32MZBO.js → bundleWithLocalizedFonts-3QG722YD.js} +2 -2
- package/dist/{capture-BDGYTE57.js → capture-WM4XDMFX.js} +2 -2
- package/dist/{capture-4VD6AKUJ.js → capture-Z6BGCTK3.js} +10 -10
- package/dist/{captureCompositionFrame-5FVLAETA.js → captureCompositionFrame-EMHSAIBZ.js} +11 -11
- package/dist/{catalog-HUYZO5D7.js → catalog-7TV6UNCG.js} +12 -12
- package/dist/{catch-up-SSGTR2ZT.js → catch-up-63IUDZRW.js} +4 -4
- package/dist/{check-JWUWHGM2.js → check-7RHZL4DV.js} +32 -32
- package/dist/{checkBrowser-X7G6YNR7.js → checkBrowser-PF56CT5X.js} +29 -29
- package/dist/{chunk-5ZMXGNGC.js → chunk-2DHCCBCN.js} +9 -9
- package/dist/{chunk-ZKBW7NU2.js → chunk-2WUDFZCR.js} +2 -2
- package/dist/{chunk-YNH6ERDL.js → chunk-44QWRPGE.js} +2 -2
- package/dist/{chunk-ACLABKEF.js → chunk-46VJ2UAV.js} +5 -5
- package/dist/{chunk-4AFNEKU7.js → chunk-57XL23DE.js} +1 -1
- package/dist/{chunk-J36JVBLR.js → chunk-5C7R55BJ.js} +1 -1
- package/dist/{chunk-WHMFW5ET.js → chunk-5DPV7LU3.js} +3 -3
- package/dist/{chunk-YQICCAVA.js → chunk-5OZI55AC.js} +1 -1
- package/dist/{chunk-5UBZPG74.js → chunk-6A45RKAK.js} +1 -1
- package/dist/{chunk-LDW3KVGK.js → chunk-6HYPEIH7.js} +2 -2
- package/dist/{chunk-2PZZMAME.js → chunk-6HZ7TSJJ.js} +13 -8
- package/dist/{chunk-ROWEDRVX.js → chunk-6OWNUXTU.js} +1 -1
- package/dist/{chunk-77QM6YRQ.js → chunk-6Q5CFUAP.js} +1 -1
- package/dist/{chunk-DTXDQ6OP.js → chunk-B2P4JRUJ.js} +1 -1
- package/dist/chunk-BPX7LZG6.js +3896 -0
- package/dist/{chunk-TH7J3JZ5.js → chunk-BU4HRISA.js} +15 -15
- package/dist/{chunk-UJZT6HNN.js → chunk-BWEZDN6U.js} +1 -1
- package/dist/{chunk-MY6CVBDD.js → chunk-CE7EQUBV.js} +1 -1
- package/dist/{chunk-TQ2RRB5V.js → chunk-DA4TQFSF.js} +1 -1
- package/dist/{chunk-RPUIMFHF.js → chunk-DG4ME554.js} +1 -1
- package/dist/{chunk-Y2M6XSNK.js → chunk-DLFAVRF3.js} +1 -1
- package/dist/{chunk-TTVHYD5R.js → chunk-EI7FP7F3.js} +1 -1
- package/dist/{chunk-22QKQDEE.js → chunk-FOLMMQF3.js} +11 -11
- package/dist/{chunk-HZ7M2MZA.js → chunk-FSTLXRIN.js} +1 -1
- package/dist/{chunk-4NTW45W5.js → chunk-FWVILDOZ.js} +3 -3
- package/dist/{chunk-FCWFV4P7.js → chunk-GYBPAKBM.js} +2269 -2270
- package/dist/{chunk-ID5AJ4L3.js → chunk-HKUZCSNZ.js} +2 -2
- package/dist/{chunk-YRAJDFIN.js → chunk-HPQSUPR7.js} +1 -1
- package/dist/{chunk-3YHGQJI6.js → chunk-I7C5GUEQ.js} +3 -3
- package/dist/{chunk-7MVODWQF.js → chunk-IA2IDJAG.js} +1 -1
- package/dist/{chunk-PEIV2SOG.js → chunk-IJYR4TTA.js} +1 -1
- package/dist/{chunk-GTMBII7X.js → chunk-JWRT2IXO.js} +1 -1
- package/dist/{chunk-2II34JPM.js → chunk-JYC53CW4.js} +11 -11
- package/dist/{chunk-U4BMRIE6.js → chunk-KDBPZOXK.js} +9 -7
- package/dist/{chunk-FRLTKTOR.js → chunk-KWSEIVR6.js} +1 -1
- package/dist/{chunk-27H2363S.js → chunk-KY7375C4.js} +1 -1
- package/dist/{chunk-KNLSYCI5.js → chunk-L243NE72.js} +7 -7
- package/dist/{chunk-R6IGQ5NO.js → chunk-LWY3PFJ7.js} +2 -2
- package/dist/{chunk-5ZMIVTHN.js → chunk-LWYZN7HS.js} +1 -1
- package/dist/{chunk-DQZMJS2P.js → chunk-MJKUCR3C.js} +1 -1
- package/dist/{chunk-7AUMTARQ.js → chunk-MTPPEEYZ.js} +3 -3
- package/dist/{chunk-UAJCTEK2.js → chunk-MXXX6WVC.js} +1 -1
- package/dist/{chunk-Z5233OAN.js → chunk-NFHRGHCJ.js} +5 -5
- package/dist/{chunk-T4RP55P2.js → chunk-NFLZNSUZ.js} +1 -1
- package/dist/{chunk-4XFFJAWS.js → chunk-NMOTNNP2.js} +1 -1
- package/dist/{chunk-WLHNIIXU.js → chunk-OJOZXWLC.js} +3 -3
- package/dist/{chunk-HOFX63F2.js → chunk-OKSYDQHY.js} +3 -3
- package/dist/{chunk-6IOBAOU7.js → chunk-PMXR3IUH.js} +1 -1
- package/dist/{chunk-RFJ6SX4U.js → chunk-PQLFJYGP.js} +5 -5
- package/dist/{chunk-PFD25NAC.js → chunk-RIJHDTM5.js} +4 -0
- package/dist/{chunk-BRTDLVK3.js → chunk-SMF6YAUL.js} +9 -11
- package/dist/{chunk-RVZNSKRH.js → chunk-T3OXKSRG.js} +2 -2
- package/dist/{chunk-Q447CX26.js → chunk-TSFA2Y5C.js} +60 -47
- package/dist/{chunk-NPDPDEPP.js → chunk-UF6RCYA5.js} +1 -1
- package/dist/{chunk-EZINX6HH.js → chunk-V6NM4KL3.js} +3 -3
- package/dist/{chunk-U7QTMRLW.js → chunk-W2KGJHUL.js} +2 -2
- package/dist/{chunk-7MLFYMFB.js → chunk-X556BGL7.js} +1 -1
- package/dist/{chunk-UJKQWGFF.js → chunk-X7HBSTCZ.js} +8 -0
- package/dist/{chunk-CML25PAF.js → chunk-XNFDDVJS.js} +27 -3
- package/dist/{chunk-ZVBEGBII.js → chunk-XPVN4R66.js} +3 -3
- package/dist/{chunk-7M7TMM4S.js → chunk-YCW345M4.js} +147 -118
- package/dist/{chunk-RNZEQTWZ.js → chunk-YV4WT7CJ.js} +4 -2
- package/dist/{chunk-K6KBOORN.js → chunk-YZ73IPW2.js} +1 -1
- package/dist/{clean-IM2JIAUT.js → clean-43O47OTN.js} +16 -16
- package/dist/cli.js +53 -53
- package/dist/{cloud-6IPBCJSX.js → cloud-ZFWKFFSU.js} +4 -4
- package/dist/{cloudrun-MWZ6S2I2.js → cloudrun-XBBESKEU.js} +5 -5
- package/dist/{compare-WD7A2TV5.js → compare-OW4ELTFZ.js} +26 -26
- package/dist/{compiler-2EKSMC3I.js → compiler-23MT6CX6.js} +4 -4
- package/dist/{component-JKALWH36.js → component-SDV7LWH6.js} +3 -3
- package/dist/{compositions-643NAG4A.js → compositions-DI5QZCEK.js} +11 -11
- package/dist/{delete-EASPXKHQ.js → delete-QY2NSQ2G.js} +4 -4
- package/dist/deploy-MJVC4EFY.js +300 -0
- package/dist/{destroy-4B5HW5FH.js → destroy-SSPAKADI.js} +13 -3
- package/dist/{dist-LM7AVHOU.js → dist-JH32CBQL.js} +1 -1
- package/dist/{dist-F6FOKRSU.js → dist-RFTGYT26.js} +3 -3
- package/dist/{doctor-7XUVYDJQ.js → doctor-7ZOECPHR.js} +11 -11
- package/dist/{events-TULV6D3G.js → events-WIX4JM2K.js} +3 -3
- package/dist/{events-5TTPYBKG.js → events-XVQQGECC.js} +2 -2
- package/dist/{feedback-4FTPWM6P.js → feedback-GBYBR6UO.js} +7 -7
- package/dist/{ffmpeg-MP7ZVYLZ.js → ffmpeg-QGQBYMO3.js} +7 -7
- package/dist/{figma-B7LFWYKO.js → figma-2CMKUGJQ.js} +3 -3
- package/dist/fontLocalizeCli.js +9 -9
- package/dist/{get-F7W4AQ2V.js → get-RWCNUOHU.js} +4 -4
- package/dist/{gpuPolicy-NSNVDFXC.js → gpuPolicy-UGWSN32D.js} +7 -7
- package/dist/{grade-compare-6QXDTLS2.js → grade-compare-J2MX7HZL.js} +27 -27
- package/dist/{help-RJAA4O74.js → help-SH3ID7MV.js} +1 -1
- package/dist/{history-OOQBDVV5.js → history-C7V5QH7J.js} +6 -6
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +65 -64
- package/dist/hyperframes-player.global.js +3 -3
- package/dist/{info-5OGGYYLO.js → info-ZN3JJ3IN.js} +8 -8
- package/dist/{init-DM3TJKW2.js → init-KRNGSRNH.js} +27 -27
- package/dist/{inspect-FECQCLS5.js → inspect-AYDZXTX7.js} +27 -27
- package/dist/{installer-WOG4VORV.js → installer-N6AIK6XQ.js} +5 -5
- package/dist/{keyframes-SZHWUF5Y.js → keyframes-2RFUCEDK.js} +6 -6
- package/dist/{lambda-JG2ZRGMC.js → lambda-VW3CF6EE.js} +20 -13
- package/dist/{layout-7ACF7ZKC.js → layout-OE64APE7.js} +27 -27
- package/dist/{lint-ZCQGMILV.js → lint-UXJTX5WS.js} +7 -7
- package/dist/{lintProject-JATQKR64.js → lintProject-XM3DLUKG.js} +1 -1
- package/dist/{list-MH2X3Y2T.js → list-KOL6JPZJ.js} +7 -7
- package/dist/{login-CK46WX5U.js → login-MAQXOPDG.js} +4 -4
- package/dist/{manager-GXTPNJVW.js → manager-3LZKRK7P.js} +8 -8
- package/dist/{media-treatment-EH2AT34Z.js → media-treatment-COLSXXQJ.js} +13 -13
- package/dist/{models-D5XP2UK6.js → models-XXWMGOD7.js} +11 -11
- package/dist/{motionShot-UR7D3SRQ.js → motionShot-RECBNTBZ.js} +14 -14
- package/dist/{normalize-audio-635JOR5W.js → normalize-audio-LKVGHZWR.js} +6 -6
- package/dist/{open-6WE5WFVM.js → open-UEQVSRKH.js} +12 -12
- package/dist/{parakeetRunner-STHZPG32.js → parakeetRunner-QVPCZ36U.js} +12 -12
- package/dist/{pipeline-LVEKRETU.js → pipeline-5VSMQBKW.js} +8 -8
- package/dist/{play-W6TDW4FE.js → play-MNEABYN4.js} +19 -19
- package/dist/{policies-XXNGXWHV.js → policies-MBWGLMWB.js} +17 -0
- package/dist/{preflight-LIMQXLR7.js → preflight-ONJ74DWQ.js} +8 -8
- package/dist/{present-RX5QX6UR.js → present-GQ24GTYX.js} +15 -15
- package/dist/{preview-KIWTWK63.js → preview-ALL7O4RG.js} +18 -18
- package/dist/{producer-TSNKSCUM.js → producer-43L2UDF6.js} +1 -1
- package/dist/{progress-NHJIHAAG.js → progress-PX3PVAIM.js} +5 -3
- package/dist/{projectConfig-NCP7JRH4.js → projectConfig-VJXUXK7U.js} +7 -7
- package/dist/{publication-5W5UZWGC.js → publication-L6E5NTFV.js} +3 -3
- package/dist/{publish-7WR4XJ6I.js → publish-LCLBJEPR.js} +9 -9
- package/dist/{remove-background-43JGBRWQ.js → remove-background-PDL4KPR4.js} +9 -9
- package/dist/{render-7T44HIM6.js → render-7HVKJ7T4.js} +9 -7
- package/dist/{render-456HCO6O.js → render-J33ZHR6I.js} +44 -28
- package/dist/{render-WHMLYIOI.js → render-OPCRLZVL.js} +31 -22
- package/dist/{render-batch-LURBJTBV.js → render-batch-ZHHIGXU3.js} +5 -5
- package/dist/renderSetupWorker.js +1 -1
- package/dist/{resolver-MBIW4VC4.js → resolver-KNNIMMBM.js} +5 -5
- package/dist/{sam-MC24SZHC.js → sam-FHJ7IL7T.js} +3 -1
- package/dist/{sherpa-5J2AALED.js → sherpa-GI62H3H5.js} +11 -11
- package/dist/{sites-MAVH57MO.js → sites-5PWHAMEG.js} +1 -1
- package/dist/skills/hyperframes/references/motion.md +1 -0
- package/dist/{skills-UHOXOGGF.js → skills-IUTD6VQO.js} +5 -5
- package/dist/{skillsUpdateCheck-PDZIFUJE.js → skillsUpdateCheck-XZE7MJG6.js} +3 -3
- package/dist/{snapshot-PFV7ALLH.js → snapshot-XEXTHLZP.js} +32 -32
- package/dist/{src-XK5YIR6L.js → src-6STRGCDC.js} +8 -8
- package/dist/{src-DCVSEODX.js → src-ZPIV4PGX.js} +10 -6
- package/dist/{state-ORRPOLMQ.js → state-JPNCCXAV.js} +3 -1
- package/dist/{staticProjectServer-4IE4G4JO.js → staticProjectServer-HJLT7MHE.js} +15 -15
- package/dist/studio/assets/{hyperframes-player-B_skzvYC.js → hyperframes-player-DwWIKKZG.js} +1 -1
- package/dist/studio/assets/{index-CgLbvCUo.js → index-Cjwz7_13.js} +1 -1
- package/dist/studio/assets/{index-Dn5FAk2e.js → index-M8lEpS0Q.js} +130 -130
- package/dist/studio/index.html +1 -1
- package/dist/{studio-api-JMWYU7KF.js → studio-api-OBHEB5ND.js} +9 -9
- package/dist/{studioServer-BWLSTGIG.js → studioServer-KRIZFYML.js} +45 -45
- package/dist/{telemetry-JCU3AV7W.js → telemetry-N5OOVBCP.js} +3 -3
- package/dist/templates/_shared/AGENTS.md +5 -0
- package/dist/templates/_shared/CLAUDE.md +5 -0
- package/dist/{timeline-BCIFQYOM.js → timeline-YUFH65OG.js} +17 -17
- package/dist/{tokens-7IXKALMQ.js → tokens-K4VYLSWJ.js} +2 -2
- package/dist/{transcribe-HW7HGJ4V.js → transcribe-CY4CVO54.js} +16 -16
- package/dist/{transcribe-ZUQD3B32.js → transcribe-GMQXKDYN.js} +10 -10
- package/dist/{updateCheck-Z6LNEK7B.js → updateCheck-2625RXK4.js} +2 -2
- package/dist/{upgrade-JYH72JGL.js → upgrade-6ZY5MSBQ.js} +2 -2
- package/dist/{urlDownloader-WKLFHSUH.js → urlDownloader-PVJWG7V3.js} +6 -6
- package/dist/{validate-QCC27YYI.js → validate-XSRHEE7E.js} +21 -21
- package/dist/{video-EZ7ONY6L.js → video-MBFHC54F.js} +7 -7
- package/dist/warmCheck.js +34 -34
- package/package.json +18 -2
- package/dist/chunk-UOZX5W5T.js +0 -3852
- package/dist/deploy-CDKCQ5MX.js +0 -196
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
renderFrameIdForRenderId,
|
|
27
27
|
resolveAuthoredTimingWindow,
|
|
28
28
|
toFps
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-YV4WT7CJ.js";
|
|
30
30
|
import {
|
|
31
31
|
AUDIO_GROUP_RENDER_ID_ATTR,
|
|
32
32
|
COLOR_GRADING_SOURCE_HIDDEN_ATTR,
|
|
@@ -43,7 +43,10 @@ import {
|
|
|
43
43
|
acceleratedCanvasTrackerSource,
|
|
44
44
|
enabledAudioFxNodes,
|
|
45
45
|
fxAutomationTarget,
|
|
46
|
+
gainedVolume,
|
|
47
|
+
gainedVolumePoints,
|
|
46
48
|
injectScriptsIntoHtml,
|
|
49
|
+
isBentSegment,
|
|
47
50
|
normalizeAudioFxParams,
|
|
48
51
|
normalizePlaybackRate,
|
|
49
52
|
normalizeRateSpec,
|
|
@@ -58,12 +61,14 @@ import {
|
|
|
58
61
|
serializeAutomation,
|
|
59
62
|
shiftRateLane,
|
|
60
63
|
sourceTimeAt,
|
|
61
|
-
timeAtSourceTime
|
|
62
|
-
|
|
64
|
+
timeAtSourceTime,
|
|
65
|
+
volumeSegmentSampleCount
|
|
66
|
+
} from "./chunk-BPX7LZG6.js";
|
|
63
67
|
import {
|
|
64
68
|
clampAudioGain,
|
|
69
|
+
clipGain,
|
|
65
70
|
isAudibleVideoElement
|
|
66
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-X7HBSTCZ.js";
|
|
67
72
|
import {
|
|
68
73
|
redactTelemetryString,
|
|
69
74
|
redactUrlQueryStrings
|
|
@@ -78,7 +83,7 @@ import {
|
|
|
78
83
|
} from "./chunk-4UMPIPCQ.js";
|
|
79
84
|
import {
|
|
80
85
|
resolveReferencedStart
|
|
81
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-TSFA2Y5C.js";
|
|
82
87
|
import {
|
|
83
88
|
FFMPEG_PATH_ENV,
|
|
84
89
|
FFPROBE_PATH_ENV,
|
|
@@ -2991,6 +2996,7 @@ async function injectResolvedVideoFramesBatch(page, updates) {
|
|
|
2991
2996
|
async (items, visualProperties, colorGradingSourceHiddenAttr) => {
|
|
2992
2997
|
const injectedIds = [];
|
|
2993
2998
|
const pendingDecodes = [];
|
|
2999
|
+
const imagesToPublish = [];
|
|
2994
3000
|
const replacementLayoutProperties = /* @__PURE__ */ new Set([
|
|
2995
3001
|
"width",
|
|
2996
3002
|
"height",
|
|
@@ -3062,6 +3068,7 @@ async function injectResolvedVideoFramesBatch(page, updates) {
|
|
|
3062
3068
|
img.style.objectFit = computedStyle.objectFit;
|
|
3063
3069
|
img.style.objectPosition = computedStyle.objectPosition;
|
|
3064
3070
|
img.style.zIndex = computedStyle.zIndex;
|
|
3071
|
+
img.style.setProperty("visibility", "hidden", "important");
|
|
3065
3072
|
img.decoding = "sync";
|
|
3066
3073
|
if (img.getAttribute("src") !== item.dataUri) {
|
|
3067
3074
|
if (item.crossOrigin) img.crossOrigin = item.crossOrigin;
|
|
@@ -3077,7 +3084,7 @@ async function injectResolvedVideoFramesBatch(page, updates) {
|
|
|
3077
3084
|
);
|
|
3078
3085
|
}
|
|
3079
3086
|
img.style.opacity = String(computedOpacity);
|
|
3080
|
-
img
|
|
3087
|
+
imagesToPublish.push(img);
|
|
3081
3088
|
video.style.setProperty("visibility", "hidden", "important");
|
|
3082
3089
|
video.style.setProperty("pointer-events", "none", "important");
|
|
3083
3090
|
injectedIds.push(item.videoId);
|
|
@@ -3085,6 +3092,7 @@ async function injectResolvedVideoFramesBatch(page, updates) {
|
|
|
3085
3092
|
if (pendingDecodes.length > 0) {
|
|
3086
3093
|
await Promise.all(pendingDecodes);
|
|
3087
3094
|
}
|
|
3095
|
+
for (const img of imagesToPublish) img.style.visibility = "visible";
|
|
3088
3096
|
if (injectedIds.length > 0) {
|
|
3089
3097
|
const redraw = window.__hf?.colorGrading?.redraw;
|
|
3090
3098
|
redraw?.();
|
|
@@ -3123,7 +3131,7 @@ async function syncVideoFrameVisibility(page, activeVideoIds) {
|
|
|
3123
3131
|
const img = video.nextElementSibling;
|
|
3124
3132
|
const hasImg = img && img.classList.contains("__render_frame__");
|
|
3125
3133
|
const ancestorHidden = isVisualAncestorHidden(video);
|
|
3126
|
-
const visible = active.has(video.id) && !ancestorHidden;
|
|
3134
|
+
const visible = active.has(window.__hfMediaId?.(video) || video.id) && !ancestorHidden;
|
|
3127
3135
|
if (visible) {
|
|
3128
3136
|
video.style.setProperty("visibility", "hidden", "important");
|
|
3129
3137
|
video.style.setProperty("pointer-events", "none", "important");
|
|
@@ -7634,6 +7642,57 @@ function getCapturePerfSummary(session) {
|
|
|
7634
7642
|
};
|
|
7635
7643
|
}
|
|
7636
7644
|
|
|
7645
|
+
// ../engine/src/services/extractedFrameIndex.ts
|
|
7646
|
+
import { readdirSync as readdirSync2 } from "fs";
|
|
7647
|
+
import { join as join4 } from "path";
|
|
7648
|
+
var FRAME_FILENAME_PREFIX = "frame_";
|
|
7649
|
+
function frameSequencePath(dir, framePattern) {
|
|
7650
|
+
return join4(dir.replaceAll("%", "%%"), framePattern);
|
|
7651
|
+
}
|
|
7652
|
+
var ExtractedFrameSequenceError = class extends Error {
|
|
7653
|
+
constructor(message) {
|
|
7654
|
+
super(message);
|
|
7655
|
+
this.name = "ExtractedFrameSequenceError";
|
|
7656
|
+
}
|
|
7657
|
+
};
|
|
7658
|
+
function extractedFrameIndex(file, format) {
|
|
7659
|
+
const match = new RegExp(`^${FRAME_FILENAME_PREFIX}(\\d+)\\.${format}$`).exec(file);
|
|
7660
|
+
if (!match) {
|
|
7661
|
+
throw new ExtractedFrameSequenceError(`Invalid extracted frame filename: ${file}`);
|
|
7662
|
+
}
|
|
7663
|
+
const ordinal = Number.parseInt(match[1], 10);
|
|
7664
|
+
if (!Number.isSafeInteger(ordinal) || ordinal < 1) {
|
|
7665
|
+
throw new ExtractedFrameSequenceError(`Invalid extracted frame ordinal: ${file}`);
|
|
7666
|
+
}
|
|
7667
|
+
return ordinal - 1;
|
|
7668
|
+
}
|
|
7669
|
+
function framePathsFromDirectory(outputDir, format, allowGaps = false) {
|
|
7670
|
+
const suffix = `.${format}`;
|
|
7671
|
+
const indexed = /* @__PURE__ */ new Map();
|
|
7672
|
+
for (const file of readdirSync2(outputDir)) {
|
|
7673
|
+
if (!file.startsWith(FRAME_FILENAME_PREFIX) || !file.endsWith(suffix)) continue;
|
|
7674
|
+
const index = extractedFrameIndex(file, format);
|
|
7675
|
+
if (indexed.has(index)) {
|
|
7676
|
+
throw new ExtractedFrameSequenceError(
|
|
7677
|
+
`Duplicate extracted frame index ${index}: ${indexed.get(index)} and ${file}`
|
|
7678
|
+
);
|
|
7679
|
+
}
|
|
7680
|
+
indexed.set(index, join4(outputDir, file));
|
|
7681
|
+
}
|
|
7682
|
+
if (allowGaps) return new Map([...indexed].sort(([a], [b]) => a - b));
|
|
7683
|
+
const ordered = /* @__PURE__ */ new Map();
|
|
7684
|
+
for (let index = 0; index < indexed.size; index += 1) {
|
|
7685
|
+
const path = indexed.get(index);
|
|
7686
|
+
if (!path) {
|
|
7687
|
+
throw new ExtractedFrameSequenceError(
|
|
7688
|
+
`Missing extracted frame index ${index} in ${outputDir}`
|
|
7689
|
+
);
|
|
7690
|
+
}
|
|
7691
|
+
ordered.set(index, path);
|
|
7692
|
+
}
|
|
7693
|
+
return ordered;
|
|
7694
|
+
}
|
|
7695
|
+
|
|
7637
7696
|
// ../engine/src/services/chunkEncoder.ts
|
|
7638
7697
|
import {
|
|
7639
7698
|
closeSync,
|
|
@@ -7644,12 +7703,12 @@ import {
|
|
|
7644
7703
|
mkdtempSync,
|
|
7645
7704
|
openSync,
|
|
7646
7705
|
readFileSync as readFileSync3,
|
|
7647
|
-
readdirSync as
|
|
7706
|
+
readdirSync as readdirSync3,
|
|
7648
7707
|
statSync as statSync3,
|
|
7649
7708
|
writeFileSync as writeFileSync2,
|
|
7650
7709
|
writeSync
|
|
7651
7710
|
} from "fs";
|
|
7652
|
-
import { join as
|
|
7711
|
+
import { join as join6, dirname as dirname2, extname } from "path";
|
|
7653
7712
|
|
|
7654
7713
|
// ../engine/src/utils/gpuEncoder.ts
|
|
7655
7714
|
import { spawn } from "child_process";
|
|
@@ -8936,7 +8995,7 @@ async function analyzeKeyframeIntervalsUncached(filePath, videoStreamStartSecond
|
|
|
8936
8995
|
|
|
8937
8996
|
// ../engine/src/utils/renderProvenance.ts
|
|
8938
8997
|
import { readFileSync as readFileSync2 } from "fs";
|
|
8939
|
-
import { dirname, join as
|
|
8998
|
+
import { dirname, join as join5 } from "path";
|
|
8940
8999
|
import { fileURLToPath } from "url";
|
|
8941
9000
|
var PROVENANCE_RENDERER_TAG = "hyperframes_renderer";
|
|
8942
9001
|
var PROVENANCE_VERSION_TAG = "hyperframes_version";
|
|
@@ -8947,7 +9006,7 @@ var MAX_WALK_DEPTH = 12;
|
|
|
8947
9006
|
function resolveOwnVersionFrom(startDir) {
|
|
8948
9007
|
let dir = startDir;
|
|
8949
9008
|
for (let depth = 0; depth < MAX_WALK_DEPTH; depth++) {
|
|
8950
|
-
const candidate =
|
|
9009
|
+
const candidate = join5(dir, "package.json");
|
|
8951
9010
|
let pkg;
|
|
8952
9011
|
try {
|
|
8953
9012
|
pkg = JSON.parse(readFileSync2(candidate, "utf8"));
|
|
@@ -9280,7 +9339,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
9280
9339
|
const startTime = Date.now();
|
|
9281
9340
|
const outputDir = dirname2(outputPath);
|
|
9282
9341
|
if (!existsSync5(outputDir)) mkdirSync2(outputDir, { recursive: true });
|
|
9283
|
-
const files =
|
|
9342
|
+
const files = readdirSync3(framesDir).filter((f) => f.match(/\.(jpg|jpeg|png)$/i));
|
|
9284
9343
|
const frameCount = files.length;
|
|
9285
9344
|
if (frameCount === 0) {
|
|
9286
9345
|
return {
|
|
@@ -9296,7 +9355,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
9296
9355
|
if (options.useGpu) {
|
|
9297
9356
|
gpuEncoder = await getCachedGpuEncoder();
|
|
9298
9357
|
}
|
|
9299
|
-
const inputPath =
|
|
9358
|
+
const inputPath = frameSequencePath(framesDir, framePattern);
|
|
9300
9359
|
const inputArgs = ["-framerate", fpsToFfmpegArg(options.fps), "-i", inputPath];
|
|
9301
9360
|
const args = buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder);
|
|
9302
9361
|
const encodeTimeout = config?.ffmpegEncodeTimeout ?? DEFAULT_CONFIG.ffmpegEncodeTimeout;
|
|
@@ -9352,7 +9411,7 @@ function buildConcatArgs(concatListPath, outputPath) {
|
|
|
9352
9411
|
var concatListSeq = 0;
|
|
9353
9412
|
function writeConcatList(dir, inputPaths) {
|
|
9354
9413
|
concatListSeq += 1;
|
|
9355
|
-
const listPath =
|
|
9414
|
+
const listPath = join6(dir, `concat-list-${process.pid}-${concatListSeq}.txt`);
|
|
9356
9415
|
const body = inputPaths.map((p) => `file '${p.replace(/'/g, "'\\''")}'`).join("\n");
|
|
9357
9416
|
writeFileSync2(listPath, body, "utf-8");
|
|
9358
9417
|
return listPath;
|
|
@@ -9382,7 +9441,7 @@ async function concatVideoFiles(inputPaths, outputPath, signal, config) {
|
|
|
9382
9441
|
}
|
|
9383
9442
|
async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, options, chunkSizeFrames, signal, config, onFramesEncoded) {
|
|
9384
9443
|
const start = Date.now();
|
|
9385
|
-
const files =
|
|
9444
|
+
const files = readdirSync3(framesDir).filter((f) => f.match(/\.(jpg|jpeg|png)$/i)).sort();
|
|
9386
9445
|
if (files.length === 0) {
|
|
9387
9446
|
return {
|
|
9388
9447
|
success: false,
|
|
@@ -9396,7 +9455,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
9396
9455
|
const chunkSize = Math.max(30, Math.floor(chunkSizeFrames));
|
|
9397
9456
|
const chunkCount = Math.ceil(files.length / chunkSize);
|
|
9398
9457
|
mkdirSync2(dirname2(outputPath), { recursive: true });
|
|
9399
|
-
const chunkDir = mkdtempSync(
|
|
9458
|
+
const chunkDir = mkdtempSync(join6(dirname2(outputPath), "chunk-encode-"));
|
|
9400
9459
|
const chunkPaths = [];
|
|
9401
9460
|
for (let i = 0; i < chunkCount; i++) {
|
|
9402
9461
|
if (signal?.aborted) {
|
|
@@ -9412,8 +9471,8 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
9412
9471
|
const startNumber = i * chunkSize;
|
|
9413
9472
|
const framesInChunk = Math.min(chunkSize, files.length - startNumber);
|
|
9414
9473
|
const ext = outputPath.endsWith(".webm") ? ".webm" : outputPath.endsWith(".mov") ? ".mov" : ".mp4";
|
|
9415
|
-
const chunkPath =
|
|
9416
|
-
const inputPath =
|
|
9474
|
+
const chunkPath = join6(chunkDir, `chunk_${String(i).padStart(4, "0")}${ext}`);
|
|
9475
|
+
const inputPath = frameSequencePath(framesDir, framePattern);
|
|
9417
9476
|
const inputArgs = [
|
|
9418
9477
|
"-framerate",
|
|
9419
9478
|
fpsToFfmpegArg(options.fps),
|
|
@@ -9582,14 +9641,14 @@ async function packageHls(videoPath, audioPath, outputDir, options) {
|
|
|
9582
9641
|
"-master_pl_name",
|
|
9583
9642
|
HLS_MASTER_PLAYLIST,
|
|
9584
9643
|
"-hls_segment_filename",
|
|
9585
|
-
|
|
9644
|
+
join6(outputDir, "%v_%05d.ts"),
|
|
9586
9645
|
// Without these the mpegts muxer starts the stream at PTS 1.4 s.
|
|
9587
9646
|
"-muxdelay",
|
|
9588
9647
|
"0",
|
|
9589
9648
|
"-muxpreload",
|
|
9590
9649
|
"0"
|
|
9591
9650
|
);
|
|
9592
|
-
args.push("-y",
|
|
9651
|
+
args.push("-y", join6(outputDir, "%v.m3u8"));
|
|
9593
9652
|
const processTimeout = options.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
9594
9653
|
const result = await runFfmpeg(args, { signal, timeout: processTimeout });
|
|
9595
9654
|
if (signal?.aborted) {
|
|
@@ -9601,7 +9660,7 @@ async function packageHls(videoPath, audioPath, outputDir, options) {
|
|
|
9601
9660
|
};
|
|
9602
9661
|
}
|
|
9603
9662
|
if (result.success && hasAudio) {
|
|
9604
|
-
const masterPath =
|
|
9663
|
+
const masterPath = join6(outputDir, HLS_MASTER_PLAYLIST);
|
|
9605
9664
|
let master;
|
|
9606
9665
|
try {
|
|
9607
9666
|
master = openSync(masterPath, "r+", 384);
|
|
@@ -10222,7 +10281,7 @@ function isKnownInactiveTimelineWindow(element, resolvedStart) {
|
|
|
10222
10281
|
// ../engine/src/utils/alphaPlaneProbe.ts
|
|
10223
10282
|
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, rmSync } from "fs";
|
|
10224
10283
|
import { tmpdir as tmpdir2 } from "os";
|
|
10225
|
-
import { join as
|
|
10284
|
+
import { join as join7 } from "path";
|
|
10226
10285
|
var BYTES_PER_SAMPLE_FRAME = 8 * 8 * 4;
|
|
10227
10286
|
var MAX_SAMPLE_BYTES = BYTES_PER_SAMPLE_FRAME * 3;
|
|
10228
10287
|
function sampledRgbaAlphaIsFullyOpaque(buf) {
|
|
@@ -10237,8 +10296,8 @@ function sampledRgbaAlphaIsFullyOpaque(buf) {
|
|
|
10237
10296
|
async function probeInputAlphaPlane(videoPath, decoder, signal) {
|
|
10238
10297
|
let probeDir;
|
|
10239
10298
|
try {
|
|
10240
|
-
probeDir = mkdtempSync2(
|
|
10241
|
-
const samplePath =
|
|
10299
|
+
probeDir = mkdtempSync2(join7(tmpdir2(), "hf-alpha-probe-"));
|
|
10300
|
+
const samplePath = join7(probeDir, "alpha.raw");
|
|
10242
10301
|
const result = await runFfmpeg(
|
|
10243
10302
|
[
|
|
10244
10303
|
"-v",
|
|
@@ -10284,7 +10343,7 @@ import {
|
|
|
10284
10343
|
mkdirSync as mkdirSync4,
|
|
10285
10344
|
lstatSync,
|
|
10286
10345
|
openSync as openSync2,
|
|
10287
|
-
readdirSync as
|
|
10346
|
+
readdirSync as readdirSync4,
|
|
10288
10347
|
rmdirSync,
|
|
10289
10348
|
rmSync as rmSync2,
|
|
10290
10349
|
statSync as statSync5,
|
|
@@ -10292,7 +10351,7 @@ import {
|
|
|
10292
10351
|
} from "fs";
|
|
10293
10352
|
import { createHash, randomUUID as randomUUID2 } from "crypto";
|
|
10294
10353
|
import { BlockList, isIP } from "net";
|
|
10295
|
-
import { dirname as dirname4, extname as extname2, join as
|
|
10354
|
+
import { dirname as dirname4, extname as extname2, join as join8 } from "path";
|
|
10296
10355
|
import { Readable, Transform } from "stream";
|
|
10297
10356
|
import { pipeline } from "stream/promises";
|
|
10298
10357
|
var inFlightDownloads = /* @__PURE__ */ new Map();
|
|
@@ -10462,7 +10521,7 @@ function sameCacheLockStatGeneration(left, right) {
|
|
|
10462
10521
|
function observeCachePathLock(lockPath) {
|
|
10463
10522
|
for (let pass = 0; pass < 3; pass += 1) {
|
|
10464
10523
|
const before = lstatSync(lockPath);
|
|
10465
|
-
const owner =
|
|
10524
|
+
const owner = readdirSync4(lockPath).find((name) => name.startsWith(CACHE_LOCK_OWNER_PREFIX));
|
|
10466
10525
|
const after = lstatSync(lockPath);
|
|
10467
10526
|
if (sameCacheLockStatGeneration(before, after)) return { stats: after, owner };
|
|
10468
10527
|
}
|
|
@@ -10484,7 +10543,7 @@ function removeCacheLockDirectoryIfEmpty(lockPath) {
|
|
|
10484
10543
|
}
|
|
10485
10544
|
function releaseOwnedCachePathLock(lockPath, owner) {
|
|
10486
10545
|
try {
|
|
10487
|
-
rmdirSync(
|
|
10546
|
+
rmdirSync(join8(lockPath, owner));
|
|
10488
10547
|
} catch (error) {
|
|
10489
10548
|
if (error.code === "ENOENT") return;
|
|
10490
10549
|
throw error;
|
|
@@ -10525,12 +10584,12 @@ async function acquireCachePathLock(localPath, timeoutMs, signal) {
|
|
|
10525
10584
|
if (createdLock) {
|
|
10526
10585
|
const owner = `${CACHE_LOCK_OWNER_PREFIX}${randomUUID2()}`;
|
|
10527
10586
|
try {
|
|
10528
|
-
mkdirSync4(
|
|
10529
|
-
const entries =
|
|
10587
|
+
mkdirSync4(join8(lockPath, owner));
|
|
10588
|
+
const entries = readdirSync4(lockPath);
|
|
10530
10589
|
if (entries.length === 1 && entries[0] === owner) {
|
|
10531
10590
|
return () => releaseOwnedCachePathLock(lockPath, owner);
|
|
10532
10591
|
}
|
|
10533
|
-
rmdirSync(
|
|
10592
|
+
rmdirSync(join8(lockPath, owner));
|
|
10534
10593
|
removeCacheLockDirectoryIfEmpty(lockPath);
|
|
10535
10594
|
continue;
|
|
10536
10595
|
} catch (error) {
|
|
@@ -10548,7 +10607,7 @@ async function acquireCachePathLock(localPath, timeoutMs, signal) {
|
|
|
10548
10607
|
if (Date.now() - observedLock.stats.mtimeMs > CACHE_LOCK_STALE_MS) {
|
|
10549
10608
|
if (observedLock.owner) {
|
|
10550
10609
|
try {
|
|
10551
|
-
rmdirSync(
|
|
10610
|
+
rmdirSync(join8(lockPath, observedLock.owner));
|
|
10552
10611
|
} catch (error) {
|
|
10553
10612
|
if (error.code === "ENOENT") continue;
|
|
10554
10613
|
throw error;
|
|
@@ -10556,7 +10615,7 @@ async function acquireCachePathLock(localPath, timeoutMs, signal) {
|
|
|
10556
10615
|
removeCacheLockDirectoryIfEmpty(lockPath);
|
|
10557
10616
|
continue;
|
|
10558
10617
|
}
|
|
10559
|
-
const reclaimPath =
|
|
10618
|
+
const reclaimPath = join8(lockPath, CACHE_LOCK_RECLAIM_NAME);
|
|
10560
10619
|
try {
|
|
10561
10620
|
mkdirSync4(reclaimPath);
|
|
10562
10621
|
} catch (error) {
|
|
@@ -10996,8 +11055,8 @@ function emitDownloadTelemetry(options, event) {
|
|
|
10996
11055
|
}
|
|
10997
11056
|
}
|
|
10998
11057
|
async function runDownloadAttempt(url, localPath, timeoutMs, attempt, options, signal) {
|
|
10999
|
-
const attemptDir = mkdtempSync3(
|
|
11000
|
-
const partialPath =
|
|
11058
|
+
const attemptDir = mkdtempSync3(join8(dirname4(localPath), ".hf-download-"));
|
|
11059
|
+
const partialPath = join8(attemptDir, "payload");
|
|
11001
11060
|
const controller = new AbortController();
|
|
11002
11061
|
let timedOut = false;
|
|
11003
11062
|
let callerAborted = signal?.aborted ?? false;
|
|
@@ -11233,7 +11292,7 @@ async function downloadToTemp(url, destDir, timeoutMs = 3e5, signal, onTransient
|
|
|
11233
11292
|
mkdirSync4(destDir, { recursive: true });
|
|
11234
11293
|
}
|
|
11235
11294
|
const filename = getFilenameFromUrl(url, validationScope);
|
|
11236
|
-
const localPath =
|
|
11295
|
+
const localPath = join8(destDir, filename);
|
|
11237
11296
|
const downloadPromise = (async () => {
|
|
11238
11297
|
const cacheStartedAt = Date.now();
|
|
11239
11298
|
const reused = await reuseOrInvalidateCachedFile(
|
|
@@ -11317,56 +11376,6 @@ import {
|
|
|
11317
11376
|
writeFileSync as writeFileSync3
|
|
11318
11377
|
} from "fs";
|
|
11319
11378
|
import { join as join9 } from "path";
|
|
11320
|
-
|
|
11321
|
-
// ../engine/src/services/extractedFrameIndex.ts
|
|
11322
|
-
import { readdirSync as readdirSync4 } from "fs";
|
|
11323
|
-
import { join as join8 } from "path";
|
|
11324
|
-
var FRAME_FILENAME_PREFIX = "frame_";
|
|
11325
|
-
var ExtractedFrameSequenceError = class extends Error {
|
|
11326
|
-
constructor(message) {
|
|
11327
|
-
super(message);
|
|
11328
|
-
this.name = "ExtractedFrameSequenceError";
|
|
11329
|
-
}
|
|
11330
|
-
};
|
|
11331
|
-
function extractedFrameIndex(file, format) {
|
|
11332
|
-
const match = new RegExp(`^${FRAME_FILENAME_PREFIX}(\\d+)\\.${format}$`).exec(file);
|
|
11333
|
-
if (!match) {
|
|
11334
|
-
throw new ExtractedFrameSequenceError(`Invalid extracted frame filename: ${file}`);
|
|
11335
|
-
}
|
|
11336
|
-
const ordinal = Number.parseInt(match[1], 10);
|
|
11337
|
-
if (!Number.isSafeInteger(ordinal) || ordinal < 1) {
|
|
11338
|
-
throw new ExtractedFrameSequenceError(`Invalid extracted frame ordinal: ${file}`);
|
|
11339
|
-
}
|
|
11340
|
-
return ordinal - 1;
|
|
11341
|
-
}
|
|
11342
|
-
function framePathsFromDirectory(outputDir, format, allowGaps = false) {
|
|
11343
|
-
const suffix = `.${format}`;
|
|
11344
|
-
const indexed = /* @__PURE__ */ new Map();
|
|
11345
|
-
for (const file of readdirSync4(outputDir)) {
|
|
11346
|
-
if (!file.startsWith(FRAME_FILENAME_PREFIX) || !file.endsWith(suffix)) continue;
|
|
11347
|
-
const index = extractedFrameIndex(file, format);
|
|
11348
|
-
if (indexed.has(index)) {
|
|
11349
|
-
throw new ExtractedFrameSequenceError(
|
|
11350
|
-
`Duplicate extracted frame index ${index}: ${indexed.get(index)} and ${file}`
|
|
11351
|
-
);
|
|
11352
|
-
}
|
|
11353
|
-
indexed.set(index, join8(outputDir, file));
|
|
11354
|
-
}
|
|
11355
|
-
if (allowGaps) return new Map([...indexed].sort(([a], [b]) => a - b));
|
|
11356
|
-
const ordered = /* @__PURE__ */ new Map();
|
|
11357
|
-
for (let index = 0; index < indexed.size; index += 1) {
|
|
11358
|
-
const path = indexed.get(index);
|
|
11359
|
-
if (!path) {
|
|
11360
|
-
throw new ExtractedFrameSequenceError(
|
|
11361
|
-
`Missing extracted frame index ${index} in ${outputDir}`
|
|
11362
|
-
);
|
|
11363
|
-
}
|
|
11364
|
-
ordered.set(index, path);
|
|
11365
|
-
}
|
|
11366
|
-
return ordered;
|
|
11367
|
-
}
|
|
11368
|
-
|
|
11369
|
-
// ../engine/src/services/extractionCache.ts
|
|
11370
11379
|
var COMPLETE_SENTINEL = ".hf-complete";
|
|
11371
11380
|
var GC_MARKER = ".hf-last-gc";
|
|
11372
11381
|
var SCHEMA_PREFIX = "hfcache-v8-";
|
|
@@ -12118,7 +12127,7 @@ async function extractVideoFramesRangeUnredacted(videoPath, videoId, startTime,
|
|
|
12118
12127
|
}
|
|
12119
12128
|
const format = resolveFrameFormat(metadata, options.format);
|
|
12120
12129
|
const framePattern = `${FRAME_FILENAME_PREFIX}%05d.${format}`;
|
|
12121
|
-
const outputPattern =
|
|
12130
|
+
const outputPattern = frameSequencePath(videoOutputDir, framePattern);
|
|
12122
12131
|
const isHdr = isHdrColorSpace(metadata.colorSpace);
|
|
12123
12132
|
const isMacOS = process.platform === "darwin";
|
|
12124
12133
|
const toneMappedToSdr = options.toneMapHdrToSdr === true && isHdr && await toneMapsWithZscale();
|
|
@@ -14054,6 +14063,8 @@ async function encodeAac(input, outputPath, totalDuration, {
|
|
|
14054
14063
|
segments.map(
|
|
14055
14064
|
({ trim, path }) => runFfmpeg(
|
|
14056
14065
|
[
|
|
14066
|
+
"-cpuflags",
|
|
14067
|
+
"0",
|
|
14057
14068
|
"-i",
|
|
14058
14069
|
input,
|
|
14059
14070
|
"-af",
|
|
@@ -14579,7 +14590,7 @@ import { join as join13 } from "path";
|
|
|
14579
14590
|
import { pathToFileURL } from "url";
|
|
14580
14591
|
|
|
14581
14592
|
// ../core/dist/generated/audio-fx-runtime-inline.js
|
|
14582
|
-
var AUDIO_FX_RUNTIME_IIFE = '"use strict";(()=>{var we="data-fx-chain",Ft=we.slice(5),W=1,F=(e="frequency",t="Frequency",n=1e3,o=20,a=2e4)=>({kind:"number",key:e,label:t,unit:"Hz",min:o,max:a,step:1,default:n,scale:"log",automatable:!0}),C=(e=.707,t="Bandwidth: higher is narrower.")=>({kind:"number",key:"q",label:"Q",unit:"",min:.1,max:20,step:.01,default:e,scale:"log",automatable:!0,hint:t}),N=(e=-40,t=40,n=0)=>({kind:"number",key:"gain",label:"Gain",unit:"dB",min:e,max:t,step:.1,default:n,automatable:!0}),K={kind:"enum",key:"poles",label:"Slope",options:[{value:"1",label:"6 dB/oct"},{value:"2",label:"12 dB/oct"}],default:"2",hint:"Two poles is the usual biquad; one pole is gentler."},z=[{id:"gain",label:"Gain",group:"dynamics",description:"Raise or lower the whole signal. Automate it to duck under something else.",params:[N(-60,12,0)],web:"gain-node"},{id:"peaking",label:"Peaking EQ",group:"filter",description:"Boost or cut a band, leaving everything either side alone.",params:[F("frequency","Frequency",1e3),N(-40,40,0),C(1)],web:"biquad-peaking"},{id:"lowshelf",label:"Low Shelf",group:"filter",description:"Lift or drop everything below the corner frequency.",params:[F("frequency","Frequency",200,20,2e3),N(-40,40,0)],web:"biquad-lowshelf"},{id:"highshelf",label:"High Shelf",group:"filter",description:"Lift or drop everything above the corner frequency.",params:[F("frequency","Frequency",4e3,500,2e4),N(-40,40,0)],web:"biquad-highshelf"},{id:"highpass",label:"High-pass",group:"filter",description:"Remove low frequencies: the usual fix for rumble on a voice.",params:[F("frequency","Cutoff",300,20,2e4),C(.707),K],web:"biquad-highpass"},{id:"lowpass",label:"Low-pass",group:"filter",description:"Remove high frequencies, which darkens or muffles a track.",params:[F("frequency","Cutoff",8e3,100,2e4),C(.707),K],web:"biquad-lowpass"},{id:"compressor",label:"Compressor",group:"dynamics",description:"Pull loud parts down so the quiet ones can come up.",params:[{kind:"number",key:"threshold",label:"Threshold",unit:"dB",min:-60,max:0,step:.5,default:-24,hint:"Level above which the compressor starts working."},{kind:"number",key:"ratio",label:"Ratio",unit:":1",min:1,max:20,step:.1,default:4},{kind:"number",key:"attack",label:"Attack",unit:"ms",min:.01,max:2e3,step:.1,default:20,scale:"log"},{kind:"number",key:"release",label:"Release",unit:"ms",min:.01,max:9e3,step:1,default:250,scale:"log"},{kind:"number",key:"knee",label:"Knee",unit:"",min:1,max:8,step:.01,default:2.83,hint:"1 is a hard corner; higher eases into it."},{kind:"number",key:"makeup",label:"Makeup",unit:"dB",min:0,max:36,step:.1,default:0},{kind:"number",key:"mix",label:"Mix",unit:"",min:0,max:1,step:.01,default:1,hint:"Below 1 blends the dry signal back in."}],web:"worklet-compressor"},{id:"limiter",label:"Limiter",group:"dynamics",description:"Peak limiter that follows the signal\'s level. It has no lookahead, so a fast peak or an inter-sample peak can pass the ceiling; use the true-peak limiter for a delivery limit.",params:[{kind:"number",key:"limit",label:"Ceiling",unit:"dB",min:-24,max:0,step:.1,default:-1},{kind:"number",key:"attack",label:"Attack",unit:"ms",min:.1,max:80,step:.1,default:5},{kind:"number",key:"release",label:"Release",unit:"ms",min:1,max:8e3,step:1,default:50,scale:"log"},{kind:"number",key:"level_out",label:"Output",unit:"dB",min:-24,max:24,step:.1,default:0}],web:"worklet-limiter"},{id:"truepeak",label:"True-peak limiter",group:"dynamics",description:"Lookahead limiter that holds a 4x estimate of the inter-sample peak at a dBTP ceiling; full-band noise can land up to about 1.7 dB above it. Adds lookahead plus about 0.3 ms of latency.",params:[{kind:"number",key:"ceiling",label:"Ceiling",unit:"dBTP",min:-24,max:0,step:.1,default:-1,hint:"Target true peak, measured on a 4x oversampled signal. Dense full-band sound can end up to about 1.7 dB above it."},{kind:"number",key:"lookahead",label:"Lookahead",unit:"ms",min:.5,max:10,step:.1,default:3,hint:"How far ahead the gain starts falling. Longer is smoother and adds the same latency."},{kind:"number",key:"release",label:"Release",unit:"ms",min:10,max:2e3,step:1,default:80,scale:"log"}],web:"worklet-truepeak"},{id:"gate",label:"Noise Gate",group:"dynamics",description:"Silence the track when it drops below the threshold.",params:[{kind:"number",key:"threshold",label:"Threshold",unit:"dB",min:-80,max:0,step:.5,default:-35},{kind:"number",key:"range",label:"Range",unit:"dB",min:-80,max:0,step:.5,default:-24,hint:"How far down the gate pulls when closed."},{kind:"number",key:"ratio",label:"Ratio",unit:":1",min:1,max:20,step:.1,default:10},{kind:"number",key:"attack",label:"Attack",unit:"ms",min:.01,max:9e3,step:.1,default:1,scale:"log"},{kind:"number",key:"release",label:"Release",unit:"ms",min:.01,max:9e3,step:1,default:100,scale:"log"},{kind:"number",key:"knee",label:"Knee",unit:"",min:1,max:8,step:.01,default:2.83}],web:"worklet-gate"},{id:"saturate",label:"Saturation",group:"nonlinear",description:"Soft-clip the waveform for warmth or outright distortion.",params:[{kind:"enum",key:"type",label:"Curve",options:[{value:"tanh",label:"Tanh"},{value:"atan",label:"Arctan"},{value:"cubic",label:"Cubic"},{value:"exp",label:"Exponential"},{value:"alg",label:"Algebraic"},{value:"quintic",label:"Quintic"},{value:"sin",label:"Sine"},{value:"erf",label:"Error function"},{value:"hard",label:"Hard clip"}],default:"tanh"},{kind:"number",key:"threshold",label:"Threshold",unit:"dB",min:-40,max:0,step:.1,default:-6},{kind:"number",key:"output",automatable:!0,label:"Output",unit:"dB",min:-24,max:24,step:.1,default:0},{kind:"number",key:"oversample",label:"Oversample",unit:"x",min:1,max:8,step:1,default:4,hint:"Higher costs more but keeps aliasing down."}],web:"waveshaper"},{id:"bitcrush",label:"Bitcrush",group:"nonlinear",description:"Drop bit depth and sample rate for a lo-fi, digital sound.",params:[{kind:"number",key:"bits",label:"Bit depth",unit:"bit",min:1,max:32,step:.1,default:8},{kind:"number",key:"samples",label:"Sample hold",unit:"x",min:1,max:250,step:1,default:1,hint:"Repeats each sample N times, a crude downsample."},{kind:"number",key:"mix",label:"Mix",unit:"",min:0,max:1,step:.01,default:1}],web:"worklet-bitcrush"},{id:"pitchshift",label:"Pitch shift",group:"time",description:"Shifts pitch up or down without changing playback speed. Adds ~50 ms of latency.",params:[{kind:"number",key:"semitones",label:"Semitones",unit:"st",min:-12,max:12,step:1,default:0},{kind:"number",key:"mix",label:"Mix",unit:"",min:0,max:1,step:.01,default:1}],web:"worklet-pitchshift"},{id:"delay",label:"Delay",group:"time",description:"Repeating echoes behind the dry signal.",params:[{kind:"number",key:"time",automatable:!0,label:"Time",unit:"ms",min:1,max:5e3,step:1,default:250,scale:"log"},{kind:"number",key:"feedback",automatable:!0,label:"Feedback",unit:"",min:.01,max:.95,step:.01,default:.35},{kind:"number",key:"mix",automatable:!0,label:"Mix",unit:"",min:0,max:1,step:.01,default:.4}],web:"delay-feedback"},{id:"chorus",label:"Chorus",group:"time",description:"Detuned copies of the signal for width and thickness.",params:[{kind:"number",key:"delay",automatable:!0,label:"Delay",unit:"ms",min:1,max:100,step:.1,default:7},{kind:"number",key:"depth",automatable:!0,label:"Depth",unit:"ms",min:0,max:10,step:.01,default:2},{kind:"number",key:"speed",automatable:!0,label:"Rate",unit:"Hz",min:.01,max:10,step:.01,default:1},{kind:"number",key:"mix",automatable:!0,label:"Mix",unit:"",min:0,max:1,step:.01,default:.5}],web:"chorus-lfo"},{id:"phaser",label:"Phaser",group:"time",description:"Sweeping notches moving through the spectrum.",params:[{kind:"number",key:"in_gain",automatable:!0,label:"Input",unit:"",min:0,max:1,step:.01,default:.4},{kind:"number",key:"out_gain",automatable:!0,label:"Output",unit:"",min:0,max:2,step:.01,default:.74},{kind:"number",key:"delay",label:"Delay",unit:"ms",min:.1,max:5,step:.1,default:3},{kind:"number",key:"decay",label:"Decay",unit:"",min:0,max:.99,step:.01,default:.4},{kind:"number",key:"speed",automatable:!0,label:"Rate",unit:"Hz",min:.1,max:2,step:.01,default:.5},{kind:"enum",key:"type",label:"Waveform",options:[{value:"0",label:"Triangular"},{value:"1",label:"Sinusoidal"}],default:"0"}],web:"allpass-phaser"},{id:"reverb",label:"Reverb",group:"time",description:"Room tail. Both ends convolve the same generated impulse, so preview matches render.",params:[{kind:"number",key:"size",label:"Room size",unit:"",min:.05,max:1,step:.01,default:.7},{kind:"number",key:"damping",label:"Damping",unit:"",min:0,max:1,step:.01,default:.5,hint:"Higher rolls the top off the tail faster."},{kind:"number",key:"wet",automatable:!0,label:"Wet",unit:"",min:0,max:1,step:.01,default:.35},{kind:"number",key:"dry",automatable:!0,label:"Dry",unit:"",min:0,max:1,step:.01,default:.7}],web:"convolver"}],S=new Map(z.map(e=>[e.id,e]));function P(e){return S.get(e)}var Pt=z.map(e=>e.id);function y(e,t){let n=S.get(e);if(!n)return{};let o={};for(let a of n.params){let r=t?.[a.key];if(a.kind==="enum"){let s=a.options.some(u=>u.value===r);o[a.key]=s?r:a.default;continue}let i=typeof r=="number"?r:typeof r=="string"&&r.trim()!==""?Number(r):Number.NaN;o[a.key]=Number.isFinite(i)?Math.min(a.max,Math.max(a.min,i)):a.default}return o}var x=class extends Error{constructor(t){super(t),this.name="AudioFxChainError"}};function J(e){let t;try{t=JSON.parse(e)}catch(a){throw new x(`Chain file is not valid JSON: ${a.message}`)}if(typeof t!="object"||t===null)throw new x("Chain file must be a JSON object.");let n=t;if(n.version!==W)throw new x(`Unsupported chain version: ${String(n.version)}`);if(!Array.isArray(n.nodes))throw new x("Chain file is missing a `nodes` array.");let o=n.nodes.map(ve);return{version:W,nodes:o}}function w(e){return e.nodes.filter(t=>t.enabled!==!1)}function ve(e,t){if(typeof e!="object"||e===null)throw new x(`Node ${t} is not an object.`);let n=e;if(typeof n.type!="string"||!S.has(n.type))throw new x(`Node ${t} has unknown effect type: ${String(n.type)}`);return Te({type:n.type,id:E(n.id),fromCarve:Y(n.fromCarve),fromPreset:E(n.fromPreset),label:E(n.label),fromEq:E(n.fromEq),fromLeveller:Y(n.fromLeveller),presetAmount:Me(n.presetAmount),enabled:n.enabled!==!1,params:y(n.type,n.params??void 0)})}function E(e){return typeof e=="string"&&e?e:void 0}function Y(e){return e===!0?!0:void 0}function Me(e){if(!(typeof e!="number"||!Number.isFinite(e)))return Math.min(1,Math.max(0,e))}function Te(e){for(let t of Object.keys(e))e[t]===void 0&&delete e[t];return e}function Fe(e,t){return Math.max(1,Math.round(e*t/1e3))}function Q(e,t){return Fe(e,t)+16}var Z=`\nconst TP_OS = 4;\nconst TP_HALF = 64;\nconst TP_BRANCH = 33;\nconst TP_DELAY = 16;\n\nfunction besselI0(x) {\n let sum = 1, term = 1;\n for (let k = 1; k < 40; k++) {\n term *= (x / (2 * k)) * (x / (2 * k));\n sum += term;\n }\n return sum;\n}\n\n/** Kaiser-windowed sinc, one array per polyphase branch, newest tap first. */\nfunction tpBranches() {\n const beta = 6;\n const norm = besselI0(beta);\n const branches = [];\n for (let p = 0; p < TP_OS; p++) {\n const taps = new Float64Array(TP_BRANCH);\n let sum = 0;\n for (let j = 0; j < TP_BRANCH; j++) {\n const k = p + TP_OS * j;\n if (k > 2 * TP_HALF) continue;\n const t = (k - TP_HALF) / TP_OS;\n const sinc = t === 0 ? 1 : Math.sin(Math.PI * t) / (Math.PI * t);\n const r = (k - TP_HALF) / TP_HALF;\n taps[j] = sinc * (besselI0(beta * Math.sqrt(Math.max(0, 1 - r * r))) / norm);\n sum += taps[j];\n }\n // Unity gain at DC on every branch, so a steady level reads as itself.\n for (let j = 0; j < TP_BRANCH; j++) taps[j] /= sum;\n branches.push(taps);\n }\n return branches;\n}\n\nclass HfTruePeak extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.look = Math.max(1, Math.round((this.p.lookahead ?? 3) * sampleRate / 1000));\n this.delayLen = this.look + TP_DELAY;\n this.branches = tpBranches();\n this.setLevels();\n this.clear();\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n if (e.data && e.data.__hfReset) { this.clear(); return; }\n this.p = { ...this.p, ...e.data };\n this.setLevels();\n };\n }\n /** Back to the state of a fresh processor: no audio in the delay line, no gain reduction. */\n clear() {\n this.ch = [];\n this.rel = 1;\n this.dqIdx = new Int32Array(this.look + 2);\n this.dqVal = new Float64Array(this.look + 2);\n this.dqHead = 0;\n this.dqTail = 0;\n this.t = 0;\n this.avgRing = new Float64Array(this.look + 1).fill(1);\n this.avgPos = 0;\n this.avgSum = this.look + 1;\n }\n setLevels() {\n this.ceiling = Math.pow(10, (this.p.ceiling ?? -1) / 20);\n this.relCoef = 1 - Math.exp(-1 / (sampleRate * Math.max(1e-4, (this.p.release ?? 80) / 1000)));\n }\n channel(c) {\n if (!this.ch[c]) {\n this.ch[c] = {\n delay: new Float32Array(this.delayLen),\n w: 0,\n hist: new Float32Array(TP_BRANCH * 2),\n pos: 0,\n };\n }\n return this.ch[c];\n }\n /** Largest interpolated magnitude across channels for the newest sample. */\n peak(chans, inputs, n) {\n let peak = 0;\n for (let c = 0; c < chans; c++) {\n const s = this.channel(c);\n const x = inputs[c][n];\n s.pos = (s.pos + 1) % TP_BRANCH;\n s.hist[s.pos] = x;\n s.hist[s.pos + TP_BRANCH] = x;\n const base = s.pos + TP_BRANCH;\n for (let p = 0; p < TP_OS; p++) {\n const taps = this.branches[p];\n let acc = 0;\n for (let j = 0; j < TP_BRANCH; j++) acc += taps[j] * s.hist[base - j];\n const a = acc < 0 ? -acc : acc;\n if (a > peak) peak = a;\n }\n }\n return peak;\n }\n /** Windowed minimum of the required gain over the last look + 1 values. */\n windowMin(v) {\n const cap = this.dqIdx.length;\n while (this.dqTail !== this.dqHead && this.dqVal[(this.dqTail + cap - 1) % cap] >= v) {\n this.dqTail = (this.dqTail + cap - 1) % cap;\n }\n this.dqIdx[this.dqTail] = this.t;\n this.dqVal[this.dqTail] = v;\n this.dqTail = (this.dqTail + 1) % cap;\n while (this.dqIdx[this.dqHead] <= this.t - (this.look + 1)) {\n this.dqHead = (this.dqHead + 1) % cap;\n }\n this.t++;\n return this.dqVal[this.dqHead];\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const chans = i.length;\n const frames = i[0].length;\n for (let n = 0; n < frames; n++) {\n const peak = this.peak(chans, i, n);\n const need = peak > this.ceiling ? this.ceiling / peak : 1;\n const m = this.windowMin(need);\n this.avgSum += m - this.avgRing[this.avgPos];\n this.avgRing[this.avgPos] = m;\n this.avgPos = (this.avgPos + 1) % this.avgRing.length;\n const target = Math.min(1, this.avgSum / this.avgRing.length);\n const released = this.rel + (1 - this.rel) * this.relCoef;\n this.rel = released < target ? released : target;\n if (target >= 1 && this.rel > 0.9999999) this.rel = 1;\n for (let c = 0; c < chans; c++) {\n const s = this.channel(c);\n o[c][n] = s.delay[s.w] * this.rel;\n s.delay[s.w] = i[c][n];\n s.w = (s.w + 1) % this.delayLen;\n }\n }\n return true;\n }\n}\nregisterProcessor("hf-truepeak", HfTruePeak);\n`;var Pe=`\nconst dbToLin = (db) => Math.pow(10, db / 20);\n\n/**\n * One-pole envelope followers, one per channel.\n *\n * Per channel matters: the followers advance once per sample, so a single shared\n * follower stepped once per channel per sample. On stereo that ran a 20 ms attack\n * as 10 ms, and gave the right channel a gain computed from an envelope that had\n * already traversed the left, so the two channels ducked by different amounts\n * from the same input and the image pumped.\n */\nclass EnvBank {\n constructor(attackMs, releaseMs) { this.set(attackMs, releaseMs); this.values = []; }\n set(attackMs, releaseMs) {\n this.a = Math.exp(-1 / (sampleRate * Math.max(1e-5, attackMs / 1000)));\n this.r = Math.exp(-1 / (sampleRate * Math.max(1e-5, releaseMs / 1000)));\n }\n push(ch, x) {\n const prev = this.values[ch] ?? 0;\n const m = Math.abs(x);\n const c = m > prev ? this.a : this.r;\n const next = m + c * (prev - m);\n this.values[ch] = next;\n return next;\n }\n}\n\n/** Soft-knee gain computer in dB, matching acompressor\'s shape. */\nfunction kneeGain(envDb, thresholdDb, ratio, kneeDb) {\n const over = envDb - thresholdDb;\n if (kneeDb > 0 && over > -kneeDb && over < kneeDb) {\n const t = (over + kneeDb) / (2 * kneeDb);\n return -((1 - 1 / ratio) * kneeDb * t * t);\n }\n return over > 0 ? -(over * (1 - 1 / ratio)) : 0;\n}\n\n// log10/exp per sample is the single most expensive thing a dynamics processor\n// can do on the audio thread, and every sample below the knee needs neither:\n// its gain is exactly unity. Comparing envelopes in the linear domain lets the\n// quiet majority of samples skip the transcendentals entirely.\nconst LN10_OVER_20 = Math.LN10 / 20;\nconst dbToLinFast = (db) => Math.exp(db * LN10_OVER_20);\n\nclass HfCompressor extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.env = new EnvBank(this.p.attack ?? 20, this.p.release ?? 250);\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n this.p = { ...this.p, ...e.data };\n this.env.set(this.p.attack ?? 20, this.p.release ?? 250);\n };\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const p = this.p;\n const makeup = dbToLin(p.makeup ?? 0);\n const mix = p.mix ?? 1;\n // Knee is expressed as a ratio in FFmpeg; convert to dB width.\n const kneeDb = 20 * Math.log10(Math.max(1.0001, p.knee ?? 2.83));\n const thresholdDb = p.threshold ?? -24;\n const ratio = p.ratio ?? 4;\n // Below this the gain computer returns unity, so the sample needs no\n // logarithm at all.\n const kneeStartLin = dbToLin(thresholdDb - kneeDb);\n const dry = 1 - mix;\n for (let ch = 0; ch < i.length; ch++) {\n const inp = i[ch], out = o[ch];\n for (let n = 0; n < inp.length; n++) {\n const x = inp[n];\n // Per-channel follower, and the sub-knee shortcut: below the knee the\n // gain is exactly unity, so the sample needs no logarithm at all.\n const env = this.env.push(ch, x);\n if (env <= kneeStartLin) {\n out[n] = x * makeup * mix + x * dry;\n continue;\n }\n const envDb = 20 * Math.log10(env);\n const g = dbToLinFast(kneeGain(envDb, thresholdDb, ratio, kneeDb));\n out[n] = x * g * makeup * mix + x * dry;\n }\n }\n return true;\n }\n}\nregisterProcessor("hf-compressor", HfCompressor);\n\nclass HfLimiter extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.env = new EnvBank(this.p.attack ?? 5, this.p.release ?? 50);\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n this.p = { ...this.p, ...e.data };\n this.env.set(this.p.attack ?? 5, this.p.release ?? 50);\n };\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const ceiling = dbToLin(this.p.limit ?? -1);\n const outGain = dbToLin(this.p.level_out ?? 0);\n for (let ch = 0; ch < i.length; ch++) {\n const inp = i[ch], out = o[ch];\n for (let n = 0; n < inp.length; n++) {\n const x = inp[n];\n const env = this.env.push(ch, x);\n // Only ever attenuate: a limiter that can raise level is a compressor.\n const g = env > ceiling ? ceiling / env : 1;\n out[n] = x * g * outGain;\n }\n }\n return true;\n }\n}\nregisterProcessor("hf-limiter", HfLimiter);\n\nclass HfGate extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.env = new EnvBank(this.p.attack ?? 1, this.p.release ?? 100);\n this.gains = [];\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n this.p = { ...this.p, ...e.data };\n this.env.set(this.p.attack ?? 1, this.p.release ?? 100);\n };\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const p = this.p;\n const threshold = dbToLin(p.threshold ?? -35);\n const floor = dbToLin(p.range ?? -24);\n const ratio = p.ratio ?? 10;\n // Knee is declared in the registry as a ratio, like the compressor\'s; a\n // hard threshold ignored it and chattered on material sitting right at the\n // gate point.\n const kneeDb = 20 * Math.log10(Math.max(1.0001, p.knee ?? 2.83));\n const kneeLin = dbToLin((p.threshold ?? -35) + kneeDb);\n for (let ch = 0; ch < i.length; ch++) {\n const inp = i[ch], out = o[ch];\n for (let n = 0; n < inp.length; n++) {\n const x = inp[n];\n const env = this.env.push(ch, x);\n let target = 1;\n // Above the threshold the gate is fully open; skip the pow entirely.\n if (env < threshold) {\n const under = env > 1e-9 ? threshold / env : 1e9;\n target = Math.max(floor, 1 / Math.pow(under, ratio - 1));\n if (!isFinite(target)) target = floor;\n }\n // Smooth toward the target so the gate does not click on every sample.\n const held = this.gains[ch] ?? 1;\n const c = target < held ? this.env.r : this.env.a;\n const smoothed = target + c * (held - target);\n this.gains[ch] = smoothed;\n out[n] = x * smoothed;\n }\n }\n return true;\n }\n}\nregisterProcessor("hf-gate", HfGate);\n\nclass HfBitcrush extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.holds = [];\n this.held = [];\n this.start = Math.round((this.p.__hfElapsed ?? 0) * sampleRate);\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n this.p = { ...this.p, ...e.data };\n };\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const p = this.p;\n const levels = Math.pow(2, (p.bits ?? 8) - 1);\n const step = Math.max(1, Math.round(p.samples ?? 1));\n const mix = p.mix ?? 1;\n for (let ch = 0; ch < i.length; ch++) {\n const inp = i[ch], out = o[ch];\n // Primed from the input: a counter seeded mid-hold would otherwise play 0 until it wraps.\n if (this.held[ch] === undefined) this.held[ch] = Math.round(inp[0] * levels) / levels;\n // The hold counter runs from the file\'s first sample, so a node built part-way in starts where it would be.\n if (this.holds[ch] === undefined) this.holds[ch] = this.start % step;\n for (let n = 0; n < inp.length; n++) {\n // Per channel: advancing one shared counter only on the last channel\n // left every earlier channel either unheld or frozen for a whole\n // 128-sample quantum, clicking once a block.\n if (this.holds[ch] === 0) this.held[ch] = Math.round(inp[n] * levels) / levels;\n out[n] = this.held[ch] * mix + inp[n] * (1 - mix);\n this.holds[ch] = (this.holds[ch] + 1) % step;\n }\n }\n return true;\n }\n}\nregisterProcessor("hf-bitcrush", HfBitcrush);\n\n/** Linear-interpolated read, \\`delaySamples\\` behind the write head. */\nfunction readTap(ring, write, delaySamples) {\n const len = ring.length;\n const pos = (write - 1 - delaySamples + len) % len;\n const i0 = Math.floor(pos);\n const frac = pos - i0;\n const i1 = (i0 + 1) % len;\n return ring[i0] * (1 - frac) + ring[i1] * frac;\n}\n\n/** Equal-power-ish crossfade, zero at a tap\'s reset point, which hides the splice. */\nfunction xfade(phase) {\n return Math.sin(Math.PI * phase);\n}\n\n/**\n * Dual-tap granular delay line: two read taps 180\\xB0 apart in a 100 ms grain,\n * each sweeping at a speed relative to the write head that shifts pitch\n * without changing duration. One tap is always fading in as the other fades\n * out, which hides the splice each tap makes when it wraps.\n *\n * write/phase are block-level state, advanced once per SAMPLE across all\n * channels together (not once per channel). Advancing them inside the\n * per-channel loop would move the tap 2x/4x too fast on a stereo/quad input.\n */\nclass HfPitchshift extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.grain = Math.round(sampleRate * 0.1);\n this.buf = [];\n this.write = 0;\n // The taps sweep at a constant rate from the start of the file, so a node built\n // part-way in joins the sweep where it would already be.\n const ratio0 = Math.pow(2, Math.max(-12, Math.min(12, this.p.semitones ?? 0)) / 12);\n const turns = (this.p.__hfElapsed ?? 0) * sampleRate * ((1 - ratio0) / this.grain);\n this.phase = turns - Math.floor(turns);\n // Samples written so far, capped at one grain. The taps read up to a grain\n // behind the write head, so until this fills they would read the ring\'s\n // zeros, so the head of every clip came out attenuated or silent.\n this.filled = 0;\n // How much of the wet (pitch-shifted) path is currently in the output, and\n // where it is heading. Crossing between dry and wet is a ~50 ms jump in the\n // signal, so it is RAMPED rather than switched: a hard swap either way is a\n // click. Ramping in both directions is also what lets a node return to true\n // bypass at semitones 0. A one-way latch left preview stuck with the delay\n // that the render, building a fresh node from the attribute, does not have.\n this.wet = 0;\n this.wetTarget = 0;\n // ~15 ms one-pole, short enough to feel immediate on a slider drag and long\n // enough that the splice is inaudible.\n this.wetCoef = Math.exp(-1 / (sampleRate * 0.015));\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n this.p = { ...this.p, ...e.data };\n };\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const p = this.p;\n const semitones = Math.max(-12, Math.min(12, p.semitones ?? 0));\n const mix = Math.max(0, Math.min(1, p.mix ?? 1));\n const grain = this.grain;\n const ringLen = grain * 2;\n const n = i[0] ? i[0].length : 0;\n for (let ch = 0; ch < i.length; ch++) {\n if (!this.buf[ch]) this.buf[ch] = new Float32Array(ringLen);\n }\n\n // Nothing to shift, or mixed fully out. The grain delay is ~grain/2\n // whatever the ratio, so at semitones=0 this degenerated into a pure 50 ms\n // delay of the signal, while the copy for that exact setting reads\n // "Unchanged pitch".\n this.wetTarget = semitones === 0 ? 0 : mix;\n\n // Fully dry AND settled: take the cheap transparent path. The ring keeps\n // filling, so a later shift does not start cold.\n if (this.wetTarget === 0 && this.wet < 1e-4) {\n this.wet = 0;\n let w = this.write;\n for (let s = 0; s < n; s++) {\n for (let ch = 0; ch < i.length; ch++) {\n const x = i[ch][s];\n this.buf[ch][w] = x;\n o[ch][s] = x;\n }\n w = (w + 1) % ringLen;\n }\n this.write = w;\n this.filled = Math.min(grain, this.filled + n);\n return true;\n }\n\n const ratio = Math.pow(2, semitones / 12);\n const inc = (1 - ratio) / grain;\n let write = this.write, phase = this.phase, filled = this.filled, wetNow = this.wet;\n const target = this.wetTarget, coef = this.wetCoef;\n for (let s = 0; s < n; s++) {\n phase += inc;\n phase -= Math.floor(phase);\n const phaseB = (phase + 0.5) % 1;\n const gA = xfade(phase), gB = xfade(phaseB);\n // Ramp the wet path in as the ring fills rather than reading zeros:\n // 100 ms of unshifted audio at the head of a clip beats 50 ms of silence.\n const warm = filled >= grain ? 1 : filled / grain;\n wetNow = target + coef * (wetNow - target);\n const wetMix = wetNow * warm;\n for (let ch = 0; ch < i.length; ch++) {\n const ring = this.buf[ch];\n const inp = i[ch], out = o[ch];\n const x = inp[s];\n ring[write] = x;\n const wet =\n readTap(ring, write, phase * grain) * gA + readTap(ring, write, phaseB * grain) * gB;\n out[s] = x * (1 - wetMix) + wet * wetMix;\n }\n write = (write + 1) % ringLen;\n if (filled < grain) filled++;\n }\n this.write = write;\n this.phase = phase;\n this.filled = filled;\n this.wet = wetNow;\n return true;\n }\n}\nregisterProcessor("hf-pitchshift", HfPitchshift);\n`+Z,L=new WeakMap;var He=new WeakSet;function O(e){let t=L.get(e);return t||(t=(async()=>{if(!e.audioWorklet)throw new Error("AudioWorklet is unavailable: the page needs a secure context (https, localhost or file://)");let n=`data:text/javascript;base64,${btoa(String.fromCharCode(...new TextEncoder().encode(Pe)))}`;await e.audioWorklet.addModule(n),He.add(e)})().catch(n=>{throw L.delete(e),n}),L.set(e,t)),t}function _e(e,t,n){let o=.6+Math.max(0,Math.min(1,t))*2.6,a=Math.max(1,Math.floor(e*o)),r=new Float32Array(a),i=Math.round(t*1e3)*2654435761+Math.round(n*1e3)*40503>>>0,s=()=>(i=i*1664525+1013904223>>>0,i/4294967295*2-1),u=Math.max(.001,1-Math.max(0,Math.min(1,n))),l=0,m=0;for(let c=0;c<a;c++){l+=u*(s()-l);let b=l*Math.pow(1-c/a,2.5);r[c]=b,m+=b*b}let p=Math.sqrt(m);if(p>0)for(let c=0;c<a;c++)r[c]=(r[c]??0)/p;return r}var f=e=>typeof e=="number"?e:Number(e??0),B=e=>e/1e3;function ae(e,t,n,o){let a=Math.max(1,Math.round(e.sampleRate)),r=e.createBuffer(1,a,e.sampleRate),i=r.getChannelData(0);for(let l=0;l<a;l++){let m=l/a;i[l]=t==="sine"?Math.sin(2*Math.PI*m):4*Math.abs((m+.75)%1-.5)-1}let s=e.createBufferSource();s.buffer=r,s.loop=!0,s.playbackRate.value=n;let u=(o*n%1+1)%1*(a/e.sampleRate);return s.start(typeof e.currentTime=="number"?e.currentTime:0,u),s}function oe(e){try{e.stop()}catch{}e.disconnect()}function I(e,t){return[{param:e},{param:t,map:n=>1-n}]}function re(e,t,n){e.gain.value=n,t.gain.value=1-n}function q(e,t,n){return{input:e,output:e,update:t,automation:n,dispose:()=>e.disconnect()}}var Re=new Set(["peaking","highpass","lowpass"]),ee=e=>Math.pow(10,e/20),Ne=(e,t)=>{let n=e.createGain(),o=a=>{n.gain.value=ee(f(a.gain))};return o(t),q(n,o,{gain:[{param:n.gain,map:ee}]})};function H(e,t){return(n,o)=>{let a=n.createBiquadFilter();a.type=e;let r=i=>{a.frequency.value=f(i.frequency),i.q!==void 0&&(a.Q.value=f(i.q)),t&&(a.gain.value=f(i.gain))};return r(o),q(a,r,{frequency:[{param:a.frequency}],...Re.has(e)?{q:[{param:a.Q}]}:{},...t?{gain:[{param:a.gain}]}:{}})}}function Ee(e){return(t,n)=>{let o=Math.tan(Math.PI*f(n.frequency)/t.sampleRate),a=e==="highpass"?t.createIIRFilter([1/(1+o),-1/(1+o)],[1,(o-1)/(o+1)]):t.createIIRFilter([o/(1+o),o/(1+o)],[1,(o-1)/(o+1)]);return q(a,()=>{})}}function M(e,t=!1){return(n,o,a)=>{let r=new AudioWorkletNode(n,e,{processorOptions:{...o,__hfElapsed:a}});return{input:r,output:r,update:i=>r.port.postMessage({...i}),...t?{reset:()=>r.port.postMessage({__hfReset:!0})}:{},dispose:()=>{r.port.postMessage({__hfDispose:!0}),r.disconnect()}}}}var De={tanh:Math.tanh,atan:e=>2/Math.PI*Math.atan(Math.PI/2*e),cubic:e=>Math.abs(e)>=1?Math.sign(e):e-e**3/3,exp:e=>Math.sign(e)*(1-Math.exp(-Math.abs(e))),alg:e=>e/Math.sqrt(1+e*e),quintic:e=>Math.abs(e)>=1?Math.sign(e):e-e**5/5,sin:e=>Math.abs(e)>=1?Math.sign(e):Math.sin(Math.PI/2*e),erf:e=>Math.tanh(1.20211*e),hard:e=>Math.max(-1,Math.min(1,e))},Ce=(e,t)=>{let n=e.createWaveShaper(),o=e.createGain(),a=e.createGain();o.connect(n).connect(a);let r=i=>{let s=De[String(i.type)]??Math.tanh,u=Math.pow(10,f(i.threshold)/20),l=8192,m=new Float32Array(l);for(let p=0;p<l;p++){let c=p/(l-1)*2-1;m[p]=s(c/Math.max(1e-6,u))*u}n.curve=m,n.oversample=f(i.oversample)>=4?"4x":f(i.oversample)>=2?"2x":"none",a.gain.value=Math.pow(10,f(i.output)/20)};return r(t),{input:o,output:a,update:r,automation:{output:[{param:a.gain,map:i=>Math.pow(10,i/20)}]},dispose:()=>{o.disconnect(),n.disconnect(),a.disconnect()}}},Se=(e,t)=>{let n=e.createGain(),o=e.createGain(),a=e.createDelay(5),r=e.createGain(),i=e.createGain(),s=e.createGain();n.connect(a),a.connect(r),r.connect(a),a.connect(i).connect(o),n.connect(s).connect(o);let u=l=>{a.delayTime.value=Math.min(5,f(l.time)/1e3),r.gain.value=f(l.feedback),re(i,s,f(l.mix))};return u(t),{input:n,output:o,update:u,automation:{time:[{param:a.delayTime,map:l=>Math.min(5,B(l))}],feedback:[{param:r.gain}],mix:I(i.gain,s.gain)},dispose:()=>[n,o,a,r,i,s].forEach(l=>l.disconnect())}},Le=(e,t,n)=>{let o=e.createGain(),a=e.createGain(),r=e.createDelay(.5),i=ae(e,"sine",f(t.speed),n),s=e.createGain(),u=e.createGain(),l=e.createGain();i.connect(s).connect(r.delayTime),o.connect(r).connect(u).connect(a),o.connect(l).connect(a);let m=p=>{r.delayTime.value=f(p.delay)/1e3,s.gain.value=f(p.depth)/1e3,i.playbackRate.value=f(p.speed),re(u,l,f(p.mix))};return m(t),{input:o,output:a,update:m,automation:{delay:[{param:r.delayTime,map:B}],depth:[{param:s.gain,map:B}],speed:[{param:i.playbackRate}],mix:I(u.gain,l.gain)},dispose:()=>{oe(i),[o,a,r,s,u,l].forEach(p=>p.disconnect())}}},Oe=6,Be=(e,t,n)=>{let o=e.createGain(),a=e.createGain(),r=e.createGain(),i=e.createGain(),s=ae(e,String(t.type)==="1"?"sine":"triangle",f(t.speed),n),u=e.createGain(),l=e.createGain(),m=e.createGain(),p=[];o.connect(r);let c=r;for(let d=0;d<Oe;d++){let h=e.createBiquadFilter();h.type="allpass",h.Q.value=.7071,u.connect(h.frequency),c.connect(h),c=h,p.push(h)}s.connect(u),c.connect(l).connect(i),r.connect(m).connect(i),i.connect(a);let b=d=>{let h=1e3/Math.max(.1,f(d.delay));for(let g of p)g.frequency.value=h;u.gain.value=h*f(d.decay),s.playbackRate.value=f(d.speed),r.gain.value=f(d.in_gain),i.gain.value=f(d.out_gain),l.gain.value=1,m.gain.value=1};return b(t),{input:o,output:a,update:b,automation:{speed:[{param:s.playbackRate}],in_gain:[{param:r.gain}],out_gain:[{param:i.gain}]},dispose:()=>{oe(s),[o,a,r,i,u,l,m,...p].forEach(d=>d.disconnect())}}},Ie=(e,t)=>{let n=e.createGain(),o=e.createGain(),a=e.createConvolver(),r=e.createGain(),i=e.createGain();a.normalize=!1,n.connect(a).connect(r).connect(o),n.connect(i).connect(o);let s="",u=l=>{let m=`${f(l.size)}:${f(l.damping)}`;if(m!==s){let p=_e(e.sampleRate,f(l.size),f(l.damping)),c=e.createBuffer(1,p.length,e.sampleRate);c.getChannelData(0).set(p),a.buffer=c,s=m}r.gain.value=f(l.wet),i.gain.value=f(l.dry)};return u(t),{input:n,output:o,update:u,automation:{wet:[{param:r.gain}],dry:[{param:i.gain}]},dispose:()=>[n,o,a,r,i].forEach(l=>l.disconnect())}},qe={"gain-node":Ne,"biquad-peaking":H("peaking",!0),"biquad-lowshelf":H("lowshelf",!0),"biquad-highshelf":H("highshelf",!0),"biquad-highpass":H("highpass",!1),"biquad-lowpass":H("lowpass",!1),"worklet-compressor":M("hf-compressor"),"worklet-limiter":M("hf-limiter"),"worklet-truepeak":M("hf-truepeak",!0),"worklet-gate":M("hf-gate"),"worklet-bitcrush":M("hf-bitcrush"),"worklet-pitchshift":M("hf-pitchshift"),waveshaper:Ce,"delay-feedback":Se,"chorus-lfo":Le,"allpass-phaser":Be,convolver:Ie};function ie(e){return e.nodes.some(t=>P(t.type)?.web.startsWith("worklet-")??!1)}function Ge(e,t,n,o=0){let a=P(t);if(!a)throw new Error(`Unknown effect type: ${t}`);let r=y(t,n);if((t==="highpass"||t==="lowpass")&&String(r.poles)==="1")return Ee(t)(e,r,o);let i=qe[a.web];if(!i)throw new Error(`No Web Audio builder for ${a.web}`);return i(e,r,o)}function te(e){let t=[];for(let n of e){let o=n.fromPreset,a=t.at(-1);if(a&&a.preset===o)a.nodes.push(n);else{let r=typeof n.presetAmount=="number"?n.presetAmount:1;t.push({...o?{preset:o}:{},amount:Math.min(1,Math.max(0,r)),nodes:[n]})}}return t}function ne(e){return w(e).map(t=>{let n=y(t.type,t.params),o=n.poles!==void 0?`:${n.poles}`:"",a=String(n.poles)==="1"?`@${n.frequency}`:"",r=t.type==="phaser"?`~${n.type}`:"",i=t.fromPreset?`%${t.fromPreset}`:"",s=t.type==="truepeak"?`^${n.lookahead}`:"";return`${t.type}${o}${a}${r}${s}${i}`}).join("|")}function se(e,t,n=0){var p;let o=e.createGain(),a=e.createGain(),r=[],i=[],s=te(w(t)),u=o;for(let c of s){let b=null;if(c.preset){let d=e.createGain(),h=e.createGain(),g=e.createGain(),T=e.createGain();g.gain.value=c.amount,h.gain.value=1-c.amount,u.connect(d),d.connect(h).connect(T),b={entry:d,wet:g,dry:h,join:T},u=d}for(let d of c.nodes){let h=Ge(e,d.type,d.params??{},n);u.connect(h.input),u=h.output,r.push({...d.id?{id:d.id}:{},type:d.type,handle:h})}b&&c.preset&&(u.connect(b.wet).connect(b.join),i.push({id:c.preset,...b}),u=b.join)}u.connect(a);let l=ne(t),m={};for(let c of i)(m[p=c.id]??(m[p]=[])).push(...I(c.wet.gain,c.dry.gain));return{input:o,output:a,presets:m,nodes:r,update(c){if(ne(c)!==l)return!1;w(c).forEach((d,h)=>{let g=r[h];g&&(g.handle.update(y(d.type,d.params)),d.id===void 0?delete g.id:g.id=d.id)});let b=0;for(let d of te(w(c))){if(!d.preset)continue;let h=i[b++];!h||h.id!==d.preset||(h.wet.gain.value=d.amount,h.dry.gain.value=1-d.amount)}return!0},reset(){for(let{handle:c}of r)c.reset?.()},dispose(){for(let{handle:c}of r)c.dispose();for(let{entry:c,wet:b,dry:d,join:h}of i)c.disconnect(),b.disconnect(),d.disconnect(),h.disconnect();o.disconnect(),a.disconnect()}}}function Ue(e){return 10**(e/20)}var ue=Ue(12);var G=.1,U=10;var Ve="data-automation",Vt=Ve.slice(5),D=1,$e=512,A=class extends Error{constructor(t){super(t),this.name="AudioAutomationError"}},V="volume",le="rate";function R(e){if(e===V)return{kind:"volume"};if(e===le)return{kind:"rate"};let t=e.split(".");if(t.length===3&&t[0]==="fx"&&t[1]===je){let a=t[2];return a?{kind:"preset",presetId:a}:null}if(t.length!==3||t[0]!=="fx")return null;let[,n,o]=t;return!n||!o?null:{kind:"fx",nodeId:n,param:o}}function ce(e,t){return`fx.${e}.${t}`}var je="preset";var Xe={min:0,max:1,step:.01,unit:"",label:"Amount",scale:"linear",default:1},de={min:0,max:ue,step:.01,unit:"",label:"Volume",scale:"linear",default:1},me={min:G,max:U,step:.05,unit:"x",label:"Speed",scale:"log",default:1};function $(e,t){let n=R(e);if(!n)return null;if(n.kind==="volume")return de;if(n.kind==="rate")return me;if(n.kind==="preset")return t?.nodes.some(s=>s.fromPreset===n.presetId)?{...Xe,label:`${n.presetId} \\xB7 Amount`}:null;let o=t?.nodes.find(i=>i.id===n.nodeId);if(!o)return null;let a=P(o.type),r=a?.params.find(i=>i.key===n.param);return!r||r.kind!=="number"?null:{min:r.min,max:r.max,step:r.step,unit:r.unit,label:`${a?.label??o.type} \\xB7 ${r.label}`,scale:r.scale==="log"&&r.min>0?"log":"linear",default:r.default}}function _(e){if(typeof e=="number")return Number.isFinite(e)?e:null;if(typeof e=="string"&&e.trim()!==""){let t=Number(e);return Number.isFinite(t)?t:null}return null}function We(e){let t=_(e);return t===null||t===0?0:Math.min(1,Math.max(-1,t))}function pe(e,t){let n=_(e),o=_(t);if(n===null||o===null)return null;let a=s=>Math.min(.999,Math.max(.001,s)),r=a(n),i=a(o);return Math.abs(r-i)<1e-6?null:{x:r,y:i}}function Ke(e){let t=We(e?.curve),n=pe(e?.viaX,e?.viaY);return{...t?{curve:t}:{},...n?{viaX:n.x,viaY:n.y}:{}}}function Ye(e,t){let n=_(e?.t),o=_(e?.v);if(n===null||o===null)return null;let a=t?Math.min(t.max,Math.max(t.min,o)):o;return{t:Math.max(0,n),v:a,...Ke(e)}}function he(e,t){let n=e.map(a=>Ye(a,t)).filter(a=>a!==null).sort((a,r)=>a.t-r.t),o=[];for(let a of n)o.length>0&&o[o.length-1].t===a.t?o[o.length-1]=a:o.push(a);return o.slice(0,$e)}function ze(e){let t=[];for(let n of e.lanes){if(!R(n.target))continue;let o=n.target===V?de:n.target===le?me:null,a=he(n.points??[],o);a.length>0&&t.push({target:n.target,points:a})}return{version:D,lanes:t}}function fe(e,t){let n=[];for(let o of e.lanes){let a=$(o.target,t);if(!a)continue;let r=he(o.points,a);r.length>0&&n.push({target:o.target,points:r})}return{version:D,lanes:n}}function be(e){let t;try{t=JSON.parse(e)}catch(a){throw new A(`Automation is not valid JSON: ${a.message}`)}if(typeof t!="object"||t===null)throw new A("Automation must be a JSON object.");let n=t;if(n.version!==D)throw new A(`Unsupported automation version: ${String(n.version)}`);if(!Array.isArray(n.lanes))throw new A("Automation is missing a `lanes` array.");let o=n.lanes.map((a,r)=>{if(typeof a!="object"||a===null)throw new A(`Lane ${r} is not an object.`);let i=a;if(typeof i.target!="string"||!R(i.target))throw new A(`Lane ${r} has an unreadable target: ${String(i.target)}`);if(!Array.isArray(i.points))throw new A(`Lane ${r} is missing a \\`points\\` array.`);return{target:i.target,points:i.points}});return ze({version:D,lanes:o})}function Je(e,t){return t?Math.pow(e,Math.pow(2,2*t)):e}function Qe(e,t){let n=e-.5,o=t-.5,a=.999,r=1e6,i=n>0?n/(a-e):n<0?-n/(e-(1-a)):0,s=o>0?o/(a-t):o<0?-o/(t-(1-a)):0,u=Math.min(r,Math.max(1,i,s));return{cx:e+n/u,cy:t+o/u,w:u}}function Ze(e,t,n){let{cx:o,cy:a,w:r}=Qe(t,n),i=2-2*r,s=e*i-1+2*r*o,u=-e*i-2*r*o,l=et(s,u,e),m=1-l,p=m*m+2*r*l*m+l*l;return p>0?(2*r*a*l*m+l*l)/p:e}function et(e,t,n){if(Math.abs(e)<1e-12)return Math.abs(t)<1e-12?n:-n/t;let o=Math.sqrt(Math.max(0,t*t-4*e*n)),a=(-t+o)/(2*e),r=(-t-o)/(2*e),i=s=>s>=-1e-9&&s<=1+1e-9;return i(a)?Math.min(1,Math.max(0,a)):i(r)?Math.min(1,Math.max(0,r)):n}function tt(e,t){let n=pe(t.viaX,t.viaY);return n?Ze(Math.min(1,Math.max(0,e)),n.x,n.y):Je(e,t.curve)}function nt(e,t,n,o){return o==="log"&&e>0&&t>0?Math.exp(Math.log(e)+(Math.log(t)-Math.log(e))*n):e+(t-e)*n}function j(e,t,n="linear"){let o=e.points;if(o.length===0)return 0;let a=o[0];if(t<=a.t)return a.v;let r=o[o.length-1];if(t>=r.t)return r.v;let i=0,s=o.length-1;for(;s-i>1;){let p=i+s>>1;o[p].t<=t?i=p:s=p}let u=o[i],l=o[s],m=l.t-u.t;return m<=0?l.v:nt(u.v,l.v,tt((t-u.t)/m,u),n)}function ge(e){return e.points.length<=1||e.points.every(t=>t.v===e.points[0].v)}function Ae(e,t,n,o,a="linear"){let r=Math.max(2,Math.floor(o)),i=new Float32Array(r),s=n-t;for(let u=0;u<r;u+=1)i[u]=j(e,t+s*u/(r-1),a);return i}var at=100,ot=8,rt=4096,X=e=>e;function it(e,t,n){return!(!!e.curve||e.viaX!==void 0&&e.viaY!==void 0)&&t==="linear"&&!n}function st(e){let t=Math.ceil(e*at);return Math.min(rt,Math.max(ot,t))}function ut(e,t,n,o,a){let{scheduledAt:r,elapsed:i,rate:s}=o,u=a??X,l=v=>r+(v-i)/s,m=e.points[t],p=e.points[t+1],c=l(p.t);if(c<=r)return null;let b=Math.max(r,l(m.t)),d=c-b;if(d<=0)return null;if(it(m,n,a))return{kind:"ramp",time:c,value:u(p.v)};let h=st(d),g=Ae(e,Math.max(m.t,i),p.t,h,n),T=new Float32Array(h);for(let v=0;v<h;v+=1)T[v]=u(g[v]??0);return{kind:"curve",time:b,duration:d,values:T}}function lt(e,t,n,o){let a=[];for(let i=0;i+1<e.points.length;i+=1){let s=ut(e,i,t,n,o);s&&a.push(s)}let r=a[0];return r?.kind==="curve"&&r.time<=n.scheduledAt||a.unshift({kind:"set",time:n.scheduledAt,value:(o??X)(j(e,n.elapsed,t))}),a}function ct(e,t){e.cancelScheduledValues(0);for(let n of t)n.kind==="set"?e.setValueAtTime(n.value,n.time):n.kind==="ramp"?e.linearRampToValueAtTime(n.value,n.time):(e.setValueAtTime(n.values[0]??0,n.time),e.linearRampToValueAtTime(n.values[n.values.length-1]??0,n.time+n.duration))}function dt(e,t){for(let n of t)if(n.kind==="set")e.setValueAtTime(n.value,n.time);else if(n.kind==="ramp")e.linearRampToValueAtTime(n.value,n.time);else try{e.setValueCurveAtTime(n.values,n.time,n.duration)}catch{ct(e,t);return}}function mt(e){for(let t of e)t.param.cancelScheduledValues(0)}function pt(e,t,n,o){if(t.points.length!==0)for(let a of e){if(mt([a]),ge(t)){let r=(a.map??X)(t.points[0].v);a.param.setValueAtTime(r,o.scheduledAt);continue}dt(a.param,lt(t,n,o,a.map))}}function xe(e,t,n,o,a){let r=new Map(n.filter(s=>s.id).map(s=>[s.id,s.handle])),i=[];for(let s of e.lanes){let u=R(s.target);if(!u)continue;let l=u.kind==="preset"?a?.[u.presetId]:u.kind==="fx"?r.get(u.nodeId)?.automation?.[u.param]:void 0;if(!l||l.length===0)continue;let m=$(s.target,t);m&&(pt(l,s,m.scale,o),i.push(...l))}return i}var ht=5,ft=.001;function k(e,t,n){let o=Number(y(e.type,e.params)[t]??0);if(!e.id||!n)return Number.isFinite(o)?o:0;let a=n.lanes.find(r=>r.target===ce(e.id??"",t));return!a||a.points.length===0?Number.isFinite(o)?o:0:a.points.reduce((r,i)=>Math.max(r,i.v),-1/0)}function bt(e,t){let n=Math.min(5,e/1e3);if(n<=0)return 0;let o=Math.max(0,Math.min(.999,t));return o<=0?n:Math.ceil(Math.log(ft)/Math.log(o))*n}function gt(e){return .6+Math.max(0,Math.min(1,e))*2.6}function At(e,t){return k(e,"wet",t)>0?gt(k(e,"size",t)):0}function xt(e,t){return k(e,"mix",t)>0?bt(k(e,"time",t),k(e,"feedback",t)):0}function yt(e,t){return k(e,"mix",t)>0?k(e,"delay",t)/1e3:0}function kt(e,t){return k(e,"mix",t)>0?.2:0}function wt(e,t){if(e.enabled===!1)return 0;switch(e.type){case"reverb":return At(e,t);case"delay":return xt(e,t);case"chorus":return yt(e,t);case"pitchshift":return kt(e,t);default:return 0}}function ye(e,t){let n=e.nodes.reduce((o,a)=>o+wt(a,t),0);return Math.min(ht,n)}function ke(e,t){return w(e).filter(n=>n.type==="truepeak").reduce((n,o)=>{let{lookahead:a}=y(o.type,o.params);return n+Q(Number(a),t)},0)}function vt(e,t,n,o,a){let r=e.createBuffer(n,o,a);for(let i=0;i<n;i++){let s=t[i];s&&r.getChannelData(i).set(s)}return r}function Mt(e,t){let n=[];for(let o=0;o<e.numberOfChannels;o++)n.push(new Float32Array(e.getChannelData(o).subarray(t)));return n}async function Tt(e,t,n,o,a=0){let r=J(n),i=Math.max(1,e.length),s=e[0]?.length??0;if(s===0)return e;let u=o?fe(be(o),r):null,l=Math.ceil(ye(r,u??void 0)*t),m=ke(r,t),p=s+l+m,c=new OfflineAudioContext(i,p,t);ie(r)&&await O(c);let b=c.createBufferSource();b.buffer=vt(c,e,i,p,t);let d=se(c,r,a);return u&&xe(u,r,d.nodes,{scheduledAt:0,elapsed:0,rate:1},d.presets),b.connect(d.input),d.output.connect(c.destination),b.start(),Mt(await c.startRendering(),m)}window.__HF_AUDIO_FX={render:Tt};})();\n';
|
|
14593
|
+
var AUDIO_FX_RUNTIME_IIFE = '"use strict";(()=>{var ve="data-fx-chain",Pt=ve.slice(5),W=1,F=(e="frequency",t="Frequency",n=1e3,o=20,a=2e4)=>({kind:"number",key:e,label:t,unit:"Hz",min:o,max:a,step:1,default:n,scale:"log",automatable:!0}),S=(e=.707,t="Bandwidth: higher is narrower.")=>({kind:"number",key:"q",label:"Q",unit:"",min:.1,max:20,step:.01,default:e,scale:"log",automatable:!0,hint:t}),N=(e=-40,t=40,n=0)=>({kind:"number",key:"gain",label:"Gain",unit:"dB",min:e,max:t,step:.1,default:n,automatable:!0}),K={kind:"enum",key:"poles",label:"Slope",options:[{value:"1",label:"6 dB/oct"},{value:"2",label:"12 dB/oct"}],default:"2",hint:"Two poles is the usual biquad; one pole is gentler."},z=[{id:"gain",label:"Gain",group:"dynamics",description:"Raise or lower the whole signal. Automate it to duck under something else.",params:[N(-60,12,0)],web:"gain-node"},{id:"peaking",label:"Peaking EQ",group:"filter",description:"Boost or cut a band, leaving everything either side alone.",params:[F("frequency","Frequency",1e3),N(-40,40,0),S(1)],web:"biquad-peaking"},{id:"lowshelf",label:"Low Shelf",group:"filter",description:"Lift or drop everything below the corner frequency.",params:[F("frequency","Frequency",200,20,2e3),N(-40,40,0)],web:"biquad-lowshelf"},{id:"highshelf",label:"High Shelf",group:"filter",description:"Lift or drop everything above the corner frequency.",params:[F("frequency","Frequency",4e3,500,2e4),N(-40,40,0)],web:"biquad-highshelf"},{id:"highpass",label:"High-pass",group:"filter",description:"Remove low frequencies: the usual fix for rumble on a voice.",params:[F("frequency","Cutoff",300,20,2e4),S(.707),K],web:"biquad-highpass"},{id:"lowpass",label:"Low-pass",group:"filter",description:"Remove high frequencies, which darkens or muffles a track.",params:[F("frequency","Cutoff",8e3,100,2e4),S(.707),K],web:"biquad-lowpass"},{id:"compressor",label:"Compressor",group:"dynamics",description:"Pull loud parts down so the quiet ones can come up.",params:[{kind:"number",key:"threshold",label:"Threshold",unit:"dB",min:-60,max:0,step:.5,default:-24,hint:"Level above which the compressor starts working."},{kind:"number",key:"ratio",label:"Ratio",unit:":1",min:1,max:20,step:.1,default:4},{kind:"number",key:"attack",label:"Attack",unit:"ms",min:.01,max:2e3,step:.1,default:20,scale:"log"},{kind:"number",key:"release",label:"Release",unit:"ms",min:.01,max:9e3,step:1,default:250,scale:"log"},{kind:"number",key:"knee",label:"Knee",unit:"",min:1,max:8,step:.01,default:2.83,hint:"1 is a hard corner; higher eases into it."},{kind:"number",key:"makeup",label:"Makeup",unit:"dB",min:0,max:36,step:.1,default:0},{kind:"number",key:"mix",label:"Mix",unit:"",min:0,max:1,step:.01,default:1,hint:"Below 1 blends the dry signal back in."}],web:"worklet-compressor"},{id:"limiter",label:"Limiter",group:"dynamics",description:"Peak limiter that follows the signal\'s level. It has no lookahead, so a fast peak or an inter-sample peak can pass the ceiling; use the true-peak limiter for a delivery limit.",params:[{kind:"number",key:"limit",label:"Ceiling",unit:"dB",min:-24,max:0,step:.1,default:-1},{kind:"number",key:"attack",label:"Attack",unit:"ms",min:.1,max:80,step:.1,default:5},{kind:"number",key:"release",label:"Release",unit:"ms",min:1,max:8e3,step:1,default:50,scale:"log"},{kind:"number",key:"level_out",label:"Output",unit:"dB",min:-24,max:24,step:.1,default:0}],web:"worklet-limiter"},{id:"truepeak",label:"True-peak limiter",group:"dynamics",description:"Lookahead limiter that holds a 4x estimate of the inter-sample peak at a dBTP ceiling; full-band noise can land up to about 1.7 dB above it. Adds lookahead plus about 0.3 ms of latency.",params:[{kind:"number",key:"ceiling",label:"Ceiling",unit:"dBTP",min:-24,max:0,step:.1,default:-1,hint:"Target true peak, measured on a 4x oversampled signal. Dense full-band sound can end up to about 1.7 dB above it."},{kind:"number",key:"lookahead",label:"Lookahead",unit:"ms",min:.5,max:10,step:.1,default:3,hint:"How far ahead the gain starts falling. Longer is smoother and adds the same latency."},{kind:"number",key:"release",label:"Release",unit:"ms",min:10,max:2e3,step:1,default:80,scale:"log"}],web:"worklet-truepeak"},{id:"gate",label:"Noise Gate",group:"dynamics",description:"Silence the track when it drops below the threshold.",params:[{kind:"number",key:"threshold",label:"Threshold",unit:"dB",min:-80,max:0,step:.5,default:-35},{kind:"number",key:"range",label:"Range",unit:"dB",min:-80,max:0,step:.5,default:-24,hint:"How far down the gate pulls when closed."},{kind:"number",key:"ratio",label:"Ratio",unit:":1",min:1,max:20,step:.1,default:10},{kind:"number",key:"attack",label:"Attack",unit:"ms",min:.01,max:9e3,step:.1,default:1,scale:"log"},{kind:"number",key:"release",label:"Release",unit:"ms",min:.01,max:9e3,step:1,default:100,scale:"log"},{kind:"number",key:"knee",label:"Knee",unit:"",min:1,max:8,step:.01,default:2.83}],web:"worklet-gate"},{id:"saturate",label:"Saturation",group:"nonlinear",description:"Soft-clip the waveform for warmth or outright distortion.",params:[{kind:"enum",key:"type",label:"Curve",options:[{value:"tanh",label:"Tanh"},{value:"atan",label:"Arctan"},{value:"cubic",label:"Cubic"},{value:"exp",label:"Exponential"},{value:"alg",label:"Algebraic"},{value:"quintic",label:"Quintic"},{value:"sin",label:"Sine"},{value:"erf",label:"Error function"},{value:"hard",label:"Hard clip"}],default:"tanh"},{kind:"number",key:"threshold",label:"Threshold",unit:"dB",min:-40,max:0,step:.1,default:-6},{kind:"number",key:"output",automatable:!0,label:"Output",unit:"dB",min:-24,max:24,step:.1,default:0},{kind:"number",key:"oversample",label:"Oversample",unit:"x",min:1,max:8,step:1,default:4,hint:"Higher costs more but keeps aliasing down."}],web:"waveshaper"},{id:"bitcrush",label:"Bitcrush",group:"nonlinear",description:"Drop bit depth and sample rate for a lo-fi, digital sound.",params:[{kind:"number",key:"bits",label:"Bit depth",unit:"bit",min:1,max:32,step:.1,default:8},{kind:"number",key:"samples",label:"Sample hold",unit:"x",min:1,max:250,step:1,default:1,hint:"Repeats each sample N times, a crude downsample."},{kind:"number",key:"mix",label:"Mix",unit:"",min:0,max:1,step:.01,default:1}],web:"worklet-bitcrush"},{id:"pitchshift",label:"Pitch shift",group:"time",description:"Shifts pitch up or down without changing playback speed. Adds ~50 ms of latency.",params:[{kind:"number",key:"semitones",label:"Semitones",unit:"st",min:-12,max:12,step:1,default:0},{kind:"number",key:"mix",label:"Mix",unit:"",min:0,max:1,step:.01,default:1}],web:"worklet-pitchshift"},{id:"delay",label:"Delay",group:"time",description:"Repeating echoes behind the dry signal.",params:[{kind:"number",key:"time",automatable:!0,label:"Time",unit:"ms",min:1,max:5e3,step:1,default:250,scale:"log"},{kind:"number",key:"feedback",automatable:!0,label:"Feedback",unit:"",min:.01,max:.95,step:.01,default:.35},{kind:"number",key:"mix",automatable:!0,label:"Mix",unit:"",min:0,max:1,step:.01,default:.4}],web:"delay-feedback"},{id:"chorus",label:"Chorus",group:"time",description:"Detuned copies of the signal for width and thickness.",params:[{kind:"number",key:"delay",automatable:!0,label:"Delay",unit:"ms",min:1,max:100,step:.1,default:7},{kind:"number",key:"depth",automatable:!0,label:"Depth",unit:"ms",min:0,max:10,step:.01,default:2},{kind:"number",key:"speed",automatable:!0,label:"Rate",unit:"Hz",min:.01,max:10,step:.01,default:1},{kind:"number",key:"mix",automatable:!0,label:"Mix",unit:"",min:0,max:1,step:.01,default:.5}],web:"chorus-lfo"},{id:"phaser",label:"Phaser",group:"time",description:"Sweeping notches moving through the spectrum.",params:[{kind:"number",key:"in_gain",automatable:!0,label:"Input",unit:"",min:0,max:1,step:.01,default:.4},{kind:"number",key:"out_gain",automatable:!0,label:"Output",unit:"",min:0,max:2,step:.01,default:.74},{kind:"number",key:"delay",label:"Delay",unit:"ms",min:.1,max:5,step:.1,default:3},{kind:"number",key:"decay",label:"Decay",unit:"",min:0,max:.99,step:.01,default:.4},{kind:"number",key:"speed",automatable:!0,label:"Rate",unit:"Hz",min:.1,max:2,step:.01,default:.5},{kind:"enum",key:"type",label:"Waveform",options:[{value:"0",label:"Triangular"},{value:"1",label:"Sinusoidal"}],default:"0"}],web:"allpass-phaser"},{id:"reverb",label:"Reverb",group:"time",description:"Room tail. Both ends convolve the same generated impulse, so preview matches render.",params:[{kind:"number",key:"size",label:"Room size",unit:"",min:.05,max:1,step:.01,default:.7},{kind:"number",key:"damping",label:"Damping",unit:"",min:0,max:1,step:.01,default:.5,hint:"Higher rolls the top off the tail faster."},{kind:"number",key:"wet",automatable:!0,label:"Wet",unit:"",min:0,max:1,step:.01,default:.35},{kind:"number",key:"dry",automatable:!0,label:"Dry",unit:"",min:0,max:1,step:.01,default:.7}],web:"convolver"}],C=new Map(z.map(e=>[e.id,e]));function P(e){return C.get(e)}var Ht=z.map(e=>e.id);function y(e,t){let n=C.get(e);if(!n)return{};let o={};for(let a of n.params){let r=t?.[a.key];if(a.kind==="enum"){let s=a.options.some(u=>u.value===r);o[a.key]=s?r:a.default;continue}let i=typeof r=="number"?r:typeof r=="string"&&r.trim()!==""?Number(r):Number.NaN;o[a.key]=Number.isFinite(i)?Math.min(a.max,Math.max(a.min,i)):a.default}return o}var x=class extends Error{constructor(t){super(t),this.name="AudioFxChainError"}};function J(e){let t;try{t=JSON.parse(e)}catch(a){throw new x(`Chain file is not valid JSON: ${a.message}`)}if(typeof t!="object"||t===null)throw new x("Chain file must be a JSON object.");let n=t;if(n.version!==W)throw new x(`Unsupported chain version: ${String(n.version)}`);if(!Array.isArray(n.nodes))throw new x("Chain file is missing a `nodes` array.");let o=n.nodes.map(Me);return{version:W,nodes:o}}function w(e){return e.nodes.filter(t=>t.enabled!==!1)}function Me(e,t){if(typeof e!="object"||e===null)throw new x(`Node ${t} is not an object.`);let n=e;if(typeof n.type!="string"||!C.has(n.type))throw new x(`Node ${t} has unknown effect type: ${String(n.type)}`);return Fe({type:n.type,id:E(n.id),fromCarve:Y(n.fromCarve),fromPreset:E(n.fromPreset),label:E(n.label),fromEq:E(n.fromEq),fromLeveller:Y(n.fromLeveller),presetAmount:Te(n.presetAmount),enabled:n.enabled!==!1,params:y(n.type,n.params??void 0)})}function E(e){return typeof e=="string"&&e?e:void 0}function Y(e){return e===!0?!0:void 0}function Te(e){if(!(typeof e!="number"||!Number.isFinite(e)))return Math.min(1,Math.max(0,e))}function Fe(e){for(let t of Object.keys(e))e[t]===void 0&&delete e[t];return e}function Pe(e,t){return Math.max(1,Math.round(e*t/1e3))}function Q(e,t){return Pe(e,t)+16}var Z=`\nconst TP_OS = 4;\nconst TP_HALF = 64;\nconst TP_BRANCH = 33;\nconst TP_DELAY = 16;\n\nfunction besselI0(x) {\n let sum = 1, term = 1;\n for (let k = 1; k < 40; k++) {\n term *= (x / (2 * k)) * (x / (2 * k));\n sum += term;\n }\n return sum;\n}\n\n/** Kaiser-windowed sinc, one array per polyphase branch, newest tap first. */\nfunction tpBranches() {\n const beta = 6;\n const norm = besselI0(beta);\n const branches = [];\n for (let p = 0; p < TP_OS; p++) {\n const taps = new Float64Array(TP_BRANCH);\n let sum = 0;\n for (let j = 0; j < TP_BRANCH; j++) {\n const k = p + TP_OS * j;\n if (k > 2 * TP_HALF) continue;\n const t = (k - TP_HALF) / TP_OS;\n const sinc = t === 0 ? 1 : Math.sin(Math.PI * t) / (Math.PI * t);\n const r = (k - TP_HALF) / TP_HALF;\n taps[j] = sinc * (besselI0(beta * Math.sqrt(Math.max(0, 1 - r * r))) / norm);\n sum += taps[j];\n }\n // Unity gain at DC on every branch, so a steady level reads as itself.\n for (let j = 0; j < TP_BRANCH; j++) taps[j] /= sum;\n branches.push(taps);\n }\n return branches;\n}\n\nclass HfTruePeak extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.look = Math.max(1, Math.round((this.p.lookahead ?? 3) * sampleRate / 1000));\n this.delayLen = this.look + TP_DELAY;\n this.branches = tpBranches();\n this.setLevels();\n this.clear();\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n if (e.data && e.data.__hfReset) { this.clear(); return; }\n this.p = { ...this.p, ...e.data };\n this.setLevels();\n };\n }\n /** Back to the state of a fresh processor: no audio in the delay line, no gain reduction. */\n clear() {\n this.ch = [];\n this.rel = 1;\n this.dqIdx = new Int32Array(this.look + 2);\n this.dqVal = new Float64Array(this.look + 2);\n this.dqHead = 0;\n this.dqTail = 0;\n this.t = 0;\n this.avgRing = new Float64Array(this.look + 1).fill(1);\n this.avgPos = 0;\n this.avgSum = this.look + 1;\n }\n setLevels() {\n this.ceiling = Math.pow(10, (this.p.ceiling ?? -1) / 20);\n this.relCoef = 1 - Math.exp(-1 / (sampleRate * Math.max(1e-4, (this.p.release ?? 80) / 1000)));\n }\n channel(c) {\n if (!this.ch[c]) {\n this.ch[c] = {\n delay: new Float32Array(this.delayLen),\n w: 0,\n hist: new Float32Array(TP_BRANCH * 2),\n pos: 0,\n };\n }\n return this.ch[c];\n }\n /** Largest interpolated magnitude across channels for the newest sample. */\n peak(chans, inputs, n) {\n let peak = 0;\n for (let c = 0; c < chans; c++) {\n const s = this.channel(c);\n const x = inputs[c][n];\n s.pos = (s.pos + 1) % TP_BRANCH;\n s.hist[s.pos] = x;\n s.hist[s.pos + TP_BRANCH] = x;\n const base = s.pos + TP_BRANCH;\n for (let p = 0; p < TP_OS; p++) {\n const taps = this.branches[p];\n let acc = 0;\n for (let j = 0; j < TP_BRANCH; j++) acc += taps[j] * s.hist[base - j];\n const a = acc < 0 ? -acc : acc;\n if (a > peak) peak = a;\n }\n }\n return peak;\n }\n /** Windowed minimum of the required gain over the last look + 1 values. */\n windowMin(v) {\n const cap = this.dqIdx.length;\n while (this.dqTail !== this.dqHead && this.dqVal[(this.dqTail + cap - 1) % cap] >= v) {\n this.dqTail = (this.dqTail + cap - 1) % cap;\n }\n this.dqIdx[this.dqTail] = this.t;\n this.dqVal[this.dqTail] = v;\n this.dqTail = (this.dqTail + 1) % cap;\n while (this.dqIdx[this.dqHead] <= this.t - (this.look + 1)) {\n this.dqHead = (this.dqHead + 1) % cap;\n }\n this.t++;\n return this.dqVal[this.dqHead];\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const chans = i.length;\n const frames = i[0].length;\n for (let n = 0; n < frames; n++) {\n const peak = this.peak(chans, i, n);\n const need = peak > this.ceiling ? this.ceiling / peak : 1;\n const m = this.windowMin(need);\n this.avgSum += m - this.avgRing[this.avgPos];\n this.avgRing[this.avgPos] = m;\n this.avgPos = (this.avgPos + 1) % this.avgRing.length;\n const target = Math.min(1, this.avgSum / this.avgRing.length);\n const released = this.rel + (1 - this.rel) * this.relCoef;\n this.rel = released < target ? released : target;\n if (target >= 1 && this.rel > 0.9999999) this.rel = 1;\n for (let c = 0; c < chans; c++) {\n const s = this.channel(c);\n o[c][n] = s.delay[s.w] * this.rel;\n s.delay[s.w] = i[c][n];\n s.w = (s.w + 1) % this.delayLen;\n }\n }\n return true;\n }\n}\nregisterProcessor("hf-truepeak", HfTruePeak);\n`;var He=`\nconst dbToLin = (db) => Math.pow(10, db / 20);\n\n/**\n * One-pole envelope followers, one per channel.\n *\n * Per channel matters: the followers advance once per sample, so a single shared\n * follower stepped once per channel per sample. On stereo that ran a 20 ms attack\n * as 10 ms, and gave the right channel a gain computed from an envelope that had\n * already traversed the left, so the two channels ducked by different amounts\n * from the same input and the image pumped.\n */\nclass EnvBank {\n constructor(attackMs, releaseMs) { this.set(attackMs, releaseMs); this.values = []; }\n set(attackMs, releaseMs) {\n this.a = Math.exp(-1 / (sampleRate * Math.max(1e-5, attackMs / 1000)));\n this.r = Math.exp(-1 / (sampleRate * Math.max(1e-5, releaseMs / 1000)));\n }\n push(ch, x) {\n const prev = this.values[ch] ?? 0;\n const m = Math.abs(x);\n const c = m > prev ? this.a : this.r;\n const next = m + c * (prev - m);\n this.values[ch] = next;\n return next;\n }\n}\n\n/** Soft-knee gain computer in dB, matching acompressor\'s shape. */\nfunction kneeGain(envDb, thresholdDb, ratio, kneeDb) {\n const over = envDb - thresholdDb;\n if (kneeDb > 0 && over > -kneeDb && over < kneeDb) {\n const t = (over + kneeDb) / (2 * kneeDb);\n return -((1 - 1 / ratio) * kneeDb * t * t);\n }\n return over > 0 ? -(over * (1 - 1 / ratio)) : 0;\n}\n\n// log10/exp per sample is the single most expensive thing a dynamics processor\n// can do on the audio thread, and every sample below the knee needs neither:\n// its gain is exactly unity. Comparing envelopes in the linear domain lets the\n// quiet majority of samples skip the transcendentals entirely.\nconst LN10_OVER_20 = Math.LN10 / 20;\nconst dbToLinFast = (db) => Math.exp(db * LN10_OVER_20);\n\nclass HfCompressor extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.env = new EnvBank(this.p.attack ?? 20, this.p.release ?? 250);\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n this.p = { ...this.p, ...e.data };\n this.env.set(this.p.attack ?? 20, this.p.release ?? 250);\n };\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const p = this.p;\n const makeup = dbToLin(p.makeup ?? 0);\n const mix = p.mix ?? 1;\n // Knee is expressed as a ratio in FFmpeg; convert to dB width.\n const kneeDb = 20 * Math.log10(Math.max(1.0001, p.knee ?? 2.83));\n const thresholdDb = p.threshold ?? -24;\n const ratio = p.ratio ?? 4;\n // Below this the gain computer returns unity, so the sample needs no\n // logarithm at all.\n const kneeStartLin = dbToLin(thresholdDb - kneeDb);\n const dry = 1 - mix;\n for (let ch = 0; ch < i.length; ch++) {\n const inp = i[ch], out = o[ch];\n for (let n = 0; n < inp.length; n++) {\n const x = inp[n];\n // Per-channel follower, and the sub-knee shortcut: below the knee the\n // gain is exactly unity, so the sample needs no logarithm at all.\n const env = this.env.push(ch, x);\n if (env <= kneeStartLin) {\n out[n] = x * makeup * mix + x * dry;\n continue;\n }\n const envDb = 20 * Math.log10(env);\n const g = dbToLinFast(kneeGain(envDb, thresholdDb, ratio, kneeDb));\n out[n] = x * g * makeup * mix + x * dry;\n }\n }\n return true;\n }\n}\nregisterProcessor("hf-compressor", HfCompressor);\n\nclass HfLimiter extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.env = new EnvBank(this.p.attack ?? 5, this.p.release ?? 50);\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n this.p = { ...this.p, ...e.data };\n this.env.set(this.p.attack ?? 5, this.p.release ?? 50);\n };\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const ceiling = dbToLin(this.p.limit ?? -1);\n const outGain = dbToLin(this.p.level_out ?? 0);\n for (let ch = 0; ch < i.length; ch++) {\n const inp = i[ch], out = o[ch];\n for (let n = 0; n < inp.length; n++) {\n const x = inp[n];\n const env = this.env.push(ch, x);\n // Only ever attenuate: a limiter that can raise level is a compressor.\n const g = env > ceiling ? ceiling / env : 1;\n out[n] = x * g * outGain;\n }\n }\n return true;\n }\n}\nregisterProcessor("hf-limiter", HfLimiter);\n\nclass HfGate extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.env = new EnvBank(this.p.attack ?? 1, this.p.release ?? 100);\n this.gains = [];\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n this.p = { ...this.p, ...e.data };\n this.env.set(this.p.attack ?? 1, this.p.release ?? 100);\n };\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const p = this.p;\n const threshold = dbToLin(p.threshold ?? -35);\n const floor = dbToLin(p.range ?? -24);\n const ratio = p.ratio ?? 10;\n // Knee is declared in the registry as a ratio, like the compressor\'s; a\n // hard threshold ignored it and chattered on material sitting right at the\n // gate point.\n const kneeDb = 20 * Math.log10(Math.max(1.0001, p.knee ?? 2.83));\n const kneeLin = dbToLin((p.threshold ?? -35) + kneeDb);\n for (let ch = 0; ch < i.length; ch++) {\n const inp = i[ch], out = o[ch];\n for (let n = 0; n < inp.length; n++) {\n const x = inp[n];\n const env = this.env.push(ch, x);\n let target = 1;\n // Above the threshold the gate is fully open; skip the pow entirely.\n if (env < threshold) {\n const under = env > 1e-9 ? threshold / env : 1e9;\n target = Math.max(floor, 1 / Math.pow(under, ratio - 1));\n if (!isFinite(target)) target = floor;\n }\n // Smooth toward the target so the gate does not click on every sample.\n const held = this.gains[ch] ?? 1;\n const c = target < held ? this.env.r : this.env.a;\n const smoothed = target + c * (held - target);\n this.gains[ch] = smoothed;\n out[n] = x * smoothed;\n }\n }\n return true;\n }\n}\nregisterProcessor("hf-gate", HfGate);\n\nclass HfBitcrush extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.holds = [];\n this.held = [];\n this.start = Math.round((this.p.__hfElapsed ?? 0) * sampleRate);\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n this.p = { ...this.p, ...e.data };\n };\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const p = this.p;\n const levels = Math.pow(2, (p.bits ?? 8) - 1);\n const step = Math.max(1, Math.round(p.samples ?? 1));\n const mix = p.mix ?? 1;\n for (let ch = 0; ch < i.length; ch++) {\n const inp = i[ch], out = o[ch];\n // Primed from the input: a counter seeded mid-hold would otherwise play 0 until it wraps.\n if (this.held[ch] === undefined) this.held[ch] = Math.round(inp[0] * levels) / levels;\n // The hold counter runs from the file\'s first sample, so a node built part-way in starts where it would be.\n if (this.holds[ch] === undefined) this.holds[ch] = this.start % step;\n for (let n = 0; n < inp.length; n++) {\n // Per channel: advancing one shared counter only on the last channel\n // left every earlier channel either unheld or frozen for a whole\n // 128-sample quantum, clicking once a block.\n if (this.holds[ch] === 0) this.held[ch] = Math.round(inp[n] * levels) / levels;\n out[n] = this.held[ch] * mix + inp[n] * (1 - mix);\n this.holds[ch] = (this.holds[ch] + 1) % step;\n }\n }\n return true;\n }\n}\nregisterProcessor("hf-bitcrush", HfBitcrush);\n\n/** Linear-interpolated read, \\`delaySamples\\` behind the write head. */\nfunction readTap(ring, write, delaySamples) {\n const len = ring.length;\n const pos = (write - 1 - delaySamples + len) % len;\n const i0 = Math.floor(pos);\n const frac = pos - i0;\n const i1 = (i0 + 1) % len;\n return ring[i0] * (1 - frac) + ring[i1] * frac;\n}\n\n/** Equal-power-ish crossfade, zero at a tap\'s reset point, which hides the splice. */\nfunction xfade(phase) {\n return Math.sin(Math.PI * phase);\n}\n\n/**\n * Dual-tap granular delay line: two read taps 180\\xB0 apart in a 100 ms grain,\n * each sweeping at a speed relative to the write head that shifts pitch\n * without changing duration. One tap is always fading in as the other fades\n * out, which hides the splice each tap makes when it wraps.\n *\n * write/phase are block-level state, advanced once per SAMPLE across all\n * channels together (not once per channel). Advancing them inside the\n * per-channel loop would move the tap 2x/4x too fast on a stereo/quad input.\n */\nclass HfPitchshift extends AudioWorkletProcessor {\n constructor(o) {\n super();\n this.p = o.processorOptions || {};\n this.grain = Math.round(sampleRate * 0.1);\n this.buf = [];\n this.write = 0;\n // The taps sweep at a constant rate from the start of the file, so a node built\n // part-way in joins the sweep where it would already be.\n const ratio0 = Math.pow(2, Math.max(-12, Math.min(12, this.p.semitones ?? 0)) / 12);\n const turns = (this.p.__hfElapsed ?? 0) * sampleRate * ((1 - ratio0) / this.grain);\n this.phase = turns - Math.floor(turns);\n // Samples written so far, capped at one grain. The taps read up to a grain\n // behind the write head, so until this fills they would read the ring\'s\n // zeros, so the head of every clip came out attenuated or silent.\n this.filled = 0;\n // How much of the wet (pitch-shifted) path is currently in the output, and\n // where it is heading. Crossing between dry and wet is a ~50 ms jump in the\n // signal, so it is RAMPED rather than switched: a hard swap either way is a\n // click. Ramping in both directions is also what lets a node return to true\n // bypass at semitones 0. A one-way latch left preview stuck with the delay\n // that the render, building a fresh node from the attribute, does not have.\n this.wet = 0;\n this.wetTarget = 0;\n // ~15 ms one-pole, short enough to feel immediate on a slider drag and long\n // enough that the splice is inaudible.\n this.wetCoef = Math.exp(-1 / (sampleRate * 0.015));\n this.port.onmessage = (e) => {\n if (e.data && e.data.__hfDispose) { this.dead = true; return; }\n this.p = { ...this.p, ...e.data };\n };\n }\n process(inputs, outputs) {\n if (this.dead) return false;\n const i = inputs[0], o = outputs[0];\n if (!i || !i.length) return true;\n const p = this.p;\n const semitones = Math.max(-12, Math.min(12, p.semitones ?? 0));\n const mix = Math.max(0, Math.min(1, p.mix ?? 1));\n const grain = this.grain;\n const ringLen = grain * 2;\n const n = i[0] ? i[0].length : 0;\n for (let ch = 0; ch < i.length; ch++) {\n if (!this.buf[ch]) this.buf[ch] = new Float32Array(ringLen);\n }\n\n // Nothing to shift, or mixed fully out. The grain delay is ~grain/2\n // whatever the ratio, so at semitones=0 this degenerated into a pure 50 ms\n // delay of the signal, while the copy for that exact setting reads\n // "Unchanged pitch".\n this.wetTarget = semitones === 0 ? 0 : mix;\n\n // Fully dry AND settled: take the cheap transparent path. The ring keeps\n // filling, so a later shift does not start cold.\n if (this.wetTarget === 0 && this.wet < 1e-4) {\n this.wet = 0;\n let w = this.write;\n for (let s = 0; s < n; s++) {\n for (let ch = 0; ch < i.length; ch++) {\n const x = i[ch][s];\n this.buf[ch][w] = x;\n o[ch][s] = x;\n }\n w = (w + 1) % ringLen;\n }\n this.write = w;\n this.filled = Math.min(grain, this.filled + n);\n return true;\n }\n\n const ratio = Math.pow(2, semitones / 12);\n const inc = (1 - ratio) / grain;\n let write = this.write, phase = this.phase, filled = this.filled, wetNow = this.wet;\n const target = this.wetTarget, coef = this.wetCoef;\n for (let s = 0; s < n; s++) {\n phase += inc;\n phase -= Math.floor(phase);\n const phaseB = (phase + 0.5) % 1;\n const gA = xfade(phase), gB = xfade(phaseB);\n // Ramp the wet path in as the ring fills rather than reading zeros:\n // 100 ms of unshifted audio at the head of a clip beats 50 ms of silence.\n const warm = filled >= grain ? 1 : filled / grain;\n wetNow = target + coef * (wetNow - target);\n const wetMix = wetNow * warm;\n for (let ch = 0; ch < i.length; ch++) {\n const ring = this.buf[ch];\n const inp = i[ch], out = o[ch];\n const x = inp[s];\n ring[write] = x;\n const wet =\n readTap(ring, write, phase * grain) * gA + readTap(ring, write, phaseB * grain) * gB;\n out[s] = x * (1 - wetMix) + wet * wetMix;\n }\n write = (write + 1) % ringLen;\n if (filled < grain) filled++;\n }\n this.write = write;\n this.phase = phase;\n this.filled = filled;\n this.wet = wetNow;\n return true;\n }\n}\nregisterProcessor("hf-pitchshift", HfPitchshift);\n`+Z,L=new WeakMap;var _e=new WeakSet;function O(e){let t=L.get(e);return t||(t=(async()=>{if(!e.audioWorklet)throw new Error("AudioWorklet is unavailable: the page needs a secure context (https, localhost or file://)");let n=`data:text/javascript;base64,${btoa(String.fromCharCode(...new TextEncoder().encode(He)))}`;await e.audioWorklet.addModule(n),_e.add(e)})().catch(n=>{throw L.delete(e),n}),L.set(e,t)),t}function Re(e,t,n){let o=.6+Math.max(0,Math.min(1,t))*2.6,a=Math.max(1,Math.floor(e*o)),r=new Float32Array(a),i=Math.round(t*1e3)*2654435761+Math.round(n*1e3)*40503>>>0,s=()=>(i=i*1664525+1013904223>>>0,i/4294967295*2-1),u=Math.max(.001,1-Math.max(0,Math.min(1,n))),l=0,m=0;for(let c=0;c<a;c++){l+=u*(s()-l);let b=l*Math.pow(1-c/a,2.5);r[c]=b,m+=b*b}let p=Math.sqrt(m);if(p>0)for(let c=0;c<a;c++)r[c]=(r[c]??0)/p;return r}var f=e=>typeof e=="number"?e:Number(e??0),B=e=>e/1e3;function ae(e,t,n,o){let a=Math.max(1,Math.round(e.sampleRate)),r=e.createBuffer(1,a,e.sampleRate),i=r.getChannelData(0);for(let l=0;l<a;l++){let m=l/a;i[l]=t==="sine"?Math.sin(2*Math.PI*m):4*Math.abs((m+.75)%1-.5)-1}let s=e.createBufferSource();s.buffer=r,s.loop=!0,s.playbackRate.value=n;let u=(o*n%1+1)%1*(a/e.sampleRate);return s.start(typeof e.currentTime=="number"?e.currentTime:0,u),s}function oe(e){try{e.stop()}catch{}e.disconnect()}function I(e,t){return[{param:e},{param:t,map:n=>1-n}]}function re(e,t,n){e.gain.value=n,t.gain.value=1-n}function q(e,t,n){return{input:e,output:e,update:t,automation:n,dispose:()=>e.disconnect()}}var Ne=new Set(["peaking","highpass","lowpass"]),ee=e=>Math.pow(10,e/20),Ee=(e,t)=>{let n=e.createGain(),o=a=>{n.gain.value=ee(f(a.gain))};return o(t),q(n,o,{gain:[{param:n.gain,map:ee}]})};function H(e,t){return(n,o)=>{let a=n.createBiquadFilter();a.type=e;let r=i=>{a.frequency.value=f(i.frequency),i.q!==void 0&&(a.Q.value=f(i.q)),t&&(a.gain.value=f(i.gain))};return r(o),q(a,r,{frequency:[{param:a.frequency}],...Ne.has(e)?{q:[{param:a.Q}]}:{},...t?{gain:[{param:a.gain}]}:{}})}}function De(e){return(t,n)=>{let o=Math.tan(Math.PI*f(n.frequency)/t.sampleRate),a=e==="highpass"?t.createIIRFilter([1/(1+o),-1/(1+o)],[1,(o-1)/(o+1)]):t.createIIRFilter([o/(1+o),o/(1+o)],[1,(o-1)/(o+1)]);return q(a,()=>{})}}function M(e,t=!1){return(n,o,a)=>{let r=new AudioWorkletNode(n,e,{processorOptions:{...o,__hfElapsed:a}});return{input:r,output:r,update:i=>r.port.postMessage({...i}),...t?{reset:()=>r.port.postMessage({__hfReset:!0})}:{},dispose:()=>{r.port.postMessage({__hfDispose:!0}),r.disconnect()}}}}var Se={tanh:Math.tanh,atan:e=>2/Math.PI*Math.atan(Math.PI/2*e),cubic:e=>Math.abs(e)>=1?Math.sign(e):e-e**3/3,exp:e=>Math.sign(e)*(1-Math.exp(-Math.abs(e))),alg:e=>e/Math.sqrt(1+e*e),quintic:e=>Math.abs(e)>=1?Math.sign(e):e-e**5/5,sin:e=>Math.abs(e)>=1?Math.sign(e):Math.sin(Math.PI/2*e),erf:e=>Math.tanh(1.20211*e),hard:e=>Math.max(-1,Math.min(1,e))},Ce=(e,t)=>{let n=e.createWaveShaper(),o=e.createGain(),a=e.createGain();o.connect(n).connect(a);let r=i=>{let s=Se[String(i.type)]??Math.tanh,u=Math.pow(10,f(i.threshold)/20),l=8192,m=new Float32Array(l);for(let p=0;p<l;p++){let c=p/(l-1)*2-1;m[p]=s(c/Math.max(1e-6,u))*u}n.curve=m,n.oversample=f(i.oversample)>=4?"4x":f(i.oversample)>=2?"2x":"none",a.gain.value=Math.pow(10,f(i.output)/20)};return r(t),{input:o,output:a,update:r,automation:{output:[{param:a.gain,map:i=>Math.pow(10,i/20)}]},dispose:()=>{o.disconnect(),n.disconnect(),a.disconnect()}}},Le=(e,t)=>{let n=e.createGain(),o=e.createGain(),a=e.createDelay(5),r=e.createGain(),i=e.createGain(),s=e.createGain();n.connect(a),a.connect(r),r.connect(a),a.connect(i).connect(o),n.connect(s).connect(o);let u=l=>{a.delayTime.value=Math.min(5,f(l.time)/1e3),r.gain.value=f(l.feedback),re(i,s,f(l.mix))};return u(t),{input:n,output:o,update:u,automation:{time:[{param:a.delayTime,map:l=>Math.min(5,B(l))}],feedback:[{param:r.gain}],mix:I(i.gain,s.gain)},dispose:()=>[n,o,a,r,i,s].forEach(l=>l.disconnect())}},Oe=(e,t,n)=>{let o=e.createGain(),a=e.createGain(),r=e.createDelay(.5),i=ae(e,"sine",f(t.speed),n),s=e.createGain(),u=e.createGain(),l=e.createGain();i.connect(s).connect(r.delayTime),o.connect(r).connect(u).connect(a),o.connect(l).connect(a);let m=p=>{r.delayTime.value=f(p.delay)/1e3,s.gain.value=f(p.depth)/1e3,i.playbackRate.value=f(p.speed),re(u,l,f(p.mix))};return m(t),{input:o,output:a,update:m,automation:{delay:[{param:r.delayTime,map:B}],depth:[{param:s.gain,map:B}],speed:[{param:i.playbackRate}],mix:I(u.gain,l.gain)},dispose:()=>{oe(i),[o,a,r,s,u,l].forEach(p=>p.disconnect())}}},Be=6,Ie=(e,t,n)=>{let o=e.createGain(),a=e.createGain(),r=e.createGain(),i=e.createGain(),s=ae(e,String(t.type)==="1"?"sine":"triangle",f(t.speed),n),u=e.createGain(),l=e.createGain(),m=e.createGain(),p=[];o.connect(r);let c=r;for(let d=0;d<Be;d++){let h=e.createBiquadFilter();h.type="allpass",h.Q.value=.7071,u.connect(h.frequency),c.connect(h),c=h,p.push(h)}s.connect(u),c.connect(l).connect(i),r.connect(m).connect(i),i.connect(a);let b=d=>{let h=1e3/Math.max(.1,f(d.delay));for(let g of p)g.frequency.value=h;u.gain.value=h*f(d.decay),s.playbackRate.value=f(d.speed),r.gain.value=f(d.in_gain),i.gain.value=f(d.out_gain),l.gain.value=1,m.gain.value=1};return b(t),{input:o,output:a,update:b,automation:{speed:[{param:s.playbackRate}],in_gain:[{param:r.gain}],out_gain:[{param:i.gain}]},dispose:()=>{oe(s),[o,a,r,i,u,l,m,...p].forEach(d=>d.disconnect())}}},qe=(e,t)=>{let n=e.createGain(),o=e.createGain(),a=e.createConvolver(),r=e.createGain(),i=e.createGain();a.normalize=!1,n.connect(a).connect(r).connect(o),n.connect(i).connect(o);let s="",u=l=>{let m=`${f(l.size)}:${f(l.damping)}`;if(m!==s){let p=Re(e.sampleRate,f(l.size),f(l.damping)),c=e.createBuffer(1,p.length,e.sampleRate);c.getChannelData(0).set(p),a.buffer=c,s=m}r.gain.value=f(l.wet),i.gain.value=f(l.dry)};return u(t),{input:n,output:o,update:u,automation:{wet:[{param:r.gain}],dry:[{param:i.gain}]},dispose:()=>[n,o,a,r,i].forEach(l=>l.disconnect())}},Ge={"gain-node":Ee,"biquad-peaking":H("peaking",!0),"biquad-lowshelf":H("lowshelf",!0),"biquad-highshelf":H("highshelf",!0),"biquad-highpass":H("highpass",!1),"biquad-lowpass":H("lowpass",!1),"worklet-compressor":M("hf-compressor"),"worklet-limiter":M("hf-limiter"),"worklet-truepeak":M("hf-truepeak",!0),"worklet-gate":M("hf-gate"),"worklet-bitcrush":M("hf-bitcrush"),"worklet-pitchshift":M("hf-pitchshift"),waveshaper:Ce,"delay-feedback":Le,"chorus-lfo":Oe,"allpass-phaser":Ie,convolver:qe};function ie(e){return e.nodes.some(t=>P(t.type)?.web.startsWith("worklet-")??!1)}function Ve(e,t,n,o=0){let a=P(t);if(!a)throw new Error(`Unknown effect type: ${t}`);let r=y(t,n);if((t==="highpass"||t==="lowpass")&&String(r.poles)==="1")return De(t)(e,r,o);let i=Ge[a.web];if(!i)throw new Error(`No Web Audio builder for ${a.web}`);return i(e,r,o)}function te(e){let t=[];for(let n of e){let o=n.fromPreset,a=t.at(-1);if(a&&a.preset===o)a.nodes.push(n);else{let r=typeof n.presetAmount=="number"?n.presetAmount:1;t.push({...o?{preset:o}:{},amount:Math.min(1,Math.max(0,r)),nodes:[n]})}}return t}function ne(e){return w(e).map(t=>{let n=y(t.type,t.params),o=n.poles!==void 0?`:${n.poles}`:"",a=String(n.poles)==="1"?`@${n.frequency}`:"",r=t.type==="phaser"?`~${n.type}`:"",i=t.fromPreset?`%${t.fromPreset}`:"",s=t.type==="truepeak"?`^${n.lookahead}`:"";return`${t.type}${o}${a}${r}${s}${i}`}).join("|")}function se(e,t,n=0){var p;let o=e.createGain(),a=e.createGain(),r=[],i=[],s=te(w(t)),u=o;for(let c of s){let b=null;if(c.preset){let d=e.createGain(),h=e.createGain(),g=e.createGain(),T=e.createGain();g.gain.value=c.amount,h.gain.value=1-c.amount,u.connect(d),d.connect(h).connect(T),b={entry:d,wet:g,dry:h,join:T},u=d}for(let d of c.nodes){let h=Ve(e,d.type,d.params??{},n);u.connect(h.input),u=h.output,r.push({...d.id?{id:d.id}:{},type:d.type,handle:h})}b&&c.preset&&(u.connect(b.wet).connect(b.join),i.push({id:c.preset,...b}),u=b.join)}u.connect(a);let l=ne(t),m={};for(let c of i)(m[p=c.id]??(m[p]=[])).push(...I(c.wet.gain,c.dry.gain));return{input:o,output:a,presets:m,nodes:r,update(c){if(ne(c)!==l)return!1;w(c).forEach((d,h)=>{let g=r[h];g&&(g.handle.update(y(d.type,d.params)),d.id===void 0?delete g.id:g.id=d.id)});let b=0;for(let d of te(w(c))){if(!d.preset)continue;let h=i[b++];!h||h.id!==d.preset||(h.wet.gain.value=d.amount,h.dry.gain.value=1-d.amount)}return!0},reset(){for(let{handle:c}of r)c.reset?.()},dispose(){for(let{handle:c}of r)c.dispose();for(let{entry:c,wet:b,dry:d,join:h}of i)c.disconnect(),b.disconnect(),d.disconnect(),h.disconnect();o.disconnect(),a.disconnect()}}}function Ue(e){return 10**(e/20)}var ue=Ue(12);var G=.1,V=10;var $e="data-automation",Xt=$e.slice(5),D=1,je=512,A=class extends Error{constructor(t){super(t),this.name="AudioAutomationError"}},U="volume",le="rate";function R(e){if(e===U)return{kind:"volume"};if(e===le)return{kind:"rate"};let t=e.split(".");if(t.length===3&&t[0]==="fx"&&t[1]===Xe){let a=t[2];return a?{kind:"preset",presetId:a}:null}if(t.length!==3||t[0]!=="fx")return null;let[,n,o]=t;return!n||!o?null:{kind:"fx",nodeId:n,param:o}}function ce(e,t){return`fx.${e}.${t}`}var Xe="preset";var We={min:0,max:1,step:.01,unit:"",label:"Amount",scale:"linear",default:1},de={min:0,max:ue,step:.01,unit:"",label:"Volume",scale:"linear",default:1};function me(e){return!!e.curve||e.viaX!==void 0&&e.viaY!==void 0}var pe={min:G,max:V,step:.05,unit:"x",label:"Speed",scale:"log",default:1};function $(e,t){let n=R(e);if(!n)return null;if(n.kind==="volume")return de;if(n.kind==="rate")return pe;if(n.kind==="preset")return t?.nodes.some(s=>s.fromPreset===n.presetId)?{...We,label:`${n.presetId} \\xB7 Amount`}:null;let o=t?.nodes.find(i=>i.id===n.nodeId);if(!o)return null;let a=P(o.type),r=a?.params.find(i=>i.key===n.param);return!r||r.kind!=="number"?null:{min:r.min,max:r.max,step:r.step,unit:r.unit,label:`${a?.label??o.type} \\xB7 ${r.label}`,scale:r.scale==="log"&&r.min>0?"log":"linear",default:r.default}}function _(e){if(typeof e=="number")return Number.isFinite(e)?e:null;if(typeof e=="string"&&e.trim()!==""){let t=Number(e);return Number.isFinite(t)?t:null}return null}function Ke(e){let t=_(e);return t===null||t===0?0:Math.min(1,Math.max(-1,t))}function he(e,t){let n=_(e),o=_(t);if(n===null||o===null)return null;let a=s=>Math.min(.999,Math.max(.001,s)),r=a(n),i=a(o);return Math.abs(r-i)<1e-6?null:{x:r,y:i}}function Ye(e){let t=Ke(e?.curve),n=he(e?.viaX,e?.viaY);return{...t?{curve:t}:{},...n?{viaX:n.x,viaY:n.y}:{}}}function ze(e,t){let n=_(e?.t),o=_(e?.v);if(n===null||o===null)return null;let a=t?Math.min(t.max,Math.max(t.min,o)):o;return{t:Math.max(0,n),v:a,...Ye(e)}}function fe(e,t){let n=e.map(a=>ze(a,t)).filter(a=>a!==null).sort((a,r)=>a.t-r.t),o=[];for(let a of n)o.length>0&&o[o.length-1].t===a.t?o[o.length-1]=a:o.push(a);return o.slice(0,je)}function Je(e){let t=[];for(let n of e.lanes){if(!R(n.target))continue;let o=n.target===U?de:n.target===le?pe:null,a=fe(n.points??[],o);a.length>0&&t.push({target:n.target,points:a})}return{version:D,lanes:t}}function be(e,t){let n=[];for(let o of e.lanes){let a=$(o.target,t);if(!a)continue;let r=fe(o.points,a);r.length>0&&n.push({target:o.target,points:r})}return{version:D,lanes:n}}function ge(e){let t;try{t=JSON.parse(e)}catch(a){throw new A(`Automation is not valid JSON: ${a.message}`)}if(typeof t!="object"||t===null)throw new A("Automation must be a JSON object.");let n=t;if(n.version!==D)throw new A(`Unsupported automation version: ${String(n.version)}`);if(!Array.isArray(n.lanes))throw new A("Automation is missing a `lanes` array.");let o=n.lanes.map((a,r)=>{if(typeof a!="object"||a===null)throw new A(`Lane ${r} is not an object.`);let i=a;if(typeof i.target!="string"||!R(i.target))throw new A(`Lane ${r} has an unreadable target: ${String(i.target)}`);if(!Array.isArray(i.points))throw new A(`Lane ${r} is missing a \\`points\\` array.`);return{target:i.target,points:i.points}});return Je({version:D,lanes:o})}function Qe(e,t){return t?Math.pow(e,Math.pow(2,2*t)):e}function Ze(e,t){let n=e-.5,o=t-.5,a=.999,r=1e6,i=n>0?n/(a-e):n<0?-n/(e-(1-a)):0,s=o>0?o/(a-t):o<0?-o/(t-(1-a)):0,u=Math.min(r,Math.max(1,i,s));return{cx:e+n/u,cy:t+o/u,w:u}}function et(e,t,n){let{cx:o,cy:a,w:r}=Ze(t,n),i=2-2*r,s=e*i-1+2*r*o,u=-e*i-2*r*o,l=tt(s,u,e),m=1-l,p=m*m+2*r*l*m+l*l;return p>0?(2*r*a*l*m+l*l)/p:e}function tt(e,t,n){if(Math.abs(e)<1e-12)return Math.abs(t)<1e-12?n:-n/t;let o=Math.sqrt(Math.max(0,t*t-4*e*n)),a=(-t+o)/(2*e),r=(-t-o)/(2*e),i=s=>s>=-1e-9&&s<=1+1e-9;return i(a)?Math.min(1,Math.max(0,a)):i(r)?Math.min(1,Math.max(0,r)):n}function nt(e,t){let n=he(t.viaX,t.viaY);return n?et(Math.min(1,Math.max(0,e)),n.x,n.y):Qe(e,t.curve)}function at(e,t,n,o){return o==="log"&&e>0&&t>0?Math.exp(Math.log(e)+(Math.log(t)-Math.log(e))*n):e+(t-e)*n}function j(e,t,n="linear"){let o=e.points;if(o.length===0)return 0;let a=o[0];if(t<=a.t)return a.v;let r=o[o.length-1];if(t>=r.t)return r.v;let i=0,s=o.length-1;for(;s-i>1;){let p=i+s>>1;o[p].t<=t?i=p:s=p}let u=o[i],l=o[s],m=l.t-u.t;return m<=0?l.v:at(u.v,l.v,nt((t-u.t)/m,u),n)}function Ae(e){return e.points.length<=1||e.points.every(t=>t.v===e.points[0].v)}function xe(e,t,n,o,a="linear"){let r=Math.max(2,Math.floor(o)),i=new Float32Array(r),s=n-t;for(let u=0;u<r;u+=1)i[u]=j(e,t+s*u/(r-1),a);return i}var ot=100,rt=8,it=4096,X=e=>e;function st(e,t,n){return!me(e)&&t==="linear"&&!n}function ut(e){let t=Math.ceil(e*ot);return Math.min(it,Math.max(rt,t))}function lt(e,t,n,o,a){let{scheduledAt:r,elapsed:i,rate:s}=o,u=a??X,l=v=>r+(v-i)/s,m=e.points[t],p=e.points[t+1],c=l(p.t);if(c<=r)return null;let b=Math.max(r,l(m.t)),d=c-b;if(d<=0)return null;if(st(m,n,a))return{kind:"ramp",time:c,value:u(p.v)};let h=ut(d),g=xe(e,Math.max(m.t,i),p.t,h,n),T=new Float32Array(h);for(let v=0;v<h;v+=1)T[v]=u(g[v]??0);return{kind:"curve",time:b,duration:d,values:T}}function ct(e,t,n,o){let a=[];for(let i=0;i+1<e.points.length;i+=1){let s=lt(e,i,t,n,o);s&&a.push(s)}let r=a[0];return r?.kind==="curve"&&r.time<=n.scheduledAt||a.unshift({kind:"set",time:n.scheduledAt,value:(o??X)(j(e,n.elapsed,t))}),a}function dt(e,t){e.cancelScheduledValues(0);for(let n of t)n.kind==="set"?e.setValueAtTime(n.value,n.time):n.kind==="ramp"?e.linearRampToValueAtTime(n.value,n.time):(e.setValueAtTime(n.values[0]??0,n.time),e.linearRampToValueAtTime(n.values[n.values.length-1]??0,n.time+n.duration))}function mt(e,t){for(let n of t)if(n.kind==="set")e.setValueAtTime(n.value,n.time);else if(n.kind==="ramp")e.linearRampToValueAtTime(n.value,n.time);else try{e.setValueCurveAtTime(n.values,n.time,n.duration)}catch{dt(e,t);return}}function pt(e){for(let t of e)t.param.cancelScheduledValues(0)}function ht(e,t,n,o){if(t.points.length!==0)for(let a of e){if(pt([a]),Ae(t)){let r=(a.map??X)(t.points[0].v);a.param.setValueAtTime(r,o.scheduledAt);continue}mt(a.param,ct(t,n,o,a.map))}}function ye(e,t,n,o,a){let r=new Map(n.filter(s=>s.id).map(s=>[s.id,s.handle])),i=[];for(let s of e.lanes){let u=R(s.target);if(!u)continue;let l=u.kind==="preset"?a?.[u.presetId]:u.kind==="fx"?r.get(u.nodeId)?.automation?.[u.param]:void 0;if(!l||l.length===0)continue;let m=$(s.target,t);m&&(ht(l,s,m.scale,o),i.push(...l))}return i}var ft=5,bt=.001;function k(e,t,n){let o=Number(y(e.type,e.params)[t]??0);if(!e.id||!n)return Number.isFinite(o)?o:0;let a=n.lanes.find(r=>r.target===ce(e.id??"",t));return!a||a.points.length===0?Number.isFinite(o)?o:0:a.points.reduce((r,i)=>Math.max(r,i.v),-1/0)}function gt(e,t){let n=Math.min(5,e/1e3);if(n<=0)return 0;let o=Math.max(0,Math.min(.999,t));return o<=0?n:Math.ceil(Math.log(bt)/Math.log(o))*n}function At(e){return .6+Math.max(0,Math.min(1,e))*2.6}function xt(e,t){return k(e,"wet",t)>0?At(k(e,"size",t)):0}function yt(e,t){return k(e,"mix",t)>0?gt(k(e,"time",t),k(e,"feedback",t)):0}function kt(e,t){return k(e,"mix",t)>0?k(e,"delay",t)/1e3:0}function wt(e,t){return k(e,"mix",t)>0?.2:0}function vt(e,t){if(e.enabled===!1)return 0;switch(e.type){case"reverb":return xt(e,t);case"delay":return yt(e,t);case"chorus":return kt(e,t);case"pitchshift":return wt(e,t);default:return 0}}function ke(e,t){let n=e.nodes.reduce((o,a)=>o+vt(a,t),0);return Math.min(ft,n)}function we(e,t){return w(e).filter(n=>n.type==="truepeak").reduce((n,o)=>{let{lookahead:a}=y(o.type,o.params);return n+Q(Number(a),t)},0)}function Mt(e,t,n,o,a){let r=e.createBuffer(n,o,a);for(let i=0;i<n;i++){let s=t[i];s&&r.getChannelData(i).set(s)}return r}function Tt(e,t){let n=[];for(let o=0;o<e.numberOfChannels;o++)n.push(new Float32Array(e.getChannelData(o).subarray(t)));return n}async function Ft(e,t,n,o,a=0){let r=J(n),i=Math.max(1,e.length),s=e[0]?.length??0;if(s===0)return e;let u=o?be(ge(o),r):null,l=Math.ceil(ke(r,u??void 0)*t),m=we(r,t),p=s+l+m,c=new OfflineAudioContext(i,p,t);ie(r)&&await O(c);let b=c.createBufferSource();b.buffer=Mt(c,e,i,p,t);let d=se(c,r,a);return u&&ye(u,r,d.nodes,{scheduledAt:0,elapsed:0,rate:1},d.presets),b.connect(d.input),d.output.connect(c.destination),b.start(),Tt(await c.startRendering(),m)}window.__HF_AUDIO_FX={render:Ft};})();\n';
|
|
14583
14594
|
function getAudioFxRuntimeScript() {
|
|
14584
14595
|
return AUDIO_FX_RUNTIME_IIFE;
|
|
14585
14596
|
}
|
|
@@ -14765,6 +14776,9 @@ function applyEnvelopeToPlanes(planes, sampleRate, gainAt, startFrame = 0) {
|
|
|
14765
14776
|
for (const plane of planes) plane[frame] = (plane[frame] ?? 0) * gain;
|
|
14766
14777
|
}
|
|
14767
14778
|
}
|
|
14779
|
+
function applyInputGain(planes, gain = 1) {
|
|
14780
|
+
if (gain !== 1) applyEnvelopeToPlanes(planes, 1, () => gain);
|
|
14781
|
+
}
|
|
14768
14782
|
var DEFAULT_STREAMING_LIMITS = {
|
|
14769
14783
|
aboveBytes: 512 * 1024 * 1024,
|
|
14770
14784
|
segmentSeconds: 300,
|
|
@@ -14838,6 +14852,7 @@ async function renderInSegments(openPage, inputWav, header, chain, outputWav, op
|
|
|
14838
14852
|
throw new AudioFxRenderError(`Audio FX cancelled for track ${options.trackId}`);
|
|
14839
14853
|
}
|
|
14840
14854
|
const planes = deinterleave(readFrames(inFd, header, seg.readFrom, seg.readTo), channels);
|
|
14855
|
+
applyInputGain(planes, options.inputGain);
|
|
14841
14856
|
const page = await openPage();
|
|
14842
14857
|
let outPlanes;
|
|
14843
14858
|
try {
|
|
@@ -14897,6 +14912,7 @@ async function applyAudioFxChain(inputWav, chain, outputWav, options) {
|
|
|
14897
14912
|
}
|
|
14898
14913
|
const input = readWav(inputWav);
|
|
14899
14914
|
const planes = deinterleave(input.samples, input.channels);
|
|
14915
|
+
applyInputGain(planes, options.inputGain);
|
|
14900
14916
|
if ((planes[0]?.length ?? 0) === 0) return { path: inputWav, envelopeBaked: false };
|
|
14901
14917
|
return withFxBrowser(options, async (openPage) => {
|
|
14902
14918
|
const page = await openPage();
|
|
@@ -15256,7 +15272,7 @@ function downloadFailure(error, elementId) {
|
|
|
15256
15272
|
detail: boundedDetail(`Download failed for audio element ${elementId}: ${message}`)
|
|
15257
15273
|
};
|
|
15258
15274
|
}
|
|
15259
|
-
function ffmpegFailure(stage, result, elementId) {
|
|
15275
|
+
function ffmpegFailure(stage, result, timeoutMs, elementId) {
|
|
15260
15276
|
const stderr = result.stderr ?? "";
|
|
15261
15277
|
let reason = "ffmpeg_failed";
|
|
15262
15278
|
let owner = "system";
|
|
@@ -15287,32 +15303,32 @@ function ffmpegFailure(stage, result, elementId) {
|
|
|
15287
15303
|
elementId,
|
|
15288
15304
|
subprocess: audioSubprocessEvidence(result),
|
|
15289
15305
|
detail: boundedDetail(
|
|
15290
|
-
result.error?.message ? `${
|
|
15306
|
+
result.error?.message ? `${describeFfmpegFailure(result, timeoutMs)}: ${result.error.message}` : describeFfmpegFailure(result, timeoutMs)
|
|
15291
15307
|
)
|
|
15292
15308
|
};
|
|
15293
15309
|
}
|
|
15294
|
-
|
|
15295
|
-
function volumeLaneKeyframes(automation, trackStart, duration) {
|
|
15310
|
+
function volumeLaneKeyframes(automation, trackStart, duration, gain, gainAhead = false) {
|
|
15296
15311
|
const lane = automation.lanes.find((l) => l.target === VOLUME_TARGET);
|
|
15297
15312
|
if (!lane || lane.points.length === 0) return null;
|
|
15313
|
+
const points = gainedVolumePoints(lane, gain, gainAhead);
|
|
15298
15314
|
const out = [];
|
|
15299
15315
|
const push = (t, v) => {
|
|
15300
15316
|
out.push({ time: trackStart + t, volume: v });
|
|
15301
15317
|
};
|
|
15302
|
-
const first =
|
|
15318
|
+
const first = points[0];
|
|
15303
15319
|
if (first.t > 0) push(0, first.v);
|
|
15304
|
-
for (let i = 0; i <
|
|
15305
|
-
const a =
|
|
15320
|
+
for (let i = 0; i < points.length; i += 1) {
|
|
15321
|
+
const a = points[i];
|
|
15306
15322
|
push(a.t, a.v);
|
|
15307
|
-
const b =
|
|
15308
|
-
if (!b || !a
|
|
15309
|
-
const steps =
|
|
15323
|
+
const b = points[i + 1];
|
|
15324
|
+
if (!b || !isBentSegment(a)) continue;
|
|
15325
|
+
const steps = volumeSegmentSampleCount(b.t - a.t);
|
|
15310
15326
|
for (let k = 1; k < steps; k += 1) {
|
|
15311
15327
|
const t = a.t + (b.t - a.t) * k / steps;
|
|
15312
|
-
push(t, sampleAutomationLane(lane, t));
|
|
15328
|
+
push(t, gainedVolume(sampleAutomationLane(lane, t), gain, gainAhead));
|
|
15313
15329
|
}
|
|
15314
15330
|
}
|
|
15315
|
-
const last =
|
|
15331
|
+
const last = points[points.length - 1];
|
|
15316
15332
|
if (duration > last.t) push(duration, last.v);
|
|
15317
15333
|
return out;
|
|
15318
15334
|
}
|
|
@@ -15429,7 +15445,7 @@ async function extractAudioSegment(srcPath, outputPath, stage, mediaStart, durat
|
|
|
15429
15445
|
failure: failure2
|
|
15430
15446
|
};
|
|
15431
15447
|
}
|
|
15432
|
-
let failure = result.success ? void 0 : ffmpegFailure(stage, result);
|
|
15448
|
+
let failure = result.success ? void 0 : ffmpegFailure(stage, result, ffmpegProcessTimeout);
|
|
15433
15449
|
const probedLinkFailedToRead = readsUrl && (failure?.reason === "invalid_media" || failure?.reason === "ffmpeg_failed");
|
|
15434
15450
|
if (failure && probedLinkFailedToRead) {
|
|
15435
15451
|
failure = { ...failure, reason: "download_failed", owner: "system", retryable: true };
|
|
@@ -15475,7 +15491,7 @@ async function generateSilence(outputPath, duration, signal, config) {
|
|
|
15475
15491
|
failure: failure2
|
|
15476
15492
|
};
|
|
15477
15493
|
}
|
|
15478
|
-
const failure = !result.success ? ffmpegFailure("silence", result) : void 0;
|
|
15494
|
+
const failure = !result.success ? ffmpegFailure("silence", result, ffmpegProcessTimeout) : void 0;
|
|
15479
15495
|
return {
|
|
15480
15496
|
success: result.success,
|
|
15481
15497
|
outputPath,
|
|
@@ -15584,7 +15600,7 @@ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config)
|
|
|
15584
15600
|
};
|
|
15585
15601
|
}
|
|
15586
15602
|
if (!result.success) {
|
|
15587
|
-
const failure = ffmpegFailure("mix", result);
|
|
15603
|
+
const failure = ffmpegFailure("mix", result, ffmpegProcessTimeout);
|
|
15588
15604
|
return {
|
|
15589
15605
|
success: false,
|
|
15590
15606
|
outputPath,
|
|
@@ -15609,14 +15625,15 @@ async function mixGroupMembers(memberTracks, outputPath, totalDuration, signal,
|
|
|
15609
15625
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
15610
15626
|
const outputDir = dirname7(outputPath);
|
|
15611
15627
|
if (!existsSync11(outputDir)) mkdirSync7(outputDir, { recursive: true });
|
|
15612
|
-
const buildInputFilters = (ignoreKeyframes) => memberTracks.map((track, i) => {
|
|
15628
|
+
const buildInputFilters = (ignoreKeyframes, padMembers) => memberTracks.map((track, i) => {
|
|
15613
15629
|
const volumeFilter = buildVolumeExpression(track, ignoreKeyframes);
|
|
15614
|
-
return buildTrackInputFilter(track, i, volumeFilter, totalDuration);
|
|
15630
|
+
return buildTrackInputFilter(track, i, volumeFilter, totalDuration, padMembers);
|
|
15615
15631
|
});
|
|
15616
15632
|
const mixInputs = memberTracks.map((_, i) => `[a${i}]`).join("");
|
|
15617
15633
|
const runOnce = async (useNormalize2, ignoreKeyframes = false) => {
|
|
15618
|
-
const inputFilters = buildInputFilters(ignoreKeyframes);
|
|
15619
|
-
const
|
|
15634
|
+
const inputFilters = buildInputFilters(ignoreKeyframes, !useNormalize2);
|
|
15635
|
+
const padUnitySumOnceToFilm = `[mixed];[mixed]apad,asetpts=N/SR/TB,atrim=0:${formatFilterNumber(totalDuration)}`;
|
|
15636
|
+
const mixFilter = useNormalize2 ? `${mixInputs}amix=inputs=${memberTracks.length}:duration=longest:dropout_transition=0:normalize=0${padUnitySumOnceToFilm}[out]` : (
|
|
15620
15637
|
// Every member is padded and trimmed to totalDuration above, and
|
|
15621
15638
|
// dropout_transition=0 keeps all N inputs active for that whole span.
|
|
15622
15639
|
// amix's default normalize therefore divides by exactly N throughout;
|
|
@@ -15685,7 +15702,7 @@ async function mixGroupMembers(memberTracks, outputPath, totalDuration, signal,
|
|
|
15685
15702
|
}
|
|
15686
15703
|
}
|
|
15687
15704
|
if (signal?.aborted || !result.success) {
|
|
15688
|
-
const failure = ffmpegFailure("mix", result);
|
|
15705
|
+
const failure = ffmpegFailure("mix", result, ffmpegProcessTimeout);
|
|
15689
15706
|
return { success: false, error: failure.detail, failure };
|
|
15690
15707
|
}
|
|
15691
15708
|
return { success: true, degradedAutomation };
|
|
@@ -15703,7 +15720,11 @@ async function mixThroughFloatSum(tracks, masterChain, outputPath, workDir, tota
|
|
|
15703
15720
|
timeout: config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout
|
|
15704
15721
|
});
|
|
15705
15722
|
if (!encoded.success) {
|
|
15706
|
-
const failure = ffmpegFailure(
|
|
15723
|
+
const failure = ffmpegFailure(
|
|
15724
|
+
"mix",
|
|
15725
|
+
encoded,
|
|
15726
|
+
config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout
|
|
15727
|
+
);
|
|
15707
15728
|
return {
|
|
15708
15729
|
success: false,
|
|
15709
15730
|
outputPath,
|
|
@@ -15868,11 +15889,17 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15868
15889
|
}
|
|
15869
15890
|
audioSrcPath = trimmedPath;
|
|
15870
15891
|
}
|
|
15871
|
-
const
|
|
15872
|
-
|
|
15873
|
-
|
|
15892
|
+
const chain = element.fxChain ? parseAudioFxChain(element.fxChain) : void 0;
|
|
15893
|
+
const automation = element.automation ? resolveAutomation(parseAutomation(element.automation), chain) : null;
|
|
15894
|
+
const gainBeforeFx = chain !== void 0 && enabledAudioFxNodes(chain).length > 0;
|
|
15895
|
+
const laneKeyframes = automation ? volumeLaneKeyframes(
|
|
15896
|
+
automation,
|
|
15897
|
+
element.start,
|
|
15898
|
+
element.end - element.start,
|
|
15899
|
+
element.volume ?? 1,
|
|
15900
|
+
gainBeforeFx
|
|
15874
15901
|
) : null;
|
|
15875
|
-
const
|
|
15902
|
+
const tweened = !laneKeyframes && (element.volumeKeyframes?.length ?? 0) > 0;
|
|
15876
15903
|
const envelopeKeyframes = laneKeyframes ?? element.volumeKeyframes;
|
|
15877
15904
|
const envelope = envelopeKeyframes && envelopeKeyframes.length > 0 ? {
|
|
15878
15905
|
keyframes: envelopeKeyframes,
|
|
@@ -15881,8 +15908,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15881
15908
|
} : null;
|
|
15882
15909
|
let bakedEnvelope = false;
|
|
15883
15910
|
let tailSeconds = 0;
|
|
15884
|
-
if (
|
|
15885
|
-
const chain = parseAudioFxChain(element.fxChain);
|
|
15911
|
+
if (chain) {
|
|
15886
15912
|
tailSeconds = chainTailSeconds(chain, automation ?? void 0);
|
|
15887
15913
|
const fxResult = await applyAudioFxChain(
|
|
15888
15914
|
audioSrcPath,
|
|
@@ -15892,7 +15918,8 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15892
15918
|
trackId: element.id,
|
|
15893
15919
|
signal: effectiveSignal,
|
|
15894
15920
|
...automation ? { automation } : {},
|
|
15895
|
-
...envelope ? { envelope } : {}
|
|
15921
|
+
...envelope ? { envelope } : {},
|
|
15922
|
+
...gainBeforeFx ? { inputGain: clipGain(element.volume ?? 1, tweened) } : {}
|
|
15896
15923
|
}
|
|
15897
15924
|
);
|
|
15898
15925
|
audioSrcPath = fxResult.path;
|
|
@@ -15914,7 +15941,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15914
15941
|
mediaStart: element.mediaStart,
|
|
15915
15942
|
duration: element.end - element.start,
|
|
15916
15943
|
// Gain is already in the samples when baked, so mix at unity.
|
|
15917
|
-
volume: bakedEnvelope ? 1 : element.volume ?? 1,
|
|
15944
|
+
volume: bakedEnvelope || gainBeforeFx && !tweened ? 1 : element.volume ?? 1,
|
|
15918
15945
|
volumeKeyframes: bakedEnvelope ? void 0 : envelopeKeyframes ?? void 0,
|
|
15919
15946
|
...element.fadeIn ? { fadeIn: element.fadeIn } : {},
|
|
15920
15947
|
...element.fadeOut ? { fadeOut: element.fadeOut } : {},
|
|
@@ -16031,7 +16058,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
16031
16058
|
parseAutomation(meta.automation),
|
|
16032
16059
|
meta.fxChain ? parseAudioFxChain(meta.fxChain) : void 0
|
|
16033
16060
|
) : null;
|
|
16034
|
-
const laneKeyframes = automation ? volumeLaneKeyframes(automation, 0, totalDuration) : null;
|
|
16061
|
+
const laneKeyframes = automation ? volumeLaneKeyframes(automation, 0, totalDuration, meta.volume) : null;
|
|
16035
16062
|
const envelope = laneKeyframes && laneKeyframes.length > 0 ? { keyframes: laneKeyframes, trackStart: 0, baseVolume: meta.volume } : null;
|
|
16036
16063
|
let groupSrcPath = groupWavPath;
|
|
16037
16064
|
let bakedEnvelope = false;
|
|
@@ -17316,6 +17343,7 @@ export {
|
|
|
17316
17343
|
assertConfiguredFfmpegBinariesExist,
|
|
17317
17344
|
cloneCaptureWarning,
|
|
17318
17345
|
cloneCaptureWarnings,
|
|
17346
|
+
installMediaRenderIdBridge,
|
|
17319
17347
|
createChromeMemorySampler,
|
|
17320
17348
|
CaptureFailure,
|
|
17321
17349
|
classifyCaptureFailure,
|
|
@@ -17344,6 +17372,7 @@ export {
|
|
|
17344
17372
|
getCompositionDuration,
|
|
17345
17373
|
percentileOf,
|
|
17346
17374
|
getCapturePerfSummary,
|
|
17375
|
+
frameSequencePath,
|
|
17347
17376
|
ManagedChildProcess,
|
|
17348
17377
|
trackChildProcess,
|
|
17349
17378
|
killTrackedProcesses,
|