hyperframes 0.8.185 → 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-ODTNPRGG.js → add-UORF6235.js} +12 -12
- package/dist/{animatedGifPrep-PEP5GWOJ.js → animatedGifPrep-23MLSEXF.js} +7 -7
- package/dist/api.js +16 -16
- package/dist/{asset-EZG42XPA.js → asset-DOKDVHCW.js} +2 -2
- package/dist/{assetCataloger-OHAJFCA7.js → assetCataloger-CD27MHEY.js} +7 -7
- package/dist/audio-3NMZHNNU.js +2317 -0
- package/dist/{auth-VP2SIZFN.js → auth-AWKTSKGC.js} +1 -1
- package/dist/{autoUpdate-RVSUX4SS.js → autoUpdate-VBOI4CNU.js} +2 -2
- package/dist/{backgroundChecks-LEHK74NS.js → backgroundChecks-S4R454YU.js} +3 -3
- package/dist/backgroundChecksWorker.js +3 -3
- package/dist/{batchRender-3X6CL7PK.js → batchRender-EGPYNDHK.js} +4 -4
- package/dist/{beats-66VPQLSC.js → beats-YENEQUOA.js} +5 -5
- package/dist/{benchmark-PBJVTOJP.js → benchmark-THSJIQG7.js} +9 -9
- package/dist/{browser-TPL5MWGC.js → browser-BDMVQ6WJ.js} +3 -3
- package/dist/{bundleWithLocalizedFonts-67MLTHKM.js → bundleWithLocalizedFonts-3QG722YD.js} +2 -2
- package/dist/{capture-YYRE7EV3.js → capture-WM4XDMFX.js} +2 -2
- package/dist/{capture-F3GNMRZM.js → capture-Z6BGCTK3.js} +10 -10
- package/dist/{captureCompositionFrame-PXV2ISHD.js → captureCompositionFrame-EMHSAIBZ.js} +15 -11
- package/dist/{catalog-6SDD3OHF.js → catalog-7TV6UNCG.js} +12 -12
- package/dist/{catch-up-CWIVA5VG.js → catch-up-63IUDZRW.js} +4 -4
- package/dist/{check-YQ6REK2S.js → check-7RHZL4DV.js} +32 -32
- package/dist/{checkBrowser-5XZCBRXM.js → checkBrowser-PF56CT5X.js} +29 -29
- package/dist/{chunk-TAX2ZJAP.js → chunk-2DHCCBCN.js} +9 -9
- package/dist/{chunk-K73RJB23.js → chunk-2WUDFZCR.js} +2 -2
- package/dist/{chunk-4CTNDBE3.js → chunk-44QWRPGE.js} +2 -2
- package/dist/{chunk-X6L6MD5B.js → chunk-46VJ2UAV.js} +6 -6
- package/dist/{chunk-BRAPBUPQ.js → chunk-57XL23DE.js} +1 -1
- package/dist/{chunk-J36JVBLR.js → chunk-5C7R55BJ.js} +1 -1
- package/dist/{chunk-SKYPMHQC.js → chunk-5DPV7LU3.js} +3 -3
- package/dist/{chunk-D42NU3DZ.js → chunk-5OZI55AC.js} +1 -1
- package/dist/{chunk-5UBZPG74.js → chunk-6A45RKAK.js} +1 -1
- package/dist/{chunk-I7QMXZSB.js → chunk-6HYPEIH7.js} +2 -2
- package/dist/{chunk-KQ4JEJLF.js → chunk-6HZ7TSJJ.js} +14 -8
- package/dist/{chunk-6ASIXMFP.js → chunk-6OWNUXTU.js} +1 -1
- package/dist/{chunk-WQLKDL35.js → chunk-6Q5CFUAP.js} +1 -1
- package/dist/{chunk-G2UANIDX.js → chunk-B2P4JRUJ.js} +1 -1
- package/dist/chunk-BPX7LZG6.js +3896 -0
- package/dist/{chunk-EMAWIZ54.js → chunk-BU4HRISA.js} +58 -21
- package/dist/{chunk-ESBMUZXZ.js → chunk-BWEZDN6U.js} +1 -1
- package/dist/{chunk-5UDRPPN2.js → chunk-CE7EQUBV.js} +8 -5
- package/dist/{chunk-UPR3MU6Z.js → chunk-DA4TQFSF.js} +1 -1
- package/dist/{chunk-HDIRZGK4.js → chunk-DG4ME554.js} +1 -1
- package/dist/{chunk-RTBSA3EA.js → chunk-DLFAVRF3.js} +1 -1
- package/dist/{chunk-TTVHYD5R.js → chunk-EI7FP7F3.js} +1 -1
- package/dist/{chunk-RUEGYANA.js → chunk-FOLMMQF3.js} +11 -11
- package/dist/{chunk-5GUKZRQN.js → chunk-FSTLXRIN.js} +1 -1
- package/dist/{chunk-CX3USX23.js → chunk-FWVILDOZ.js} +3 -3
- package/dist/{chunk-IVEFOUZI.js → chunk-GYBPAKBM.js} +2287 -2264
- package/dist/{chunk-HXUTX5GR.js → chunk-HKUZCSNZ.js} +2 -2
- package/dist/{chunk-PMMQRJDE.js → chunk-HPQSUPR7.js} +1 -1
- package/dist/{chunk-7RAY4WZP.js → chunk-I7C5GUEQ.js} +3 -3
- package/dist/{chunk-55YELV7B.js → chunk-IA2IDJAG.js} +1 -1
- package/dist/{chunk-7PS6RVWD.js → chunk-IJYR4TTA.js} +1 -1
- package/dist/{chunk-6RIZY7OP.js → chunk-JWRT2IXO.js} +1 -1
- package/dist/{chunk-2TAS5O4P.js → chunk-JYC53CW4.js} +108 -20
- package/dist/{chunk-IDFNKFPL.js → chunk-KDBPZOXK.js} +18 -7
- package/dist/{chunk-ISTRDGIX.js → chunk-KWSEIVR6.js} +1 -1
- package/dist/{chunk-IPDKZ5IN.js → chunk-KY7375C4.js} +1 -1
- package/dist/{chunk-F3OPUWQX.js → chunk-L243NE72.js} +7 -7
- package/dist/{chunk-H6UY725W.js → chunk-LWY3PFJ7.js} +2 -2
- package/dist/{chunk-6GPR3G3C.js → chunk-LWYZN7HS.js} +1 -1
- package/dist/{chunk-OAG47RE6.js → chunk-MJKUCR3C.js} +1 -1
- package/dist/{chunk-SQVRXHOW.js → chunk-MTPPEEYZ.js} +3 -3
- package/dist/{chunk-TOI35GN2.js → chunk-MXXX6WVC.js} +1 -1
- package/dist/{chunk-BH7TTIPN.js → chunk-NFHRGHCJ.js} +5 -5
- package/dist/{chunk-IDGLGXPA.js → chunk-NFLZNSUZ.js} +1 -1
- package/dist/{chunk-K2ZKXTVL.js → chunk-NMOTNNP2.js} +1 -1
- package/dist/{chunk-LYKSTRAY.js → chunk-OJOZXWLC.js} +3 -3
- package/dist/{chunk-FX6RUJUY.js → chunk-OKSYDQHY.js} +3 -3
- package/dist/{chunk-H5SM5PJI.js → chunk-PMXR3IUH.js} +1 -1
- package/dist/{chunk-A6QCO474.js → chunk-PQLFJYGP.js} +5 -5
- package/dist/{chunk-PFD25NAC.js → chunk-RIJHDTM5.js} +4 -0
- package/dist/{chunk-Q47FAYYK.js → chunk-SMF6YAUL.js} +9 -11
- package/dist/{chunk-EMSDWBG3.js → chunk-T3OXKSRG.js} +2 -2
- package/dist/{chunk-VHZ6DL3M.js → chunk-TMK4Q7JA.js} +10 -7
- package/dist/{chunk-Q447CX26.js → chunk-TSFA2Y5C.js} +60 -47
- package/dist/{chunk-XWC7CNPY.js → chunk-UF6RCYA5.js} +1 -1
- package/dist/{chunk-I45HL3VP.js → chunk-V6NM4KL3.js} +3 -3
- package/dist/{chunk-FFJ5TDRD.js → chunk-VHBBV5DH.js} +10 -3
- package/dist/{chunk-HHSJJFVM.js → chunk-W2KGJHUL.js} +2 -2
- package/dist/{chunk-37CNYTQQ.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-J5XEZ24I.js → chunk-XPVN4R66.js} +3 -3
- package/dist/{chunk-I3IH5MV5.js → chunk-YCW345M4.js} +222 -118
- package/dist/{chunk-KROXPHXX.js → chunk-YV4WT7CJ.js} +5 -9
- package/dist/{chunk-PHMBMOXF.js → chunk-YZ73IPW2.js} +48 -4
- package/dist/{clean-7J6ZCCGV.js → clean-43O47OTN.js} +16 -16
- package/dist/cli.js +53 -53
- package/dist/{cloud-7IJCJCDD.js → cloud-ZFWKFFSU.js} +4 -4
- package/dist/{cloudrun-UNZRIIDB.js → cloudrun-XBBESKEU.js} +5 -5
- package/dist/{compare-Q7VW5JZT.js → compare-OW4ELTFZ.js} +26 -26
- package/dist/{compiler-KPHYGFXL.js → compiler-23MT6CX6.js} +4 -4
- package/dist/{component-UUBREJKN.js → component-SDV7LWH6.js} +3 -3
- package/dist/{compositions-T4FTKIDP.js → compositions-DI5QZCEK.js} +11 -11
- package/dist/{delete-WZQF2IIA.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-ULGTPSU4.js → dist-RFTGYT26.js} +7 -5
- package/dist/{doctor-VSNGRKXJ.js → doctor-7ZOECPHR.js} +11 -11
- package/dist/{events-E63YBUL2.js → events-WIX4JM2K.js} +3 -3
- package/dist/{events-CRW2DYUT.js → events-XVQQGECC.js} +2 -2
- package/dist/{feedback-OSZWVR6N.js → feedback-GBYBR6UO.js} +7 -7
- package/dist/{ffmpeg-JLH3Z7G7.js → ffmpeg-QGQBYMO3.js} +7 -7
- package/dist/{figma-2BQNNR53.js → figma-2CMKUGJQ.js} +3 -3
- package/dist/fontLocalizeCli.js +9 -9
- package/dist/{get-P2XI53QW.js → get-RWCNUOHU.js} +4 -4
- package/dist/{gpuPolicy-NTKQJY75.js → gpuPolicy-UGWSN32D.js} +7 -7
- package/dist/{grade-compare-OYASIHIB.js → grade-compare-J2MX7HZL.js} +27 -27
- package/dist/{help-HK6K2N4G.js → help-SH3ID7MV.js} +1 -1
- package/dist/{history-JMTYKLXW.js → history-C7V5QH7J.js} +6 -6
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +70 -69
- package/dist/hyperframes-player.global.js +3 -3
- package/dist/{info-AW7CIVRE.js → info-ZN3JJ3IN.js} +8 -8
- package/dist/{init-6KIDG4LB.js → init-KRNGSRNH.js} +65 -47
- package/dist/{inspect-B7BLYMRZ.js → inspect-AYDZXTX7.js} +27 -27
- package/dist/{installer-ZP2LQZUQ.js → installer-N6AIK6XQ.js} +5 -5
- package/dist/{keyframes-EQ36A5JE.js → keyframes-2RFUCEDK.js} +6 -6
- package/dist/{lambda-E6VURLIS.js → lambda-VW3CF6EE.js} +20 -13
- package/dist/{layout-XYC64QQQ.js → layout-OE64APE7.js} +27 -27
- package/dist/{lint-V5DJYWBX.js → lint-UXJTX5WS.js} +7 -7
- package/dist/{lintProject-JATQKR64.js → lintProject-XM3DLUKG.js} +1 -1
- package/dist/{list-RUGVRTVD.js → list-KOL6JPZJ.js} +7 -7
- package/dist/{login-ZTKGKXGS.js → login-MAQXOPDG.js} +4 -4
- package/dist/{manager-JUP7PM6N.js → manager-3LZKRK7P.js} +8 -8
- package/dist/{media-treatment-VIUPK4OR.js → media-treatment-COLSXXQJ.js} +13 -13
- package/dist/{models-LJS2HBOZ.js → models-XXWMGOD7.js} +11 -11
- package/dist/{motionShot-3UAXZ7SN.js → motionShot-RECBNTBZ.js} +14 -14
- package/dist/{normalize-4UDISLPL.js → normalize-3AYFSN2F.js} +1 -1
- package/dist/{normalize-audio-SBYI4YIH.js → normalize-audio-LKVGHZWR.js} +6 -6
- package/dist/{open-S27TUD3A.js → open-UEQVSRKH.js} +12 -12
- package/dist/{parakeetRunner-ETLMLJ4L.js → parakeetRunner-QVPCZ36U.js} +12 -12
- package/dist/{pipeline-I2M22M24.js → pipeline-5VSMQBKW.js} +8 -8
- package/dist/{play-COJU6VJK.js → play-MNEABYN4.js} +19 -19
- package/dist/{policies-XXNGXWHV.js → policies-MBWGLMWB.js} +17 -0
- package/dist/{preflight-FZ24NCHH.js → preflight-ONJ74DWQ.js} +8 -8
- package/dist/{present-YAHKLC5D.js → present-GQ24GTYX.js} +15 -15
- package/dist/{preview-KQOEU5YE.js → preview-ALL7O4RG.js} +18 -18
- package/dist/{producer-DTNNG4T6.js → producer-43L2UDF6.js} +1 -1
- package/dist/{progress-NHJIHAAG.js → progress-PX3PVAIM.js} +5 -3
- package/dist/{projectConfig-4A76VYUP.js → projectConfig-VJXUXK7U.js} +7 -7
- package/dist/{publication-SQPMC2W3.js → publication-L6E5NTFV.js} +3 -3
- package/dist/{publish-QUEJBGGT.js → publish-LCLBJEPR.js} +9 -9
- package/dist/{remove-background-FP2ETAQH.js → remove-background-PDL4KPR4.js} +9 -9
- package/dist/{render-P664SMC7.js → render-7HVKJ7T4.js} +9 -7
- package/dist/{render-BIFIL4U6.js → render-J33ZHR6I.js} +44 -28
- package/dist/{render-GQFXJWLV.js → render-OPCRLZVL.js} +31 -22
- package/dist/{render-batch-ZCPMYH6Z.js → render-batch-ZHHIGXU3.js} +5 -5
- package/dist/renderSetupWorker.js +1 -1
- package/dist/{resolver-BUC2GFZQ.js → resolver-KNNIMMBM.js} +5 -5
- package/dist/{sam-MC24SZHC.js → sam-FHJ7IL7T.js} +3 -1
- package/dist/{sherpa-HFT6XEP6.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-3WEVO3NU.js → skills-IUTD6VQO.js} +5 -5
- package/dist/{skillsUpdateCheck-BFPWFOK5.js → skillsUpdateCheck-XZE7MJG6.js} +3 -3
- package/dist/{snapshot-WDQ6S7CJ.js → snapshot-XEXTHLZP.js} +37 -34
- package/dist/{src-NPR2JZOJ.js → src-6STRGCDC.js} +8 -8
- package/dist/{src-RZ574OKP.js → src-ZPIV4PGX.js} +10 -6
- package/dist/{state-ORRPOLMQ.js → state-JPNCCXAV.js} +3 -1
- package/dist/{staticProjectServer-FTGTEZI4.js → staticProjectServer-HJLT7MHE.js} +15 -15
- package/dist/studio/assets/{hyperframes-player-CoOdf435.js → hyperframes-player-DwWIKKZG.js} +1 -1
- package/dist/studio/assets/{index-CR85xg2x.js → index-Cjwz7_13.js} +1 -1
- package/dist/studio/assets/{index-C_6rTpaB.js → index-M8lEpS0Q.js} +131 -131
- package/dist/studio/index.html +1 -1
- package/dist/{studio-api-TAWB7B23.js → studio-api-OBHEB5ND.js} +9 -9
- package/dist/{studioServer-HRQGJTBU.js → studioServer-KRIZFYML.js} +47 -46
- package/dist/{telemetry-QZ6HJF4W.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-IKUEW42T.js → timeline-YUFH65OG.js} +17 -17
- package/dist/{tokens-AUGUEWHS.js → tokens-K4VYLSWJ.js} +2 -2
- package/dist/{transcribe-7734CG3N.js → transcribe-CY4CVO54.js} +17 -17
- package/dist/{transcribe-FWU6EMEO.js → transcribe-GMQXKDYN.js} +10 -10
- package/dist/{updateCheck-VIPQ3FPW.js → updateCheck-2625RXK4.js} +2 -2
- package/dist/{upgrade-4VNDQTVO.js → upgrade-6ZY5MSBQ.js} +2 -2
- package/dist/{urlDownloader-6CXL4CL7.js → urlDownloader-PVJWG7V3.js} +6 -6
- package/dist/{validate-QWCMVWID.js → validate-XSRHEE7E.js} +21 -21
- package/dist/{video-245Q5WN4.js → video-MBFHC54F.js} +7 -7
- package/dist/warmCheck.js +34 -34
- package/package.json +18 -2
- package/dist/audio-65R4T74I.js +0 -1154
- package/dist/chunk-3NPOFC43.js +0 -3805
- package/dist/deploy-JU7A3XNC.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,
|
|
@@ -37,10 +37,16 @@ import {
|
|
|
37
37
|
HF_COLOR_GRADING_CANVAS_ID_PREFIX,
|
|
38
38
|
MEDIA_DURATION_CLAMP_EPSILON_SECONDS,
|
|
39
39
|
MEDIA_RENDER_ID_ATTR,
|
|
40
|
+
PAGE_DEVICE_SCALE_GLOBAL,
|
|
41
|
+
PAGE_MOTION_BLUR_OFF_GLOBAL,
|
|
40
42
|
VOLUME_TARGET,
|
|
43
|
+
acceleratedCanvasTrackerSource,
|
|
41
44
|
enabledAudioFxNodes,
|
|
42
45
|
fxAutomationTarget,
|
|
46
|
+
gainedVolume,
|
|
47
|
+
gainedVolumePoints,
|
|
43
48
|
injectScriptsIntoHtml,
|
|
49
|
+
isBentSegment,
|
|
44
50
|
normalizeAudioFxParams,
|
|
45
51
|
normalizePlaybackRate,
|
|
46
52
|
normalizeRateSpec,
|
|
@@ -55,12 +61,14 @@ import {
|
|
|
55
61
|
serializeAutomation,
|
|
56
62
|
shiftRateLane,
|
|
57
63
|
sourceTimeAt,
|
|
58
|
-
timeAtSourceTime
|
|
59
|
-
|
|
64
|
+
timeAtSourceTime,
|
|
65
|
+
volumeSegmentSampleCount
|
|
66
|
+
} from "./chunk-BPX7LZG6.js";
|
|
60
67
|
import {
|
|
61
68
|
clampAudioGain,
|
|
69
|
+
clipGain,
|
|
62
70
|
isAudibleVideoElement
|
|
63
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-X7HBSTCZ.js";
|
|
64
72
|
import {
|
|
65
73
|
redactTelemetryString,
|
|
66
74
|
redactUrlQueryStrings
|
|
@@ -75,7 +83,7 @@ import {
|
|
|
75
83
|
} from "./chunk-4UMPIPCQ.js";
|
|
76
84
|
import {
|
|
77
85
|
resolveReferencedStart
|
|
78
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-TSFA2Y5C.js";
|
|
79
87
|
import {
|
|
80
88
|
FFMPEG_PATH_ENV,
|
|
81
89
|
FFPROBE_PATH_ENV,
|
|
@@ -2506,6 +2514,68 @@ var MotionBlurAccumulator = class {
|
|
|
2506
2514
|
}
|
|
2507
2515
|
};
|
|
2508
2516
|
|
|
2517
|
+
// ../engine/src/services/pageMotionBlur.ts
|
|
2518
|
+
function pageOffReason(options) {
|
|
2519
|
+
if (options.motionBlur) return "engine-motion-blur";
|
|
2520
|
+
if (options.pageMotionBlur === "off") return "preview-unsupported";
|
|
2521
|
+
return options.pageMotionBlur ?? null;
|
|
2522
|
+
}
|
|
2523
|
+
async function installPageMotionBlur(page, options) {
|
|
2524
|
+
await page.evaluateOnNewDocument(acceleratedCanvasTrackerSource());
|
|
2525
|
+
const scale = options.deviceScaleFactor ?? 1;
|
|
2526
|
+
if (scale > 1) {
|
|
2527
|
+
await page.evaluateOnNewDocument(
|
|
2528
|
+
(key, v) => {
|
|
2529
|
+
window[key] = v;
|
|
2530
|
+
},
|
|
2531
|
+
PAGE_DEVICE_SCALE_GLOBAL,
|
|
2532
|
+
scale
|
|
2533
|
+
);
|
|
2534
|
+
}
|
|
2535
|
+
const off = pageOffReason(options);
|
|
2536
|
+
if (!off) return;
|
|
2537
|
+
await page.evaluateOnNewDocument(
|
|
2538
|
+
(key, v) => {
|
|
2539
|
+
window[key] = v;
|
|
2540
|
+
},
|
|
2541
|
+
PAGE_MOTION_BLUR_OFF_GLOBAL,
|
|
2542
|
+
off
|
|
2543
|
+
);
|
|
2544
|
+
}
|
|
2545
|
+
async function readPageMotionBlur(page) {
|
|
2546
|
+
return page.evaluate(() => {
|
|
2547
|
+
const api = Reflect.get(window, "__hfMotionBlur");
|
|
2548
|
+
const report = api && typeof api === "object" ? Reflect.get(api, "report") : null;
|
|
2549
|
+
const got = typeof report === "function" ? report() : null;
|
|
2550
|
+
if (!got || typeof got !== "object") return null;
|
|
2551
|
+
return {
|
|
2552
|
+
state: String(Reflect.get(got, "state")),
|
|
2553
|
+
code: String(Reflect.get(got, "reasonCode")),
|
|
2554
|
+
movers: Number(Reflect.get(got, "movers")),
|
|
2555
|
+
failedFrames: Number(Reflect.get(got, "failedFrames")) || 0
|
|
2556
|
+
};
|
|
2557
|
+
});
|
|
2558
|
+
}
|
|
2559
|
+
var formatPageMotionBlur = (r) => `[motion-blur] ${r.state} reasonCode=${r.code} movers=${r.movers} failedFrames=${r.failedFrames}`;
|
|
2560
|
+
async function describePageMotionBlur(page) {
|
|
2561
|
+
const r = await readPageMotionBlur(page);
|
|
2562
|
+
return r && formatPageMotionBlur(r);
|
|
2563
|
+
}
|
|
2564
|
+
async function describePageMotionBlurAtEnd(page, timeoutMs = 1500) {
|
|
2565
|
+
let timer;
|
|
2566
|
+
const stuck = new Promise((resolve) => {
|
|
2567
|
+
timer = setTimeout(() => resolve(null), timeoutMs);
|
|
2568
|
+
});
|
|
2569
|
+
try {
|
|
2570
|
+
const r = await Promise.race([readPageMotionBlur(page), stuck]);
|
|
2571
|
+
return r && r.failedFrames > 0 ? `${formatPageMotionBlur(r)} (session end)` : null;
|
|
2572
|
+
} catch {
|
|
2573
|
+
return null;
|
|
2574
|
+
} finally {
|
|
2575
|
+
clearTimeout(timer);
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2509
2579
|
// ../engine/src/services/videoFrameTransport.ts
|
|
2510
2580
|
import { randomUUID } from "crypto";
|
|
2511
2581
|
var base64ImagePrefix = /^data:(image\/[\w.+-]+);base64,/;
|
|
@@ -2926,6 +2996,7 @@ async function injectResolvedVideoFramesBatch(page, updates) {
|
|
|
2926
2996
|
async (items, visualProperties, colorGradingSourceHiddenAttr) => {
|
|
2927
2997
|
const injectedIds = [];
|
|
2928
2998
|
const pendingDecodes = [];
|
|
2999
|
+
const imagesToPublish = [];
|
|
2929
3000
|
const replacementLayoutProperties = /* @__PURE__ */ new Set([
|
|
2930
3001
|
"width",
|
|
2931
3002
|
"height",
|
|
@@ -2997,6 +3068,7 @@ async function injectResolvedVideoFramesBatch(page, updates) {
|
|
|
2997
3068
|
img.style.objectFit = computedStyle.objectFit;
|
|
2998
3069
|
img.style.objectPosition = computedStyle.objectPosition;
|
|
2999
3070
|
img.style.zIndex = computedStyle.zIndex;
|
|
3071
|
+
img.style.setProperty("visibility", "hidden", "important");
|
|
3000
3072
|
img.decoding = "sync";
|
|
3001
3073
|
if (img.getAttribute("src") !== item.dataUri) {
|
|
3002
3074
|
if (item.crossOrigin) img.crossOrigin = item.crossOrigin;
|
|
@@ -3012,7 +3084,7 @@ async function injectResolvedVideoFramesBatch(page, updates) {
|
|
|
3012
3084
|
);
|
|
3013
3085
|
}
|
|
3014
3086
|
img.style.opacity = String(computedOpacity);
|
|
3015
|
-
img
|
|
3087
|
+
imagesToPublish.push(img);
|
|
3016
3088
|
video.style.setProperty("visibility", "hidden", "important");
|
|
3017
3089
|
video.style.setProperty("pointer-events", "none", "important");
|
|
3018
3090
|
injectedIds.push(item.videoId);
|
|
@@ -3020,6 +3092,7 @@ async function injectResolvedVideoFramesBatch(page, updates) {
|
|
|
3020
3092
|
if (pendingDecodes.length > 0) {
|
|
3021
3093
|
await Promise.all(pendingDecodes);
|
|
3022
3094
|
}
|
|
3095
|
+
for (const img of imagesToPublish) img.style.visibility = "visible";
|
|
3023
3096
|
if (injectedIds.length > 0) {
|
|
3024
3097
|
const redraw = window.__hf?.colorGrading?.redraw;
|
|
3025
3098
|
redraw?.();
|
|
@@ -3058,7 +3131,7 @@ async function syncVideoFrameVisibility(page, activeVideoIds) {
|
|
|
3058
3131
|
const img = video.nextElementSibling;
|
|
3059
3132
|
const hasImg = img && img.classList.contains("__render_frame__");
|
|
3060
3133
|
const ancestorHidden = isVisualAncestorHidden(video);
|
|
3061
|
-
const visible = active.has(video.id) && !ancestorHidden;
|
|
3134
|
+
const visible = active.has(window.__hfMediaId?.(video) || video.id) && !ancestorHidden;
|
|
3062
3135
|
if (visible) {
|
|
3063
3136
|
video.style.setProperty("visibility", "hidden", "important");
|
|
3064
3137
|
video.style.setProperty("pointer-events", "none", "important");
|
|
@@ -5437,6 +5510,7 @@ async function constructCaptureSession(input) {
|
|
|
5437
5510
|
}
|
|
5438
5511
|
}, variablesJson);
|
|
5439
5512
|
}
|
|
5513
|
+
await installPageMotionBlur(page, options);
|
|
5440
5514
|
const browserVersion = await browser.version();
|
|
5441
5515
|
const sessionOptions = resolveCaptureSessionOptions(options, browserVersion);
|
|
5442
5516
|
const expectedMajor = config?.expectedChromiumMajor;
|
|
@@ -6614,6 +6688,7 @@ async function createStaticVerificationPage(session) {
|
|
|
6614
6688
|
window.__hfVariables = JSON.parse(json);
|
|
6615
6689
|
}, variablesJson);
|
|
6616
6690
|
}
|
|
6691
|
+
await installPageMotionBlur(page, session.options);
|
|
6617
6692
|
await page.setViewport({
|
|
6618
6693
|
width: session.options.width,
|
|
6619
6694
|
height: session.options.height,
|
|
@@ -7159,6 +7234,10 @@ async function captureFrameCore(session, frameIndex, time) {
|
|
|
7159
7234
|
session.capturePerf.screenshotMs += screenshotMs;
|
|
7160
7235
|
session.capturePerf.totalMs += captureTimeMs;
|
|
7161
7236
|
session.capturePerf.frameMs.push(captureTimeMs);
|
|
7237
|
+
if (session.capturePerf.frames === 1) {
|
|
7238
|
+
const blur = await describePageMotionBlur(session.page).catch(() => null);
|
|
7239
|
+
if (blur) console.log(blur);
|
|
7240
|
+
}
|
|
7162
7241
|
if (session.staticFrames) {
|
|
7163
7242
|
session.lastFrameBuffer = buffer;
|
|
7164
7243
|
session.lastFrameAbsoluteIndex = absFrameIndex;
|
|
@@ -7371,6 +7450,10 @@ async function closeCaptureSession(session) {
|
|
|
7371
7450
|
session.chromeMemory.stop();
|
|
7372
7451
|
await session.chromeMemory.sampleOnce();
|
|
7373
7452
|
}
|
|
7453
|
+
if (session.page && !session.pageReleased) {
|
|
7454
|
+
const blur = await describePageMotionBlurAtEnd(session.page);
|
|
7455
|
+
if (blur) console.log(blur);
|
|
7456
|
+
}
|
|
7374
7457
|
const reused = session.staticDedupCount ?? 0;
|
|
7375
7458
|
if (session.staticFrames && reused > 0) {
|
|
7376
7459
|
const captured = session.capturePerf.frames;
|
|
@@ -7559,6 +7642,57 @@ function getCapturePerfSummary(session) {
|
|
|
7559
7642
|
};
|
|
7560
7643
|
}
|
|
7561
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
|
+
|
|
7562
7696
|
// ../engine/src/services/chunkEncoder.ts
|
|
7563
7697
|
import {
|
|
7564
7698
|
closeSync,
|
|
@@ -7569,12 +7703,12 @@ import {
|
|
|
7569
7703
|
mkdtempSync,
|
|
7570
7704
|
openSync,
|
|
7571
7705
|
readFileSync as readFileSync3,
|
|
7572
|
-
readdirSync as
|
|
7706
|
+
readdirSync as readdirSync3,
|
|
7573
7707
|
statSync as statSync3,
|
|
7574
7708
|
writeFileSync as writeFileSync2,
|
|
7575
7709
|
writeSync
|
|
7576
7710
|
} from "fs";
|
|
7577
|
-
import { join as
|
|
7711
|
+
import { join as join6, dirname as dirname2, extname } from "path";
|
|
7578
7712
|
|
|
7579
7713
|
// ../engine/src/utils/gpuEncoder.ts
|
|
7580
7714
|
import { spawn } from "child_process";
|
|
@@ -8861,7 +8995,7 @@ async function analyzeKeyframeIntervalsUncached(filePath, videoStreamStartSecond
|
|
|
8861
8995
|
|
|
8862
8996
|
// ../engine/src/utils/renderProvenance.ts
|
|
8863
8997
|
import { readFileSync as readFileSync2 } from "fs";
|
|
8864
|
-
import { dirname, join as
|
|
8998
|
+
import { dirname, join as join5 } from "path";
|
|
8865
8999
|
import { fileURLToPath } from "url";
|
|
8866
9000
|
var PROVENANCE_RENDERER_TAG = "hyperframes_renderer";
|
|
8867
9001
|
var PROVENANCE_VERSION_TAG = "hyperframes_version";
|
|
@@ -8872,7 +9006,7 @@ var MAX_WALK_DEPTH = 12;
|
|
|
8872
9006
|
function resolveOwnVersionFrom(startDir) {
|
|
8873
9007
|
let dir = startDir;
|
|
8874
9008
|
for (let depth = 0; depth < MAX_WALK_DEPTH; depth++) {
|
|
8875
|
-
const candidate =
|
|
9009
|
+
const candidate = join5(dir, "package.json");
|
|
8876
9010
|
let pkg;
|
|
8877
9011
|
try {
|
|
8878
9012
|
pkg = JSON.parse(readFileSync2(candidate, "utf8"));
|
|
@@ -9205,7 +9339,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
9205
9339
|
const startTime = Date.now();
|
|
9206
9340
|
const outputDir = dirname2(outputPath);
|
|
9207
9341
|
if (!existsSync5(outputDir)) mkdirSync2(outputDir, { recursive: true });
|
|
9208
|
-
const files =
|
|
9342
|
+
const files = readdirSync3(framesDir).filter((f) => f.match(/\.(jpg|jpeg|png)$/i));
|
|
9209
9343
|
const frameCount = files.length;
|
|
9210
9344
|
if (frameCount === 0) {
|
|
9211
9345
|
return {
|
|
@@ -9221,7 +9355,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
9221
9355
|
if (options.useGpu) {
|
|
9222
9356
|
gpuEncoder = await getCachedGpuEncoder();
|
|
9223
9357
|
}
|
|
9224
|
-
const inputPath =
|
|
9358
|
+
const inputPath = frameSequencePath(framesDir, framePattern);
|
|
9225
9359
|
const inputArgs = ["-framerate", fpsToFfmpegArg(options.fps), "-i", inputPath];
|
|
9226
9360
|
const args = buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder);
|
|
9227
9361
|
const encodeTimeout = config?.ffmpegEncodeTimeout ?? DEFAULT_CONFIG.ffmpegEncodeTimeout;
|
|
@@ -9277,7 +9411,7 @@ function buildConcatArgs(concatListPath, outputPath) {
|
|
|
9277
9411
|
var concatListSeq = 0;
|
|
9278
9412
|
function writeConcatList(dir, inputPaths) {
|
|
9279
9413
|
concatListSeq += 1;
|
|
9280
|
-
const listPath =
|
|
9414
|
+
const listPath = join6(dir, `concat-list-${process.pid}-${concatListSeq}.txt`);
|
|
9281
9415
|
const body = inputPaths.map((p) => `file '${p.replace(/'/g, "'\\''")}'`).join("\n");
|
|
9282
9416
|
writeFileSync2(listPath, body, "utf-8");
|
|
9283
9417
|
return listPath;
|
|
@@ -9307,7 +9441,7 @@ async function concatVideoFiles(inputPaths, outputPath, signal, config) {
|
|
|
9307
9441
|
}
|
|
9308
9442
|
async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, options, chunkSizeFrames, signal, config, onFramesEncoded) {
|
|
9309
9443
|
const start = Date.now();
|
|
9310
|
-
const files =
|
|
9444
|
+
const files = readdirSync3(framesDir).filter((f) => f.match(/\.(jpg|jpeg|png)$/i)).sort();
|
|
9311
9445
|
if (files.length === 0) {
|
|
9312
9446
|
return {
|
|
9313
9447
|
success: false,
|
|
@@ -9321,7 +9455,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
9321
9455
|
const chunkSize = Math.max(30, Math.floor(chunkSizeFrames));
|
|
9322
9456
|
const chunkCount = Math.ceil(files.length / chunkSize);
|
|
9323
9457
|
mkdirSync2(dirname2(outputPath), { recursive: true });
|
|
9324
|
-
const chunkDir = mkdtempSync(
|
|
9458
|
+
const chunkDir = mkdtempSync(join6(dirname2(outputPath), "chunk-encode-"));
|
|
9325
9459
|
const chunkPaths = [];
|
|
9326
9460
|
for (let i = 0; i < chunkCount; i++) {
|
|
9327
9461
|
if (signal?.aborted) {
|
|
@@ -9337,8 +9471,8 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
9337
9471
|
const startNumber = i * chunkSize;
|
|
9338
9472
|
const framesInChunk = Math.min(chunkSize, files.length - startNumber);
|
|
9339
9473
|
const ext = outputPath.endsWith(".webm") ? ".webm" : outputPath.endsWith(".mov") ? ".mov" : ".mp4";
|
|
9340
|
-
const chunkPath =
|
|
9341
|
-
const inputPath =
|
|
9474
|
+
const chunkPath = join6(chunkDir, `chunk_${String(i).padStart(4, "0")}${ext}`);
|
|
9475
|
+
const inputPath = frameSequencePath(framesDir, framePattern);
|
|
9342
9476
|
const inputArgs = [
|
|
9343
9477
|
"-framerate",
|
|
9344
9478
|
fpsToFfmpegArg(options.fps),
|
|
@@ -9507,14 +9641,14 @@ async function packageHls(videoPath, audioPath, outputDir, options) {
|
|
|
9507
9641
|
"-master_pl_name",
|
|
9508
9642
|
HLS_MASTER_PLAYLIST,
|
|
9509
9643
|
"-hls_segment_filename",
|
|
9510
|
-
|
|
9644
|
+
join6(outputDir, "%v_%05d.ts"),
|
|
9511
9645
|
// Without these the mpegts muxer starts the stream at PTS 1.4 s.
|
|
9512
9646
|
"-muxdelay",
|
|
9513
9647
|
"0",
|
|
9514
9648
|
"-muxpreload",
|
|
9515
9649
|
"0"
|
|
9516
9650
|
);
|
|
9517
|
-
args.push("-y",
|
|
9651
|
+
args.push("-y", join6(outputDir, "%v.m3u8"));
|
|
9518
9652
|
const processTimeout = options.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
9519
9653
|
const result = await runFfmpeg(args, { signal, timeout: processTimeout });
|
|
9520
9654
|
if (signal?.aborted) {
|
|
@@ -9526,7 +9660,7 @@ async function packageHls(videoPath, audioPath, outputDir, options) {
|
|
|
9526
9660
|
};
|
|
9527
9661
|
}
|
|
9528
9662
|
if (result.success && hasAudio) {
|
|
9529
|
-
const masterPath =
|
|
9663
|
+
const masterPath = join6(outputDir, HLS_MASTER_PLAYLIST);
|
|
9530
9664
|
let master;
|
|
9531
9665
|
try {
|
|
9532
9666
|
master = openSync(masterPath, "r+", 384);
|
|
@@ -10147,7 +10281,7 @@ function isKnownInactiveTimelineWindow(element, resolvedStart) {
|
|
|
10147
10281
|
// ../engine/src/utils/alphaPlaneProbe.ts
|
|
10148
10282
|
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, rmSync } from "fs";
|
|
10149
10283
|
import { tmpdir as tmpdir2 } from "os";
|
|
10150
|
-
import { join as
|
|
10284
|
+
import { join as join7 } from "path";
|
|
10151
10285
|
var BYTES_PER_SAMPLE_FRAME = 8 * 8 * 4;
|
|
10152
10286
|
var MAX_SAMPLE_BYTES = BYTES_PER_SAMPLE_FRAME * 3;
|
|
10153
10287
|
function sampledRgbaAlphaIsFullyOpaque(buf) {
|
|
@@ -10162,8 +10296,8 @@ function sampledRgbaAlphaIsFullyOpaque(buf) {
|
|
|
10162
10296
|
async function probeInputAlphaPlane(videoPath, decoder, signal) {
|
|
10163
10297
|
let probeDir;
|
|
10164
10298
|
try {
|
|
10165
|
-
probeDir = mkdtempSync2(
|
|
10166
|
-
const samplePath =
|
|
10299
|
+
probeDir = mkdtempSync2(join7(tmpdir2(), "hf-alpha-probe-"));
|
|
10300
|
+
const samplePath = join7(probeDir, "alpha.raw");
|
|
10167
10301
|
const result = await runFfmpeg(
|
|
10168
10302
|
[
|
|
10169
10303
|
"-v",
|
|
@@ -10209,7 +10343,7 @@ import {
|
|
|
10209
10343
|
mkdirSync as mkdirSync4,
|
|
10210
10344
|
lstatSync,
|
|
10211
10345
|
openSync as openSync2,
|
|
10212
|
-
readdirSync as
|
|
10346
|
+
readdirSync as readdirSync4,
|
|
10213
10347
|
rmdirSync,
|
|
10214
10348
|
rmSync as rmSync2,
|
|
10215
10349
|
statSync as statSync5,
|
|
@@ -10217,7 +10351,7 @@ import {
|
|
|
10217
10351
|
} from "fs";
|
|
10218
10352
|
import { createHash, randomUUID as randomUUID2 } from "crypto";
|
|
10219
10353
|
import { BlockList, isIP } from "net";
|
|
10220
|
-
import { dirname as dirname4, extname as extname2, join as
|
|
10354
|
+
import { dirname as dirname4, extname as extname2, join as join8 } from "path";
|
|
10221
10355
|
import { Readable, Transform } from "stream";
|
|
10222
10356
|
import { pipeline } from "stream/promises";
|
|
10223
10357
|
var inFlightDownloads = /* @__PURE__ */ new Map();
|
|
@@ -10387,7 +10521,7 @@ function sameCacheLockStatGeneration(left, right) {
|
|
|
10387
10521
|
function observeCachePathLock(lockPath) {
|
|
10388
10522
|
for (let pass = 0; pass < 3; pass += 1) {
|
|
10389
10523
|
const before = lstatSync(lockPath);
|
|
10390
|
-
const owner =
|
|
10524
|
+
const owner = readdirSync4(lockPath).find((name) => name.startsWith(CACHE_LOCK_OWNER_PREFIX));
|
|
10391
10525
|
const after = lstatSync(lockPath);
|
|
10392
10526
|
if (sameCacheLockStatGeneration(before, after)) return { stats: after, owner };
|
|
10393
10527
|
}
|
|
@@ -10409,7 +10543,7 @@ function removeCacheLockDirectoryIfEmpty(lockPath) {
|
|
|
10409
10543
|
}
|
|
10410
10544
|
function releaseOwnedCachePathLock(lockPath, owner) {
|
|
10411
10545
|
try {
|
|
10412
|
-
rmdirSync(
|
|
10546
|
+
rmdirSync(join8(lockPath, owner));
|
|
10413
10547
|
} catch (error) {
|
|
10414
10548
|
if (error.code === "ENOENT") return;
|
|
10415
10549
|
throw error;
|
|
@@ -10450,12 +10584,12 @@ async function acquireCachePathLock(localPath, timeoutMs, signal) {
|
|
|
10450
10584
|
if (createdLock) {
|
|
10451
10585
|
const owner = `${CACHE_LOCK_OWNER_PREFIX}${randomUUID2()}`;
|
|
10452
10586
|
try {
|
|
10453
|
-
mkdirSync4(
|
|
10454
|
-
const entries =
|
|
10587
|
+
mkdirSync4(join8(lockPath, owner));
|
|
10588
|
+
const entries = readdirSync4(lockPath);
|
|
10455
10589
|
if (entries.length === 1 && entries[0] === owner) {
|
|
10456
10590
|
return () => releaseOwnedCachePathLock(lockPath, owner);
|
|
10457
10591
|
}
|
|
10458
|
-
rmdirSync(
|
|
10592
|
+
rmdirSync(join8(lockPath, owner));
|
|
10459
10593
|
removeCacheLockDirectoryIfEmpty(lockPath);
|
|
10460
10594
|
continue;
|
|
10461
10595
|
} catch (error) {
|
|
@@ -10473,7 +10607,7 @@ async function acquireCachePathLock(localPath, timeoutMs, signal) {
|
|
|
10473
10607
|
if (Date.now() - observedLock.stats.mtimeMs > CACHE_LOCK_STALE_MS) {
|
|
10474
10608
|
if (observedLock.owner) {
|
|
10475
10609
|
try {
|
|
10476
|
-
rmdirSync(
|
|
10610
|
+
rmdirSync(join8(lockPath, observedLock.owner));
|
|
10477
10611
|
} catch (error) {
|
|
10478
10612
|
if (error.code === "ENOENT") continue;
|
|
10479
10613
|
throw error;
|
|
@@ -10481,7 +10615,7 @@ async function acquireCachePathLock(localPath, timeoutMs, signal) {
|
|
|
10481
10615
|
removeCacheLockDirectoryIfEmpty(lockPath);
|
|
10482
10616
|
continue;
|
|
10483
10617
|
}
|
|
10484
|
-
const reclaimPath =
|
|
10618
|
+
const reclaimPath = join8(lockPath, CACHE_LOCK_RECLAIM_NAME);
|
|
10485
10619
|
try {
|
|
10486
10620
|
mkdirSync4(reclaimPath);
|
|
10487
10621
|
} catch (error) {
|
|
@@ -10921,8 +11055,8 @@ function emitDownloadTelemetry(options, event) {
|
|
|
10921
11055
|
}
|
|
10922
11056
|
}
|
|
10923
11057
|
async function runDownloadAttempt(url, localPath, timeoutMs, attempt, options, signal) {
|
|
10924
|
-
const attemptDir = mkdtempSync3(
|
|
10925
|
-
const partialPath =
|
|
11058
|
+
const attemptDir = mkdtempSync3(join8(dirname4(localPath), ".hf-download-"));
|
|
11059
|
+
const partialPath = join8(attemptDir, "payload");
|
|
10926
11060
|
const controller = new AbortController();
|
|
10927
11061
|
let timedOut = false;
|
|
10928
11062
|
let callerAborted = signal?.aborted ?? false;
|
|
@@ -11158,7 +11292,7 @@ async function downloadToTemp(url, destDir, timeoutMs = 3e5, signal, onTransient
|
|
|
11158
11292
|
mkdirSync4(destDir, { recursive: true });
|
|
11159
11293
|
}
|
|
11160
11294
|
const filename = getFilenameFromUrl(url, validationScope);
|
|
11161
|
-
const localPath =
|
|
11295
|
+
const localPath = join8(destDir, filename);
|
|
11162
11296
|
const downloadPromise = (async () => {
|
|
11163
11297
|
const cacheStartedAt = Date.now();
|
|
11164
11298
|
const reused = await reuseOrInvalidateCachedFile(
|
|
@@ -11242,56 +11376,6 @@ import {
|
|
|
11242
11376
|
writeFileSync as writeFileSync3
|
|
11243
11377
|
} from "fs";
|
|
11244
11378
|
import { join as join9 } from "path";
|
|
11245
|
-
|
|
11246
|
-
// ../engine/src/services/extractedFrameIndex.ts
|
|
11247
|
-
import { readdirSync as readdirSync4 } from "fs";
|
|
11248
|
-
import { join as join8 } from "path";
|
|
11249
|
-
var FRAME_FILENAME_PREFIX = "frame_";
|
|
11250
|
-
var ExtractedFrameSequenceError = class extends Error {
|
|
11251
|
-
constructor(message) {
|
|
11252
|
-
super(message);
|
|
11253
|
-
this.name = "ExtractedFrameSequenceError";
|
|
11254
|
-
}
|
|
11255
|
-
};
|
|
11256
|
-
function extractedFrameIndex(file, format) {
|
|
11257
|
-
const match = new RegExp(`^${FRAME_FILENAME_PREFIX}(\\d+)\\.${format}$`).exec(file);
|
|
11258
|
-
if (!match) {
|
|
11259
|
-
throw new ExtractedFrameSequenceError(`Invalid extracted frame filename: ${file}`);
|
|
11260
|
-
}
|
|
11261
|
-
const ordinal = Number.parseInt(match[1], 10);
|
|
11262
|
-
if (!Number.isSafeInteger(ordinal) || ordinal < 1) {
|
|
11263
|
-
throw new ExtractedFrameSequenceError(`Invalid extracted frame ordinal: ${file}`);
|
|
11264
|
-
}
|
|
11265
|
-
return ordinal - 1;
|
|
11266
|
-
}
|
|
11267
|
-
function framePathsFromDirectory(outputDir, format, allowGaps = false) {
|
|
11268
|
-
const suffix = `.${format}`;
|
|
11269
|
-
const indexed = /* @__PURE__ */ new Map();
|
|
11270
|
-
for (const file of readdirSync4(outputDir)) {
|
|
11271
|
-
if (!file.startsWith(FRAME_FILENAME_PREFIX) || !file.endsWith(suffix)) continue;
|
|
11272
|
-
const index = extractedFrameIndex(file, format);
|
|
11273
|
-
if (indexed.has(index)) {
|
|
11274
|
-
throw new ExtractedFrameSequenceError(
|
|
11275
|
-
`Duplicate extracted frame index ${index}: ${indexed.get(index)} and ${file}`
|
|
11276
|
-
);
|
|
11277
|
-
}
|
|
11278
|
-
indexed.set(index, join8(outputDir, file));
|
|
11279
|
-
}
|
|
11280
|
-
if (allowGaps) return new Map([...indexed].sort(([a], [b]) => a - b));
|
|
11281
|
-
const ordered = /* @__PURE__ */ new Map();
|
|
11282
|
-
for (let index = 0; index < indexed.size; index += 1) {
|
|
11283
|
-
const path = indexed.get(index);
|
|
11284
|
-
if (!path) {
|
|
11285
|
-
throw new ExtractedFrameSequenceError(
|
|
11286
|
-
`Missing extracted frame index ${index} in ${outputDir}`
|
|
11287
|
-
);
|
|
11288
|
-
}
|
|
11289
|
-
ordered.set(index, path);
|
|
11290
|
-
}
|
|
11291
|
-
return ordered;
|
|
11292
|
-
}
|
|
11293
|
-
|
|
11294
|
-
// ../engine/src/services/extractionCache.ts
|
|
11295
11379
|
var COMPLETE_SENTINEL = ".hf-complete";
|
|
11296
11380
|
var GC_MARKER = ".hf-last-gc";
|
|
11297
11381
|
var SCHEMA_PREFIX = "hfcache-v8-";
|
|
@@ -12043,7 +12127,7 @@ async function extractVideoFramesRangeUnredacted(videoPath, videoId, startTime,
|
|
|
12043
12127
|
}
|
|
12044
12128
|
const format = resolveFrameFormat(metadata, options.format);
|
|
12045
12129
|
const framePattern = `${FRAME_FILENAME_PREFIX}%05d.${format}`;
|
|
12046
|
-
const outputPattern =
|
|
12130
|
+
const outputPattern = frameSequencePath(videoOutputDir, framePattern);
|
|
12047
12131
|
const isHdr = isHdrColorSpace(metadata.colorSpace);
|
|
12048
12132
|
const isMacOS = process.platform === "darwin";
|
|
12049
12133
|
const toneMappedToSdr = options.toneMapHdrToSdr === true && isHdr && await toneMapsWithZscale();
|
|
@@ -13979,6 +14063,8 @@ async function encodeAac(input, outputPath, totalDuration, {
|
|
|
13979
14063
|
segments.map(
|
|
13980
14064
|
({ trim, path }) => runFfmpeg(
|
|
13981
14065
|
[
|
|
14066
|
+
"-cpuflags",
|
|
14067
|
+
"0",
|
|
13982
14068
|
"-i",
|
|
13983
14069
|
input,
|
|
13984
14070
|
"-af",
|
|
@@ -14504,7 +14590,7 @@ import { join as join13 } from "path";
|
|
|
14504
14590
|
import { pathToFileURL } from "url";
|
|
14505
14591
|
|
|
14506
14592
|
// ../core/dist/generated/audio-fx-runtime-inline.js
|
|
14507
|
-
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';
|
|
14508
14594
|
function getAudioFxRuntimeScript() {
|
|
14509
14595
|
return AUDIO_FX_RUNTIME_IIFE;
|
|
14510
14596
|
}
|
|
@@ -14690,6 +14776,9 @@ function applyEnvelopeToPlanes(planes, sampleRate, gainAt, startFrame = 0) {
|
|
|
14690
14776
|
for (const plane of planes) plane[frame] = (plane[frame] ?? 0) * gain;
|
|
14691
14777
|
}
|
|
14692
14778
|
}
|
|
14779
|
+
function applyInputGain(planes, gain = 1) {
|
|
14780
|
+
if (gain !== 1) applyEnvelopeToPlanes(planes, 1, () => gain);
|
|
14781
|
+
}
|
|
14693
14782
|
var DEFAULT_STREAMING_LIMITS = {
|
|
14694
14783
|
aboveBytes: 512 * 1024 * 1024,
|
|
14695
14784
|
segmentSeconds: 300,
|
|
@@ -14763,6 +14852,7 @@ async function renderInSegments(openPage, inputWav, header, chain, outputWav, op
|
|
|
14763
14852
|
throw new AudioFxRenderError(`Audio FX cancelled for track ${options.trackId}`);
|
|
14764
14853
|
}
|
|
14765
14854
|
const planes = deinterleave(readFrames(inFd, header, seg.readFrom, seg.readTo), channels);
|
|
14855
|
+
applyInputGain(planes, options.inputGain);
|
|
14766
14856
|
const page = await openPage();
|
|
14767
14857
|
let outPlanes;
|
|
14768
14858
|
try {
|
|
@@ -14822,6 +14912,7 @@ async function applyAudioFxChain(inputWav, chain, outputWav, options) {
|
|
|
14822
14912
|
}
|
|
14823
14913
|
const input = readWav(inputWav);
|
|
14824
14914
|
const planes = deinterleave(input.samples, input.channels);
|
|
14915
|
+
applyInputGain(planes, options.inputGain);
|
|
14825
14916
|
if ((planes[0]?.length ?? 0) === 0) return { path: inputWav, envelopeBaked: false };
|
|
14826
14917
|
return withFxBrowser(options, async (openPage) => {
|
|
14827
14918
|
const page = await openPage();
|
|
@@ -15181,7 +15272,7 @@ function downloadFailure(error, elementId) {
|
|
|
15181
15272
|
detail: boundedDetail(`Download failed for audio element ${elementId}: ${message}`)
|
|
15182
15273
|
};
|
|
15183
15274
|
}
|
|
15184
|
-
function ffmpegFailure(stage, result, elementId) {
|
|
15275
|
+
function ffmpegFailure(stage, result, timeoutMs, elementId) {
|
|
15185
15276
|
const stderr = result.stderr ?? "";
|
|
15186
15277
|
let reason = "ffmpeg_failed";
|
|
15187
15278
|
let owner = "system";
|
|
@@ -15212,32 +15303,32 @@ function ffmpegFailure(stage, result, elementId) {
|
|
|
15212
15303
|
elementId,
|
|
15213
15304
|
subprocess: audioSubprocessEvidence(result),
|
|
15214
15305
|
detail: boundedDetail(
|
|
15215
|
-
result.error?.message ? `${
|
|
15306
|
+
result.error?.message ? `${describeFfmpegFailure(result, timeoutMs)}: ${result.error.message}` : describeFfmpegFailure(result, timeoutMs)
|
|
15216
15307
|
)
|
|
15217
15308
|
};
|
|
15218
15309
|
}
|
|
15219
|
-
|
|
15220
|
-
function volumeLaneKeyframes(automation, trackStart, duration) {
|
|
15310
|
+
function volumeLaneKeyframes(automation, trackStart, duration, gain, gainAhead = false) {
|
|
15221
15311
|
const lane = automation.lanes.find((l) => l.target === VOLUME_TARGET);
|
|
15222
15312
|
if (!lane || lane.points.length === 0) return null;
|
|
15313
|
+
const points = gainedVolumePoints(lane, gain, gainAhead);
|
|
15223
15314
|
const out = [];
|
|
15224
15315
|
const push = (t, v) => {
|
|
15225
15316
|
out.push({ time: trackStart + t, volume: v });
|
|
15226
15317
|
};
|
|
15227
|
-
const first =
|
|
15318
|
+
const first = points[0];
|
|
15228
15319
|
if (first.t > 0) push(0, first.v);
|
|
15229
|
-
for (let i = 0; i <
|
|
15230
|
-
const a =
|
|
15320
|
+
for (let i = 0; i < points.length; i += 1) {
|
|
15321
|
+
const a = points[i];
|
|
15231
15322
|
push(a.t, a.v);
|
|
15232
|
-
const b =
|
|
15233
|
-
if (!b || !a
|
|
15234
|
-
const steps =
|
|
15323
|
+
const b = points[i + 1];
|
|
15324
|
+
if (!b || !isBentSegment(a)) continue;
|
|
15325
|
+
const steps = volumeSegmentSampleCount(b.t - a.t);
|
|
15235
15326
|
for (let k = 1; k < steps; k += 1) {
|
|
15236
15327
|
const t = a.t + (b.t - a.t) * k / steps;
|
|
15237
|
-
push(t, sampleAutomationLane(lane, t));
|
|
15328
|
+
push(t, gainedVolume(sampleAutomationLane(lane, t), gain, gainAhead));
|
|
15238
15329
|
}
|
|
15239
15330
|
}
|
|
15240
|
-
const last =
|
|
15331
|
+
const last = points[points.length - 1];
|
|
15241
15332
|
if (duration > last.t) push(duration, last.v);
|
|
15242
15333
|
return out;
|
|
15243
15334
|
}
|
|
@@ -15354,7 +15445,7 @@ async function extractAudioSegment(srcPath, outputPath, stage, mediaStart, durat
|
|
|
15354
15445
|
failure: failure2
|
|
15355
15446
|
};
|
|
15356
15447
|
}
|
|
15357
|
-
let failure = result.success ? void 0 : ffmpegFailure(stage, result);
|
|
15448
|
+
let failure = result.success ? void 0 : ffmpegFailure(stage, result, ffmpegProcessTimeout);
|
|
15358
15449
|
const probedLinkFailedToRead = readsUrl && (failure?.reason === "invalid_media" || failure?.reason === "ffmpeg_failed");
|
|
15359
15450
|
if (failure && probedLinkFailedToRead) {
|
|
15360
15451
|
failure = { ...failure, reason: "download_failed", owner: "system", retryable: true };
|
|
@@ -15400,7 +15491,7 @@ async function generateSilence(outputPath, duration, signal, config) {
|
|
|
15400
15491
|
failure: failure2
|
|
15401
15492
|
};
|
|
15402
15493
|
}
|
|
15403
|
-
const failure = !result.success ? ffmpegFailure("silence", result) : void 0;
|
|
15494
|
+
const failure = !result.success ? ffmpegFailure("silence", result, ffmpegProcessTimeout) : void 0;
|
|
15404
15495
|
return {
|
|
15405
15496
|
success: result.success,
|
|
15406
15497
|
outputPath,
|
|
@@ -15509,7 +15600,7 @@ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config)
|
|
|
15509
15600
|
};
|
|
15510
15601
|
}
|
|
15511
15602
|
if (!result.success) {
|
|
15512
|
-
const failure = ffmpegFailure("mix", result);
|
|
15603
|
+
const failure = ffmpegFailure("mix", result, ffmpegProcessTimeout);
|
|
15513
15604
|
return {
|
|
15514
15605
|
success: false,
|
|
15515
15606
|
outputPath,
|
|
@@ -15534,14 +15625,15 @@ async function mixGroupMembers(memberTracks, outputPath, totalDuration, signal,
|
|
|
15534
15625
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
15535
15626
|
const outputDir = dirname7(outputPath);
|
|
15536
15627
|
if (!existsSync11(outputDir)) mkdirSync7(outputDir, { recursive: true });
|
|
15537
|
-
const buildInputFilters = (ignoreKeyframes) => memberTracks.map((track, i) => {
|
|
15628
|
+
const buildInputFilters = (ignoreKeyframes, padMembers) => memberTracks.map((track, i) => {
|
|
15538
15629
|
const volumeFilter = buildVolumeExpression(track, ignoreKeyframes);
|
|
15539
|
-
return buildTrackInputFilter(track, i, volumeFilter, totalDuration);
|
|
15630
|
+
return buildTrackInputFilter(track, i, volumeFilter, totalDuration, padMembers);
|
|
15540
15631
|
});
|
|
15541
15632
|
const mixInputs = memberTracks.map((_, i) => `[a${i}]`).join("");
|
|
15542
15633
|
const runOnce = async (useNormalize2, ignoreKeyframes = false) => {
|
|
15543
|
-
const inputFilters = buildInputFilters(ignoreKeyframes);
|
|
15544
|
-
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]` : (
|
|
15545
15637
|
// Every member is padded and trimmed to totalDuration above, and
|
|
15546
15638
|
// dropout_transition=0 keeps all N inputs active for that whole span.
|
|
15547
15639
|
// amix's default normalize therefore divides by exactly N throughout;
|
|
@@ -15610,7 +15702,7 @@ async function mixGroupMembers(memberTracks, outputPath, totalDuration, signal,
|
|
|
15610
15702
|
}
|
|
15611
15703
|
}
|
|
15612
15704
|
if (signal?.aborted || !result.success) {
|
|
15613
|
-
const failure = ffmpegFailure("mix", result);
|
|
15705
|
+
const failure = ffmpegFailure("mix", result, ffmpegProcessTimeout);
|
|
15614
15706
|
return { success: false, error: failure.detail, failure };
|
|
15615
15707
|
}
|
|
15616
15708
|
return { success: true, degradedAutomation };
|
|
@@ -15628,7 +15720,11 @@ async function mixThroughFloatSum(tracks, masterChain, outputPath, workDir, tota
|
|
|
15628
15720
|
timeout: config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout
|
|
15629
15721
|
});
|
|
15630
15722
|
if (!encoded.success) {
|
|
15631
|
-
const failure = ffmpegFailure(
|
|
15723
|
+
const failure = ffmpegFailure(
|
|
15724
|
+
"mix",
|
|
15725
|
+
encoded,
|
|
15726
|
+
config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout
|
|
15727
|
+
);
|
|
15632
15728
|
return {
|
|
15633
15729
|
success: false,
|
|
15634
15730
|
outputPath,
|
|
@@ -15793,11 +15889,17 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15793
15889
|
}
|
|
15794
15890
|
audioSrcPath = trimmedPath;
|
|
15795
15891
|
}
|
|
15796
|
-
const
|
|
15797
|
-
|
|
15798
|
-
|
|
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
|
|
15799
15901
|
) : null;
|
|
15800
|
-
const
|
|
15902
|
+
const tweened = !laneKeyframes && (element.volumeKeyframes?.length ?? 0) > 0;
|
|
15801
15903
|
const envelopeKeyframes = laneKeyframes ?? element.volumeKeyframes;
|
|
15802
15904
|
const envelope = envelopeKeyframes && envelopeKeyframes.length > 0 ? {
|
|
15803
15905
|
keyframes: envelopeKeyframes,
|
|
@@ -15806,8 +15908,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15806
15908
|
} : null;
|
|
15807
15909
|
let bakedEnvelope = false;
|
|
15808
15910
|
let tailSeconds = 0;
|
|
15809
|
-
if (
|
|
15810
|
-
const chain = parseAudioFxChain(element.fxChain);
|
|
15911
|
+
if (chain) {
|
|
15811
15912
|
tailSeconds = chainTailSeconds(chain, automation ?? void 0);
|
|
15812
15913
|
const fxResult = await applyAudioFxChain(
|
|
15813
15914
|
audioSrcPath,
|
|
@@ -15817,7 +15918,8 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15817
15918
|
trackId: element.id,
|
|
15818
15919
|
signal: effectiveSignal,
|
|
15819
15920
|
...automation ? { automation } : {},
|
|
15820
|
-
...envelope ? { envelope } : {}
|
|
15921
|
+
...envelope ? { envelope } : {},
|
|
15922
|
+
...gainBeforeFx ? { inputGain: clipGain(element.volume ?? 1, tweened) } : {}
|
|
15821
15923
|
}
|
|
15822
15924
|
);
|
|
15823
15925
|
audioSrcPath = fxResult.path;
|
|
@@ -15839,7 +15941,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15839
15941
|
mediaStart: element.mediaStart,
|
|
15840
15942
|
duration: element.end - element.start,
|
|
15841
15943
|
// Gain is already in the samples when baked, so mix at unity.
|
|
15842
|
-
volume: bakedEnvelope ? 1 : element.volume ?? 1,
|
|
15944
|
+
volume: bakedEnvelope || gainBeforeFx && !tweened ? 1 : element.volume ?? 1,
|
|
15843
15945
|
volumeKeyframes: bakedEnvelope ? void 0 : envelopeKeyframes ?? void 0,
|
|
15844
15946
|
...element.fadeIn ? { fadeIn: element.fadeIn } : {},
|
|
15845
15947
|
...element.fadeOut ? { fadeOut: element.fadeOut } : {},
|
|
@@ -15956,7 +16058,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15956
16058
|
parseAutomation(meta.automation),
|
|
15957
16059
|
meta.fxChain ? parseAudioFxChain(meta.fxChain) : void 0
|
|
15958
16060
|
) : null;
|
|
15959
|
-
const laneKeyframes = automation ? volumeLaneKeyframes(automation, 0, totalDuration) : null;
|
|
16061
|
+
const laneKeyframes = automation ? volumeLaneKeyframes(automation, 0, totalDuration, meta.volume) : null;
|
|
15960
16062
|
const envelope = laneKeyframes && laneKeyframes.length > 0 ? { keyframes: laneKeyframes, trackStart: 0, baseVolume: meta.volume } : null;
|
|
15961
16063
|
let groupSrcPath = groupWavPath;
|
|
15962
16064
|
let bakedEnvelope = false;
|
|
@@ -17241,6 +17343,7 @@ export {
|
|
|
17241
17343
|
assertConfiguredFfmpegBinariesExist,
|
|
17242
17344
|
cloneCaptureWarning,
|
|
17243
17345
|
cloneCaptureWarnings,
|
|
17346
|
+
installMediaRenderIdBridge,
|
|
17244
17347
|
createChromeMemorySampler,
|
|
17245
17348
|
CaptureFailure,
|
|
17246
17349
|
classifyCaptureFailure,
|
|
@@ -17269,6 +17372,7 @@ export {
|
|
|
17269
17372
|
getCompositionDuration,
|
|
17270
17373
|
percentileOf,
|
|
17271
17374
|
getCapturePerfSummary,
|
|
17375
|
+
frameSequencePath,
|
|
17272
17376
|
ManagedChildProcess,
|
|
17273
17377
|
trackChildProcess,
|
|
17274
17378
|
killTrackedProcesses,
|