// mod_audio_adsr.omfx — OmniScript Modulator // Audio-triggered ADSR envelope. Detects transients above a threshold // and fires a classic four-stage envelope. Perfect for sidechain-style // ducking, audio-reactive filter sweeps, or "VCA-on-transient" effects. @name: Audio-Triggered ADSR @desc: Fires an ADSR envelope when audio crosses a threshold @mode: modulator @param[0] name="Threshold" min=-60.0 max=0.0 default=-20.0 unit="dB" @param[1] name="Attack" min=0.1 max=500 default=5 unit="ms" @param[2] name="Decay" min=1 max=2000 default=100 unit="ms" @param[3] name="Sustain" min=0.0 max=1.0 default=0.5 @param[4] name="Release" min=1 max=3000 default=200 unit="ms" @param[5] name="Hold" min=0 max=2000 default=0 unit="ms" fn db_to_linear(db) { return pow(10.0, db * 0.05); } fn time_coeff(ms, sr) { return exp(-1.0 / max(ms * 0.001 * sr, 1.0)); } // Stage: 0=idle 1=attack 2=hold 3=decay 4=sustain 5=release @init stage = 0; level = 0.0; hold_counter = 0.0; retrigger_guard = 0.0; env_in = 0.0; @sample // Fast envelope follower on input (peak with quick decay) rect = max(abs(spl0), abs(spl1)); if rect > env_in { env_in = rect; } else { env_in = env_in * 0.999; } thr = db_to_linear(param[0]); // Retrigger guard prevents double-fires on single transients if retrigger_guard > 0.0 { retrigger_guard = retrigger_guard - 1.0; } // Trigger detection: rising edge above threshold if env_in > thr && retrigger_guard < 0.5 && stage < 0.5 { stage = 1; retrigger_guard = sample_rate * 0.010; // 10 ms lockout } // Stage 1 — Attack if stage > 0.5 && stage < 1.5 { a = time_coeff(param[1], sample_rate); level = level * a + 1.0 * (1.0 - a); if level > 0.99 { level = 1.0; stage = 2; hold_counter = param[5] * 0.001 * sample_rate; } } // Stage 2 — Hold if stage > 1.5 && stage < 2.5 { hold_counter = hold_counter - 1.0; if hold_counter <= 0.0 { stage = 3; } } // Stage 3 — Decay to sustain if stage > 2.5 && stage < 3.5 { d = time_coeff(param[2], sample_rate); level = level * d + param[3] * (1.0 - d); if abs(level - param[3]) < 0.001 { stage = 4; } } // Stage 4 — Sustain (hold until input drops) if stage > 3.5 && stage < 4.5 { level = param[3]; if env_in < thr * 0.5 { stage = 5; } } // Stage 5 — Release if stage > 4.5 { r = time_coeff(param[4], sample_rate); level = level * r; if level < 0.0005 { level = 0.0; stage = 0; } } output = clamp(level, 0.0, 1.0);