uvi-script
Musical event scripting with Lua
Loading...
Searching...
No Matches
Lua Language Reference

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()
function onNote(e)
local id = postEvent(e)
tracker:add(e.note, id)
end
function onRelease(e)
tracker:remove(e.note)
end
function onPitchBend(e)
tracker:forEach(function(note, id)
changeTune(id, e.bend * 2)
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:

local osc = Program.layers[1].keygroups[1].oscillators[1]
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