Skip to content

MIDI

Plinken supports full MIDI recording, editing, and playback with a built-in synthesizer and routing to external instrument plugins (AU, CLAP, VST3). MIDI data is stored in regions on tracks and processed at tick-level precision (960 PPQ).

PropertyTypeDefaultDescription
track_counti640Number of MIDI tracks
recordingboolfalseWhether MIDI recording is active
selected_track_idi64-1Currently selected MIDI track (−1 = none)
synth_volumef64Built-in synth master volume (0.0–1.0)
synth_attackf64Synth ADSR attack time (seconds)
synth_decayf64Synth ADSR decay time (seconds)
synth_sustainf64Synth ADSR sustain level (0.0–1.0)
synth_releasef64Synth ADSR release time (seconds)
-- Read MIDI module state
local count = plinken.get("midi", "track_count")
local recording = plinken.get("midi", "recording")
local selected = plinken.get("midi", "selected_track_id")
  1. Create or select a MIDI track
  2. Arm the track for recording
  3. Press R to arm transport recording
  4. Press Space to start — play your MIDI controller
  5. Press Space to stop — the recorded MIDI becomes a region on the track
ModeDescription
ReplacePunch holes in existing regions within the recording range, then add new regions
MergeLayer new recordings on top of existing regions
CommandDescription
midi:start-recordingStart recording MIDI on armed tracks
midi:stop-recordingStop recording and create MIDI regions
midi:process-recordingProcess MIDI recording buffer (called internally each frame)

MIDI recording uses a lock-free ring buffer architecture for glitch-free capture:

  1. Audio thread — Pushes RecordingEvent to ring buffer (~100ns, lock-free)
  2. Background thread — Drains events and builds notes incrementally (matching note-on/off pairs)
  3. Main thread — Calls stop_recording() to finalize regions
-- Start MIDI recording (usually triggered by transport)
pl.cmd("midi:start-recording")
-- Stop and create regions
pl.cmd("midi:stop-recording")

Each track maintains a persistent recording counter. Successive recordings on the same track produce incrementing names: Piano_1, Piano_2, Piano_3, etc.

MIDI playback routes note events to either:

  1. The track’s instrument plugin (AU, CLAP, or VST3) — if assigned
  2. The built-in synthesizer — as fallback when no plugin is assigned
Event TypeDescription
NoteOnStart a note (track, channel, pitch, velocity)
NoteOffEnd a note (track, channel, pitch)
ControlChangeCC event (track, channel, cc, value)
PitchBendPitch bend (track, channel, value as i16)
AllNotesOffPanic — silence all notes on a track

Each MIDI track has its own properties independent of timeline tracks:

PropertyTypeDefaultDescription
idu8autoUnique track ID
namestringTrack name
armboolfalseArmed for recording
muteboolfalseMuted
soloboolfalseSoloed
volumef641.0Track volume (0.0–2.0)
panf640.0Track pan (−1.0 to +1.0)

Open the MIDI Editor from View > Editor. The editor provides multiple views:

The piano roll shows MIDI notes on a grid with pitch on the vertical axis and time on the horizontal axis. Click to create notes, drag to move or resize them. Velocity is shown as note color intensity.

A grid-based view optimized for drum patterns. Each row represents a drum sound (mapped to MIDI notes), and you toggle hits on the grid.

A tabular view showing all MIDI events as a sortable, editable list. Useful for precise numeric editing of:

  • Note positions (tick)
  • Pitches (MIDI note number)
  • Velocities (0–127)
  • Durations (ticks)

Edit MIDI CC (Continuous Controller) data as curves. Draw and modify automation shapes for expression, modulation, sustain, and other controllers.

MIDI regions contain note events and CC data:

PropertyTypeDescription
start_ticki64Region start position in ticks
lengthi64Region length in ticks (min: 240)
clip_offseti64Offset into source clip
eventsEventTuple[]Original MIDI note events
events_quantizedEventTuple[]Quantized events (used for playback)
cc_eventsCCEventTuple[]CC automation events

MIDI regions support quantization. The events array holds the original recorded notes, while events_quantized holds the quantized version used during playback. This allows non-destructive quantization — you can always revert to the original timing.

Open the virtual MIDI keyboard with the Music Keys panel. This lets you play notes using your computer keyboard when no MIDI controller is connected.

Plinken includes a built-in polyphonic synthesizer as the default sound source for MIDI tracks without an assigned plugin instrument.

PropertyTypeRangeDescription
synth_volumef320.0–1.0Master volume
synth_attackf32≥ 0.0ADSR attack time (seconds)
synth_decayf32≥ 0.0ADSR decay time (seconds)
synth_sustainf320.0–1.0ADSR sustain level
synth_releasef32≥ 0.0ADSR release time (seconds)
-- Adjust built-in synth ADSR
plinken.set("midi", "synth_attack", 0.01)
plinken.set("midi", "synth_decay", 0.2)
plinken.set("midi", "synth_sustain", 0.6)
plinken.set("midi", "synth_release", 0.3)
-- Set synth volume
plinken.set("midi", "synth_volume", 0.8)

Configure MIDI input and output devices in Settings > MIDI. Each device can be independently enabled for different protocol types.

PropertyTypeDescription
device_idstringSystem device identifier
namestringHuman-readable device name
midiboolRoute standard MIDI messages
mcboolRoute MIDI Clock
mtcboolRoute MIDI Time Code
mmcboolRoute MIDI Machine Control

Each track can filter which MIDI input device and channel it listens to:

PropertyTypeDefaultDescription
midi_input_filterstring"all""all", "none", or a specific device_id
midi_channel_filteru800 = All channels, 1–16 = specific channel
-- Set track to listen only to a specific device
pl.cmd("track:set-midi-input-filter", {
track_id = 1,
filter = "Arturia KeyLab 61"
})
-- Filter to MIDI channel 1 only
pl.cmd("track:set-midi-channel-filter", {
track_id = 1,
channel = 1
})

Plinken can send MIDI synchronization to external hardware:

ProtocolDescriptionCommand
MTC (MIDI Time Code)Positional sync — sends SMPTE timecodetransport:toggle-mtc
MC (MIDI Clock)Tempo sync — sends 24 ppqn clocktransport:toggle-mc

Per-device sync routing is configured in the MIDI device settings. You can enable MTC or MC independently for each output device.

-- Check which devices have MTC enabled
local mtc = plinken.get("transport", "mtc_enabled")
-- Toggle MTC output
pl.cmd("transport:toggle-mtc")
-- Set up: arm track, arm transport, play
pl.cmd("track:toggle-record", { track_id = 1 })
pl.cmd("transport:toggle-record")
pl.cmd("transport:toggle-play")
-- ... play your MIDI controller ...
-- Stop recording
pl.cmd("transport:toggle-play")
-- Assign an AU instrument to track 1
pl.cmd("track:set-instrument", {
track_id = 1,
plugin_id = "com.apple.DLSSynth",
format = "AU"
})
-- Slow attack, long release pad
plinken.set("midi", "synth_attack", 0.5)
plinken.set("midi", "synth_decay", 0.3)
plinken.set("midi", "synth_sustain", 0.7)
plinken.set("midi", "synth_release", 1.5)