Search guides and the APICtrl K
  1. API reference
  2. Web editor bridge

gui.h

The plugin's native interface.

#include <sonore/gui.h>
On this page

Design notes

A generated plugin's face is an HTML document (the same uihtml the browser preview renders), shown natively in an OS webview and wired to the parameters through a window.sonore bridge that is API-identical to the preview's. That identity is the whole point: the picture the user approved in the studio is the picture that opens in their DAW, running the same markup.

This header is platform-INDEPENDENT. It owns:

  • the parameter/meter state a UI observes,
  • the JS bridge source injected before the page loads,
  • the message protocol in both directions,
  • VU ballistics, computed here so the needle matches the preview's exactly.

The window itself comes from a backend (webview_win32.h and friends).

Threading: everything here is called from the MAIN thread except pushMeter(), which the audio thread calls once per block. That crossing is the only shared state, and it is a pair of atomics, never a lock, never an allocation, on the audio thread.

Types 5

One block's peak and RMS, from whatever the plugin just produced.

This loop existed four times -- CLAP twice, AU, the standalone -- and LV2 was the format that never got a copy, which is why an LV2 plugin's meters had nothing to show. Four copies and one omission is the shape that says the loop belongs somewhere both can reach.

[audio-thread]. Reads the FIRST channel: a meter is a level display, not a correlation analyser, and averaging channels would hide a dead side.

What the audio thread publishes for the meters, and the UI thread reads.

Two halves on two threads. push() is the audio thread's: one block's linear peak and RMS, two relaxed atomic stores, nothing else. tick() and the readers are the UI thread's: tick(dt) runs the ballistics -- a peak that jumps up and decays with a 0.6 s time constant, and a VU needle calibrated to 0 VU at -9 dBFS and settling in 0.35 s -- and level(), db() and vu() read the result. Call tick() on a steady clock (the editors use their ~30 Hz timer) with the seconds since the last call; the readings do not move without it. pushGr() and gr() are the gain reduction a DSP reports, carried by the same needle movement.

The numbers match the browser preview's meter (the same calibration and the same critically damped movement), so a needle looks the same in the studio and in a DAW.

The UI -> AUDIO channel: a single-producer/single-consumer ring.

The main thread writes what the user did to the interface; the audio thread drains it at the top of process(). Lock-free by construction: a mutex here would put the UI thread's scheduling in the audio callback's critical path, which is the classic way a plugin makes a DAW crackle.

Overflow DROPS the newest event rather than blocking or growing. 256 slots is dozens of frames of continuous knob dragging; if a host ever stalls processing for that long, a stale gesture is worth less than a glitch.

But not every event: parameter edits stop short of the last kReserved slots, which only gestures and notes may take. Those come in pairs, and the second half is the one that arrives after a long drag: a key held while a knob was turned on a host that was not draining -- a track whose processing it suspended -- lost its note-off to the knob's edits and sounded for ever once the host drained, and a gesture lost its end and stayed open. A refused edit loses only the host's report of it: every wrapper writes the parameter on the main thread as well as queueing it.

Which keys are sounding, so a UI can draw a keyboard that MOVES.

An on-screen keyboard that lights only under the mouse is half a keyboard: the notes the DAW is playing are the ones a user most wants to see, and without this the plugin's own face is the one place in the session that cannot show them.

Counted, not flagged. Two note-ons on one key -- two MPE fingers, or a sustained chord retriggered -- are two notes, and a single note-off must not put the key out while the other is still held. The counts are touched only by the audio thread; what crosses to the UI is the pair of masks, and they are atomic because the editor reads them on its own clock and a torn 64-bit read would light a key nobody pressed.

A message from the page, already parsed.

Functions 16

↑↓ to move↵ to openEvery public name in the headers, and every guide