OMNi

Plugins and Advanced · 5 min

OmniScript Scripting Language

A manual for writing custom generative algorithms, MIDI processors, modulators, and simple DSP effects in the integrated OmniScript language.

📜 OmniScript Scripting Language

WSKAZÓWKA

[!TIP] OmniScript is an integrated scripting language compiled to a safe virtual machine bytecode (no allocation, no panicking), allowing you to write custom MIDI processors, modulators, and simple DSP effects without compiling external plugins. You can find ready-to-use examples in omni_engine/examples/scripts/*.omfx.


💡 1. Three Types of Scripts (.omfx)

An .omfx file declares its type in the header via @mode:, which determines where the script is available and what built-in variables it can see:

  • @mode: audio — a simple DSP effect inserted into an insert chain (e.g., gain, bitcrusher, filter).
  • @mode: midi — a generative MIDI processor (arpeggiator, chord generator, humanizer, scale quantizer).
  • @mode: modulator — a custom modulator in the modulation rack (see /docs/modulators).

A script does not define its own entry function like fn process_note(). Instead, the host directly calls standardized sections of the file if they are present:

SectionWhen it is called
@initOnce, upon loading/resetting the script.
@blockOnce per audio buffer.
@sampleOnce per sample (sample-accurate).
@midi_note_on / @midi_note_offUpon every incoming MIDI event (only @mode: midi).
@modulateOlder, single entry point for a modulator — replaced by @block/@sample writing to the output variable.

💻 2. Real Examples

Example A: Simple DSP Effect (fx_simple_gain.omfx)

code
@name: Simple Gain
@desc: Volume control
@mode: audio

@param[0] name="Gain" min=0.0 max=2.0 default=1.0 unit="x"

@sample
  spl0 = spl0 * param[0];
  spl1 = spl1 * param[0];

Example B: Modulator with Custom Function (mod_step_lfo.omfx)

code
@name: Step LFO
@mode: modulator

@param[0] name="Rate"   min=0.1  max=20.0 default=2.0  unit="Hz"
@param[1] name="Steps"  min=2    max=16   default=8

fn saw_wave(p) {
    return p * 2.0 - 1.0;
}

@init
  phase = 0.0;

@block
  n_steps = floor(param[1] + 0.5);

@sample
  phase = phase + param[0] / sample_rate;
  if phase >= 1.0 { phase = phase - 1.0; }
  output = saw_wave(phase);

Syntax note: OmniScript does not have a let keyword — the first assignment to a name automatically declares it (name = expression;). for loops use a Rust-like range syntax: for i in 0..8 { ... }, rather than a classic C-style for(;;).


🛠️ 3. Built-in Variables

All variables in OmniScript are floating-point numbers (f64) — there are no separate u8/f32 types visible from the script (e.g., note/velocity are simply numbers 0–127).

VariableAvailable in modeDescription
sample_rateallThe audio engine's sample rate.
tempoallThe current project tempo in BPM.
beat_posallThe current playback position in the bar.
framesallThe size of the current audio buffer (number of samples).
play_stateall1.0 when transport is playing, 0.0 when stopped.
param[0]…param[63]allParameter values defined by @param[n] in the file header — for reading and writing.
spl0 / spl1audio onlyAudio signal sample, left/right channel.
notemidi onlyPitch of the processed MIDI note (0–127).
velocitymidi onlyDynamics of the processed MIDI note (0–127).
sample_offsetmidi onlyThe offset of the MIDI event in samples relative to the start of the buffer.
outputmodulator onlyThe modulation value written by the script, read by the modulation rack.

⚙️ 4. Built-in Functions

FunctionDescription
sin(x), cos(x), tan(x), atan(x), atan2(y, x)Trigonometric functions.
sqrt(x), abs(x), floor(x), ceil(x)Basic mathematical functions.
exp(x), log(x), pow(x, y)Exponential, natural logarithm, power.
min(a, b), max(a, b), clamp(val, min, max)Value clamping.
rand()Pseudorandom number from the range [0.0, 1.0).
emit_note(note, velocity, offset)Emits a MIDI note with a given pitch and velocity, offset is the sample offset within the current buffer (only @mode: midi).
emit_note_off(note, offset)Emits a note-off message, offset as above (only @mode: midi).

Additionally, there are constants PI, TWO_PI, E, boolean literals true/false (1.0/0.0), as well as custom user functions declared via fn name(arguments) { ... return expression; }.


⚡ 5. Real-Time Smoothness

OmniScript code is compiled to bytecode and executed by a built-in virtual machine (not an interpreter walking an abstract syntax tree), with limits on operation counts, stack depth, and loop iterations to guarantee audio thread safety. The @sample section is executed once for every sample, ensuring Sample-Accurate Timing.