@hyperframes/core 0.8.11 → 0.8.13
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/audio/audioFxWorklets.d.ts.map +1 -1
- package/dist/audio/audioFxWorklets.js +53 -4
- package/dist/audio/audioFxWorklets.js.map +1 -1
- package/dist/audioCarve.d.ts +27 -0
- package/dist/audioCarve.d.ts.map +1 -1
- package/dist/audioCarve.js +32 -0
- package/dist/audioCarve.js.map +1 -1
- package/dist/audioFx.d.ts.map +1 -1
- package/dist/audioFx.js +86 -47
- package/dist/audioFx.js.map +1 -1
- package/dist/audioGroups.d.ts +39 -28
- package/dist/audioGroups.d.ts.map +1 -1
- package/dist/audioGroups.js +83 -59
- package/dist/audioGroups.js.map +1 -1
- package/dist/compiler/index.d.ts +1 -1
- package/dist/compiler/index.d.ts.map +1 -1
- package/dist/compiler/index.js +1 -1
- package/dist/compiler/index.js.map +1 -1
- package/dist/compiler/mediaRenderIds.d.ts +12 -0
- package/dist/compiler/mediaRenderIds.d.ts.map +1 -1
- package/dist/compiler/mediaRenderIds.js +84 -0
- package/dist/compiler/mediaRenderIds.js.map +1 -1
- package/dist/editing/affordances.d.ts.map +1 -1
- package/dist/editing/affordances.js +21 -11
- package/dist/editing/affordances.js.map +1 -1
- 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 +81 -32
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/media.d.ts +0 -5
- package/dist/runtime/media.d.ts.map +1 -1
- package/dist/runtime/media.js +9 -5
- package/dist/runtime/media.js.map +1 -1
- package/package.json +4 -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 pe="data-fx-chain",ct=pe.slice(5),$=1,M=(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}),D=(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:[D(-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:[M("frequency","Frequency",1e3),D(-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:[M("frequency","Frequency",200,20,2e3),D(-40,40,0)],web:"biquad-lowshelf"},{id:"highshelf",label:"High Shelf",group:"filter",description:"Lift or drop everything above the corner frequency.",params:[M("frequency","Frequency",4e3,500,2e4),D(-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:[M("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:[M("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:"pitchshift",label:"Pitch shift",group:"time",description:"Shifts pitch up or down without changing playback speed.",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"}],O=new Map(W.map(e=>[e.id,e]));function F(e){return O.get(e)}var dt=W.map(e=>e.id);function k(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:k(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\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 \\u2014 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) \\u2014 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 this.phase = 0;\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 ratio = Math.pow(2, semitones / 12);\n const grain = this.grain;\n const ringLen = grain * 2;\n const inc = (1 - ratio) / grain;\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 let write = this.write, phase = this.phase;\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 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 - mix) + wet * mix;\n }\n write = (write + 1) % ringLen;\n }\n this.write = write;\n this.phase = phase;\n return true;\n }\n}\nregisterProcessor("hf-pitchshift", HfPitchshift);\n`,B=new WeakMap;var he=new WeakSet;function S(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(fe)))}`;await e.audioWorklet.addModule(n),he.add(e)})().catch(n=>{throw B.delete(e),n}),B.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),G=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 I(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 E(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),E(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),E(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 E(a,()=>{})}}function T(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,G(u))}],feedback:[{param:i.gain}],mix:I(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:G}],depth:[{param:s.gain,map:G}],speed:[{param:r.playbackRate}],mix:I(l.gain,u.gain)},dispose:()=>{Q(r),[o,a,i,s,l,u].forEach(m=>m.disconnect())}}},Me=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<Me;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":T("hf-compressor"),"worklet-limiter":T("hf-limiter"),"worklet-gate":T("hf-gate"),"worklet-bitcrush":T("hf-bitcrush"),"worklet-pitchshift":T("hf-pitchshift"),waveshaper:ke,"delay-feedback":ve,"chorus-lfo":we,"allpass-phaser":Fe,convolver:He};function ee(e){return e.nodes.some(t=>F(t.type)?.web.startsWith("worklet-")??!1)}function Te(e,t,n,o=0){let a=F(t);if(!a)throw new Error(`Unknown effect type: ${t}`);let i=k(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=k(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(...I(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(k(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",kt=Ne.slice(5),C=1,_e=512,x=class extends Error{constructor(t){super(t),this.name="AudioAutomationError"}},L="volume";function _(e){if(e===L)return{kind:"volume"};let t=e.split(".");if(t.length===3&&t[0]==="fx"&&t[1]===De){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 De="preset";var Ce={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=_(e);if(!n)return null;if(n.kind==="volume")return oe;if(n.kind==="preset")return t?.nodes.some(s=>s.fromPreset===n.presetId)?{...Ce,label:`${n.presetId} \\xB7 Amount`}:null;let o=t?.nodes.find(r=>r.id===n.nodeId);if(!o)return null;let a=F(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 N(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=N(e);return t===null||t===0?0:Math.min(1,Math.max(-1,t))}function re(e,t){let n=N(e),o=N(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 Be(e,t){let n=N(e?.t),o=N(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=>Be(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 Se(e){let t=[];for(let n of e.lanes){if(!_(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:C,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:C,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!==C)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"||!_(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 Se({version:C,lanes:o})}function Ge(e,t){return t?Math.pow(e,Math.pow(2,2*t)):e}function Ie(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 Ee(e,t,n){let{cx:o,cy:a,w:i}=Ie(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?Ee(Math.min(1,Math.max(0,e)),n.x,n.y):Ge(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=_(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 y(e,t,n){let o=Number(k(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){return y(e,"wet",t)>0?.6+Math.max(0,Math.min(1,y(e,"size",t)))*2.6:0}function at(e,t){return y(e,"mix",t)>0?tt(y(e,"time",t),y(e,"feedback",t)):0}function ot(e,t){return y(e,"mix",t)>0?y(e,"delay",t)/1e3:0}function rt(e,t){return y(e,"mix",t)>0?.2:0}function it(e,t){if(e.enabled===!1)return 0;switch(e.type){case"reverb":return nt(e,t);case"delay":return at(e,t);case"chorus":return ot(e,t);case"pitchshift":return rt(e,t);default:return 0}}function me(e,t){let n=e.nodes.reduce((o,a)=>o+it(a,t),0);return Math.min(Ze,n)}function st(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 ut(e){let t=[];for(let n=0;n<e.numberOfChannels;n++)t.push(new Float32Array(e.getChannelData(n)));return t}async function lt(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 S(u);let c=u.createBufferSource();c.buffer=st(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(),ut(await u.startRendering())}window.__HF_AUDIO_FX={render:lt};})();\n';
|
|
2
|
+
const AUDIO_FX_RUNTIME_IIFE = '"use strict";(()=>{var fe="data-fx-chain",ft=fe.slice(5),X=1,M=(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}),O=(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}),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}),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."},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:[M("frequency","Frequency",1e3),R(-40,40,0),O(1)],web:"biquad-peaking"},{id:"lowshelf",label:"Low Shelf",group:"filter",description:"Lift or drop everything below the corner frequency.",params:[M("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:[M("frequency","Frequency",4e3,500,2e4),R(-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:[M("frequency","Cutoff",300,20,2e4),O(.707),W],web:"biquad-highpass"},{id:"lowpass",label:"Low-pass",group:"filter",description:"Remove high frequencies \\u2014 darkens or muffles a track.",params:[M("frequency","Cutoff",8e3,100,2e4),O(.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:"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"}],B=new Map(z.map(e=>[e.id,e]));function F(e){return B.get(e)}var bt=z.map(e=>e.id);function w(e,t){let n=B.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 Y(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!==X)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(be);return{version:X,nodes:o}}function H(e){return e.nodes.filter(t=>t.enabled!==!1)}function be(e,t){if(typeof e!="object"||e===null)throw new A(`Node ${t} is not an object.`);let n=e;if(typeof n.type!="string"||!B.has(n.type))throw new A(`Node ${t} has unknown effect type: ${String(n.type)}`);return xe({type:n.type,id:C(n.id),fromCarve:j(n.fromCarve),fromPreset:C(n.fromPreset),label:C(n.label),fromEq:C(n.fromEq),fromLeveller:j(n.fromLeveller),presetAmount:ge(n.presetAmount),enabled:n.enabled!==!1,params:w(n.type,n.params??void 0)})}function C(e){return typeof e=="string"&&e?e:void 0}function j(e){return e===!0?!0:void 0}function ge(e){if(!(typeof e!="number"||!Number.isFinite(e)))return Math.min(1,Math.max(0,e))}function xe(e){for(let t of Object.keys(e))e[t]===void 0&&delete e[t];return e}var Ae=`\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\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 \\u2014 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) \\u2014 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 this.phase = 0;\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 \\u2014 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 \\u2014 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 \\u2014 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`,S=new WeakMap;var ye=new WeakSet;function G(e){let t=S.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(Ae)))}`;await e.audioWorklet.addModule(n),ye.add(e)})().catch(n=>{throw S.delete(e),n}),S.set(e,t)),t}function we(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,d=0;for(let c=0;c<a;c++){u+=l*(s()-u);let b=u*Math.pow(1-c/a,2.5);i[c]=b,d+=b*b}let m=Math.sqrt(d);if(m>0)for(let c=0;c<a;c++)i[c]=(i[c]??0)/m;return i}var f=e=>typeof e=="number"?e:Number(e??0),I=e=>e/1e3;function Z(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 d=u/a;r[u]=t==="sine"?Math.sin(2*Math.PI*d):4*Math.abs((d+.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 ee(e){try{e.stop()}catch{}e.disconnect()}function L(e,t){return[{param:e},{param:t,map:n=>1-n}]}function te(e,t,n){e.gain.value=n,t.gain.value=1-n}function E(e,t,n){return{input:e,output:e,update:t,automation:n,dispose:()=>e.disconnect()}}var ke=new Set(["peaking","highpass","lowpass"]),J=e=>Math.pow(10,e/20),ve=(e,t)=>{let n=e.createGain(),o=a=>{n.gain.value=J(f(a.gain))};return o(t),E(n,o,{gain:[{param:n.gain,map:J}]})};function P(e,t){return(n,o)=>{let a=n.createBiquadFilter();a.type=e;let i=r=>{a.frequency.value=f(r.frequency),r.q!==void 0&&(a.Q.value=f(r.q)),t&&(a.gain.value=f(r.gain))};return i(o),E(a,i,{frequency:[{param:a.frequency}],...ke.has(e)?{q:[{param:a.Q}]}:{},...t?{gain:[{param:a.gain}]}:{}})}}function Me(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 E(a,()=>{})}}function T(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 Fe={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))},He=(e,t)=>{let n=e.createWaveShaper(),o=e.createGain(),a=e.createGain();o.connect(n).connect(a);let i=r=>{let s=Fe[String(r.type)]??Math.tanh,l=Math.pow(10,f(r.threshold)/20),u=8192,d=new Float32Array(u);for(let m=0;m<u;m++){let c=m/(u-1)*2-1;d[m]=s(c/Math.max(1e-6,l))*l}n.curve=d,n.oversample=f(r.oversample)>=4?"4x":f(r.oversample)>=2?"2x":"none",a.gain.value=Math.pow(10,f(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()}}},Pe=(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,f(u.time)/1e3),i.gain.value=f(u.feedback),te(r,s,f(u.mix))};return l(t),{input:n,output:o,update:l,automation:{time:[{param:a.delayTime,map:u=>Math.min(5,I(u))}],feedback:[{param:i.gain}],mix:L(r.gain,s.gain)},dispose:()=>[n,o,a,i,r,s].forEach(u=>u.disconnect())}},Te=(e,t,n)=>{let o=e.createGain(),a=e.createGain(),i=e.createDelay(.5),r=Z(e,"sine",f(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 d=m=>{i.delayTime.value=f(m.delay)/1e3,s.gain.value=f(m.depth)/1e3,r.playbackRate.value=f(m.speed),te(l,u,f(m.mix))};return d(t),{input:o,output:a,update:d,automation:{delay:[{param:i.delayTime,map:I}],depth:[{param:s.gain,map:I}],speed:[{param:r.playbackRate}],mix:L(l.gain,u.gain)},dispose:()=>{ee(r),[o,a,i,s,l,u].forEach(m=>m.disconnect())}}},Ne=6,_e=(e,t,n)=>{let o=e.createGain(),a=e.createGain(),i=e.createGain(),r=e.createGain(),s=Z(e,String(t.type)==="1"?"sine":"triangle",f(t.speed),n),l=e.createGain(),u=e.createGain(),d=e.createGain(),m=[];o.connect(i);let c=i;for(let p=0;p<Ne;p++){let h=e.createBiquadFilter();h.type="allpass",h.Q.value=.7071,l.connect(h.frequency),c.connect(h),c=h,m.push(h)}s.connect(l),c.connect(u).connect(r),i.connect(d).connect(r),r.connect(a);let b=p=>{let h=1e3/Math.max(.1,f(p.delay));for(let g of m)g.frequency.value=h;l.gain.value=h*f(p.decay),s.playbackRate.value=f(p.speed),i.gain.value=f(p.in_gain),r.gain.value=f(p.out_gain),u.gain.value=1,d.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:()=>{ee(s),[o,a,i,r,l,u,d,...m].forEach(p=>p.disconnect())}}},Re=(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 d=`${f(u.size)}:${f(u.damping)}`;if(d!==s){let m=we(e.sampleRate,f(u.size),f(u.damping)),c=e.createBuffer(1,m.length,e.sampleRate);c.getChannelData(0).set(m),a.buffer=c,s=d}i.gain.value=f(u.wet),r.gain.value=f(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())}},Ce={"gain-node":ve,"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":T("hf-compressor"),"worklet-limiter":T("hf-limiter"),"worklet-gate":T("hf-gate"),"worklet-bitcrush":T("hf-bitcrush"),"worklet-pitchshift":T("hf-pitchshift"),waveshaper:He,"delay-feedback":Pe,"chorus-lfo":Te,"allpass-phaser":_e,convolver:Re};function ne(e){return e.nodes.some(t=>F(t.type)?.web.startsWith("worklet-")??!1)}function De(e,t,n,o=0){let a=F(t);if(!a)throw new Error(`Unknown effect type: ${t}`);let i=w(t,n);if((t==="highpass"||t==="lowpass")&&String(i.poles)==="1")return Me(t)(e,i,o);let r=Ce[a.web];if(!r)throw new Error(`No Web Audio builder for ${a.web}`);return r(e,i,o)}function K(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 Q(e){return H(e).map(t=>{let n=w(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 ae(e,t,n=0){var m;let o=e.createGain(),a=e.createGain(),i=[],r=[],s=K(H(t)),l=o;for(let c of s){let b=null;if(c.preset){let p=e.createGain(),h=e.createGain(),g=e.createGain(),v=e.createGain();g.gain.value=c.amount,h.gain.value=1-c.amount,l.connect(p),p.connect(h).connect(v),b={entry:p,wet:g,dry:h,join:v},l=p}for(let p of c.nodes){let h=De(e,p.type,p.params??{},n);l.connect(h.input),l=h.output,i.push({...p.id?{id:p.id}:{},type:p.type,handle:h})}b&&c.preset&&(l.connect(b.wet).connect(b.join),r.push({id:c.preset,...b}),l=b.join)}l.connect(a);let u=Q(t),d={};for(let c of r)(d[m=c.id]??(d[m]=[])).push(...L(c.wet.gain,c.dry.gain));return{input:o,output:a,presets:d,nodes:i,update(c){if(Q(c)!==u)return!1;H(c).forEach((p,h)=>{let g=i[h];g&&(g.handle.update(w(p.type,p.params)),p.id===void 0?delete g.id:g.id=p.id)});let b=0;for(let p of K(H(c))){if(!p.preset)continue;let h=r[b++];!h||h.id!==p.preset||(h.wet.gain.value=p.amount,h.dry.gain.value=1-p.amount)}return!0},dispose(){for(let{handle:c}of i)c.dispose();for(let{entry:c,wet:b,dry:p,join:h}of r)c.disconnect(),b.disconnect(),p.disconnect(),h.disconnect();o.disconnect(),a.disconnect()}}}var oe=3.981071705534972;var Oe="data-automation",Ht=Oe.slice(5),D=1,Be=512,x=class extends Error{constructor(t){super(t),this.name="AudioAutomationError"}},q="volume";function _(e){if(e===q)return{kind:"volume"};let t=e.split(".");if(t.length===3&&t[0]==="fx"&&t[1]===Se){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 ie(e,t){return`fx.${e}.${t}`}var Se="preset";var Ge={min:0,max:1,step:.01,unit:"",label:"Amount",scale:"linear",default:1},re={min:0,max:oe,step:.01,unit:"",label:"Volume",scale:"linear",default:1};function V(e,t){let n=_(e);if(!n)return null;if(n.kind==="volume")return re;if(n.kind==="preset")return t?.nodes.some(s=>s.fromPreset===n.presetId)?{...Ge,label:`${n.presetId} \\xB7 Amount`}:null;let o=t?.nodes.find(r=>r.id===n.nodeId);if(!o)return null;let a=F(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 N(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 Ie(e){let t=N(e);return t===null||t===0?0:Math.min(1,Math.max(-1,t))}function se(e,t){let n=N(e),o=N(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 Le(e){let t=Ie(e?.curve),n=se(e?.viaX,e?.viaY);return{...t?{curve:t}:{},...n?{viaX:n.x,viaY:n.y}:{}}}function Ee(e,t){let n=N(e?.t),o=N(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,...Le(e)}}function ue(e,t){let n=e.map(a=>Ee(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,Be)}function qe(e){let t=[];for(let n of e.lanes){if(!_(n.target))continue;let o=n.target===q?re:null,a=ue(n.points??[],o);a.length>0&&t.push({target:n.target,points:a})}return{version:D,lanes:t}}function le(e,t){let n=[];for(let o of e.lanes){let a=V(o.target,t);if(!a)continue;let i=ue(o.points,a);i.length>0&&n.push({target:o.target,points:i})}return{version:D,lanes:n}}function de(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"||!_(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 qe({version:D,lanes:o})}function Ve(e,t){return t?Math.pow(e,Math.pow(2,2*t)):e}function Ue(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 $e(e,t,n){let{cx:o,cy:a,w:i}=Ue(t,n),r=2-2*i,s=e*r-1+2*i*o,l=-e*r-2*i*o,u=Xe(s,l,e),d=1-u,m=d*d+2*i*u*d+u*u;return m>0?(2*i*a*u*d+u*u)/m:e}function Xe(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 We(e,t){let n=se(t.viaX,t.viaY);return n?$e(Math.min(1,Math.max(0,e)),n.x,n.y):Ve(e,t.curve)}function je(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 U(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],d=u.t-l.t;return d<=0?u.v:je(l.v,u.v,We((t-l.t)/d,l),n)}function ce(e){return e.points.length<=1||e.points.every(t=>t.v===e.points[0].v)}function me(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]=U(e,t+s*l/(i-1),a);return r}var ze=100,Ye=8,Je=4096,$=e=>e;function Ke(e,t,n){return!(!!e.curve||e.viaX!==void 0&&e.viaY!==void 0)&&t==="linear"&&!n}function Qe(e){let t=Math.ceil(e*ze);return Math.min(Je,Math.max(Ye,t))}function Ze(e,t,n,o,a){let{scheduledAt:i,elapsed:r,rate:s}=o,l=a??$,u=k=>i+(k-r)/s,d=e.points[t],m=e.points[t+1],c=u(m.t);if(c<=i)return null;let b=Math.max(i,u(d.t)),p=c-b;if(p<=0)return null;if(Ke(d,n,a))return{kind:"ramp",time:c,value:l(m.v)};let h=Qe(p),g=me(e,Math.max(d.t,r),m.t,h,n),v=new Float32Array(h);for(let k=0;k<h;k+=1)v[k]=l(g[k]??0);return{kind:"curve",time:b,duration:p,values:v}}function et(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(e,n.elapsed,t))}),a}function tt(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 nt(e){for(let t of e)t.param.cancelScheduledValues(0)}function at(e,t,n,o){if(t.points.length!==0)for(let a of e){if(nt([a]),ce(t)){let i=(a.map??$)(t.points[0].v);a.param.setValueAtTime(i,o.scheduledAt);continue}tt(a.param,et(t,n,o,a.map))}}function pe(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=_(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 d=V(s.target,t);d&&(at(u,s,d.scale,o),r.push(...u))}return r}var ot=5,it=.001;function y(e,t,n){let o=Number(w(e.type,e.params)[t]??0);if(!e.id||!n)return Number.isFinite(o)?o:0;let a=n.lanes.find(i=>i.target===ie(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 rt(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(it)/Math.log(o))*n}function st(e,t){return y(e,"wet",t)>0?.6+Math.max(0,Math.min(1,y(e,"size",t)))*2.6:0}function ut(e,t){return y(e,"mix",t)>0?rt(y(e,"time",t),y(e,"feedback",t)):0}function lt(e,t){return y(e,"mix",t)>0?y(e,"delay",t)/1e3:0}function dt(e,t){return y(e,"mix",t)>0?.2:0}function ct(e,t){if(e.enabled===!1)return 0;switch(e.type){case"reverb":return st(e,t);case"delay":return ut(e,t);case"chorus":return lt(e,t);case"pitchshift":return dt(e,t);default:return 0}}function he(e,t){let n=e.nodes.reduce((o,a)=>o+ct(a,t),0);return Math.min(ot,n)}function mt(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 pt(e){let t=[];for(let n=0;n<e.numberOfChannels;n++)t.push(new Float32Array(e.getChannelData(n)));return t}async function ht(e,t,n,o){let a=Y(n),i=Math.max(1,e.length),r=e[0]?.length??0;if(r===0)return e;let s=o?le(de(o),a):null,l=Math.ceil(he(a,s??void 0)*t),u=new OfflineAudioContext(i,r+l,t);ne(a)&&await G(u);let d=u.createBufferSource();d.buffer=mt(u,e,i,r,t);let m=ae(u,a);return s&&pe(s,a,m.nodes,{scheduledAt:0,elapsed:0,rate:1},m.presets),d.connect(m.input),m.output.connect(u.destination),d.start(),pt(await u.startRendering())}window.__HF_AUDIO_FX={render:ht};})();\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,6nsCAA6nsC,CAAC;AAEhosC,wEAAwE;AACxE,MAAM,UAAU,uBAAuB;IACrC,OAAO,qBAAqB,CAAC;AAC/B,CAAC"}
|