Overview
uvi-script is built on the Lua programming language, a lightweight, fast, and embeddable scripting language. Understanding Lua is essential for writing effective uvi-scripts.
This page covers the Lua-specific aspects of uvi-script: which version is used, what libraries are available, and the real-time constraints that shape how you write scripts.
Learning Lua
If you're new to Lua or need a reference, we recommend these excellent resources:
These resources are well-written and provide a solid foundation for scripting. Using a simple, widespread, yet powerful scripting language that can be learned online was one of the main motivations for choosing Lua.
What you need to know:
- Basic syntax (variables, functions, tables, control structures)
- Tables (Lua's primary data structure)
- Functions and closures
- Local vs global variables (critical for uvi-script!)
- Coroutines (used internally for threading)
The Lua Virtual Machine
uvi-script uses a sandboxed Lua 5.1 virtual machine with custom extensions designed for real-time audio processing.
Real-Time Execution Constraints
Most script code executes with real-time priority to ensure sample-accurate timing and low-latency performance. This places specific constraints on what you can do:
Real-Time Requirements:
- Predictable execution time - Code must complete within a bounded time
- No memory allocation - All memory is pre-allocated from a custom pool
- No blocking operations - Cannot wait on external resources (files, network, etc.)
- Bounded loops - Avoid lengthy operations like iterating over large tables (10000+ entries)
Exceptions (Non-Real-Time): These callbacks run with relaxed constraints and can allocate memory:
- Main script chunk (executed once at load time)
- onLoad - Called when restoring saved state
- onSave - Called when saving state
Memory Management
To maintain real-time safety, uvi-script uses a pre-allocated memory pool for all Lua operations during real-time execution.
Best Practices:
- Pre-allocate tables in the main chunk rather than in callbacks
- Reuse tables instead of creating new ones in loops
- Use local variables for temporary data (faster and doesn't allocate)
- Avoid string concatenation in tight loops (creates new strings)
Available Standard Libraries
uvi-script provides access to a subset of Lua's standard libraries. Libraries that require operating system access or blocking I/O are not available to maintain real-time safety.
Available Libraries
base - Basic Functions
- Core functions:
assert, error, pcall, type, tonumber, tostring
- Table/iteration:
pairs, ipairs, next
- Utility:
print, select, unpack
package - Module System
require - Load Lua modules
- Note: Only Lua modules can be loaded, not C libraries
table - Table Manipulation
table.insert, table.remove
table.concat
table.sort
table.copy(t) - uvi-script extension: creates a shallow copy of a table
- Note: Be mindful of performance on large tables in real-time code
string - String Operations
string.sub, string.find, string.match
string.format, string.gsub
- Pattern matching (Lua patterns, not full regex)
math - Mathematical Functions
- Trigonometry:
math.sin, math.cos, math.tan, math.asin, etc.
- Utilities:
math.abs, math.floor, math.ceil, math.min, math.max
- Random:
math.random, math.randomseed
- Constants:
math.pi, math.huge
class - Object-Oriented Programming
Not Available (Real-Time Safety)
os - Operating System Facilities
- Not available: Would break real-time guarantees
- Reasons: Blocking system calls, unpredictable timing
io - File I/O
- Not available: Would block real-time thread
- Alternative: Use asynchronous API for file operations
Third-Party Lua Libraries
- Not allowed: Cannot load external C libraries or most Lua libraries
- Reasons: Real-time safety, memory allocation, security
- Note
- For file I/O and other blocking operations, use the asynchronous API which runs on background threads and safely communicates results back to your script.
Object-Oriented Programming
uvi-script includes a built-in class system. Use class to define classes with constructors, methods, and instance state. This is useful for encapsulating complex logic like sequencers, state machines, or multi-voice managers.
Defining a Class
class 'NoteTracker'
function NoteTracker:__init()
self.voices = {}
end
function NoteTracker:add(note, id)
self.voices[note] = id
end
function NoteTracker:remove(note)
self.voices[note] = nil
end
function NoteTracker:forEach(fn)
for note, id in pairs(self.voices) do
fn(note, id)
end
end
Create instances with the class name as a function call:
local tracker = NoteTracker()
tracker:add(e.note, id)
end
function onRelease(e)
tracker:remove(e.note)
end
tracker:forEach(function(note, id)
end)
end
void onNote(table e)
event callback that will receive all incoming note-on events if defined.
void onPitchBend(table e)
event callback that will receive all incoming pitch-bend events if defined.
function postEvent(e, delta)
send a script event back to the script engine event queue.
Definition api.lua:853
function changeTune(voiceId, shift, relative, immediate)
change the tuning of specific voice in (fractionnal) semitones.
Definition api.lua:488
Engine Objects
The synthesis engine objects (Program, Layer, Keygroup, Oscillator, etc.) are C++ objects exposed to Lua. They support the same colon method syntax:
osc:setParameter("Gain", 0.5) -- method call
local info = osc.sampleInfo -- property access
print(info.name, info.duration)
A Patch that represents a monotimbral instrument.
Definition Engine.cpp:238
table layers
Layer list for this Program (1-indexed, use Program.layers to get the count)
Definition Engine.cpp:247
Threading and Coroutines
uvi-script uses Lua coroutines internally to implement its cooperative threading model. Each event callback (onNote, onRelease, etc.) runs as a separate coroutine that can be suspended using wait, waitBeat, or waitForRelease.
What you need to know:
- Each callback is an independent thread (coroutine)
- Threads are cooperative - they must explicitly yield using
wait() functions
- Local variables in callbacks are thread-local
- Global variables are shared across all threads
For detailed information about threading and timing, see Threading and Timing.
See Also