uvi-script
Musical event scripting with Lua
Loading...
Searching...
No Matches
Asynchronous Operations

Overview

uvi-script provides asynchronous operations to prevent blocking the real-time audio thread when performing file I/O, loading samples, or other time-consuming tasks.

All asynchronous operations run on background threads and communicate their results back to the script thread safely, ensuring real-time audio processing is never interrupted.

Common Patterns

Callback Pattern

All async functions accept a callback that will be called upon completion. This is the recommended pattern for most use cases:

browseForFile("open", "Select file", "", "*.wav", function(task)
if task.success then
print("Selected: " .. task.result)
else
print("Operation cancelled or failed")
end
end)
function browseForFile(mode, title, initialFileOrDirectory, filePatterns, callback)
launch a file chooser to select a file to open or save
Definition api.lua:32

The callback receives an AsyncTask object with the following properties:

  • task.success - Boolean indicating if the operation succeeded
  • task.result - The result data (file path, loaded data, etc.)
  • task.finished - Boolean indicating completion status

Polling Pattern

Alternatively, you can poll the task status in a loop. This is useful when you need to perform other work while waiting:

local task = purge(Program.layers[1])
-- Continue doing other work while the purge happens
while not task.finished do
print("Purging samples...")
wait(100) -- Check every 100ms
end
print("Purge complete!")
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
function purge(target, callback)
purge an/several element(s)/oscillator(s) from its/their content in order to release memory.
Definition api.lua:350
function wait(ms)
suspend the current thread callback execution for the given number of samples.
Definition wrapper.lua:39

Available Async Operations

File Dialogs

Use browseForFile to let users select files without blocking audio:

-- Open file dialog
browseForFile("open", "Select Audio File", "", "*.wav;*.aif", function(task)
if task.success then
local filepath = task.result
print("User selected: " .. filepath)
end
end)
-- Save file dialog
browseForFile("save", "Save Preset", "MyPreset", "*.preset", function(task)
if task.success then
saveData(presetData, task.result)
end
end)
function saveData(data, path, callback)
save data to file.
Definition api.lua:228

Sample Management

Loading and purging samples can take time. Use async operations to avoid audio glitches:

-- Purge samples from a layer
local task = purge(Program.layers[1])
-- Optionally wait for completion
while not task.finished do
wait(50)
end
print("Layer purged successfully")
A layer of sounds.
Definition Engine.cpp:255

State Management

Save and restore the entire ScriptProcessor state, including widget values and custom data stored via onSave / onLoad :

  • saveState — serialize the processor state to a file
  • loadState — restore processor state from a file
-- Let the user pick a destination, then save state
browseForFile("save", "Save State", "MyState", "*.state", function(task)
if task.success then
saveState(task.result, function(t)
if t.success then print("State saved") end
end)
end
end)
-- Restore state from a user-selected file
browseForFile("open", "Load State", "", "*.state", function(task)
if task.success then
loadState(task.result, function(t)
if t.success then print("State restored") end
end)
end
end)
function saveState(path, callback)
save state from file.
Definition api.lua:110
function loadState(path, callback)
load state to file.
Definition api.lua:95

Custom Data I/O

Save and load arbitrary data to and from files:

JSON encoding does not support cyclic references or userdata values. Only plain Lua types (numbers, strings, booleans, and nested tables) are serialized.

-- Save a preset table as JSON
local preset = {name = "Warm Pad", cutoff = 800, resonance = 0.6}
saveData(preset, "/path/to/preset.json", function(task)
if task.success then print("Preset saved") end
end)
-- Load it back
loadData("/path/to/preset.json", function(task)
if task.success then
local p = task.result
print("Loaded preset: " .. p.name)
end
end)
-- Save raw text
saveTextData("some config line\nanother line", "/path/to/config.txt", function(task)
if task.success then print("Text saved") end
end)
-- Load raw text
loadTextData("/path/to/config.txt", function(task)
if task.success then
print("Contents: " .. task.result)
end
end)
function saveTextData(data, path, callback)
save string to file.
Definition api.lua:162
function loadTextData(path, callback)
load string from file.
Definition api.lua:193
function loadData(path, callback)
load data from file.
Definition api.lua:134

Sample Loading

Load audio files into oscillators and configure their playback parameters:

  • loadSample — load an audio file into an oscillator
  • setPlaybackOptions — set start, end, loop points, and direction (units are in samples; loopType: 0=None, 1=Forward, 2=Alternate, 3=OneShot)
  • unpurge — reload previously purged samples
local osc = Program.layers[1].oscillators[1]
-- Load a sample and configure loop points
loadSample(osc, "/path/to/kick.wav", function(task)
if task.success then
-- Set playback: start=0, end=44100, loop Forward from 1000 to 40000, forward
setPlaybackOptions(osc, 0, 44100, 1, 1000, 40000, true, function(t)
if t.success then print("Playback configured") end
end)
end
end)
-- Reload purged samples
unpurge(osc, function(task)
if task.success then print("Samples reloaded") end
end)
function setPlaybackOptions(oscillator, start, end_, loopType, loopStart, loopEnd, playForward, callback)
set playback options inside the oscillator
Definition api.lua:81
function loadSample(oscillator, path, callback)
load a sample inside the oscillator
Definition api.lua:56
function unpurge(target, callback)
unpurge an/several element(s)/oscillator(s) content in order to load their memory.
Definition api.lua:384

Oscillator Introspection

After loading a sample, you can query the oscillator for metadata:

local osc = Program.layers[1].keygroups[1].oscillators[1]
-- Sample information (available on sample-based oscillators)
local info = osc.sampleInfo
if info then
print("Name:", info.name)
print("Duration:", info.duration, "ms")
print("Sample rate:", info.samplerate, "Hz")
print("Channels:", info.channels)
end
-- Slice information (available on Slice oscillators)
if osc.numSlices > 0 then
for i = 1, osc.numSlices do
local slice = osc:getSliceInfo(i)
print("Slice", i, "start:", slice.start, "ms", "duration:", slice.duration, "ms")
end
end

See Oscillator for the full list of properties including purged, looplabInfo, and numSlices.

MIDI File Operations

Load, create, and save MIDI files:

-- Load a MIDI file and iterate its events
loadMidi("/path/to/pattern.mid", function(task)
if task.success then
local seq = task.result
for i, event in ipairs(seq.events) do
print("Event " .. i .. ": type=" .. event.type
.. " note=" .. (event.note or ""))
end
end
end)
-- Create an empty Type-1 MIDI file at 480 PPQ with 2 tracks, then save
createMidiFile(2, 1, 480, function(task)
if task.success then
local seq = task.result
saveMidi(seq, "/path/to/output.mid", function(t)
if t.success then print("MIDI file saved") end
end)
end
end)
function createMidiFile(maxNumTracks, maxNumEvents, callback)
create a midi file asynchronously
Definition api.lua:264
function loadMidi(path, callback)
load a midi file asynchronously
Definition api.lua:249
function saveMidi(midifile, path, callback)
save a midi file asynchronously
Definition api.lua:285

Impulse Response Loading

Load impulse response files into a IReverb effect:

local reverb = Program.inserts[1] -- a SampledReverb effect
loadImpulse(reverb, "/path/to/hall.wav", function(task)
if task.success then print("IR loaded") end
end)
table inserts
all InsertEffect for this node
Definition Engine.cpp:243
SampledReverb.
Definition Engine.cpp:192
function loadImpulse(reverb, path, callback)
load and impulse response inside the reverb.
Definition api.lua:316

Thread Safety

Background Execution
All async operations execute on background threads, completely separate from the real-time audio thread. This ensures audio processing is never interrupted.
Callback Execution
The completion callback is executed on the script's main thread, making it safe to access script variables and call uvi-script API functions.
Data Sharing
Data passed between threads is safely copied. You don't need to worry about race conditions or locks when using async operations.

Best Practices

Check Success Status
Always verify the operation succeeded before using results:
browseForFile("open", "Select file", "", "*.*", function(task)
if task.success then
-- Safe to use task.result
print("File: " .. task.result)
else
-- User cancelled or error occurred
print("No file selected")
end
end)
See also
Async, AsyncTask, browseForFile, purge
spawn, wait, onSave, onLoad