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;
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 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 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 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 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 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;
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 exposes the duration in milliseconds of the current audio block.
tempoLag := tempoLag + BlocDuration * 10.0;
RefreshSpeed is the interval in milliseconds of the UI refresh loop. Tie slower automation to this cadence when running inside ProcessIDLE.
SamplingRate holds the current audio sampling rate configured in the setup panel.
samplesPerBeat := Round((60 / hostTempo) * SamplingRate);
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');
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 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');
Sets the module tint in the patch so custom scripts integrate visually with other modules.
procedure Init;
begin
....
ModuleColor($FF8E44AD);
end;
| 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 |
procedure SetTimecodePos(pos: double);
Sets the current Usine timecode position to the specified value.
if jumpButton.asInteger = 1 then
SetTimecodePos(musicTime);
function GetTimecodePos: double;
Gets the current Usine timecode position.
trace(GetTimecodePos);
version 7.0.250121
Edit All Pages