@hyperframes/core 0.8.3 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audioAutomation.d.ts +9 -2
- package/dist/audioAutomation.d.ts.map +1 -1
- package/dist/audioAutomation.js +8 -1
- package/dist/audioAutomation.js.map +1 -1
- package/dist/audioGain.d.ts +41 -0
- package/dist/audioGain.d.ts.map +1 -0
- package/dist/audioGain.js +105 -0
- package/dist/audioGain.js.map +1 -0
- package/dist/audioLeveller.d.ts +10 -5
- package/dist/audioLeveller.d.ts.map +1 -1
- package/dist/audioLeveller.js +10 -5
- package/dist/audioLeveller.js.map +1 -1
- package/dist/compiler/index.d.ts +1 -0
- package/dist/compiler/index.d.ts.map +1 -1
- package/dist/compiler/index.js +1 -0
- package/dist/compiler/index.js.map +1 -1
- package/dist/compiler/mediaRenderIds.d.ts +44 -0
- package/dist/compiler/mediaRenderIds.d.ts.map +1 -0
- package/dist/compiler/mediaRenderIds.js +75 -0
- package/dist/compiler/mediaRenderIds.js.map +1 -0
- package/dist/generated/audio-fx-runtime-inline.js +1 -1
- package/dist/generated/audio-fx-runtime-inline.js.map +1 -1
- package/dist/generated/runtime-inline.js +1 -1
- package/dist/generated/runtime-inline.js.map +1 -1
- package/dist/hyperframe.runtime.iife.js +25 -25
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/media.d.ts.map +1 -1
- package/dist/runtime/media.js +20 -5
- package/dist/runtime/media.js.map +1 -1
- package/dist/runtime/mediaVolumeEnvelope.d.ts.map +1 -1
- package/dist/runtime/mediaVolumeEnvelope.js +25 -22
- package/dist/runtime/mediaVolumeEnvelope.js.map +1 -1
- package/dist/runtime/renderFrameSibling.d.ts +41 -0
- package/dist/runtime/renderFrameSibling.d.ts.map +1 -0
- package/dist/runtime/renderFrameSibling.js +55 -0
- package/dist/runtime/renderFrameSibling.js.map +1 -0
- package/package.json +8 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// AUTO-GENERATED by scripts/build-audio-fx-runtime.ts - do not edit
|
|
2
|
-
const AUDIO_FX_RUNTIME_IIFE = '"use strict";(()=>{var me="data-fx-chain",rt=me.slice(5),U=1,F=(e="frequency",t="Frequency",n=1e3,o=20,a=2e4)=>({kind:"number",key:e,label:t,unit:"Hz",min:o,max:a,step:1,default:n,scale:"log",automatable:!0}),S=(e=.707,t="Bandwidth \\u2014 higher is narrower.")=>({kind:"number",key:"q",label:"Q",unit:"",min:.1,max:20,step:.01,default:e,scale:"log",automatable:!0,hint:t}),C=(e=-40,t=40,n=0)=>({kind:"number",key:"gain",label:"Gain",unit:"dB",min:e,max:t,step:.1,default:n,automatable:!0}),W={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."},X=[{id:"gain",label:"Gain",group:"dynamics",description:"Raise or lower the whole signal. Automate it to duck under something else.",params:[C(-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),C(-40,40,0),S(1)],web:"biquad-peaking"},{id:"lowshelf",label:"Low Shelf",group:"filter",description:"Lift or drop everything below the corner frequency.",params:[F("frequency","Frequency",200,20,2e3),C(-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),C(-40,40,0)],web:"biquad-highshelf"},{id:"highpass",label:"High-pass",group:"filter",description:"Remove low frequencies \\u2014 the usual fix for rumble on a voice.",params:[F("frequency","Cutoff",300,20,2e4),S(.707),W],web:"biquad-highpass"},{id:"lowpass",label:"Low-pass",group:"filter",description:"Remove high frequencies \\u2014 darkens or muffles a track.",params:[F("frequency","Cutoff",8e3,100,2e4),S(.707),W],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:"Hard ceiling \\u2014 nothing gets past the 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:"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 \\u2014 a crude downsample."},{kind:"number",key:"mix",label:"Mix",unit:"",min:0,max:1,step:.01,default:1}],web:"worklet-bitcrush"},{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"}],O=new Map(X.map(e=>[e.id,e]));function H(e){return O.get(e)}var it=X.map(e=>e.id);function A(e,t){let n=O.get(e);if(!n)return{};let o={};for(let a of n.params){let i=t?.[a.key];if(a.kind==="enum"){let s=a.options.some(l=>l.value===i);o[a.key]=s?i:a.default;continue}let r=typeof i=="number"?i:typeof i=="string"&&i.trim()!==""?Number(i):Number.NaN;o[a.key]=Number.isFinite(r)?Math.min(a.max,Math.max(a.min,r)):a.default}return o}var y=class extends Error{constructor(t){super(t),this.name="AudioFxChainError"}};function j(e){let t;try{t=JSON.parse(e)}catch(a){throw new y(`Chain file is not valid JSON: ${a.message}`)}if(typeof t!="object"||t===null)throw new y("Chain file must be a JSON object.");let n=t;if(n.version!==U)throw new y(`Unsupported chain version: ${String(n.version)}`);if(!Array.isArray(n.nodes))throw new y("Chain file is missing a `nodes` array.");let o=n.nodes.map((a,i)=>{if(typeof a!="object"||a===null)throw new y(`Node ${i} is not an object.`);let r=a;if(typeof r.type!="string"||!O.has(r.type))throw new y(`Node ${i} has unknown effect type: ${String(r.type)}`);return{type:r.type,...typeof r.id=="string"&&r.id?{id:r.id}:{},...r.fromCarve===!0?{fromCarve:!0}:{},...typeof r.fromPreset=="string"&&r.fromPreset?{fromPreset:r.fromPreset}:{},...typeof r.label=="string"&&r.label?{label:r.label}:{},...typeof r.fromEq=="string"&&r.fromEq?{fromEq:r.fromEq}:{},...r.fromLeveller===!0?{fromLeveller:!0}:{},...typeof r.presetAmount=="number"&&Number.isFinite(r.presetAmount)?{presetAmount:Math.min(1,Math.max(0,r.presetAmount))}:{},enabled:r.enabled!==!1,params:A(r.type,r.params??void 0)}});return{version:U,nodes:o}}function M(e){return e.nodes.filter(t=>t.enabled!==!1)}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 \\u2014 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.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 if (this.held[ch] === undefined) this.held[ch] = 0;\n if (this.holds[ch] === undefined) this.holds[ch] = 0;\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`,B=new WeakMap;var fe=new WeakSet;function D(e){let t=B.get(e);return t||(t=(async()=>{if(!e.audioWorklet)throw new Error("AudioWorklet is unavailable \\u2014 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),fe.add(e)})().catch(n=>{throw B.delete(e),n}),B.set(e,t)),t}function he(e,t,n){let o=.6+Math.max(0,Math.min(1,t))*2.6,a=Math.max(1,Math.floor(e*o)),i=new Float32Array(a),r=Math.round(t*1e3)*2654435761+Math.round(n*1e3)*40503>>>0,s=()=>(r=r*1664525+1013904223>>>0,r/4294967295*2-1),l=Math.max(.001,1-Math.max(0,Math.min(1,n))),u=0,c=0;for(let d=0;d<a;d++){u+=l*(s()-u);let b=u*Math.pow(1-d/a,2.5);i[d]=b,c+=b*b}let m=Math.sqrt(c);if(m>0)for(let d=0;d<a;d++)i[d]=(i[d]??0)/m;return i}var h=e=>typeof e=="number"?e:Number(e??0),E=e=>e/1e3;function K(e,t,n,o){let a=Math.max(1,Math.round(e.sampleRate)),i=e.createBuffer(1,a,e.sampleRate),r=i.getChannelData(0);for(let u=0;u<a;u++){let c=u/a;r[u]=t==="sine"?Math.sin(2*Math.PI*c):4*Math.abs((c+.75)%1-.5)-1}let s=e.createBufferSource();s.buffer=i,s.loop=!0,s.playbackRate.value=n;let l=(o*n%1+1)%1*(a/e.sampleRate);return s.start(typeof e.currentTime=="number"?e.currentTime:0,l),s}function Q(e){try{e.stop()}catch{}e.disconnect()}function L(e,t){return[{param:e},{param:t,map:n=>1-n}]}function Z(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 be=new Set(["peaking","highpass","lowpass"]),z=e=>Math.pow(10,e/20),ge=(e,t)=>{let n=e.createGain(),o=a=>{n.gain.value=z(h(a.gain))};return o(t),q(n,o,{gain:[{param:n.gain,map:z}]})};function P(e,t){return(n,o)=>{let a=n.createBiquadFilter();a.type=e;let i=r=>{a.frequency.value=h(r.frequency),r.q!==void 0&&(a.Q.value=h(r.q)),t&&(a.gain.value=h(r.gain))};return i(o),q(a,i,{frequency:[{param:a.frequency}],...be.has(e)?{q:[{param:a.Q}]}:{},...t?{gain:[{param:a.gain}]}:{}})}}function xe(e){return(t,n)=>{let o=Math.tan(Math.PI*h(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 _(e){return(t,n)=>{let o=new AudioWorkletNode(t,e,{processorOptions:{...n}});return{input:o,output:o,update:a=>o.port.postMessage({...a}),dispose:()=>{o.port.postMessage({__hfDispose:!0}),o.disconnect()}}}}var ye={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))},Ae=(e,t)=>{let n=e.createWaveShaper(),o=e.createGain(),a=e.createGain();o.connect(n).connect(a);let i=r=>{let s=ye[String(r.type)]??Math.tanh,l=Math.pow(10,h(r.threshold)/20),u=8192,c=new Float32Array(u);for(let m=0;m<u;m++){let d=m/(u-1)*2-1;c[m]=s(d/Math.max(1e-6,l))*l}n.curve=c,n.oversample=h(r.oversample)>=4?"4x":h(r.oversample)>=2?"2x":"none",a.gain.value=Math.pow(10,h(r.output)/20)};return i(t),{input:o,output:a,update:i,automation:{output:[{param:a.gain,map:r=>Math.pow(10,r/20)}]},dispose:()=>{o.disconnect(),n.disconnect(),a.disconnect()}}},ke=(e,t)=>{let n=e.createGain(),o=e.createGain(),a=e.createDelay(5),i=e.createGain(),r=e.createGain(),s=e.createGain();n.connect(a),a.connect(i),i.connect(a),a.connect(r).connect(o),n.connect(s).connect(o);let l=u=>{a.delayTime.value=Math.min(5,h(u.time)/1e3),i.gain.value=h(u.feedback),Z(r,s,h(u.mix))};return l(t),{input:n,output:o,update:l,automation:{time:[{param:a.delayTime,map:u=>Math.min(5,E(u))}],feedback:[{param:i.gain}],mix:L(r.gain,s.gain)},dispose:()=>[n,o,a,i,r,s].forEach(u=>u.disconnect())}},ve=(e,t,n)=>{let o=e.createGain(),a=e.createGain(),i=e.createDelay(.5),r=K(e,"sine",h(t.speed),n),s=e.createGain(),l=e.createGain(),u=e.createGain();r.connect(s).connect(i.delayTime),o.connect(i).connect(l).connect(a),o.connect(u).connect(a);let c=m=>{i.delayTime.value=h(m.delay)/1e3,s.gain.value=h(m.depth)/1e3,r.playbackRate.value=h(m.speed),Z(l,u,h(m.mix))};return c(t),{input:o,output:a,update:c,automation:{delay:[{param:i.delayTime,map:E}],depth:[{param:s.gain,map:E}],speed:[{param:r.playbackRate}],mix:L(l.gain,u.gain)},dispose:()=>{Q(r),[o,a,i,s,l,u].forEach(m=>m.disconnect())}}},we=6,Fe=(e,t,n)=>{let o=e.createGain(),a=e.createGain(),i=e.createGain(),r=e.createGain(),s=K(e,String(t.type)==="1"?"sine":"triangle",h(t.speed),n),l=e.createGain(),u=e.createGain(),c=e.createGain(),m=[];o.connect(i);let d=i;for(let p=0;p<we;p++){let f=e.createBiquadFilter();f.type="allpass",f.Q.value=.7071,l.connect(f.frequency),d.connect(f),d=f,m.push(f)}s.connect(l),d.connect(u).connect(r),i.connect(c).connect(r),r.connect(a);let b=p=>{let f=1e3/Math.max(.1,h(p.delay));for(let g of m)g.frequency.value=f;l.gain.value=f*h(p.decay),s.playbackRate.value=h(p.speed),i.gain.value=h(p.in_gain),r.gain.value=h(p.out_gain),u.gain.value=1,c.gain.value=1};return b(t),{input:o,output:a,update:b,automation:{speed:[{param:s.playbackRate}],in_gain:[{param:i.gain}],out_gain:[{param:r.gain}]},dispose:()=>{Q(s),[o,a,i,r,l,u,c,...m].forEach(p=>p.disconnect())}}},He=(e,t)=>{let n=e.createGain(),o=e.createGain(),a=e.createConvolver(),i=e.createGain(),r=e.createGain();a.normalize=!1,n.connect(a).connect(i).connect(o),n.connect(r).connect(o);let s="",l=u=>{let c=`${h(u.size)}:${h(u.damping)}`;if(c!==s){let m=he(e.sampleRate,h(u.size),h(u.damping)),d=e.createBuffer(1,m.length,e.sampleRate);d.getChannelData(0).set(m),a.buffer=d,s=c}i.gain.value=h(u.wet),r.gain.value=h(u.dry)};return l(t),{input:n,output:o,update:l,automation:{wet:[{param:i.gain}],dry:[{param:r.gain}]},dispose:()=>[n,o,a,i,r].forEach(u=>u.disconnect())}},Me={"gain-node":ge,"biquad-peaking":P("peaking",!0),"biquad-lowshelf":P("lowshelf",!0),"biquad-highshelf":P("highshelf",!0),"biquad-highpass":P("highpass",!1),"biquad-lowpass":P("lowpass",!1),"worklet-compressor":_("hf-compressor"),"worklet-limiter":_("hf-limiter"),"worklet-gate":_("hf-gate"),"worklet-bitcrush":_("hf-bitcrush"),waveshaper:Ae,"delay-feedback":ke,"chorus-lfo":ve,"allpass-phaser":Fe,convolver:He};function ee(e){return e.nodes.some(t=>H(t.type)?.web.startsWith("worklet-")??!1)}function Pe(e,t,n,o=0){let a=H(t);if(!a)throw new Error(`Unknown effect type: ${t}`);let i=A(t,n);if((t==="highpass"||t==="lowpass")&&String(i.poles)==="1")return xe(t)(e,i,o);let r=Me[a.web];if(!r)throw new Error(`No Web Audio builder for ${a.web}`);return r(e,i,o)}function Y(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 i=typeof n.presetAmount=="number"?n.presetAmount:1;t.push({...o?{preset:o}:{},amount:Math.min(1,Math.max(0,i)),nodes:[n]})}}return t}function J(e){return M(e).map(t=>{let n=A(t.type,t.params),o=n.poles!==void 0?`:${n.poles}`:"",a=String(n.poles)==="1"?`@${n.frequency}`:"",i=t.type==="phaser"?`~${n.type}`:"",r=t.fromPreset?`%${t.fromPreset}`:"";return`${t.type}${o}${a}${i}${r}`}).join("|")}function te(e,t,n=0){var m;let o=e.createGain(),a=e.createGain(),i=[],r=[],s=Y(M(t)),l=o;for(let d of s){let b=null;if(d.preset){let p=e.createGain(),f=e.createGain(),g=e.createGain(),w=e.createGain();g.gain.value=d.amount,f.gain.value=1-d.amount,l.connect(p),p.connect(f).connect(w),b={entry:p,wet:g,dry:f,join:w},l=p}for(let p of d.nodes){let f=Pe(e,p.type,p.params??{},n);l.connect(f.input),l=f.output,i.push({...p.id?{id:p.id}:{},type:p.type,handle:f})}b&&d.preset&&(l.connect(b.wet).connect(b.join),r.push({id:d.preset,...b}),l=b.join)}l.connect(a);let u=J(t),c={};for(let d of r)(c[m=d.id]??(c[m]=[])).push(...L(d.wet.gain,d.dry.gain));return{input:o,output:a,presets:c,nodes:i,update(d){if(J(d)!==u)return!1;M(d).forEach((p,f)=>{let g=i[f];g&&(g.handle.update(A(p.type,p.params)),p.id===void 0?delete g.id:g.id=p.id)});let b=0;for(let p of Y(M(d))){if(!p.preset)continue;let f=r[b++];!f||f.id!==p.preset||(f.wet.gain.value=p.amount,f.dry.gain.value=1-p.amount)}return!0},dispose(){for(let{handle:d}of i)d.dispose();for(let{entry:d,wet:b,dry:p,join:f}of r)d.disconnect(),b.disconnect(),p.disconnect(),f.disconnect();o.disconnect(),a.disconnect()}}}var Te="data-automation",ft=Te.slice(5),R=1,Ne=512,x=class extends Error{constructor(t){super(t),this.name="AudioAutomationError"}},G="volume";function N(e){if(e===G)return{kind:"volume"};let t=e.split(".");if(t.length===3&&t[0]==="fx"&&t[1]===Ce){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 ne(e,t){return`fx.${e}.${t}`}var Ce="preset";var _e={min:0,max:1,step:.01,unit:"",label:"Amount",scale:"linear",default:1},ae={min:0,max:1,step:.01,unit:"",label:"Volume",scale:"linear",default:1};function I(e,t){let n=N(e);if(!n)return null;if(n.kind==="volume")return ae;if(n.kind==="preset")return t?.nodes.some(s=>s.fromPreset===n.presetId)?{..._e,label:`${n.presetId} \\xB7 Amount`}:null;let o=t?.nodes.find(r=>r.id===n.nodeId);if(!o)return null;let a=H(o.type),i=a?.params.find(r=>r.key===n.param);return!i||i.kind!=="number"?null:{min:i.min,max:i.max,step:i.step,unit:i.unit,label:`${a?.label??o.type} \\xB7 ${i.label}`,scale:i.scale==="log"&&i.min>0?"log":"linear",default:i.default}}function T(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 Re(e){let t=T(e);return t===null||t===0?0:Math.min(1,Math.max(-1,t))}function oe(e,t){let n=T(e),o=T(t);if(n===null||o===null)return null;let a=s=>Math.min(.999,Math.max(.001,s)),i=a(n),r=a(o);return Math.abs(i-r)<1e-6?null:{x:i,y:r}}function Se(e){let t=Re(e?.curve),n=oe(e?.viaX,e?.viaY);return{...t?{curve:t}:{},...n?{viaX:n.x,viaY:n.y}:{}}}function Oe(e,t){let n=T(e?.t),o=T(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,...Se(e)}}function re(e,t){let n=e.map(a=>Oe(a,t)).filter(a=>a!==null).sort((a,i)=>a.t-i.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,Ne)}function Be(e){let t=[];for(let n of e.lanes){if(!N(n.target))continue;let o=n.target===G?ae:null,a=re(n.points??[],o);a.length>0&&t.push({target:n.target,points:a})}return{version:R,lanes:t}}function ie(e,t){let n=[];for(let o of e.lanes){let a=I(o.target,t);if(!a)continue;let i=re(o.points,a);i.length>0&&n.push({target:o.target,points:i})}return{version:R,lanes:n}}function se(e){let t;try{t=JSON.parse(e)}catch(a){throw new x(`Automation is not valid JSON: ${a.message}`)}if(typeof t!="object"||t===null)throw new x("Automation must be a JSON object.");let n=t;if(n.version!==R)throw new x(`Unsupported automation version: ${String(n.version)}`);if(!Array.isArray(n.lanes))throw new x("Automation is missing a `lanes` array.");let o=n.lanes.map((a,i)=>{if(typeof a!="object"||a===null)throw new x(`Lane ${i} is not an object.`);let r=a;if(typeof r.target!="string"||!N(r.target))throw new x(`Lane ${i} has an unreadable target: ${String(r.target)}`);if(!Array.isArray(r.points))throw new x(`Lane ${i} is missing a \\`points\\` array.`);return{target:r.target,points:r.points}});return Be({version:R,lanes:o})}function De(e,t){return t?Math.pow(e,Math.pow(2,2*t)):e}function Ee(e,t){let n=e-.5,o=t-.5,a=.999,i=1e6,r=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,l=Math.min(i,Math.max(1,r,s));return{cx:e+n/l,cy:t+o/l,w:l}}function Le(e,t,n){let{cx:o,cy:a,w:i}=Ee(t,n),r=2-2*i,s=e*r-1+2*i*o,l=-e*r-2*i*o,u=qe(s,l,e),c=1-u,m=c*c+2*i*u*c+u*u;return m>0?(2*i*a*u*c+u*u)/m:e}function qe(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),i=(-t-o)/(2*e),r=s=>s>=-1e-9&&s<=1+1e-9;return r(a)?Math.min(1,Math.max(0,a)):r(i)?Math.min(1,Math.max(0,i)):n}function Ge(e,t){let n=oe(t.viaX,t.viaY);return n?Le(Math.min(1,Math.max(0,e)),n.x,n.y):De(e,t.curve)}function Ie(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 V(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 i=o[o.length-1];if(t>=i.t)return i.v;let r=0,s=o.length-1;for(;s-r>1;){let m=r+s>>1;o[m].t<=t?r=m:s=m}let l=o[r],u=o[s],c=u.t-l.t;return c<=0?u.v:Ie(l.v,u.v,Ge((t-l.t)/c,l),n)}function ue(e){return e.points.length<=1||e.points.every(t=>t.v===e.points[0].v)}function le(e,t,n,o,a="linear"){let i=Math.max(2,Math.floor(o)),r=new Float32Array(i),s=n-t;for(let l=0;l<i;l+=1)r[l]=V(e,t+s*l/(i-1),a);return r}var Ve=100,$e=8,Ue=4096,$=e=>e;function We(e,t,n){return!(!!e.curve||e.viaX!==void 0&&e.viaY!==void 0)&&t==="linear"&&!n}function Xe(e){let t=Math.ceil(e*Ve);return Math.min(Ue,Math.max($e,t))}function je(e,t,n,o,a){let{scheduledAt:i,elapsed:r,rate:s}=o,l=a??$,u=v=>i+(v-r)/s,c=e.points[t],m=e.points[t+1],d=u(m.t);if(d<=i)return null;let b=Math.max(i,u(c.t)),p=d-b;if(p<=0)return null;if(We(c,n,a))return{kind:"ramp",time:d,value:l(m.v)};let f=Xe(p),g=le(e,Math.max(c.t,r),m.t,f,n),w=new Float32Array(f);for(let v=0;v<f;v+=1)w[v]=l(g[v]??0);return{kind:"curve",time:b,duration:p,values:w}}function ze(e,t,n,o){let a=[];for(let r=0;r+1<e.points.length;r+=1){let s=je(e,r,t,n,o);s&&a.push(s)}let i=a[0];return i?.kind==="curve"&&i.time<=n.scheduledAt||a.unshift({kind:"set",time:n.scheduledAt,value:(o??$)(V(e,n.elapsed,t))}),a}function Ye(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 Je(e){for(let t of e)t.param.cancelScheduledValues(0)}function Ke(e,t,n,o){if(t.points.length!==0)for(let a of e){if(Je([a]),ue(t)){let i=(a.map??$)(t.points[0].v);a.param.setValueAtTime(i,o.scheduledAt);continue}Ye(a.param,ze(t,n,o,a.map))}}function ce(e,t,n,o,a){let i=new Map(n.filter(s=>s.id).map(s=>[s.id,s.handle])),r=[];for(let s of e.lanes){let l=N(s.target);if(!l)continue;let u=l.kind==="preset"?a?.[l.presetId]:l.kind==="fx"?i.get(l.nodeId)?.automation?.[l.param]:void 0;if(!u||u.length===0)continue;let c=I(s.target,t);c&&(Ke(u,s,c.scale,o),r.push(...u))}return r}var Qe=5,Ze=.001;function k(e,t,n){let o=Number(A(e.type,e.params)[t]??0);if(!e.id||!n)return Number.isFinite(o)?o:0;let a=n.lanes.find(i=>i.target===ne(e.id??"",t));return!a||a.points.length===0?Number.isFinite(o)?o:0:a.points.reduce((i,r)=>Math.max(i,r.v),-1/0)}function et(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(Ze)/Math.log(o))*n}function tt(e,t){if(e.enabled===!1)return 0;switch(e.type){case"reverb":return k(e,"wet",t)>0?.6+Math.max(0,Math.min(1,k(e,"size",t)))*2.6:0;case"delay":return k(e,"mix",t)>0?et(k(e,"time",t),k(e,"feedback",t)):0;case"chorus":return k(e,"mix",t)>0?k(e,"delay",t)/1e3:0;default:return 0}}function de(e,t){let n=e.nodes.reduce((o,a)=>o+tt(a,t),0);return Math.min(Qe,n)}function nt(e,t,n,o,a){let i=e.createBuffer(n,o,a);for(let r=0;r<n;r++){let s=t[r];s&&i.getChannelData(r).set(s)}return i}function at(e){let t=[];for(let n=0;n<e.numberOfChannels;n++)t.push(new Float32Array(e.getChannelData(n)));return t}async function ot(e,t,n,o){let a=j(n),i=Math.max(1,e.length),r=e[0]?.length??0;if(r===0)return e;let s=o?ie(se(o),a):null,l=Math.ceil(de(a,s??void 0)*t),u=new OfflineAudioContext(i,r+l,t);ee(a)&&await D(u);let c=u.createBufferSource();c.buffer=nt(u,e,i,r,t);let m=te(u,a);return s&&ce(s,a,m.nodes,{scheduledAt:0,elapsed:0,rate:1},m.presets),c.connect(m.input),m.output.connect(u.destination),c.start(),at(await u.startRendering())}window.__HF_AUDIO_FX={render:ot};})();\n';
|
|
2
|
+
const AUDIO_FX_RUNTIME_IIFE = '"use strict";(()=>{var pe="data-fx-chain",it=pe.slice(5),$=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}),R=(e=.707,t="Bandwidth \\u2014 higher is narrower.")=>({kind:"number",key:"q",label:"Q",unit:"",min:.1,max:20,step:.01,default:e,scale:"log",automatable:!0,hint:t}),_=(e=-40,t=40,n=0)=>({kind:"number",key:"gain",label:"Gain",unit:"dB",min:e,max:t,step:.1,default:n,automatable:!0}),X={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."},W=[{id:"gain",label:"Gain",group:"dynamics",description:"Raise or lower the whole signal. Automate it to duck under something else.",params:[_(-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),_(-40,40,0),R(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),_(-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),_(-40,40,0)],web:"biquad-highshelf"},{id:"highpass",label:"High-pass",group:"filter",description:"Remove low frequencies \\u2014 the usual fix for rumble on a voice.",params:[F("frequency","Cutoff",300,20,2e4),R(.707),X],web:"biquad-highpass"},{id:"lowpass",label:"Low-pass",group:"filter",description:"Remove high frequencies \\u2014 darkens or muffles a track.",params:[F("frequency","Cutoff",8e3,100,2e4),R(.707),X],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:"Hard ceiling \\u2014 nothing gets past the 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:"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 \\u2014 a crude downsample."},{kind:"number",key:"mix",label:"Mix",unit:"",min:0,max:1,step:.01,default:1}],web:"worklet-bitcrush"},{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"}],O=new Map(W.map(e=>[e.id,e]));function M(e){return O.get(e)}var st=W.map(e=>e.id);function y(e,t){let n=O.get(e);if(!n)return{};let o={};for(let a of n.params){let i=t?.[a.key];if(a.kind==="enum"){let s=a.options.some(l=>l.value===i);o[a.key]=s?i:a.default;continue}let r=typeof i=="number"?i:typeof i=="string"&&i.trim()!==""?Number(i):Number.NaN;o[a.key]=Number.isFinite(r)?Math.min(a.max,Math.max(a.min,r)):a.default}return o}var A=class extends Error{constructor(t){super(t),this.name="AudioFxChainError"}};function j(e){let t;try{t=JSON.parse(e)}catch(a){throw new A(`Chain file is not valid JSON: ${a.message}`)}if(typeof t!="object"||t===null)throw new A("Chain file must be a JSON object.");let n=t;if(n.version!==$)throw new A(`Unsupported chain version: ${String(n.version)}`);if(!Array.isArray(n.nodes))throw new A("Chain file is missing a `nodes` array.");let o=n.nodes.map((a,i)=>{if(typeof a!="object"||a===null)throw new A(`Node ${i} is not an object.`);let r=a;if(typeof r.type!="string"||!O.has(r.type))throw new A(`Node ${i} has unknown effect type: ${String(r.type)}`);return{type:r.type,...typeof r.id=="string"&&r.id?{id:r.id}:{},...r.fromCarve===!0?{fromCarve:!0}:{},...typeof r.fromPreset=="string"&&r.fromPreset?{fromPreset:r.fromPreset}:{},...typeof r.label=="string"&&r.label?{label:r.label}:{},...typeof r.fromEq=="string"&&r.fromEq?{fromEq:r.fromEq}:{},...r.fromLeveller===!0?{fromLeveller:!0}:{},...typeof r.presetAmount=="number"&&Number.isFinite(r.presetAmount)?{presetAmount:Math.min(1,Math.max(0,r.presetAmount))}:{},enabled:r.enabled!==!1,params:y(r.type,r.params??void 0)}});return{version:$,nodes:o}}function H(e){return e.nodes.filter(t=>t.enabled!==!1)}var fe=`\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 \\u2014 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.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 if (this.held[ch] === undefined) this.held[ch] = 0;\n if (this.holds[ch] === undefined) this.holds[ch] = 0;\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`,G=new WeakMap;var he=new WeakSet;function I(e){let t=G.get(e);return t||(t=(async()=>{if(!e.audioWorklet)throw new Error("AudioWorklet is unavailable \\u2014 the page needs a secure context (https, localhost or file://)");let n=`data:text/javascript;base64,${btoa(String.fromCharCode(...new TextEncoder().encode(fe)))}`;await e.audioWorklet.addModule(n),he.add(e)})().catch(n=>{throw G.delete(e),n}),G.set(e,t)),t}function be(e,t,n){let o=.6+Math.max(0,Math.min(1,t))*2.6,a=Math.max(1,Math.floor(e*o)),i=new Float32Array(a),r=Math.round(t*1e3)*2654435761+Math.round(n*1e3)*40503>>>0,s=()=>(r=r*1664525+1013904223>>>0,r/4294967295*2-1),l=Math.max(.001,1-Math.max(0,Math.min(1,n))),u=0,c=0;for(let d=0;d<a;d++){u+=l*(s()-u);let b=u*Math.pow(1-d/a,2.5);i[d]=b,c+=b*b}let m=Math.sqrt(c);if(m>0)for(let d=0;d<a;d++)i[d]=(i[d]??0)/m;return i}var h=e=>typeof e=="number"?e:Number(e??0),B=e=>e/1e3;function K(e,t,n,o){let a=Math.max(1,Math.round(e.sampleRate)),i=e.createBuffer(1,a,e.sampleRate),r=i.getChannelData(0);for(let u=0;u<a;u++){let c=u/a;r[u]=t==="sine"?Math.sin(2*Math.PI*c):4*Math.abs((c+.75)%1-.5)-1}let s=e.createBufferSource();s.buffer=i,s.loop=!0,s.playbackRate.value=n;let l=(o*n%1+1)%1*(a/e.sampleRate);return s.start(typeof e.currentTime=="number"?e.currentTime:0,l),s}function Q(e){try{e.stop()}catch{}e.disconnect()}function E(e,t){return[{param:e},{param:t,map:n=>1-n}]}function Z(e,t,n){e.gain.value=n,t.gain.value=1-n}function S(e,t,n){return{input:e,output:e,update:t,automation:n,dispose:()=>e.disconnect()}}var ge=new Set(["peaking","highpass","lowpass"]),z=e=>Math.pow(10,e/20),xe=(e,t)=>{let n=e.createGain(),o=a=>{n.gain.value=z(h(a.gain))};return o(t),S(n,o,{gain:[{param:n.gain,map:z}]})};function P(e,t){return(n,o)=>{let a=n.createBiquadFilter();a.type=e;let i=r=>{a.frequency.value=h(r.frequency),r.q!==void 0&&(a.Q.value=h(r.q)),t&&(a.gain.value=h(r.gain))};return i(o),S(a,i,{frequency:[{param:a.frequency}],...ge.has(e)?{q:[{param:a.Q}]}:{},...t?{gain:[{param:a.gain}]}:{}})}}function Ae(e){return(t,n)=>{let o=Math.tan(Math.PI*h(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 S(a,()=>{})}}function C(e){return(t,n)=>{let o=new AudioWorkletNode(t,e,{processorOptions:{...n}});return{input:o,output:o,update:a=>o.port.postMessage({...a}),dispose:()=>{o.port.postMessage({__hfDispose:!0}),o.disconnect()}}}}var ye={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))},ke=(e,t)=>{let n=e.createWaveShaper(),o=e.createGain(),a=e.createGain();o.connect(n).connect(a);let i=r=>{let s=ye[String(r.type)]??Math.tanh,l=Math.pow(10,h(r.threshold)/20),u=8192,c=new Float32Array(u);for(let m=0;m<u;m++){let d=m/(u-1)*2-1;c[m]=s(d/Math.max(1e-6,l))*l}n.curve=c,n.oversample=h(r.oversample)>=4?"4x":h(r.oversample)>=2?"2x":"none",a.gain.value=Math.pow(10,h(r.output)/20)};return i(t),{input:o,output:a,update:i,automation:{output:[{param:a.gain,map:r=>Math.pow(10,r/20)}]},dispose:()=>{o.disconnect(),n.disconnect(),a.disconnect()}}},ve=(e,t)=>{let n=e.createGain(),o=e.createGain(),a=e.createDelay(5),i=e.createGain(),r=e.createGain(),s=e.createGain();n.connect(a),a.connect(i),i.connect(a),a.connect(r).connect(o),n.connect(s).connect(o);let l=u=>{a.delayTime.value=Math.min(5,h(u.time)/1e3),i.gain.value=h(u.feedback),Z(r,s,h(u.mix))};return l(t),{input:n,output:o,update:l,automation:{time:[{param:a.delayTime,map:u=>Math.min(5,B(u))}],feedback:[{param:i.gain}],mix:E(r.gain,s.gain)},dispose:()=>[n,o,a,i,r,s].forEach(u=>u.disconnect())}},we=(e,t,n)=>{let o=e.createGain(),a=e.createGain(),i=e.createDelay(.5),r=K(e,"sine",h(t.speed),n),s=e.createGain(),l=e.createGain(),u=e.createGain();r.connect(s).connect(i.delayTime),o.connect(i).connect(l).connect(a),o.connect(u).connect(a);let c=m=>{i.delayTime.value=h(m.delay)/1e3,s.gain.value=h(m.depth)/1e3,r.playbackRate.value=h(m.speed),Z(l,u,h(m.mix))};return c(t),{input:o,output:a,update:c,automation:{delay:[{param:i.delayTime,map:B}],depth:[{param:s.gain,map:B}],speed:[{param:r.playbackRate}],mix:E(l.gain,u.gain)},dispose:()=>{Q(r),[o,a,i,s,l,u].forEach(m=>m.disconnect())}}},Fe=6,Me=(e,t,n)=>{let o=e.createGain(),a=e.createGain(),i=e.createGain(),r=e.createGain(),s=K(e,String(t.type)==="1"?"sine":"triangle",h(t.speed),n),l=e.createGain(),u=e.createGain(),c=e.createGain(),m=[];o.connect(i);let d=i;for(let p=0;p<Fe;p++){let f=e.createBiquadFilter();f.type="allpass",f.Q.value=.7071,l.connect(f.frequency),d.connect(f),d=f,m.push(f)}s.connect(l),d.connect(u).connect(r),i.connect(c).connect(r),r.connect(a);let b=p=>{let f=1e3/Math.max(.1,h(p.delay));for(let g of m)g.frequency.value=f;l.gain.value=f*h(p.decay),s.playbackRate.value=h(p.speed),i.gain.value=h(p.in_gain),r.gain.value=h(p.out_gain),u.gain.value=1,c.gain.value=1};return b(t),{input:o,output:a,update:b,automation:{speed:[{param:s.playbackRate}],in_gain:[{param:i.gain}],out_gain:[{param:r.gain}]},dispose:()=>{Q(s),[o,a,i,r,l,u,c,...m].forEach(p=>p.disconnect())}}},He=(e,t)=>{let n=e.createGain(),o=e.createGain(),a=e.createConvolver(),i=e.createGain(),r=e.createGain();a.normalize=!1,n.connect(a).connect(i).connect(o),n.connect(r).connect(o);let s="",l=u=>{let c=`${h(u.size)}:${h(u.damping)}`;if(c!==s){let m=be(e.sampleRate,h(u.size),h(u.damping)),d=e.createBuffer(1,m.length,e.sampleRate);d.getChannelData(0).set(m),a.buffer=d,s=c}i.gain.value=h(u.wet),r.gain.value=h(u.dry)};return l(t),{input:n,output:o,update:l,automation:{wet:[{param:i.gain}],dry:[{param:r.gain}]},dispose:()=>[n,o,a,i,r].forEach(u=>u.disconnect())}},Pe={"gain-node":xe,"biquad-peaking":P("peaking",!0),"biquad-lowshelf":P("lowshelf",!0),"biquad-highshelf":P("highshelf",!0),"biquad-highpass":P("highpass",!1),"biquad-lowpass":P("lowpass",!1),"worklet-compressor":C("hf-compressor"),"worklet-limiter":C("hf-limiter"),"worklet-gate":C("hf-gate"),"worklet-bitcrush":C("hf-bitcrush"),waveshaper:ke,"delay-feedback":ve,"chorus-lfo":we,"allpass-phaser":Me,convolver:He};function ee(e){return e.nodes.some(t=>M(t.type)?.web.startsWith("worklet-")??!1)}function Te(e,t,n,o=0){let a=M(t);if(!a)throw new Error(`Unknown effect type: ${t}`);let i=y(t,n);if((t==="highpass"||t==="lowpass")&&String(i.poles)==="1")return Ae(t)(e,i,o);let r=Pe[a.web];if(!r)throw new Error(`No Web Audio builder for ${a.web}`);return r(e,i,o)}function Y(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 i=typeof n.presetAmount=="number"?n.presetAmount:1;t.push({...o?{preset:o}:{},amount:Math.min(1,Math.max(0,i)),nodes:[n]})}}return t}function J(e){return H(e).map(t=>{let n=y(t.type,t.params),o=n.poles!==void 0?`:${n.poles}`:"",a=String(n.poles)==="1"?`@${n.frequency}`:"",i=t.type==="phaser"?`~${n.type}`:"",r=t.fromPreset?`%${t.fromPreset}`:"";return`${t.type}${o}${a}${i}${r}`}).join("|")}function te(e,t,n=0){var m;let o=e.createGain(),a=e.createGain(),i=[],r=[],s=Y(H(t)),l=o;for(let d of s){let b=null;if(d.preset){let p=e.createGain(),f=e.createGain(),g=e.createGain(),w=e.createGain();g.gain.value=d.amount,f.gain.value=1-d.amount,l.connect(p),p.connect(f).connect(w),b={entry:p,wet:g,dry:f,join:w},l=p}for(let p of d.nodes){let f=Te(e,p.type,p.params??{},n);l.connect(f.input),l=f.output,i.push({...p.id?{id:p.id}:{},type:p.type,handle:f})}b&&d.preset&&(l.connect(b.wet).connect(b.join),r.push({id:d.preset,...b}),l=b.join)}l.connect(a);let u=J(t),c={};for(let d of r)(c[m=d.id]??(c[m]=[])).push(...E(d.wet.gain,d.dry.gain));return{input:o,output:a,presets:c,nodes:i,update(d){if(J(d)!==u)return!1;H(d).forEach((p,f)=>{let g=i[f];g&&(g.handle.update(y(p.type,p.params)),p.id===void 0?delete g.id:g.id=p.id)});let b=0;for(let p of Y(H(d))){if(!p.preset)continue;let f=r[b++];!f||f.id!==p.preset||(f.wet.gain.value=p.amount,f.dry.gain.value=1-p.amount)}return!0},dispose(){for(let{handle:d}of i)d.dispose();for(let{entry:d,wet:b,dry:p,join:f}of r)d.disconnect(),b.disconnect(),p.disconnect(),f.disconnect();o.disconnect(),a.disconnect()}}}var ne=3.981071705534972;var Ne="data-automation",gt=Ne.slice(5),D=1,_e=512,x=class extends Error{constructor(t){super(t),this.name="AudioAutomationError"}},L="volume";function N(e){if(e===L)return{kind:"volume"};let t=e.split(".");if(t.length===3&&t[0]==="fx"&&t[1]===Ce){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 ae(e,t){return`fx.${e}.${t}`}var Ce="preset";var De={min:0,max:1,step:.01,unit:"",label:"Amount",scale:"linear",default:1},oe={min:0,max:ne,step:.01,unit:"",label:"Volume",scale:"linear",default:1};function q(e,t){let n=N(e);if(!n)return null;if(n.kind==="volume")return oe;if(n.kind==="preset")return t?.nodes.some(s=>s.fromPreset===n.presetId)?{...De,label:`${n.presetId} \\xB7 Amount`}:null;let o=t?.nodes.find(r=>r.id===n.nodeId);if(!o)return null;let a=M(o.type),i=a?.params.find(r=>r.key===n.param);return!i||i.kind!=="number"?null:{min:i.min,max:i.max,step:i.step,unit:i.unit,label:`${a?.label??o.type} \\xB7 ${i.label}`,scale:i.scale==="log"&&i.min>0?"log":"linear",default:i.default}}function T(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 Re(e){let t=T(e);return t===null||t===0?0:Math.min(1,Math.max(-1,t))}function re(e,t){let n=T(e),o=T(t);if(n===null||o===null)return null;let a=s=>Math.min(.999,Math.max(.001,s)),i=a(n),r=a(o);return Math.abs(i-r)<1e-6?null:{x:i,y:r}}function Oe(e){let t=Re(e?.curve),n=re(e?.viaX,e?.viaY);return{...t?{curve:t}:{},...n?{viaX:n.x,viaY:n.y}:{}}}function Ge(e,t){let n=T(e?.t),o=T(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,...Oe(e)}}function ie(e,t){let n=e.map(a=>Ge(a,t)).filter(a=>a!==null).sort((a,i)=>a.t-i.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 Ie(e){let t=[];for(let n of e.lanes){if(!N(n.target))continue;let o=n.target===L?oe:null,a=ie(n.points??[],o);a.length>0&&t.push({target:n.target,points:a})}return{version:D,lanes:t}}function se(e,t){let n=[];for(let o of e.lanes){let a=q(o.target,t);if(!a)continue;let i=ie(o.points,a);i.length>0&&n.push({target:o.target,points:i})}return{version:D,lanes:n}}function ue(e){let t;try{t=JSON.parse(e)}catch(a){throw new x(`Automation is not valid JSON: ${a.message}`)}if(typeof t!="object"||t===null)throw new x("Automation must be a JSON object.");let n=t;if(n.version!==D)throw new x(`Unsupported automation version: ${String(n.version)}`);if(!Array.isArray(n.lanes))throw new x("Automation is missing a `lanes` array.");let o=n.lanes.map((a,i)=>{if(typeof a!="object"||a===null)throw new x(`Lane ${i} is not an object.`);let r=a;if(typeof r.target!="string"||!N(r.target))throw new x(`Lane ${i} has an unreadable target: ${String(r.target)}`);if(!Array.isArray(r.points))throw new x(`Lane ${i} is missing a \\`points\\` array.`);return{target:r.target,points:r.points}});return Ie({version:D,lanes:o})}function Be(e,t){return t?Math.pow(e,Math.pow(2,2*t)):e}function Ee(e,t){let n=e-.5,o=t-.5,a=.999,i=1e6,r=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,l=Math.min(i,Math.max(1,r,s));return{cx:e+n/l,cy:t+o/l,w:l}}function Se(e,t,n){let{cx:o,cy:a,w:i}=Ee(t,n),r=2-2*i,s=e*r-1+2*i*o,l=-e*r-2*i*o,u=Le(s,l,e),c=1-u,m=c*c+2*i*u*c+u*u;return m>0?(2*i*a*u*c+u*u)/m:e}function Le(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),i=(-t-o)/(2*e),r=s=>s>=-1e-9&&s<=1+1e-9;return r(a)?Math.min(1,Math.max(0,a)):r(i)?Math.min(1,Math.max(0,i)):n}function qe(e,t){let n=re(t.viaX,t.viaY);return n?Se(Math.min(1,Math.max(0,e)),n.x,n.y):Be(e,t.curve)}function Ve(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 V(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 i=o[o.length-1];if(t>=i.t)return i.v;let r=0,s=o.length-1;for(;s-r>1;){let m=r+s>>1;o[m].t<=t?r=m:s=m}let l=o[r],u=o[s],c=u.t-l.t;return c<=0?u.v:Ve(l.v,u.v,qe((t-l.t)/c,l),n)}function le(e){return e.points.length<=1||e.points.every(t=>t.v===e.points[0].v)}function ce(e,t,n,o,a="linear"){let i=Math.max(2,Math.floor(o)),r=new Float32Array(i),s=n-t;for(let l=0;l<i;l+=1)r[l]=V(e,t+s*l/(i-1),a);return r}var Ue=100,$e=8,Xe=4096,U=e=>e;function We(e,t,n){return!(!!e.curve||e.viaX!==void 0&&e.viaY!==void 0)&&t==="linear"&&!n}function je(e){let t=Math.ceil(e*Ue);return Math.min(Xe,Math.max($e,t))}function ze(e,t,n,o,a){let{scheduledAt:i,elapsed:r,rate:s}=o,l=a??U,u=v=>i+(v-r)/s,c=e.points[t],m=e.points[t+1],d=u(m.t);if(d<=i)return null;let b=Math.max(i,u(c.t)),p=d-b;if(p<=0)return null;if(We(c,n,a))return{kind:"ramp",time:d,value:l(m.v)};let f=je(p),g=ce(e,Math.max(c.t,r),m.t,f,n),w=new Float32Array(f);for(let v=0;v<f;v+=1)w[v]=l(g[v]??0);return{kind:"curve",time:b,duration:p,values:w}}function Ye(e,t,n,o){let a=[];for(let r=0;r+1<e.points.length;r+=1){let s=ze(e,r,t,n,o);s&&a.push(s)}let i=a[0];return i?.kind==="curve"&&i.time<=n.scheduledAt||a.unshift({kind:"set",time:n.scheduledAt,value:(o??U)(V(e,n.elapsed,t))}),a}function Je(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 Ke(e){for(let t of e)t.param.cancelScheduledValues(0)}function Qe(e,t,n,o){if(t.points.length!==0)for(let a of e){if(Ke([a]),le(t)){let i=(a.map??U)(t.points[0].v);a.param.setValueAtTime(i,o.scheduledAt);continue}Je(a.param,Ye(t,n,o,a.map))}}function de(e,t,n,o,a){let i=new Map(n.filter(s=>s.id).map(s=>[s.id,s.handle])),r=[];for(let s of e.lanes){let l=N(s.target);if(!l)continue;let u=l.kind==="preset"?a?.[l.presetId]:l.kind==="fx"?i.get(l.nodeId)?.automation?.[l.param]:void 0;if(!u||u.length===0)continue;let c=q(s.target,t);c&&(Qe(u,s,c.scale,o),r.push(...u))}return r}var Ze=5,et=.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(i=>i.target===ae(e.id??"",t));return!a||a.points.length===0?Number.isFinite(o)?o:0:a.points.reduce((i,r)=>Math.max(i,r.v),-1/0)}function tt(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(et)/Math.log(o))*n}function nt(e,t){if(e.enabled===!1)return 0;switch(e.type){case"reverb":return k(e,"wet",t)>0?.6+Math.max(0,Math.min(1,k(e,"size",t)))*2.6:0;case"delay":return k(e,"mix",t)>0?tt(k(e,"time",t),k(e,"feedback",t)):0;case"chorus":return k(e,"mix",t)>0?k(e,"delay",t)/1e3:0;default:return 0}}function me(e,t){let n=e.nodes.reduce((o,a)=>o+nt(a,t),0);return Math.min(Ze,n)}function at(e,t,n,o,a){let i=e.createBuffer(n,o,a);for(let r=0;r<n;r++){let s=t[r];s&&i.getChannelData(r).set(s)}return i}function ot(e){let t=[];for(let n=0;n<e.numberOfChannels;n++)t.push(new Float32Array(e.getChannelData(n)));return t}async function rt(e,t,n,o){let a=j(n),i=Math.max(1,e.length),r=e[0]?.length??0;if(r===0)return e;let s=o?se(ue(o),a):null,l=Math.ceil(me(a,s??void 0)*t),u=new OfflineAudioContext(i,r+l,t);ee(a)&&await I(u);let c=u.createBufferSource();c.buffer=at(u,e,i,r,t);let m=te(u,a);return s&&de(s,a,m.nodes,{scheduledAt:0,elapsed:0,rate:1},m.presets),c.connect(m.input),m.output.connect(u.destination),c.start(),ot(await u.startRendering())}window.__HF_AUDIO_FX={render:rt};})();\n';
|
|
3
3
|
/** Returns the pre-built audio-FX runtime IIFE as a string constant. */
|
|
4
4
|
export function getAudioFxRuntimeScript() {
|
|
5
5
|
return AUDIO_FX_RUNTIME_IIFE;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-fx-runtime-inline.js","sourceRoot":"","sources":["../../src/generated/audio-fx-runtime-inline.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,MAAM,qBAAqB,GACzB,
|
|
1
|
+
{"version":3,"file":"audio-fx-runtime-inline.js","sourceRoot":"","sources":["../../src/generated/audio-fx-runtime-inline.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,MAAM,qBAAqB,GACzB,2hhCAA2hhC,CAAC;AAE9hhC,wEAAwE;AACxE,MAAM,UAAU,uBAAuB;IACrC,OAAO,qBAAqB,CAAC;AAC/B,CAAC"}
|