lv2_ports.h
The LV2 port map, in one place.
#include <sonore/lv2_ports.h>- 31other declarations
- Guides: Getting started, Platforms and formats
On this page
Design notes
An LV2 UI is a separate module from the plugin and receives port EVENTS by index. It therefore has to agree with the plugin about which index is what -- and until this file existed it did not have to, because it only ever looked at ports 0..numParams-1 and those happen to come first.
The first port that broke that assumption was a meter. lv2_wrapper.h includes lv2_ui.h, so the UI could not see the wrapper's numbering even though it depended on it; the numbering has moved HERE, where both include it and neither can drift from the other.
Everything below is derived from kDesc and the DSP's traits. No state, no ordering hazard, and one answer to "which port is that".
Functions 31
- fn
inline int numControlPorts()lv2::numControlPorts · lv2_ports.h:25 - fn
inline bool isInstrument()lv2::isInstrument · lv2_ports.h:26 - fn
inline int lv2Width()How wide the main bus is in this format.lv2::lv2Width · lv2_ports.h:41More
LV2 ports are FIXED: there is no arrangement to negotiate the way CLAP and VST3 negotiate one, so the bundle has to commit to a width when its TTL is written. Stereo where the DSP allows it, and otherwise the nearest edge of the range it declared -- mono for a mono-only DSP. Surround a host picks stays a negotiated-format feature (CLAP/VST3/AU).
That last clause is the fix. This used to fall back to ONE channel whenever stereo was not allowed, which is right for a mono-only DSP and wrong for a surround-only one: a plugin declaring 4..8 would have been given a single channel it never said it could process. Sharing defaultMainChannels() with the CLAP wrapper means the two cannot disagree about what a descriptor means.
- fn
inline int audioInPorts()lv2::audioInPorts · lv2_ports.h:42 - fn
inline int midiPorts()A MIDI input for every plugin that reads MIDI, the same rule CLAP and VST3 use (clapwrap::wantsMidiIn): an instrument, and an EFFECT whose DSP takes MIDI. Instruments only, a MIDI-reading effect never heard a note.lv2::midiPorts · lv2_ports.h:46 - fn
inline int enabledPorts()Host bypass, LV2's way: a designated lv2:enabled control port. Effects only -- an instrument has no dry signal for a bypass to pass.lv2::enabledPorts · lv2_ports.h:49 - fn
inline int latencyPorts()Latency, LV2's way: a designated lv2:latency OUTPUT control the plugin writes each run(). No port, and an oversampling effect plays early against everything the host time-aligns. Only exists when the DSP declares it.lv2::latencyPorts · lv2_ports.h:53 - fn
inline int scPorts()A sidechain DSP grows a second stereo input pair, marked lv2:isSideChain and connectionOptional -- hosts route a key signal or leave it silent.lv2::scPorts · lv2_ports.h:93 - fn
inline int midiOutPorts()An atom:Sequence OUTPUT port when the plugin emits MIDI.lv2::midiOutPorts · lv2_ports.h:97 - fn
inline int auxOutChannels()Aux output channels, flattened: LV2 has no bus concept, so each aux bus becomes its own run of audio output ports named after it.lv2::auxOutChannels · lv2_ports.h:102 - fn
inline int freeWheelPorts()lv2:freeWheeling, which every plugin gets.lv2::freeWheelPorts · lv2_ports.h:120More
This is LV2's way of saying what clap.render and VST3's processMode say: the host is rendering faster than real time, so nothing has to keep up with a clock and a DSP that trades quality for CPU can stop trading. The SDK grew that flag and wired it into two formats; this is the third, and without it an LV2 bounce silently got the monitoring-quality render.
Unconditional, and connectionOptional, so a host that does not know about it simply leaves the pointer null and the plugin reads "not freewheeling" -- which is the safe answer and the one every existing host gives.
- fn
inline int meterPorts()Two output control ports carrying the block's peak and RMS.lv2::meterPorts · lv2_ports.h:136More
Every other format's editor has meters and an LV2 build's had none, because an LV2 UI is a different module and the only way anything reaches it is a port. Raw peak and RMS rather than a finished needle position: the ballistics live in MeterState, the UI already owns one, and computing them here would be the same curve written twice with the plugin's copy running on the audio thread.
At the END of the port list on purpose. Every index before them is already published in a ttl file, and inserting in the middle would silently renumber somebody's saved session.
- fn
inline int uiAtomPorts()The two ATOM ports an interface talks over.lv2::uiAtomPorts · lv2_ports.h:156More
A control port carries one float, which is enough for a knob and a meter and nothing else. Anything structured -- which sample is loaded, what a waveform looks like -- needs an atom port, and that is the mechanism every LV2 plugin with a file browser uses.
TWO of them, and the second is the reason this took a second attempt. A notification port alone only works for a UI that was already open when the plugin changed; one opened afterwards has missed everything and has no way to catch up. So the UI ASKS on the way up, over the input port, and the plugin answers on the output one.
Present on every plugin rather than only the ones that need it: the port map is published in a ttl file, and a layout that varies with what a DSP happens to declare is a layout that renumbers when somebody adds a feature.
- fn
inline int totalPorts()lv2::totalPorts · lv2_ports.h:158 - fn
inline int portEnabled()lv2::portEnabled · lv2_ports.h:164 - fn
inline int portLatency()lv2::portLatency · lv2_ports.h:165 - fn
inline int portFreeWheel()lv2::portFreeWheel · lv2_ports.h:166 - fn
inline int portAudioInL()lv2::portAudioInL · lv2_ports.h:167 - fn
inline int portScInL()lv2::portScInL · lv2_ports.h:170 - fn
inline int portAudioOutL()lv2::portAudioOutL · lv2_ports.h:171 - fn
inline int portAuxOut0()lv2::portAuxOut0 · lv2_ports.h:172 - fn
inline int portMidiOut()lv2::portMidiOut · lv2_ports.h:173 - fn
inline int portMeterPeak()lv2::portMeterPeak · lv2_ports.h:174 - fn
inline int portMeterRms()lv2::portMeterRms · lv2_ports.h:175 - fn
inline int portUiControl()UI -> plugin: "tell me what you have".lv2::portUiControl · lv2_ports.h:177 - fn
inline int portUiNotify()Plugin -> UI: the answer, and anything that changes later.lv2::portUiNotify · lv2_ports.h:179 - fn
inline int portMidiIn()lv2::portMidiIn · lv2_ports.h:180 - fn
inline int timePorts()The host's timeline, for a DSP that takes the transport: an atom input declaring time:Position, which is what makes a host send one. Last, so no port before it moves for the plugins that have it.lv2::timePorts · lv2_ports.h:184 - fn
inline int portTimeIn()lv2::portTimeIn · lv2_ports.h:185 - fn
inline int grPorts()How far the plugin is turning the signal down, in positive decibels, for a DSP that declares gainReduction(). Its own port rather than a field in the UI atom, for the reason the level has one: an LV2 interface is a separate module that can only see what the ports carry, and a number the host can read too is worth more than a private message. After every other port, so no index moves for the plugins that do not have it, and declared only by a DSP with something to report.lv2::grPorts · lv2_ports.h:193 - fn
inline int portMeterGr()lv2::portMeterGr · lv2_ports.h:194