EngineState
EngineState is the engine's live runtime state. It's passed to build_uniforms() every frame and to prepare(). Reading from it is how your effect reacts to audio, LFOs, tempo, and the current parameter values.
Audio state
#![allow(unused)] fn main() { let volume = engine.audio.volume; // f32 — RMS loudness [0, 1] let bass = engine.audio.fft[0]; // f32 — lowest FFT band [0, 1] let kick = engine.audio.fft[1]; // f32 — next band up let beat_pulse = engine.audio.beat_pulse; // bool — true on beat onset let bpm = engine.audio.bpm; // f32 — estimated BPM (audio only) let beat_phase = engine.audio.beat_phase; // f32 — [0, 1) position within the current beat }
fft is an array of 8 bands covering the audible spectrum from bass to treble. Band 0 is the lowest frequencies; band 7 is the highest.
When tempo sync is active, prefer
engine.effective_bpm()andengine.effective_beat_phase()overengine.audio.bpm. See Tempo Sync.
Parameters
The get_param method returns a parameter's effective value — base slider value plus all active modulations (LFO, audio routing):
#![allow(unused)] fn main() { let intensity = engine.get_param("intensity").unwrap_or(0.5); }
Returns None if no parameter with that id is registered. The returned value is already clamped to the parameter's declared [min, max] range.
LFO state
You rarely need to read LFO state directly in build_uniforms() — declare parameters and get_param() includes LFO contributions automatically. But you can read raw LFO values if you need them:
#![allow(unused)] fn main() { let lfo_a_value = engine.lfo.banks[0].current_value; // f32 [-1, 1] }
See LFOs.
Tempo
#![allow(unused)] fn main() { // Always dispatches on the active sync source: let bpm = engine.effective_bpm(); let phase = engine.effective_beat_phase(); // [0, 1) // Which source is active: match engine.sync_source { SyncSource::Audio => { /* audio beat detection */ } SyncSource::Link => { /* Ableton Link session */ } SyncSource::ProDj => { /* ProDJ Link */ } } }
effective_bpm() and effective_beat_phase() are the right calls for any tempo-reactive effect. They follow the active source automatically.
MIDI Timecode (optional)
When the mtc feature is enabled:
#![allow(unused)] fn main() { if let Some(pos) = &engine.mtc.position { let seconds = pos.as_seconds_f64(); let frame_rate = pos.frame_rate; // 24, 25, 29.97, 30 } }
MTC is a position reference, not a BPM source — use it for timeline-locked visuals.
Input / output state
#![allow(unused)] fn main() { // Current input dimensions let (w, h) = (engine.input.width, engine.input.height); // Whether an input source is active let has_input = engine.input.active; }
Sending commands
EngineState uses command enums rather than direct mutation to change subsystem state. Commands are typically sent from a custom GUI tab, not from build_uniforms(). The engine processes them at the start of the next frame.
#![allow(unused)] fn main() { engine.input_commands.push(InputCommand::SetDevice(DeviceId(0))); engine.output_commands.push(OutputCommand::EnableNdi(true)); engine.lfo_commands.push(LfoCommand::SetRate { bank: 0, hz: 1.0 }); }
You won't need these in simple effects, but they're how the built-in tabs work internally.