uvi-script
Musical event scripting with Lua
Loading...
Searching...
No Matches
MIDI Event Generation

Overview

Beyond receiving MIDI events through callbacks like onNote and onController, uvi-script can also generate outgoing MIDI events. This is useful for building arpeggiators, chord generators, MIDI processors, and automation scripts.

There are two approaches:

  • Helper functions — concise, dedicated functions for each event type
  • postEvent() — low-level, allows full control over all event fields

Helper Functions

These convenience functions wrap postEvent with the correct event type. All accept optional channel and input parameters for routing to specific Parts.

Control Change

Send a MIDI CC message:

function onNote(e)
-- send mod wheel (CC1) value 100 on the same channel
controlChange(1, 100, e.channel)
end
-- reset all controllers on note release
function onRelease(e)
for cc = 1, 127 do
end
end
void onNote(table e)
event callback that will receive all incoming note-on events if defined.
function postEvent(e, delta)
send a script event back to the script engine event queue.
Definition api.lua:842
function controlChange(cc, val, ch, inp)
sends a ControlChange event.
Definition api.lua:1033
See also
controlChange, onController

Pitch Bend

Send a pitch bend message. The value is normalized to [-1.0; 1.0]:

-- pitch bend ramp on each note
function onNote(e)
spawn(function()
for i = 0, 100 do
pitchBend(i / 100) -- ramp from 0 to +1
wait(10)
end
pitchBend(0) -- reset
end)
end
function pitchBend(b, ch, inp)
sends a PitchBend event.
Definition api.lua:1064
function wait(ms)
suspend the current thread callback execution for the given number of samples.
Definition wrapper.lua:39
function spawn(fun,...)
Launch a function in a separate parallel execution thread (deferred execution)
Definition api.lua:1165
See also
pitchBend, onPitchBend

Program Change

Send a program change message:

-- switch program based on velocity range
function onNote(e)
if e.velocity > 100 then
programChange(1) -- high velocity: program 1
else
programChange(0) -- low velocity: program 0
end
end
function programChange(val, ch, inp)
sends a ProgramChange event.
Definition api.lua:1049
See also
programChange, onProgramChange

Aftertouch

Send channel aftertouch or polyphonic aftertouch:

-- convert velocity to channel aftertouch
function onNote(e)
afterTouch(e.velocity)
end
-- per-note pressure from velocity
function onNote(e)
polyAfterTouch(e.velocity, e.note)
end
function afterTouch(v, ch, inp)
sends an AfterTouch event.
Definition api.lua:1079
function polyAfterTouch(v, n, ch, inp)
sends a PolyAfterTouch event.
Definition api.lua:1095
See also
afterTouch, polyAfterTouch, onAfterTouch, onPolyAfterTouch

Modifying Incoming Events

Instead of generating new events, you can modify incoming events before forwarding them with postEvent. This is the most efficient way to transform MIDI data:

-- Remap CC1 (mod wheel) to CC11 (expression)
function onController(e)
if e.controller == 1 then
e.controller = 11
end
end
void onController(table e)
event callback that will receive all incoming control-change events when defined.

You can modify any field of the event table before posting it:

-- Transpose all notes up one octave
function onNote(e)
e.note = e.note + 12
end
function onRelease(e)
e.note = e.note + 12
end
void onRelease(table e)
event callback executed whenever a note off message is received.
Note
When modifying onNote events, remember to apply the same transformation in onRelease so the engine can match the note-off to the correct voice.

Using postEvent Directly

The helper functions are shortcuts for postEvent. You can achieve the same result (and more) by constructing event tables directly:

-- these two are equivalent:
postEvent({type = Event.ControlChange, controller = 1, value = 64})
Event types.
Definition Engine.cpp:548

Using postEvent directly gives access to additional fields like layer and delta (timing offset in ms):

-- send CC to a specific layer with a 10ms delay
type = Event.ControlChange,
controller = 1,
value = 100,
layer = 2
}, 10)

Raw MIDI with postMidiEvent

For playback of MIDI files or raw MIDI data, use postMidiEvent with MidiEvent objects:

local event = MidiEvent(0, Event.NoteOn, 0, 60, 100)
A Midi Event.
Definition Engine.cpp:566
void postMidiEvent(MidiEvent e)
send a MidiEvent directly to the script engine event queue.
Note
Events posted via postMidiEvent don't have voice IDs. For advanced voice tracking, use playNote and releaseVoice instead.
See also
MIDI, Events, Event Callbacks, Asynchronous Operations