Script Overview

The Usine scripting language turns patches into programmable control surfaces, timing engines, and content generators. It extends Object Pascal with BrainModular-specific APIs so scripts can react to audio, MIDI, spatial data, and UI events across a workspace.

Script V2.0 introduces method-based access on parameters and Usine objects. If you are migrating from Script V1.x, review the compatibility notes to replace legacy helpers with their method equivalents.

Usine evaluates scripts at compile time, at the audio block rate, at the user interface refresh rate, and whenever an inlet changes. The sections below detail how to structure a script, which APIs are available, and how those APIs map to BrainModular Usine features such as racks, the Master Synchro panel, MIDI Matrix modules, and XY Draw views.

procedure Callback(N: Integer);
begin
  if N = transpose then Trace('Semitones', transpose.asInteger);
end;

Script Architecture

Init

Init runs when the script module loads or recompiles. Create every inlet/outlet and initialize state because Usine clears globals between compile passes.

procedure Init;
begin
  noteIn := CreateParam('MIDI In', ptMidi, pioInput);
  noteOut := CreateParam('MIDI Out', ptMidi, pioOutput);

  transpose := CreateParam('Transpose', ptDataFader, pioInput);
  transpose.Min(-24);
  transpose.Max(24);
  transpose.Format('%.0f');

  tick := CreateParam('Tick', ptButton, pioInput);
  tick.CallbackMode(cbImmediate);
end;

This example maps to MIDI In/Out modules and a tick button that can drive an arpeggiator inside a patch.

Destroy

Destroy fires when the module is deleted or recompiled. Free dynamic structures created in Init.

Inlets and outlets are released automatically.

procedure Destroy;
begin
  noteNames.Free;
  SetLength(activeNotes, 0);
end;

Process

Process executes every audio block (typically 3 ms). Keep it deterministic, avoid GUI calls, and push results into outlets.

procedure Process;
var msg: TMIDI;
    i: Integer;
begin
  for i := 0 to noteIn.Length - 1 do
  begin
    msg := noteIn.asMidi(i);
    msg.data1 := msg.data1 + transpose.asInteger;
    noteOut.asMidi(i, msg);
  end;
  noteOut.Length(noteIn.Length);
end;

This transforms incoming MIDI data before it enters downstream MIDI Transformer or Sequencer modules.

ProcessThread

ProcessThread runs in its own worker thread every 10 ms. Use it for heavy calculations that should not block the audio chain or polling external devices.

procedure ProcessThread;
var i: Integer;
begin
  for i := 0 to pointsCount - 1 do
  begin
    cachedX[i] := GetObjectFloat(pointNames[i]);
    SetObjectFloat(targetNames[i], cachedX[i]);
  end;
end;

Avoid MIDI manipulation here; the worker thread is not sample accurate.

ProcessIDLE

ProcessIDLE runs on the UI refresh clock (25–50 ms). Trigger Usine messages, update colors, and perform housekeeping that should stay off the realtime thread like GUI stuffs.

procedure ProcessIDLE;
begin
  SendUsineMsg('panel', 'refresh', 'custom-display');
  ModuleColor($FF3298DB);
end;

Callback

Callback executes when an inlet changes. The integer argument identifies the parameter that triggered the event, making this the most efficient place to respond to user interaction. That allows an optimization over constant running Process procedure: the code will run only if input changed.

The Callback will scan the inputs from 0 to n and can trig a specific code when input change is detected.

So if results are supposed to be computed only when input changed, prefer the callback to the process procedure. However if things need computations at a different block time that input changed, still need the process procedure.

For example an input change can set to 1 a parameter in callback, but if wanna set this param back to 0 next block, (like a 'has chg' behavior) the code to set back to 0 will occur in Process Procedure, unless it's triggered by an input.

You can use either Callback or Process Procedure, or booth simultaneously according to your needs.

By default Callback is fast, called immediately after the inlet has been modified in the audio thread. You can turn the Callback to slow for an inlet with the procedure : MyInlet.CallbackMode(cbMainThread). Slow callbacks are suitable for GUI modification and dynamic patching. Slow callbacks are provided at messages rate, typically every 25 or 50ms depending on the setup refresh speed.

For finer control, use CallbackMode to target a specific thread — for example cbImmediate for callback immediate (in audio thread), cbMainThread for general purpose callbacks, cbGraphic for UI repaints, cbNetwork for OSC/TCP output, or cbAsync for heavy background work. See Parameter Creation and Layout for the full list of modes.

procedure Callback(N: Integer);
begin
  case N of
    transpose:
      Trace('Transpose semitones', transpose.asInteger);
    tick:
      if activeNotesCount > 0 then PlayNextNote;
  end;
end;

Execution Context Constants

BlocSize

BlocSize returns the number of samples in the current audio block. Use it to scale per-block accumulations or convert counts to milliseconds.

procedure Process;
begin
  rms := rms + sqr(input.asFloat) / BlocSize;
end;

BlocDuration

BlocDuration exposes the duration in milliseconds of the current audio block.

tempoLag := tempoLag + BlocDuration * 10.0;

RefreshSpeed

RefreshSpeed is the interval in milliseconds of the UI refresh loop. Tie slower automation to this cadence when running inside ProcessIDLE.

SamplingRate

SamplingRate holds the current audio sampling rate configured in the setup panel.

samplesPerBeat := Round((60 / hostTempo) * SamplingRate);

Platform

Platform is a TPlatform enum that identifies the active operating system and hardware.

Type Description
plWindowsIntel Windows on x86/x64 processors.
plWindowsARM Windows on ARM processors.
plMacOSIntel macOS on Intel processors.
plMacOSARM macOS on Apple Silicon/ARM processors.
plLinuxIntel Linux on x86/x64 processors.
plLinuxARM Linux on ARM processors.
plIOSARM iOS/iPadOS on ARM processors.
plAndroidARM Android on ARM processors.
if Platform in [plWindowsIntel, plWindowsARM] then
  Trace('Running on Windows')
else
  Trace('Running on other platform');

Messaging and Diagnostics

Use these helpers to monitor script behavior and communicate with other BrainModular subsystems such as the Master Synchro panel, browser, or timeline.

Procedure Description
procedure Trace(S: string; Value: variant); Output a debug message combining a label and a value (variant type) to the Usine trace panel
procedure Trace(S: string); Output a text message to the Usine trace panel
procedure Trace(I: Integer); Output an integer value to the Usine trace panel
procedure Trace(F: Single); Output a floating-point value to the Usine trace panel

Trace

Trace is overloaded for strings, integers, floats, and variants. It writes to the Usine panel and to the script editor log.

Trace('Active voices', voiceCount.asInteger);
Trace('Tempo', hostTempo);
Trace('BlocSize');

Module Styling

ModuleColor

Sets the module tint in the patch so custom scripts integrate visually with other modules.

procedure Init;
begin
  ....
  ModuleColor($FF8E44AD);
end;

Transport Commands

Procedure Description
function GetTimecodePos: double; Gets the current Usine timecode position (in seconds or beats, depending on context)
procedure SetTimecodePos(pos: double); Sets the current Usine timecode position to the specified value

SetTimecodePos

procedure SetTimecodePos(pos: double);

Sets the current Usine timecode position to the specified value.

if jumpButton.asInteger = 1 then
  SetTimecodePos(musicTime);

GetTimecodePos

function GetTimecodePos: double;

Gets the current Usine timecode position.

  trace(GetTimecodePos);

more about scripts

version 7.0.250121

Edit All Pages