VST3: optional self-contained VSTGUI editor#139
Conversation
Adds an embedded VST3 editor built on VSTGUI (bundled with the VST3 SDK), so avnd VST3 plug-ins get a real GUI in any host without Qt. - vstgui_editor.hpp: a custom IPlugView hosting a VSTGUI CFrame, auto-laying out one self-drawing widget per parameter (CKnob for continuous, CCheckBox for toggles, CTextLabel names), bound to the controller via the standard IEditController + IComponentHandler API. No bitmaps, no uidesc. - Controller<T>::createView returns it when built with AVND_VST3_VSTGUI. - avendish.vst3.cmake: AVND_ENABLE_VST3_VSTGUI option (default ON); links the `vstgui` target and defines AVND_VST3_VSTGUI when the VST3 SDK was configured with SMTG_ADD_VSTGUI=ON. First cut: generic auto-layout. Honoring the halp `struct ui` layout and theming are follow-ups.
VSTGUI::init = initPlatform + CFontDesc::init (global fonts). Calling only initPlatform left kNormalFont null, crashing CParamDisplay's ctor at the first label. Also init on macOS (bundleEntry's CFBundleRef) and Windows (module HINSTANCE via GetModuleHandleEx), which never initialised VSTGUI.
|
wops, we need to synchronize because I had an agent working on this but at a more general level than just VST / VST3 |
| Steinberg::IPlugFrame* plugFrame{}; | ||
| std::atomic<Steinberg::uint32> m_ref{1}; | ||
|
|
||
| static constexpr int cols = 4; |
There was a problem hiding this comment.
very strange that this would be hardcoded, I think we have to make it so that things are flexible for the end user
| extern "C" AVND_EXPORTED_SYMBOL bool bundleEntry(CFBundleRef ref) | ||
| { | ||
| #if defined(AVND_VST3_VSTGUI) | ||
| VSTGUI::init(ref); |
There was a problem hiding this comment.
this seems to use the official vstgui backend from steinberg which is really terrible for making UIs (and won't help us when we want to use CLAP etc.)
There was a problem hiding this comment.
Real Edu here: What do you propose as a cross-platform UI tool for plugins?
There was a problem hiding this comment.
BTW, the decision was based on portability. The first TimeMachine UI indeed looks bad, but it is just a bunch of sliders for now.
There was a problem hiding this comment.
avendish shouldn't enforce a specific UI backend in its implementation, it should be entirely up to the end-user to build their UI with the lib that fits their requirements, as some people will prefer a slim and basic UI in C++, others a more complex one that uses a proper toolkit that has internationalization etc, others will prefer using a built-in web UI that uses a webview, etc.
For making a simple plug-in that will work across all backends the best way is to follow what DPF is doing : https://github.com/DISTRHO/DPF
- pugl or fenster for opening a window with potentially a OpenGL or Vulkan context on it
- something like nuklear or clay for the UI rendering on that context if it's something simple or visage for something more beautiful but heavier: https://visage.dev/examples/Showcase/
- something like https://github.com/memononen/nanovg or https://github.com/a-e-k/canvas_ity for classical canvas-like painting depending on whether opengl is desired or not
There was a problem hiding this comment.
other good options for context usecase:
https://github.com/thorvg/thorvg https://github.com/sammycage/plutovg
my main requirement for the one I'm using for examples is that it can work all the way from esp32 to wasm as those are all avendish-supported platforms, so if someone wants to make "an ui for my algorithm" then the "default choice" should not cause build errors on any of those (which restricts to simple, pixel-graphics-only framework)
|
@jcelerier, if you want, I can hold this until you finish the UI part on your end |
… knobs with labels - Plugin name (avnd::get_name<T>) shown in a title bar on top. - Toggles render as on/off text buttons instead of a tiny checkbox. - Continuous params render as filled corona knobs (accent color, thicker handle) with the parameter name centered below.
Toggles reported stepCount=0 (no range.step), so hosts and the VSTGUI editor drew them as continuous knobs. Set stepCount=1 for bool_parameter controls -> host generic UIs show a checkbox and the editor a toggle.
…able The on/off button used the default (light) gradient, so the light 'off' label was invisible on a white fill. Set explicit dark (off) and accent (on) gradients with contrasting text.
ParameterInfo has no notion of a momentary control, so the generic VSTGUI editor drew every stepCount==1 parameter as a latched on/off toggle. Add ControllerCommon::isMomentaryParameter(), overridden by the typed Controller from the control's widget enumerators (pushbutton / bang / impulse), and use it in the editor to pick CTextButton kKickStyle: 1 while pressed, 0 on release. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8rKjEZfGtrHLkU5iXR4gn
Adds a value readout label per continuous parameter (formatted via the controller's getParamStringByValue), refreshed in valueChanged as the user turns the knob.
processControl only applied the last point of each parameter queue. VSTGUI's kick-style buttons emit press (1) and release (0) back-to-back on mouse-up, so both landed in the same block and the processor only ever saw 0 - momentary buttons never fired at all. For bool parameters, apply the maximum point of the block's queue instead (latching the press for the whole block) and, when the queue ends released, defer the false assignment to the start of the next block. Continuous parameters keep the last-point behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8rKjEZfGtrHLkU5iXR4gn
… disabled) The control-object overload of map_control_to_01 gated itself on an unqualified map_control_to_01(t.value), which has no viable overload (the value overloads need explicit <T>), so the requires never held and the overload was always disabled. Every caller (VST3 getParamNormalized, ...) then got 0. Qualify the requires with <T>.
Use 6 columns instead of 4 when a plugin exposes more than 20 parameters, keeping the window height manageable (e.g. 40 parameters: 7 rows instead of 10). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K8rKjEZfGtrHLkU5iXR4gn
Hi @jcelerier — Claude here, assistant to Edu Meneses (SAT, Montréal). This adds an embedded VST3 editor built on VSTGUI (bundled with the VST3 SDK), so avnd VST3 plug-ins get a real GUI in any host, without Qt.
Controller::createViewwas returningnullptr; this fills it in.What it does
include/avnd/binding/vst3/vstgui_editor.hpp: a customIPlugViewhosting a VSTGUICFrame, auto-laying-out one self-drawing widget per parameter (CKnob/CCheckBox/CTextLabel), bound through the standardIEditController+IComponentHandlerAPI. No bitmaps, no.uidesc.Controller<T>::createViewreturns it underAVND_VST3_VSTGUI.avendish.vst3.cmake:AVND_ENABLE_VST3_VSTGUIoption (default ON); links thevstguitarget when the SDK is built withSMTG_ADD_VSTGUI=ON.prototype.cpp.in:VSTGUI::init/exitin the module entries (Linux dlopen handle, macOSCFBundleRef, Windows moduleHINSTANCE).Independent bug fix (cherry-pickable on its own — commit for "define DEVELOPMENT only in Debug")
DEVELOPMENTwas defined unconditionally, which arms the VST3 SDK'sFDebugBreakSIGTRAP assertions in release builds → crashes on teardown for all avnd VST3 plug-ins (hit this closing the editor). Now Debug-only.Notes / for discussion
dynamic_cast/throw), so the addon must setAVND_USES_EXCEPTIONS. Should the editor force that automatically when enabled?IRunLoop/IEventHandler/ITimerHandlerare inVSTGUI::X11::on 3.7.x (what I built against) butVSTGUI::in newer VSTGUI — you'll know which VSTGUI avnd targets; happy to adjust the namespace./Zc:preprocessor, a force-includedvstgui_assertshim) works around VSTGUI+MSVC variadic-macro issues under the Visual Studio generator; open to a cleaner fix.struct uilayout and theming are natural follow-ups.Tested
Builds on Linux/macOS/Windows CI. Editor open/close verified in Reaper (Linux) and via a minimal X11 host (attach → service run loop → teardown; no leaks or crashes).
Reference build & CI: https://github.com/edumeneses/TimeMachine
🤖 Generated with Claude Code