hyperframes 0.8.180 → 0.8.183
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/bin/hyperframes.mjs +3 -1
- package/dist/{add-HUVEWSVU.js → add-HW3ZGR36.js} +22 -22
- package/dist/{animatedGifPrep-V6GJQOEB.js → animatedGifPrep-EPLN37ZN.js} +13 -13
- package/dist/api.js +28 -28
- package/dist/{asset-ZGQHDMIO.js → asset-TOASKL3Z.js} +3 -3
- package/dist/{assetCataloger-MZWCOV6C.js → assetCataloger-PZN5HYG2.js} +15 -13
- package/dist/{audio-XDWHLNIW.js → audio-F5OIQVJO.js} +29 -29
- package/dist/{auth-VIV3M7S6.js → auth-YVQLRS3T.js} +4 -4
- package/dist/{autoUpdate-R32XDL42.js → autoUpdate-EG4ZFW4D.js} +2 -2
- package/dist/{backgroundChecks-A2N6EVCP.js → backgroundChecks-WINNQVR2.js} +3 -3
- package/dist/backgroundChecksWorker.js +3 -3
- package/dist/{batchRender-3AYDQRBI.js → batchRender-PASZTMEE.js} +7 -7
- package/dist/{beats-2O423Z6Q.js → beats-KGIOEBLW.js} +12 -12
- package/dist/{benchmark-YXP3PTZP.js → benchmark-ZNY2L7HN.js} +24 -24
- package/dist/{browser-M47BNOTJ.js → browser-REAO7RU4.js} +20 -20
- package/dist/{bundleWithLocalizedFonts-GCIHOMIU.js → bundleWithLocalizedFonts-OAYLYC4W.js} +2 -2
- package/dist/{capture-IV3Y2MT4.js → capture-IK6GWQDD.js} +8 -5
- package/dist/{capture-BT2ZXN5P.js → capture-WOSF276U.js} +49 -41
- package/dist/{captureCompositionFrame-BMZEKPOU.js → captureCompositionFrame-DDZNKYRE.js} +21 -20
- package/dist/{captureTimeout-K2JNVVCI.js → captureTimeout-RIMG6IYH.js} +1 -1
- package/dist/{catalog-GI6P22KI.js → catalog-AKTXS7OJ.js} +42 -42
- package/dist/{catch-up-QS6HUHAO.js → catch-up-O3VZMPVE.js} +15 -15
- package/dist/check-6E46NIWI.js +116 -0
- package/dist/checkBrowser-UNZHQFNH.js +117 -0
- package/dist/{chunk-ST2ARHQ5.js → chunk-3BA2GP4H.js} +5 -5
- package/dist/{chunk-7Z6AARJR.js → chunk-3OLIGYWN.js} +3 -3
- package/dist/{chunk-OR5WCFWZ.js → chunk-3Q47QC46.js} +2 -2
- package/dist/{chunk-Q2ASTBSG.js → chunk-4FQKS7BJ.js} +16 -14
- package/dist/{chunk-G2ZMCNRN.js → chunk-4UFJWRHG.js} +527 -283
- package/dist/{chunk-537IYAEE.js → chunk-4UMPIPCQ.js} +9 -1
- package/dist/{chunk-65CLIHK2.js → chunk-5MBLWFLM.js} +1 -1
- package/dist/{chunk-UVEQLDKB.js → chunk-5OGDBU4G.js} +3 -2
- package/dist/{chunk-L3YOGIPG.js → chunk-66LREXGS.js} +1 -1
- package/dist/{chunk-W4SLIGGM.js → chunk-73LTCCZI.js} +249 -161
- package/dist/{chunk-GZVP4TWN.js → chunk-7JAD4MEW.js} +1 -1
- package/dist/{chunk-NAMGMP3R.js → chunk-7KVVNF7B.js} +1 -1
- package/dist/{chunk-GK2FIKIQ.js → chunk-ADDHW2FS.js} +214 -37
- package/dist/{chunk-WUQUR33W.js → chunk-AKFADCMS.js} +1 -1
- package/dist/{chunk-2VJNKE7C.js → chunk-ANR6WOC3.js} +1 -1
- package/dist/{chunk-L527FLMV.js → chunk-ATMDOUNN.js} +2 -2
- package/dist/{chunk-ZJPCQBU7.js → chunk-B2SZZFWW.js} +4 -4
- package/dist/{chunk-TO6JNTML.js → chunk-BP4VNTZL.js} +13 -8
- package/dist/{chunk-3YXTFXT7.js → chunk-CAHTO62O.js} +8 -8
- package/dist/{chunk-MPWLGNM2.js → chunk-CGK4XZ2J.js} +1 -1
- package/dist/{chunk-NOXGG4C7.js → chunk-CJYAJ3QM.js} +37 -33
- package/dist/{chunk-F5VMPELS.js → chunk-COTLNID2.js} +16 -16
- package/dist/{chunk-E24HSMZV.js → chunk-CYBXXSC4.js} +65 -14
- package/dist/{chunk-NZZYJOMJ.js → chunk-CZUAX3BV.js} +6 -5
- package/dist/{chunk-K7RGIADJ.js → chunk-DEORFAWP.js} +12 -12
- package/dist/{chunk-GV5R5B7Q.js → chunk-DGECWPTL.js} +1 -1
- package/dist/{chunk-BRHI7EIA.js → chunk-DKPNNYBZ.js} +4 -0
- package/dist/{chunk-R46P7TGY.js → chunk-DLC2FICN.js} +7 -7
- package/dist/{chunk-D3ZWBHZP.js → chunk-DYAPHNV6.js} +3 -2
- package/dist/{chunk-RGHQID2E.js → chunk-EHSBBBR2.js} +5 -3
- package/dist/{chunk-IWTRHPTP.js → chunk-EVAQPW6O.js} +8 -8
- package/dist/{chunk-S3RYRF3W.js → chunk-F6WHPHLF.js} +1 -1
- package/dist/chunk-FG4BBFZI.js +122 -0
- package/dist/{chunk-V6MQP32U.js → chunk-GAGMLVCV.js} +2 -2
- package/dist/chunk-GMNHAOMJ.js +3805 -0
- package/dist/{chunk-KIO3R4QN.js → chunk-GPE3WWTO.js} +16 -6
- package/dist/{chunk-ULWZYLCV.js → chunk-GTE5WZAM.js} +72 -140
- package/dist/{chunk-2JDLESMS.js → chunk-HLBODNGW.js} +1 -1
- package/dist/{chunk-KMAAOHLO.js → chunk-HRPWRZGF.js} +1 -1
- package/dist/{chunk-UPFUC4DB.js → chunk-HTIUNMTP.js} +1 -1
- package/dist/{chunk-FCUTQM2P.js → chunk-IC2WYONA.js} +5 -5
- package/dist/{chunk-VE3OKVSD.js → chunk-ITSLKUQM.js} +2 -2
- package/dist/{chunk-YJDB4FLT.js → chunk-JQYJXUJW.js} +1 -1
- package/dist/{chunk-PBN6GBJE.js → chunk-JVBRJMOP.js} +14 -1
- package/dist/{chunk-XH262OWN.js → chunk-K2Z5DZBS.js} +1 -1
- package/dist/{chunk-RHWJKSGH.js → chunk-KANNRHY7.js} +1 -1
- package/dist/{chunk-Y5APFFMD.js → chunk-KRFAX5JK.js} +1 -1
- package/dist/{chunk-4J7DHVN2.js → chunk-LBWRQ4XA.js} +1 -1
- package/dist/{chunk-MQOE5RE4.js → chunk-MIZDJVPZ.js} +9 -9
- package/dist/{chunk-32GKETR7.js → chunk-MMQ52NIZ.js} +74 -0
- package/dist/chunk-MOEGCMKI.js +180 -0
- package/dist/{chunk-E6RPWJVC.js → chunk-NAUKCKCJ.js} +5 -1
- package/dist/{chunk-JBCU6NR5.js → chunk-NZ3BE2QE.js} +56 -55
- package/dist/{chunk-VKMP3IVR.js → chunk-O7N3A7PE.js} +2 -2
- package/dist/{chunk-3QENZQSL.js → chunk-OH56PXFH.js} +5 -5
- package/dist/{chunk-H4YZZ2VQ.js → chunk-PFD25NAC.js} +1 -1
- package/dist/{check-HE5E4Q7Q.js → chunk-Q6UYKWBG.js} +198 -142
- package/dist/{chunk-3DKN5WKL.js → chunk-QH3T4WI2.js} +77 -6
- package/dist/{chunk-RGXAN3YU.js → chunk-QX4GGPMY.js} +2 -2
- package/dist/{chunk-A3NFBMYX.js → chunk-R6OV4J4K.js} +6 -6
- package/dist/{chunk-LL5VQ4WS.js → chunk-RZXUQR42.js} +2 -2
- package/dist/{chunk-SEMOZNVE.js → chunk-SAQYJLI6.js} +1 -1
- package/dist/{chunk-55P7GDT6.js → chunk-TJB4GAV6.js} +2 -2
- package/dist/{checkBrowser-BHLYP3YW.js → chunk-TOHA5NAU.js} +187 -114
- package/dist/{chunk-RXQHSGAQ.js → chunk-TXGZZKDF.js} +2 -2
- package/dist/{chunk-KZ3VI5K6.js → chunk-U5YVWG5W.js} +3 -3
- package/dist/{chunk-ICMXC3MI.js → chunk-U6RXZNZE.js} +31 -18
- package/dist/{chunk-XO6CWU7D.js → chunk-UNOSCGBG.js} +2 -2
- package/dist/{chunk-NXYBR4QX.js → chunk-VAK7A2N6.js} +16 -16
- package/dist/{chunk-OFY6JTZC.js → chunk-VDJO4DJ7.js} +2 -2
- package/dist/{chunk-LBQFLUIY.js → chunk-VFBAKPRC.js} +9 -49
- package/dist/{chunk-K3KANDZQ.js → chunk-VH5KESLX.js} +1 -1
- package/dist/{chunk-22QHO26F.js → chunk-W64L7IFS.js} +1 -1
- package/dist/{chunk-4IEH6EAC.js → chunk-WT4PYV2J.js} +4 -6
- package/dist/{chunk-XFCLWWL3.js → chunk-X3IA5RYH.js} +2 -2
- package/dist/{chunk-UZOZ2W2E.js → chunk-XAQDR5Y5.js} +2 -2
- package/dist/{chunk-BI52RNRW.js → chunk-XJHKJXT4.js} +103 -20
- package/dist/{chunk-HFZ4UILX.js → chunk-XOVHO3TT.js} +25 -14
- package/dist/{chunk-VARUKXOZ.js → chunk-XR5BSEUF.js} +7 -15
- package/dist/{chunk-IZM562YJ.js → chunk-Z4R35NJS.js} +4 -4
- package/dist/{chunk-AMX53HVX.js → chunk-ZFO3A3QL.js} +2 -2
- package/dist/{clean-TQOW24OF.js → clean-WNEIVBF3.js} +40 -40
- package/dist/cli.js +68 -64
- package/dist/{cloud-7TOVPUMY.js → cloud-6GSSDCJL.js} +4 -4
- package/dist/{cloudrun-2BKGY53X.js → cloudrun-4NEJRE4T.js} +8 -8
- package/dist/commands/layout-audit.browser.js +15 -4
- package/dist/commands/motion-signature.browser.js +52 -0
- package/dist/{compare-WDV3Q6WC.js → compare-2JWQ5ABA.js} +50 -47
- package/dist/{compiler-U3W6CJW7.js → compiler-27KQD7KW.js} +6 -6
- package/dist/{component-U66GBIUO.js → component-HCRET3MH.js} +4 -4
- package/dist/{compositions-Y63YGLCE.js → compositions-HTSQ6SUJ.js} +28 -29
- package/dist/{contactSheet-27SRU7A4.js → contactSheet-CAGMKI3E.js} +3 -1
- package/dist/{delete-TBAIAD7M.js → delete-GHQ5UYM7.js} +9 -9
- package/dist/{deploy-BIWOSWS3.js → deploy-I3Z3NAVA.js} +11 -11
- package/dist/{destroy-QBLMYDBF.js → destroy-4B5HW5FH.js} +5 -5
- package/dist/{deterministicFonts-L7YEEFGI.js → deterministicFonts-JFLSSKRB.js} +2 -2
- package/dist/{dist-2FMTAVGL.js → dist-SENYPQGW.js} +4 -4
- package/dist/{dist-VO7OXOFK.js → dist-WRDLWRMS.js} +5 -5
- package/dist/{docs-MPOM3PHH.js → docs-UEWLIP6D.js} +1 -1
- package/dist/{doctor-GC4REGB6.js → doctor-UGFVQZHI.js} +27 -27
- package/dist/{events-PUUP24FT.js → events-BLJGWMNP.js} +9 -9
- package/dist/{events-4NMQVUZR.js → events-YS4OVX3I.js} +6 -6
- package/dist/{feedback-BIDXK7TE.js → feedback-AZ3SJ5HF.js} +35 -35
- package/dist/{ffmpeg-2VJDEWQQ.js → ffmpeg-2AECXG7V.js} +15 -15
- package/dist/{figma-THMXYLMU.js → figma-DRYIJQHV.js} +3 -3
- package/dist/fontLocalizeCli.js +23 -23
- package/dist/{get-5T4B7ERH.js → get-DDEAOFPB.js} +9 -9
- package/dist/{gpuPolicy-GLUATV5I.js → gpuPolicy-UDJT2MQK.js} +13 -13
- package/dist/{grade-compare-2A65YEHZ.js → grade-compare-LJKK5NSY.js} +55 -50
- package/dist/{help-JFBDXFRC.js → help-TQBGYOID.js} +1 -1
- package/dist/{history-OJEWFWMS.js → history-5TOOCF2Y.js} +15 -15
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +17 -17
- package/dist/hyperframes-player.global.js +8 -3
- package/dist/{info-KJZBU3SZ.js → info-ENI5MP4W.js} +20 -20
- package/dist/{init-6V4ELU4Z.js → init-JXPXWV3G.js} +68 -68
- package/dist/{inspect-WGBZZUZU.js → inspect-WZNYQ5AB.js} +45 -44
- package/dist/{installer-EPRSH6QI.js → installer-3SGRBLJW.js} +7 -7
- package/dist/{keyframes-57LYKC2M.js → keyframes-HEOC4CT4.js} +16 -16
- package/dist/{lambda-5P5X2X5E.js → lambda-2VHQ4AL2.js} +15 -15
- package/dist/{layout-PM2VAUT2.js → layout-7WEUNO6T.js} +45 -44
- package/dist/{lint-DXS6QRYZ.js → lint-5YMAF3L7.js} +23 -23
- package/dist/{lintProject-ARKJXDQF.js → lintProject-C2ONSH2U.js} +4 -4
- package/dist/{list-6IBSX7FU.js → list-BIE3X7CO.js} +10 -10
- package/dist/{login-RY3PWK44.js → login-YDIGJWCG.js} +7 -7
- package/dist/{logout-DKJANASY.js → logout-DDC4QLVK.js} +3 -3
- package/dist/{manager-N3COY6BJ.js → manager-D2UV6RDQ.js} +16 -16
- package/dist/{manager-V2QCCPOJ.js → manager-UHWEKPXC.js} +8 -3
- package/dist/{media-treatment-3HEZR7ZS.js → media-treatment-FSKHPMWA.js} +33 -33
- package/dist/{media-use-4XXGABIL.js → media-use-JBN57JE3.js} +1 -1
- package/dist/{models-FEULJWSX.js → models-COEK6LXG.js} +24 -24
- package/dist/{motionShot-BZTEIZEO.js → motionShot-HYZ2ZGJQ.js} +28 -26
- package/dist/{normalize-audio-O4X4LPLK.js → normalize-audio-UXR6WQ7Q.js} +12 -12
- package/dist/{open-H3MX4TI2.js → open-OFT2GIDG.js} +24 -24
- package/dist/{parakeetRunner-5ABMPCI6.js → parakeetRunner-4NZ3QXS3.js} +25 -25
- package/dist/{pipeline-RJ4DOVWX.js → pipeline-ZKE54JMG.js} +18 -18
- package/dist/{play-3PBKEZIX.js → play-2VG77FT6.js} +39 -38
- package/dist/{policies-5NTZOHJA.js → policies-XXNGXWHV.js} +1 -1
- package/dist/{preflight-3RABQJT3.js → preflight-QTWDIM5C.js} +18 -18
- package/dist/{present-6SNRG3GI.js → present-OONXQLJG.js} +33 -33
- package/dist/{preview-LSDFCJ3H.js → preview-6RZJFU76.js} +49 -49
- package/dist/{producer-SUZDRXHJ.js → producer-6YM3UH3B.js} +1 -1
- package/dist/{progress-I37JB7R4.js → progress-NHJIHAAG.js} +2 -2
- package/dist/{projectConfig-5QCNTGQT.js → projectConfig-YMW4P2X6.js} +9 -9
- package/dist/{publication-2QT7VVE2.js → publication-SEINUAPZ.js} +5 -5
- package/dist/{publish-53EBL7OO.js → publish-KZBTBWFO.js} +27 -27
- package/dist/{refresh-MGCVWX56.js → refresh-RXTOPRBB.js} +3 -3
- package/dist/{remove-background-4G4MX5WK.js → remove-background-BIQNMIR7.js} +31 -31
- package/dist/{render-RXKUYTYI.js → render-65K3K6SD.js} +31 -31
- package/dist/{render-SKY6FKYB.js → render-FIH37HJA.js} +101 -96
- package/dist/{render-75CRB3BW.js → render-LW76HOMJ.js} +8 -8
- package/dist/{render-batch-CEVWUYBP.js → render-batch-POLEEYD4.js} +8 -8
- package/dist/renderSetupWorker.js +12 -9
- package/dist/{resolver-4ICE2AT6.js → resolver-O2SED3XB.js} +7 -7
- package/dist/{screenshotCapture-HXEZ3TNJ.js → screenshotCapture-UBY6TT75.js} +33 -6
- package/dist/{sherpa-TMX3WBTM.js → sherpa-HTFFFTVQ.js} +24 -24
- package/dist/{sites-3ZBYB5X4.js → sites-MAVH57MO.js} +2 -2
- package/dist/skills/hyperframes/references/review-loop.md +1 -1
- package/dist/{skills-MCWFAGIP.js → skills-J6MBGS5O.js} +17 -17
- package/dist/{skillsUpdateCheck-NXOI35BW.js → skillsUpdateCheck-ZAXNLBF3.js} +3 -3
- package/dist/{snapshot-ESZMKQ6J.js → snapshot-TL7TTFS6.js} +72 -71
- package/dist/{src-XRTZOBO5.js → src-EL6HGIS4.js} +35 -26
- package/dist/{src-GYYWBQXJ.js → src-Z5H5N5T4.js} +19 -19
- package/dist/{state-KAXGHF77.js → state-ORRPOLMQ.js} +2 -2
- package/dist/{staticProjectServer-GYPUQGI7.js → staticProjectServer-EO7X7RAY.js} +20 -20
- package/dist/{status-BV7QAUQD.js → status-CM7IQTU2.js} +6 -6
- package/dist/studio/assets/{hyperframes-player-1wdLVP-x.js → hyperframes-player-hz_VqxBZ.js} +7 -2
- package/dist/studio/assets/{index-CaWPPTQ1.js → index-CV_ojgp5.js} +1 -1
- package/dist/studio/assets/{index-mdtW0Ug-.js → index-S1ceEagc.js} +71 -71
- package/dist/studio/index.html +1 -1
- package/dist/{studio-api-IXGX5ZFI.js → studio-api-CUZDC5B3.js} +17 -17
- package/dist/{studioServer-XVGYU6O3.js → studioServer-EZPRV43Y.js} +90 -84
- package/dist/{telemetry-E355EUDZ.js → telemetry-LFUVHARP.js} +10 -10
- package/dist/{telemetry-CQYHCCPL.js → telemetry-QG5AANZ4.js} +1 -1
- package/dist/{timeline-VWH6WSD7.js → timeline-GSH743JE.js} +41 -41
- package/dist/{tokens-IOAVF4UI.js → tokens-3ZMPSH7B.js} +3 -3
- package/dist/{transcribe-EGUXJGD4.js → transcribe-4SHNDPWW.js} +47 -47
- package/dist/{transcribe-EJA7OOHH.js → transcribe-TV7JBZQS.js} +19 -19
- package/dist/{tts-EDK3QS7M.js → tts-A2WILYGN.js} +6 -6
- package/dist/{updateCheck-U2CVTKPF.js → updateCheck-W7FTFOQF.js} +2 -2
- package/dist/{upgrade-CWEKQ4JO.js → upgrade-7YYYNAPF.js} +3 -3
- package/dist/{urlDownloader-DY5BLAFA.js → urlDownloader-PQSUS2CP.js} +12 -12
- package/dist/{validate-XOMNMKD6.js → validate-GCHAEVXM.js} +39 -38
- package/dist/{video-22NN6OEH.js → video-KGN7NJS5.js} +16 -14
- package/dist/warmCheck.js +227 -0
- package/dist/warmCheckClient.js +139 -0
- package/package.json +4 -2
- package/dist/chunk-BAZ5LCR3.js +0 -3805
- package/dist/hyperframe-runtime.js +0 -1125
- package/dist/{chunk-B4VT6YU2.js → chunk-QGOYNPQT.js} +3 -3
- package/dist/{synthesize-I2C24XJ5.js → synthesize-PKTB5ATG.js} +1 -1
|
@@ -4,13 +4,6 @@ import { dirname as __hf_dirname } from "node:path";
|
|
|
4
4
|
var require = __hf_createRequire(import.meta.url);
|
|
5
5
|
var __filename = __hf_fileURLToPath(import.meta.url);
|
|
6
6
|
var __dirname = __hf_dirname(__filename);
|
|
7
|
-
import {
|
|
8
|
-
LOW_MEMORY_TOTAL_MB_THRESHOLD,
|
|
9
|
-
describeWslGpuAccess,
|
|
10
|
-
detectWsl,
|
|
11
|
-
getSystemTotalMb,
|
|
12
|
-
isLowMemorySystem
|
|
13
|
-
} from "./chunk-W2LGJSEN.js";
|
|
14
7
|
import {
|
|
15
8
|
DISTRIBUTED_FPS,
|
|
16
9
|
MEDIA_VISUAL_STYLE_PROPERTIES,
|
|
@@ -29,13 +22,14 @@ import {
|
|
|
29
22
|
renderFrameIdForRenderId,
|
|
30
23
|
resolveAuthoredTimingWindow,
|
|
31
24
|
toFps
|
|
32
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-VFBAKPRC.js";
|
|
33
26
|
import {
|
|
34
27
|
AUDIO_GROUP_RENDER_ID_ATTR,
|
|
35
28
|
COLOR_GRADING_SOURCE_HIDDEN_ATTR,
|
|
36
29
|
COMPOSITION_SOURCE_URL,
|
|
37
30
|
HF_AUDIO_AUTOMATION_ATTR,
|
|
38
31
|
HF_AUDIO_FX_ATTR,
|
|
32
|
+
HF_COLOR_GRADING_ATTR,
|
|
39
33
|
HF_COLOR_GRADING_CANVAS_ID_PREFIX,
|
|
40
34
|
MEDIA_DURATION_CLAMP_EPSILON_SECONDS,
|
|
41
35
|
MEDIA_RENDER_ID_ATTR,
|
|
@@ -58,7 +52,7 @@ import {
|
|
|
58
52
|
shiftRateLane,
|
|
59
53
|
sourceTimeAt,
|
|
60
54
|
timeAtSourceTime
|
|
61
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-GMNHAOMJ.js";
|
|
62
56
|
import {
|
|
63
57
|
clampAudioGain,
|
|
64
58
|
isAudibleVideoElement
|
|
@@ -68,31 +62,38 @@ import {
|
|
|
68
62
|
redactUrlQueryStrings
|
|
69
63
|
} from "./chunk-634HSJ3J.js";
|
|
70
64
|
import {
|
|
65
|
+
browserPathFromEnv,
|
|
71
66
|
chromeMajorCeiling,
|
|
72
67
|
compareBrowserVersionsDescending,
|
|
73
68
|
exceedsChromeCeiling,
|
|
74
69
|
isManagedChromeDir,
|
|
75
70
|
pickSharedShell
|
|
76
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-4UMPIPCQ.js";
|
|
77
72
|
import {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
} from "./chunk-6BNTGNGD.js";
|
|
73
|
+
resolveReferencedStart
|
|
74
|
+
} from "./chunk-ADDHW2FS.js";
|
|
81
75
|
import {
|
|
82
76
|
FFMPEG_PATH_ENV,
|
|
83
77
|
FFPROBE_PATH_ENV,
|
|
84
78
|
findFfBinary
|
|
85
79
|
} from "./chunk-VAS6HQX3.js";
|
|
86
80
|
import {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
} from "./chunk-
|
|
81
|
+
isWithinProjectRoot,
|
|
82
|
+
resolveProjectRelativeSrc
|
|
83
|
+
} from "./chunk-6BNTGNGD.js";
|
|
90
84
|
import {
|
|
91
85
|
isPathInside
|
|
92
86
|
} from "./chunk-BSQ7XANM.js";
|
|
93
87
|
import {
|
|
94
88
|
parseHTML
|
|
95
89
|
} from "./chunk-SDYDNHEE.js";
|
|
90
|
+
import {
|
|
91
|
+
LOW_MEMORY_TOTAL_MB_THRESHOLD,
|
|
92
|
+
describeWslGpuAccess,
|
|
93
|
+
detectWsl,
|
|
94
|
+
getSystemTotalMb,
|
|
95
|
+
isLowMemorySystem
|
|
96
|
+
} from "./chunk-W2LGJSEN.js";
|
|
96
97
|
|
|
97
98
|
// ../engine/src/config.ts
|
|
98
99
|
import { tmpdir } from "os";
|
|
@@ -421,7 +422,6 @@ function resolveConfig(overrides) {
|
|
|
421
422
|
"PRODUCER_LARGE_RENDER_THRESHOLD",
|
|
422
423
|
DEFAULT_CONFIG.largeRenderThreshold
|
|
423
424
|
),
|
|
424
|
-
chromePath: env("PRODUCER_HEADLESS_SHELL_PATH"),
|
|
425
425
|
disableGpu: envBool("PRODUCER_DISABLE_GPU", DEFAULT_CONFIG.disableGpu),
|
|
426
426
|
browserGpuMode: envBrowserGpuMode(),
|
|
427
427
|
allowSoftwareWebGpu: envBool(
|
|
@@ -1013,23 +1013,14 @@ function resolveHeadlessShellPath(config) {
|
|
|
1013
1013
|
if (config?.chromePath) {
|
|
1014
1014
|
return config.chromePath;
|
|
1015
1015
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
if (!existsSync(
|
|
1016
|
+
const fromEnv = browserPathFromEnv();
|
|
1017
|
+
if (fromEnv) {
|
|
1018
|
+
if (!existsSync(fromEnv.path)) {
|
|
1019
1019
|
throw new Error(
|
|
1020
|
-
`[BrowserManager] Chrome binary not found at
|
|
1020
|
+
`[BrowserManager] Chrome binary not found at ${fromEnv.name}="${fromEnv.path}". Run \`hyperframes browser ensure\` to re-download.`
|
|
1021
1021
|
);
|
|
1022
1022
|
}
|
|
1023
|
-
return
|
|
1024
|
-
}
|
|
1025
|
-
if (process.env.HYPERFRAMES_BROWSER_PATH) {
|
|
1026
|
-
const envPath = process.env.HYPERFRAMES_BROWSER_PATH;
|
|
1027
|
-
if (!existsSync(envPath)) {
|
|
1028
|
-
throw new Error(
|
|
1029
|
-
`[BrowserManager] Chrome binary not found at HYPERFRAMES_BROWSER_PATH="${envPath}". Run \`hyperframes browser ensure\` to re-download.`
|
|
1030
|
-
);
|
|
1031
|
-
}
|
|
1032
|
-
return envPath;
|
|
1023
|
+
return fromEnv.path;
|
|
1033
1024
|
}
|
|
1034
1025
|
const ours = join2(homedir(), ".cache", "hyperframes", "chrome", "chrome-headless-shell");
|
|
1035
1026
|
const shared = join2(homedir(), ".cache", "puppeteer", "chrome-headless-shell");
|
|
@@ -1689,9 +1680,6 @@ identically under Docker. Consider:
|
|
|
1689
1680
|
}
|
|
1690
1681
|
|
|
1691
1682
|
// ../engine/src/services/frameCapture.ts
|
|
1692
|
-
import {
|
|
1693
|
-
CDPSession
|
|
1694
|
-
} from "puppeteer-core";
|
|
1695
1683
|
import { existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
|
|
1696
1684
|
import { join as join3 } from "path";
|
|
1697
1685
|
|
|
@@ -5413,6 +5401,7 @@ async function constructCaptureSession(input) {
|
|
|
5413
5401
|
const page = await browser.newPage();
|
|
5414
5402
|
onPageCreated(page);
|
|
5415
5403
|
const frame = page.mainFrame();
|
|
5404
|
+
const { CDPSession } = await import("puppeteer-core");
|
|
5416
5405
|
if ("client" in frame && frame.client instanceof CDPSession) {
|
|
5417
5406
|
await frame.client.send("Network.enable", {
|
|
5418
5407
|
maxTotalBufferSize: 32 * 1024 * 1024,
|
|
@@ -5772,6 +5761,22 @@ async function pollImagesReady(page, timeoutMs, intervalMs = 100) {
|
|
|
5772
5761
|
}
|
|
5773
5762
|
return check();
|
|
5774
5763
|
}
|
|
5764
|
+
async function waitForImagesReady(page, timeoutMs, onUnloaded) {
|
|
5765
|
+
const ready = await pollImagesReady(page, timeoutMs);
|
|
5766
|
+
if (!ready) {
|
|
5767
|
+
onUnloaded(
|
|
5768
|
+
await page.evaluate(
|
|
5769
|
+
() => Array.from(document.querySelectorAll("img")).filter((img) => {
|
|
5770
|
+
const src = img.getAttribute("src") || "";
|
|
5771
|
+
if (!src || src.startsWith("data:")) return false;
|
|
5772
|
+
return !(img.complete && img.naturalWidth > 0);
|
|
5773
|
+
}).map((img) => img.src || img.getAttribute("src") || "(no src)").join(", ")
|
|
5774
|
+
)
|
|
5775
|
+
);
|
|
5776
|
+
}
|
|
5777
|
+
await decodeAllImages(page);
|
|
5778
|
+
return ready;
|
|
5779
|
+
}
|
|
5775
5780
|
async function collectMediaReadinessWarnings(page, skipIds, timeoutMs) {
|
|
5776
5781
|
const snapshot = await page.evaluate((skipIdList) => {
|
|
5777
5782
|
const skipped = new Set(skipIdList);
|
|
@@ -6099,23 +6104,13 @@ async function initializeSession(session) {
|
|
|
6099
6104
|
const skipVideoIds = session.options.skipReadinessVideoIds ?? [];
|
|
6100
6105
|
const [videosReady] = await Promise.all([
|
|
6101
6106
|
pollVideosReady(page, skipVideoIds, pageReadyTimeout),
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
return !(ie.complete && ie.naturalWidth > 0);
|
|
6110
|
-
}).map((img) => img.src || img.getAttribute("src") || "(no src)").join(", ");
|
|
6111
|
-
});
|
|
6112
|
-
console.warn(
|
|
6113
|
-
`[FrameCapture] Some image elements did not load within ${pageReadyTimeout}ms: ${failedImages}. Continuing render. Affected images may appear blank/missing in early frames.`
|
|
6114
|
-
);
|
|
6115
|
-
}
|
|
6116
|
-
await decodeAllImages(page);
|
|
6117
|
-
return ready;
|
|
6118
|
-
}),
|
|
6107
|
+
waitForImagesReady(
|
|
6108
|
+
page,
|
|
6109
|
+
pageReadyTimeout,
|
|
6110
|
+
(failedImages) => console.warn(
|
|
6111
|
+
`[FrameCapture] Some image elements did not load within ${pageReadyTimeout}ms: ${failedImages}. Continuing render. Affected images may appear blank/missing in early frames.`
|
|
6112
|
+
)
|
|
6113
|
+
),
|
|
6119
6114
|
page.evaluate(`document.fonts?.ready`),
|
|
6120
6115
|
waitForOptionalTailwindReady(page, pageReadyTimeout)
|
|
6121
6116
|
]);
|
|
@@ -6205,23 +6200,13 @@ async function initializeSession(session) {
|
|
|
6205
6200
|
const bfSkipVideoIds = session.options.skipReadinessVideoIds ?? [];
|
|
6206
6201
|
const [bfVideosReady] = await Promise.all([
|
|
6207
6202
|
pollVideosReady(page, bfSkipVideoIds, pageReadyTimeout),
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
return !(ie.complete && ie.naturalWidth > 0);
|
|
6216
|
-
}).map((img) => img.src || img.getAttribute("src") || "(no src)").join(", ");
|
|
6217
|
-
});
|
|
6218
|
-
console.warn(
|
|
6219
|
-
`[FrameCapture] Some image elements did not load within ${pageReadyTimeout}ms: ${failedImages}. Continuing render. Affected images may appear blank/missing in early frames.`
|
|
6220
|
-
);
|
|
6221
|
-
}
|
|
6222
|
-
await decodeAllImages(page);
|
|
6223
|
-
return ready;
|
|
6224
|
-
}),
|
|
6203
|
+
waitForImagesReady(
|
|
6204
|
+
page,
|
|
6205
|
+
pageReadyTimeout,
|
|
6206
|
+
(failedImages) => console.warn(
|
|
6207
|
+
`[FrameCapture] Some image elements did not load within ${pageReadyTimeout}ms: ${failedImages}. Continuing render. Affected images may appear blank/missing in early frames.`
|
|
6208
|
+
)
|
|
6209
|
+
),
|
|
6225
6210
|
page.evaluate(`document.fonts?.ready`),
|
|
6226
6211
|
waitForOptionalTailwindReady(page, pageReadyTimeout)
|
|
6227
6212
|
]);
|
|
@@ -8136,11 +8121,20 @@ var ABORTED_BEFORE_START = {
|
|
|
8136
8121
|
};
|
|
8137
8122
|
async function runFfmpeg(args, opts) {
|
|
8138
8123
|
if (opts?.signal?.aborted) return { ...ABORTED_BEFORE_START };
|
|
8139
|
-
const
|
|
8124
|
+
const stallTimeout = opts?.stallTimeout;
|
|
8125
|
+
let framesWritten = 0;
|
|
8126
|
+
const readStats = ffmpegStatsReader(({ frames }) => {
|
|
8127
|
+
if (frames === void 0 || frames <= framesWritten) return;
|
|
8128
|
+
framesWritten = frames;
|
|
8129
|
+
managed.markActivity();
|
|
8130
|
+
});
|
|
8140
8131
|
const managed = new ManagedChildProcess(spawnFfmpeg(args), {
|
|
8141
8132
|
signal: opts?.signal,
|
|
8142
|
-
deadlineAtMs: Date.now() + timeout,
|
|
8143
|
-
onStderr: opts?.onStderr
|
|
8133
|
+
...stallTimeout === void 0 ? { deadlineAtMs: Date.now() + (opts?.timeout ?? DEFAULT_TIMEOUT) } : { inactivityTimeoutMs: stallTimeout },
|
|
8134
|
+
onStderr: stallTimeout === void 0 ? opts?.onStderr : (chunk) => {
|
|
8135
|
+
readStats(chunk);
|
|
8136
|
+
opts?.onStderr?.(chunk);
|
|
8137
|
+
}
|
|
8144
8138
|
});
|
|
8145
8139
|
return toResult(await managed.wait());
|
|
8146
8140
|
}
|
|
@@ -9045,7 +9039,7 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
|
|
|
9045
9039
|
);
|
|
9046
9040
|
options = { ...options, hdr: void 0 };
|
|
9047
9041
|
}
|
|
9048
|
-
const args = [...inputArgs, "-r", fpsToFfmpegArg(fps)];
|
|
9042
|
+
const args = ["-xerror", ...inputArgs, "-r", fpsToFfmpegArg(fps)];
|
|
9049
9043
|
const shouldUseGpu = useGpu && gpuEncoder !== null;
|
|
9050
9044
|
if (codec === "h264" || codec === "h265") {
|
|
9051
9045
|
if (shouldUseGpu) {
|
|
@@ -9198,6 +9192,18 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
|
|
|
9198
9192
|
return args;
|
|
9199
9193
|
}
|
|
9200
9194
|
var framesReader = (onFrames) => onFrames && ffmpegStatsReader(({ frames }) => frames !== void 0 && onFrames(frames));
|
|
9195
|
+
function frameReadFailure(stderr, framePattern, startNumber, framesRead, frameCount) {
|
|
9196
|
+
const lines = stderr.match(/^.*(?:Error during demuxing|Could not open file).*$/gm) ?? [];
|
|
9197
|
+
const readError = [...new Set(lines.map((line) => line.trim()))].join("; ");
|
|
9198
|
+
if (!readError) return "";
|
|
9199
|
+
const name = framePattern.replace(
|
|
9200
|
+
/%0(\d+)d/,
|
|
9201
|
+
(_, width) => String(startNumber + framesRead).padStart(Number(width), "0")
|
|
9202
|
+
);
|
|
9203
|
+
const at = framesRead < frameCount ? ` at ${name} (${framesRead} of ${frameCount} read)` : "";
|
|
9204
|
+
return `[FFmpeg] could not read a frame${at}: ${readError}
|
|
9205
|
+
`;
|
|
9206
|
+
}
|
|
9201
9207
|
var secondsReader = (onSeconds) => onSeconds && ffmpegStatsReader(({ seconds }) => seconds !== void 0 && onSeconds(seconds));
|
|
9202
9208
|
async function encodeFramesFromDir(framesDir, framePattern, outputPath, options, signal, config, onFramesEncoded) {
|
|
9203
9209
|
const startTime = Date.now();
|
|
@@ -9223,10 +9229,14 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
9223
9229
|
const inputArgs = ["-framerate", fpsToFfmpegArg(options.fps), "-i", inputPath];
|
|
9224
9230
|
const args = buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder);
|
|
9225
9231
|
const encodeTimeout = config?.ffmpegEncodeTimeout ?? DEFAULT_CONFIG.ffmpegEncodeTimeout;
|
|
9232
|
+
let framesRead = 0;
|
|
9226
9233
|
const result = await runFfmpeg(args, {
|
|
9227
9234
|
signal,
|
|
9228
9235
|
timeout: encodeTimeout,
|
|
9229
|
-
onStderr: framesReader(
|
|
9236
|
+
onStderr: framesReader((frames) => {
|
|
9237
|
+
framesRead = frames;
|
|
9238
|
+
onFramesEncoded?.(frames);
|
|
9239
|
+
})
|
|
9230
9240
|
});
|
|
9231
9241
|
if (result.terminationReason === "abort") {
|
|
9232
9242
|
return {
|
|
@@ -9246,7 +9256,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
|
|
|
9246
9256
|
framesEncoded: 0,
|
|
9247
9257
|
fileSize: 0,
|
|
9248
9258
|
error: appendEncodeTimeoutMessage(
|
|
9249
|
-
formatFfmpegError(result.exitCode, result.stderr),
|
|
9259
|
+
frameReadFailure(result.stderr, framePattern, 0, framesRead, frameCount) + formatFfmpegError(result.exitCode, result.stderr),
|
|
9250
9260
|
result.terminationReason === "deadline",
|
|
9251
9261
|
encodeTimeout
|
|
9252
9262
|
),
|
|
@@ -9347,17 +9357,25 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
|
|
|
9347
9357
|
if (options.useGpu) gpuEncoder = await getCachedGpuEncoder();
|
|
9348
9358
|
const args = buildEncoderArgs(options, inputArgs, chunkPath, gpuEncoder);
|
|
9349
9359
|
const encodeTimeout = config?.ffmpegEncodeTimeout ?? DEFAULT_CONFIG.ffmpegEncodeTimeout;
|
|
9360
|
+
let framesRead = 0;
|
|
9350
9361
|
const processResult = await runFfmpeg(args, {
|
|
9351
9362
|
signal,
|
|
9352
9363
|
timeout: encodeTimeout,
|
|
9353
|
-
onStderr: framesReader(
|
|
9354
|
-
|
|
9355
|
-
|
|
9364
|
+
onStderr: framesReader((frames) => {
|
|
9365
|
+
framesRead = frames;
|
|
9366
|
+
onFramesEncoded?.(startNumber + frames);
|
|
9367
|
+
})
|
|
9356
9368
|
});
|
|
9357
9369
|
const chunkResult = {
|
|
9358
9370
|
success: processResult.success,
|
|
9359
9371
|
error: processResult.success ? void 0 : appendEncodeTimeoutMessage(
|
|
9360
|
-
|
|
9372
|
+
frameReadFailure(
|
|
9373
|
+
processResult.stderr,
|
|
9374
|
+
framePattern,
|
|
9375
|
+
startNumber,
|
|
9376
|
+
framesRead,
|
|
9377
|
+
framesInChunk
|
|
9378
|
+
) + `Chunk ${i} encode failed: ${processResult.stderr.slice(-400)}`,
|
|
9361
9379
|
processResult.terminationReason === "deadline",
|
|
9362
9380
|
encodeTimeout
|
|
9363
9381
|
)
|
|
@@ -10122,52 +10140,6 @@ function createConcurrencyLimit(limit) {
|
|
|
10122
10140
|
};
|
|
10123
10141
|
}
|
|
10124
10142
|
|
|
10125
|
-
// ../engine/src/services/referenceResolver.ts
|
|
10126
|
-
function findReferenceTargetEl(doc, refId) {
|
|
10127
|
-
return doc.getElementById(refId) ?? doc.querySelector(`[data-composition-id="${refId}"]`);
|
|
10128
|
-
}
|
|
10129
|
-
function resolveReferencedStart(doc, el, startCache, visiting) {
|
|
10130
|
-
const cached = startCache.get(el);
|
|
10131
|
-
if (cached !== void 0) return cached;
|
|
10132
|
-
if (visiting.has(el)) return 0;
|
|
10133
|
-
visiting.add(el);
|
|
10134
|
-
try {
|
|
10135
|
-
const expression = parseStartExpression(el.getAttribute("data-start"));
|
|
10136
|
-
if (!expression) {
|
|
10137
|
-
startCache.set(el, 0);
|
|
10138
|
-
return 0;
|
|
10139
|
-
}
|
|
10140
|
-
if (expression.kind === "absolute") {
|
|
10141
|
-
const value = Math.max(0, expression.value);
|
|
10142
|
-
startCache.set(el, value);
|
|
10143
|
-
return value;
|
|
10144
|
-
}
|
|
10145
|
-
const target = findReferenceTargetEl(doc, expression.refId);
|
|
10146
|
-
if (!target) {
|
|
10147
|
-
startCache.set(el, 0);
|
|
10148
|
-
return 0;
|
|
10149
|
-
}
|
|
10150
|
-
const targetStart = resolveReferencedStart(doc, target, startCache, visiting);
|
|
10151
|
-
const targetDuration = resolveReferencedDuration(doc, target, startCache, visiting);
|
|
10152
|
-
const resolved = targetDuration != null && targetDuration > 0 ? Math.max(0, targetStart + targetDuration + expression.offset) : Math.max(0, targetStart + expression.offset);
|
|
10153
|
-
startCache.set(el, resolved);
|
|
10154
|
-
return resolved;
|
|
10155
|
-
} finally {
|
|
10156
|
-
visiting.delete(el);
|
|
10157
|
-
}
|
|
10158
|
-
}
|
|
10159
|
-
function resolveReferencedDuration(doc, el, startCache, visiting) {
|
|
10160
|
-
const durationAttr = parseNumeric(el.getAttribute("data-duration"));
|
|
10161
|
-
if (durationAttr != null && durationAttr > 0) return durationAttr;
|
|
10162
|
-
const endAttr = parseNumeric(el.getAttribute("data-end"));
|
|
10163
|
-
if (endAttr != null) {
|
|
10164
|
-
const start = resolveReferencedStart(doc, el, startCache, visiting);
|
|
10165
|
-
const delta = endAttr - start;
|
|
10166
|
-
if (Number.isFinite(delta) && delta > 0) return delta;
|
|
10167
|
-
}
|
|
10168
|
-
return null;
|
|
10169
|
-
}
|
|
10170
|
-
|
|
10171
10143
|
// ../engine/src/services/mediaTimelineWindow.ts
|
|
10172
10144
|
function isKnownInactiveTimelineWindow(element, resolvedStart) {
|
|
10173
10145
|
const duration = parseStrictFiniteTimingNumber(element.getAttribute("data-duration"));
|
|
@@ -11602,6 +11574,12 @@ var SDR_TO_HDR_COLORSPACE_FILTER = "colorspace=all=bt2020:iall=bt709:range=tv";
|
|
|
11602
11574
|
var HDR_TO_SDR_ZSCALE_TRANSFORM_KEY = "hdr2sdr-hable-srgb";
|
|
11603
11575
|
var HDR_TO_SDR_VIDEOTOOLBOX_TRANSFORM_KEY = "hdr2sdr-videotoolbox-hdr";
|
|
11604
11576
|
var SDR_CANVAS_PASSTHROUGH_FILTER = "setparams=color_primaries=bt709:color_trc=iec61966-2-1";
|
|
11577
|
+
function halveToCoverScaleFilter(box) {
|
|
11578
|
+
const fit = `max(${Math.ceil(box.width)}/iw\\,${Math.ceil(box.height)}/ih)`;
|
|
11579
|
+
const halvings = `floor(log(1/${fit})/log(2))`;
|
|
11580
|
+
const side = (dim) => `if(lt(${fit}\\,0.5)\\,2*ceil(${dim}/pow(2\\,${halvings})/2)\\,${dim})`;
|
|
11581
|
+
return `scale=w=${side("iw")}:h=${side("ih")}:flags=area+accurate_rnd`;
|
|
11582
|
+
}
|
|
11605
11583
|
var SDR_JPEG_AS_BT601_FULL_RANGE_FILTER = "scale=flags=neighbor+accurate_rnd,format=gbrp,scale=out_color_matrix=bt601:out_range=pc:flags=neighbor,format=yuv420p";
|
|
11606
11584
|
var warnedMissingZscale = false;
|
|
11607
11585
|
async function toneMapsWithZscale() {
|
|
@@ -11930,11 +11908,21 @@ function parseImageElements(html) {
|
|
|
11930
11908
|
}
|
|
11931
11909
|
return images;
|
|
11932
11910
|
}
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
11911
|
+
var firstFrameColourCache = /* @__PURE__ */ new Map();
|
|
11912
|
+
function readFirstFrameColour(videoPath) {
|
|
11913
|
+
const identity = mediaFileIdentity(videoPath);
|
|
11914
|
+
const cached = readMetadataCache(firstFrameColourCache, videoPath, identity);
|
|
11915
|
+
if (cached) return cached;
|
|
11916
|
+
const probe = inExtractionSlot(
|
|
11917
|
+
() => runFfmpeg([...urlInputArgs(videoPath), ...firstFrameColourArgs(videoPath)])
|
|
11918
|
+
).then((result) => {
|
|
11919
|
+
if (!result.success && firstFrameColourCache.get(videoPath)?.promise === probe) {
|
|
11920
|
+
firstFrameColourCache.delete(videoPath);
|
|
11921
|
+
}
|
|
11922
|
+
return parseFirstFrameColour(result.stderr);
|
|
11936
11923
|
});
|
|
11937
|
-
|
|
11924
|
+
writeMetadataCache(firstFrameColourCache, videoPath, identity, probe);
|
|
11925
|
+
return probe;
|
|
11938
11926
|
}
|
|
11939
11927
|
var CHROME_BT601_UNTAGGED_CODECS = /* @__PURE__ */ new Set(["vp9", "av1"]);
|
|
11940
11928
|
function chromeGuessForUntaggedMatrix(metadata) {
|
|
@@ -12097,7 +12085,7 @@ async function extractVideoFramesRangeUnredacted(videoPath, videoId, startTime,
|
|
|
12097
12085
|
vfFilters.push(SDR_TO_HDR_COLORSPACE_FILTER);
|
|
12098
12086
|
}
|
|
12099
12087
|
if (toneMappedToSdr) {
|
|
12100
|
-
const frameColour = await readFirstFrameColour(videoPath
|
|
12088
|
+
const frameColour = await readFirstFrameColour(videoPath);
|
|
12101
12089
|
vfFilters.push(hdrToSdrToneMapFilter(metadata.colorSpace ?? {}, frameColour));
|
|
12102
12090
|
}
|
|
12103
12091
|
if (toneMappedToSdr || !isHdr && !options.sdrToHdrTransfer) {
|
|
@@ -12105,12 +12093,15 @@ async function extractVideoFramesRangeUnredacted(videoPath, videoId, startTime,
|
|
|
12105
12093
|
...isHdr ? [] : chromeGuessForUntaggedMatrix(metadata),
|
|
12106
12094
|
SDR_CANVAS_PASSTHROUGH_FILTER
|
|
12107
12095
|
);
|
|
12108
|
-
if (format === "jpg")
|
|
12096
|
+
if (format === "jpg") {
|
|
12097
|
+
if (options.shownBox) vfFilters.push(halveToCoverScaleFilter(options.shownBox));
|
|
12098
|
+
vfFilters.push(SDR_JPEG_AS_BT601_FULL_RANGE_FILTER);
|
|
12099
|
+
}
|
|
12109
12100
|
}
|
|
12110
12101
|
const encodeArgs = ["-q:v", format === "jpg" ? String(Math.ceil((100 - quality) / 3)) : "0"];
|
|
12111
12102
|
if (format === "png") encodeArgs.push("-compression_level", "1");
|
|
12112
12103
|
encodeArgs.push("-y", outputPattern);
|
|
12113
|
-
const runOptions = { signal,
|
|
12104
|
+
const runOptions = { signal, stallTimeout: ffmpegProcessTimeout };
|
|
12114
12105
|
const filterArgsFor = (seek) => {
|
|
12115
12106
|
const filters = [...vfFilters];
|
|
12116
12107
|
filters.splice(samplingAt, 0, ...samplingFilters(seek));
|
|
@@ -12150,8 +12141,8 @@ async function extractVideoFramesRangeUnredacted(videoPath, videoId, startTime,
|
|
|
12150
12141
|
}
|
|
12151
12142
|
if (!processResult.success) {
|
|
12152
12143
|
const hdrPrefix = options.sdrToHdrTransfer ? `SDR\u2192HDR conversion failed (colorspace filter in extract pass, target ${options.sdrToHdrTransfer}): ` : "";
|
|
12153
|
-
const timedOut = processResult.terminationReason === "
|
|
12154
|
-
const timeoutSuffix = timedOut ? ` (
|
|
12144
|
+
const timedOut = processResult.terminationReason === "inactivity";
|
|
12145
|
+
const timeoutSuffix = timedOut ? ` (no new frame for ${ffmpegProcessTimeout} ms)` : "";
|
|
12155
12146
|
const diagnostic = `${hdrPrefix}FFmpeg exited with code ${processResult.exitCode}${timeoutSuffix}: ` + processResult.stderr.slice(-500);
|
|
12156
12147
|
if (timedOut) {
|
|
12157
12148
|
throw new VideoSourceExtractionError(
|
|
@@ -13513,6 +13504,52 @@ async function setVideoElementsVisibility(page, videoIds, visible) {
|
|
|
13513
13504
|
visible
|
|
13514
13505
|
);
|
|
13515
13506
|
}
|
|
13507
|
+
async function measureVideoShownBoxes(page, videoIds, times) {
|
|
13508
|
+
if (videoIds.length === 0 || times.length === 0) return /* @__PURE__ */ new Map();
|
|
13509
|
+
const boxes = await page.evaluate(
|
|
13510
|
+
async (ids, seekTimes, gradingAttr, gradingCanvasPrefix) => {
|
|
13511
|
+
const hf = window.__hf;
|
|
13512
|
+
if (typeof hf?.seek !== "function") return [];
|
|
13513
|
+
const waitForSeek = window.__hfWaitForSeekCompletion;
|
|
13514
|
+
const largest = /* @__PURE__ */ new Map();
|
|
13515
|
+
const fullSize = /* @__PURE__ */ new Set();
|
|
13516
|
+
const keepsFullSize = (video) => {
|
|
13517
|
+
if (video.hasAttribute(gradingAttr)) return true;
|
|
13518
|
+
const fit = window.getComputedStyle(video).objectFit;
|
|
13519
|
+
return fit === "none" || fit === "scale-down";
|
|
13520
|
+
};
|
|
13521
|
+
const measure = (id) => {
|
|
13522
|
+
const video = window.__hfMediaEl?.(id) ?? document.getElementById(id);
|
|
13523
|
+
if (!video || keepsFullSize(video)) {
|
|
13524
|
+
fullSize.add(id);
|
|
13525
|
+
return;
|
|
13526
|
+
}
|
|
13527
|
+
const rect = video.getBoundingClientRect();
|
|
13528
|
+
const box = largest.get(id) ?? { width: 0, height: 0 };
|
|
13529
|
+
box.width = Math.max(box.width, rect.width * window.devicePixelRatio);
|
|
13530
|
+
box.height = Math.max(box.height, rect.height * window.devicePixelRatio);
|
|
13531
|
+
largest.set(id, box);
|
|
13532
|
+
};
|
|
13533
|
+
const pageHasCanvas = () => Array.from(document.querySelectorAll("canvas")).some(
|
|
13534
|
+
(canvas) => !canvas.id.startsWith(gradingCanvasPrefix)
|
|
13535
|
+
);
|
|
13536
|
+
for (const t of seekTimes) {
|
|
13537
|
+
hf.seek(t);
|
|
13538
|
+
await waitForSeek?.();
|
|
13539
|
+
if (pageHasCanvas()) return [];
|
|
13540
|
+
for (const id of ids) measure(id);
|
|
13541
|
+
}
|
|
13542
|
+
return Array.from(largest).filter(
|
|
13543
|
+
([id, box]) => !fullSize.has(id) && box.width > 0 && box.height > 0
|
|
13544
|
+
);
|
|
13545
|
+
},
|
|
13546
|
+
[...videoIds],
|
|
13547
|
+
[...times],
|
|
13548
|
+
HF_COLOR_GRADING_ATTR,
|
|
13549
|
+
HF_COLOR_GRADING_CANVAS_ID_PREFIX
|
|
13550
|
+
);
|
|
13551
|
+
return new Map(boxes);
|
|
13552
|
+
}
|
|
13516
13553
|
async function queryVideoElementBounds(page, videoIds) {
|
|
13517
13554
|
if (videoIds.length === 0) return [];
|
|
13518
13555
|
return page.evaluate((ids) => {
|
|
@@ -13773,7 +13810,16 @@ async function queryElementStacking(page, nativeHdrIds) {
|
|
|
13773
13810
|
}
|
|
13774
13811
|
|
|
13775
13812
|
// ../engine/src/services/audioMixer.ts
|
|
13776
|
-
import {
|
|
13813
|
+
import {
|
|
13814
|
+
closeSync as closeSync4,
|
|
13815
|
+
existsSync as existsSync11,
|
|
13816
|
+
mkdirSync as mkdirSync7,
|
|
13817
|
+
mkdtempSync as mkdtempSync7,
|
|
13818
|
+
openSync as openSync4,
|
|
13819
|
+
renameSync as renameSync3,
|
|
13820
|
+
rmSync as rmSync8,
|
|
13821
|
+
writeFileSync as writeFileSync7
|
|
13822
|
+
} from "fs";
|
|
13777
13823
|
import { join as join14, dirname as dirname7, isAbsolute, relative } from "path";
|
|
13778
13824
|
|
|
13779
13825
|
// ../engine/src/utils/notMediaPayload.ts
|
|
@@ -13918,18 +13964,18 @@ function planSegments(totalDuration, count) {
|
|
|
13918
13964
|
const cuts = Array.from({ length: count + 1 }, (_, k) => Math.round(k * packets / count));
|
|
13919
13965
|
return cuts.slice(0, -1).map((cut, k) => {
|
|
13920
13966
|
const next = cuts[k + 1];
|
|
13921
|
-
const firstFrame =
|
|
13922
|
-
const end = k === count - 1 ? "" :
|
|
13923
|
-
const trim = `atrim=start_sample=${firstFrame * FRAME_SAMPLES}${end}`;
|
|
13967
|
+
const firstFrame = cut - PRE_ROLL_FRAMES;
|
|
13968
|
+
const end = k === count - 1 ? "" : `end_sample=${(next + POST_ROLL_FRAMES) * FRAME_SAMPLES}`;
|
|
13969
|
+
const trim = firstFrame < 0 ? `${end && `atrim=${end},`}adelay=delays=${-firstFrame * FRAME_SAMPLES}S:all=1` : `atrim=start_sample=${firstFrame * FRAME_SAMPLES}${end && `:${end}`}`;
|
|
13924
13970
|
return { trim, keepFrom: cut - firstFrame, keepTo: next - firstFrame };
|
|
13925
13971
|
});
|
|
13926
13972
|
}
|
|
13927
|
-
async function encodeAac(input, outputPath, totalDuration, {
|
|
13928
|
-
|
|
13929
|
-
|
|
13930
|
-
|
|
13931
|
-
|
|
13932
|
-
|
|
13973
|
+
async function encodeAac(input, outputPath, totalDuration, {
|
|
13974
|
+
signal,
|
|
13975
|
+
timeout,
|
|
13976
|
+
filter,
|
|
13977
|
+
segments: count = aacSegmentCount(totalDuration)
|
|
13978
|
+
}) {
|
|
13933
13979
|
const dir = mkdtempSync4(join11(dirname5(outputPath), ".aac-segments-"));
|
|
13934
13980
|
try {
|
|
13935
13981
|
const segments = planSegments(totalDuration, count).map((segment, k) => ({
|
|
@@ -13938,10 +13984,23 @@ async function encodeAac(input, outputPath, totalDuration, { signal, timeout })
|
|
|
13938
13984
|
}));
|
|
13939
13985
|
const results = await Promise.all(
|
|
13940
13986
|
segments.map(
|
|
13941
|
-
({ trim, path }) => runFfmpeg(
|
|
13942
|
-
|
|
13943
|
-
|
|
13944
|
-
|
|
13987
|
+
({ trim, path }) => runFfmpeg(
|
|
13988
|
+
[
|
|
13989
|
+
"-i",
|
|
13990
|
+
input,
|
|
13991
|
+
"-af",
|
|
13992
|
+
[filter, `aresample=${SAMPLE_RATE}`, trim].filter(Boolean).join(","),
|
|
13993
|
+
...AAC_ARGS,
|
|
13994
|
+
"-f",
|
|
13995
|
+
"adts",
|
|
13996
|
+
"-y",
|
|
13997
|
+
path
|
|
13998
|
+
],
|
|
13999
|
+
{
|
|
14000
|
+
signal,
|
|
14001
|
+
timeout
|
|
14002
|
+
}
|
|
14003
|
+
)
|
|
13945
14004
|
)
|
|
13946
14005
|
);
|
|
13947
14006
|
const failed = results.find((result) => !result.success);
|
|
@@ -14452,7 +14511,7 @@ import { join as join13 } from "path";
|
|
|
14452
14511
|
import { pathToFileURL } from "url";
|
|
14453
14512
|
|
|
14454
14513
|
// ../core/dist/generated/audio-fx-runtime-inline.js
|
|
14455
|
-
var AUDIO_FX_RUNTIME_IIFE = '"use strict";(()=>{var we="data-fx-chain",Tt=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}),R=(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:[R(-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),R(-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),R(-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),R(-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"}],L=new Map(z.map(e=>[e.id,e]));function P(e){return L.get(e)}var Ft=z.map(e=>e.id);function y(e,t){let n=L.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"||!L.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,S=new WeakMap;var He=new WeakSet;function O(e){let t=S.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 S.delete(e),n}),S.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 Ne=new Set(["peaking","highpass","lowpass"]),ee=e=>Math.pow(10,e/20),Re=(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 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()}}},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())}},Se=(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":Re,"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":Se,"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",Ut=Ve.slice(5),D=1,$e=512,A=class extends Error{constructor(t){super(t),this.name="AudioAutomationError"}},V="volume",le="rate";function N(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=N(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(!N(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"||!N(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){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{let o=n.values[n.values.length-1]??0;e.linearRampToValueAtTime(o,n.time+n.duration)}}function dt(e){for(let t of e)t.param.cancelScheduledValues(0)}function mt(e,t,n,o){if(t.points.length!==0)for(let a of e){if(dt([a]),ge(t)){let r=(a.map??X)(t.points[0].v);a.param.setValueAtTime(r,o.scheduledAt);continue}ct(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=N(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&&(mt(l,s,m.scale,o),i.push(...l))}return i}var pt=5,ht=.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 ft(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(ht)/Math.log(o))*n}function bt(e){return .6+Math.max(0,Math.min(1,e))*2.6}function gt(e,t){return k(e,"wet",t)>0?bt(k(e,"size",t)):0}function At(e,t){return k(e,"mix",t)>0?ft(k(e,"time",t),k(e,"feedback",t)):0}function xt(e,t){return k(e,"mix",t)>0?k(e,"delay",t)/1e3:0}function yt(e,t){return k(e,"mix",t)>0?.2:0}function kt(e,t){if(e.enabled===!1)return 0;switch(e.type){case"reverb":return gt(e,t);case"delay":return At(e,t);case"chorus":return xt(e,t);case"pitchshift":return yt(e,t);default:return 0}}function ye(e,t){let n=e.nodes.reduce((o,a)=>o+kt(a,t),0);return Math.min(pt,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 wt(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 vt(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 Mt(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=wt(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(),vt(await c.startRendering(),m)}window.__HF_AUDIO_FX={render:Mt};})();\n';
|
|
14514
|
+
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';
|
|
14456
14515
|
function getAudioFxRuntimeScript() {
|
|
14457
14516
|
return AUDIO_FX_RUNTIME_IIFE;
|
|
14458
14517
|
}
|
|
@@ -15254,13 +15313,18 @@ function parseAudioElements(html) {
|
|
|
15254
15313
|
}
|
|
15255
15314
|
return elements;
|
|
15256
15315
|
}
|
|
15316
|
+
var FAIL_ON_READ_ERROR_INSTEAD_OF_ENDING_THERE = ["-xerror"];
|
|
15257
15317
|
async function extractAudioSegment(srcPath, outputPath, stage, mediaStart, duration, playbackRate = 1, signal, config) {
|
|
15258
15318
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
15259
15319
|
const outputDir = dirname7(outputPath);
|
|
15260
15320
|
if (!existsSync11(outputDir)) mkdirSync7(outputDir, { recursive: true });
|
|
15261
15321
|
const normalizedPlaybackRate = normalizeRateSpec(playbackRate);
|
|
15262
15322
|
const sourceDuration = sourceTimeAt(normalizedPlaybackRate, duration);
|
|
15263
|
-
const
|
|
15323
|
+
const readsUrl = isHttpUrl(srcPath);
|
|
15324
|
+
const metadata = readsUrl ? await extractAudioMetadata(srcPath, { signal }).catch((error) => {
|
|
15325
|
+
if (signal?.aborted) throw error;
|
|
15326
|
+
return extractAudioMetadata(srcPath, { signal });
|
|
15327
|
+
}) : await extractAudioMetadata(srcPath).catch(() => null);
|
|
15264
15328
|
const inputSeek = mediaStart >= (metadata?.latestStreamLeadSeconds ?? 0);
|
|
15265
15329
|
const start = formatFilterNumber(mediaStart);
|
|
15266
15330
|
const timestampTrim = inputSeek ? null : `atrim=start=${start}:end=${formatFilterNumber(mediaStart + sourceDuration)},asetpts=PTS-${start}/TB,aresample=async=1:first_pts=0`;
|
|
@@ -15270,7 +15334,13 @@ async function extractAudioSegment(srcPath, outputPath, stage, mediaStart, durat
|
|
|
15270
15334
|
duration,
|
|
15271
15335
|
timestampTrim
|
|
15272
15336
|
);
|
|
15273
|
-
const args =
|
|
15337
|
+
const args = [
|
|
15338
|
+
...readsUrl ? FAIL_ON_READ_ERROR_INSTEAD_OF_ENDING_THERE : [],
|
|
15339
|
+
...urlInputArgs(srcPath),
|
|
15340
|
+
...inputSeek ? ["-ss", String(mediaStart), "-t", String(sourceDuration)] : [],
|
|
15341
|
+
"-i",
|
|
15342
|
+
srcPath
|
|
15343
|
+
];
|
|
15274
15344
|
args.push("-vn", "-acodec", "pcm_s16le", "-ar", "48000", ...outputArgs);
|
|
15275
15345
|
if (normalizedPlaybackRate !== 1) args.push("-t", String(duration));
|
|
15276
15346
|
args.push("-y", outputPath);
|
|
@@ -15291,7 +15361,11 @@ async function extractAudioSegment(srcPath, outputPath, stage, mediaStart, durat
|
|
|
15291
15361
|
failure: failure2
|
|
15292
15362
|
};
|
|
15293
15363
|
}
|
|
15294
|
-
|
|
15364
|
+
let failure = result.success ? void 0 : ffmpegFailure(stage, result);
|
|
15365
|
+
const probedLinkFailedToRead = readsUrl && (failure?.reason === "invalid_media" || failure?.reason === "ffmpeg_failed");
|
|
15366
|
+
if (failure && probedLinkFailedToRead) {
|
|
15367
|
+
failure = { ...failure, reason: "download_failed", owner: "system", retryable: true };
|
|
15368
|
+
}
|
|
15295
15369
|
return {
|
|
15296
15370
|
success: result.success,
|
|
15297
15371
|
outputPath,
|
|
@@ -15342,7 +15416,7 @@ async function generateSilence(outputPath, duration, signal, config) {
|
|
|
15342
15416
|
failure
|
|
15343
15417
|
};
|
|
15344
15418
|
}
|
|
15345
|
-
async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config
|
|
15419
|
+
async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config) {
|
|
15346
15420
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
15347
15421
|
const masterOutputGain = config?.audioGain ?? DEFAULT_CONFIG.audioGain;
|
|
15348
15422
|
if (tracks.length === 0) {
|
|
@@ -15387,7 +15461,10 @@ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config,
|
|
|
15387
15461
|
scriptPath,
|
|
15388
15462
|
"-map",
|
|
15389
15463
|
"[out]",
|
|
15390
|
-
|
|
15464
|
+
"-acodec",
|
|
15465
|
+
"pcm_f32le",
|
|
15466
|
+
"-ar",
|
|
15467
|
+
"48000",
|
|
15391
15468
|
"-t",
|
|
15392
15469
|
String(totalDuration),
|
|
15393
15470
|
"-y",
|
|
@@ -15547,7 +15624,7 @@ async function mixGroupMembers(memberTracks, outputPath, totalDuration, signal,
|
|
|
15547
15624
|
}
|
|
15548
15625
|
async function mixThroughFloatSum(tracks, masterChain, outputPath, workDir, totalDuration, signal, config) {
|
|
15549
15626
|
const sumPath = join14(workDir, "master-sum.wav");
|
|
15550
|
-
const sum = await mixAudioTracks(tracks, sumPath, totalDuration, signal, config
|
|
15627
|
+
const sum = await mixAudioTracks(tracks, sumPath, totalDuration, signal, config);
|
|
15551
15628
|
if (!sum.success) return { ...sum, outputPath };
|
|
15552
15629
|
const encoderInput = masterChain ? (await applyAudioFxChain(sumPath, masterChain, join14(workDir, "master-fx.wav"), {
|
|
15553
15630
|
trackId: "master",
|
|
@@ -15568,6 +15645,7 @@ async function mixThroughFloatSum(tracks, masterChain, outputPath, workDir, tota
|
|
|
15568
15645
|
failures: [failure]
|
|
15569
15646
|
};
|
|
15570
15647
|
}
|
|
15648
|
+
if (config?.keepEncoderInputAt) renameSync3(encoderInput, config.keepEncoderInputAt);
|
|
15571
15649
|
return { ...sum, outputPath, durationMs: sum.durationMs + encoded.durationMs };
|
|
15572
15650
|
}
|
|
15573
15651
|
async function processCompositionAudio(elements, baseDir, workDir, outputPath, totalDuration, signal, config, compiledDir) {
|
|
@@ -15599,11 +15677,16 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15599
15677
|
}
|
|
15600
15678
|
try {
|
|
15601
15679
|
let srcPath = element.src;
|
|
15680
|
+
let readInPlace = false;
|
|
15602
15681
|
if (!isHttpUrl(srcPath)) {
|
|
15603
15682
|
srcPath = resolveProjectRelativeSrc(element.src, baseDir, compiledDir);
|
|
15604
|
-
|
|
15683
|
+
const remoteUrl = config?.urlByAbsentLocalPath?.get(srcPath);
|
|
15684
|
+
if (remoteUrl) {
|
|
15685
|
+
srcPath = remoteUrl;
|
|
15686
|
+
readInPlace = true;
|
|
15687
|
+
}
|
|
15605
15688
|
}
|
|
15606
|
-
if (isHttpUrl(srcPath)) {
|
|
15689
|
+
if (isHttpUrl(srcPath) && !readInPlace) {
|
|
15607
15690
|
try {
|
|
15608
15691
|
srcPath = await downloadToTemp(
|
|
15609
15692
|
srcPath,
|
|
@@ -15620,7 +15703,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15620
15703
|
return;
|
|
15621
15704
|
}
|
|
15622
15705
|
}
|
|
15623
|
-
if (!existsSync11(srcPath)) {
|
|
15706
|
+
if (!readInPlace && !existsSync11(srcPath)) {
|
|
15624
15707
|
failures.push({
|
|
15625
15708
|
stage: "source",
|
|
15626
15709
|
reason: "source_not_found",
|
|
@@ -15647,7 +15730,10 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15647
15730
|
if (element.end - element.start <= 0) {
|
|
15648
15731
|
let metadata;
|
|
15649
15732
|
try {
|
|
15650
|
-
metadata = await extractAudioMetadata(
|
|
15733
|
+
metadata = await extractAudioMetadata(
|
|
15734
|
+
srcPath,
|
|
15735
|
+
readInPlace ? { signal: effectiveSignal } : void 0
|
|
15736
|
+
);
|
|
15651
15737
|
} catch (err) {
|
|
15652
15738
|
failures.push(
|
|
15653
15739
|
probeFailure(err instanceof Error ? err.message : String(err), element.id)
|
|
@@ -15938,7 +16024,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
15938
16024
|
if (failures.length > 0) return bail();
|
|
15939
16025
|
let mixResult;
|
|
15940
16026
|
try {
|
|
15941
|
-
mixResult =
|
|
16027
|
+
mixResult = tracks.length > 0 ? await mixThroughFloatSum(
|
|
15942
16028
|
tracks,
|
|
15943
16029
|
masterChain,
|
|
15944
16030
|
outputPath,
|
|
@@ -17171,6 +17257,7 @@ export {
|
|
|
17171
17257
|
deriveBeginFrameProbeTimeTicks,
|
|
17172
17258
|
completeDeferredDrawElementInit,
|
|
17173
17259
|
createCaptureSession,
|
|
17260
|
+
waitForImagesReady,
|
|
17174
17261
|
VfxFailureError,
|
|
17175
17262
|
initializeSession,
|
|
17176
17263
|
captureFrame,
|
|
@@ -17233,8 +17320,6 @@ export {
|
|
|
17233
17320
|
spawnStreamingEncoder,
|
|
17234
17321
|
HF_AUDIO_GROUP_TAG,
|
|
17235
17322
|
HF_AUDIO_GROUP_ATTR,
|
|
17236
|
-
resolveReferencedStart,
|
|
17237
|
-
resolveReferencedDuration,
|
|
17238
17323
|
safeDownloadUrlIdentity,
|
|
17239
17324
|
writeUrlDownloadTelemetry,
|
|
17240
17325
|
isBlockedNetworkHost,
|
|
@@ -17271,6 +17356,7 @@ export {
|
|
|
17271
17356
|
createVideoFrameInjector,
|
|
17272
17357
|
hideVideoElements,
|
|
17273
17358
|
showVideoElements,
|
|
17359
|
+
measureVideoShownBoxes,
|
|
17274
17360
|
queryVideoElementBounds,
|
|
17275
17361
|
queryElementStacking,
|
|
17276
17362
|
NOT_MEDIA_PAYLOAD,
|
|
@@ -17278,6 +17364,8 @@ export {
|
|
|
17278
17364
|
fingerprintElementId,
|
|
17279
17365
|
isNotMediaPayload,
|
|
17280
17366
|
assertMediaPayload,
|
|
17367
|
+
aacSegmentCount,
|
|
17368
|
+
encodeAac,
|
|
17281
17369
|
audioFailureDiagnostics,
|
|
17282
17370
|
MIXED_AUDIO_FILENAME,
|
|
17283
17371
|
parseMasterFxChain,
|