function build() { if (st.ctx) return true; if (!Lazy.loaded('feature:eq-core') || !Lazy.loaded('feature:vocal-reducer')) { loadSoundFeatures().then(()=>{apply(); Level.apply();}).catch(error=>toast(error.message)); return false; } try { st.ctx = new AC(); st.filters = EQ_BANDS.map((b) => { const f = st.ctx.createBiquadFilter(); f.type = b.type; f.frequency.value = b.f; f.Q.value = 1.0; return f; }); for (let i = 0; i < st.filters.length - 1; i++) st.filters[i].connect(st.filters[i + 1]); // Loudness levelling: gain toward -14 LUFS, then a limiter so a boosted // quiet track can never clip. st.level = st.ctx.createGain(); st.limiter = st.ctx.createDynamicsCompressor(); st.limiter.threshold.value = -1; st.limiter.knee.value = 0; st.limiter.ratio.value = 20; st.limiter.attack.value = 0.003; st.limiter.release.value = 0.25; st.vocal = VocalReducer.create(st.ctx, st.filters[st.filters.length - 1]); st.widener = EqCore.createWidener(st.ctx, st.vocal.output); st.widener.output.connect(st.level); st.level.connect(st.limiter); st.limiter.connect(st.ctx.destination); return true; } catch { st.ctx = null; return false; } }