Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,15 @@ micropython-pydevices' `build_mp.py` with the `vst3-engine` variant (no
sockets, SSL or FFI; see [What the shipped engine cannot do](docs/how-it-works/security.md)) and this repository
among its modules, by path. It carries the `audioinstruments` and
`audioeffects` packages frozen in, pydevices (the editor's `appdev` and `multimer`),
the editor's panel (`controlpanel`),
audiodsp's DSP, audioif, LVGL and ulab:

```bash
../micropython-pydevices/build_mp.py --port windows --variant vst3-engine \
--modules audioinstruments,audioeffects,audiodsp,audioif,lvgl,pydevices,ulab,$PWD \
--modules audioinstruments,audioeffects,audiodsp,audioif,controlpanel,lvgl,pydevices,ulab,$PWD \
ENGINE_ICON=$PWD/installer/art/mpvst.ico
../micropython-pydevices/build_mp.py --port unix --variant vst3-engine \
--modules audioinstruments,audioeffects,audiodsp,audioif,lvgl,pydevices,ulab,$PWD
--modules audioinstruments,audioeffects,audiodsp,audioif,controlpanel,lvgl,pydevices,ulab,$PWD
```

It lands in `../micropython-pydevices/builds/<port>/vst3-engine/`, and the
Expand Down
4 changes: 2 additions & 2 deletions docs/get-started/first-program.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ You can play a library instrument in two minutes, and run a script of your own i

MPVST does not install as one plug-in. Every instrument and effect in the library appears in your FX browser under its own name and category. Search for **TR-808**, **Minimoog** or **Tape Delay** and drop it on a track like any other VST3.

Its editor has a patch selector, a bypass switch, an engine-status light and a slider per macro, labelled with whatever that instrument calls its knobs. Click a control to focus it, then scroll: sideways adjusts it, up and down move between controls. If your host provides no editor, its generic parameter editor reaches the same parameters.
Its editor has patch pads, a bypass button, an engine light and an encoder per macro, labelled with whatever that instrument calls its knobs. Drag an encoder up to turn it, or click a control to focus it, then scroll: sideways adjusts it, up and down move between controls. If your host provides no editor, its generic parameter editor reaches the same parameters.

The names are not compiled in. Each is a two-line Python script the plug-in builds when you insert it, and your project saves that script as plain text. Adding an instrument is writing a script, not rebuilding anything.

Expand Down Expand Up @@ -62,7 +62,7 @@ Each new instance reads that file. Toggle `Reload Script` off and on to re-read
What each line is for:

- `on_event` receives every note, pressure and pitch-bend event at its exact sample position, and `vstaudio.output` tells the plug-in which node to pull audio from. The full list of events is in the [script reference](../reference/script-api.md).
- `MACRO_LABELS` and `PATCHES` are the script's declarations to the editor. This script has no macros, so it declares none and the editor draws no sliders.
- `MACRO_LABELS` and `PATCHES` are the script's declarations to the editor. This script has no macros, so it declares none and the editor's encoders stay dark.
- Notes are keyed by pitch here. A script that has to cope with overlapping notes on one pitch would key them by `note_id`, as the bundle's `default_instrument.py` does.

## Hear it without a DAW
Expand Down
9 changes: 5 additions & 4 deletions docs/guides/linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ will leave the plug-in unable to start its engine.

## Controls

Identical to Windows, including the plug-in's own editor: a patch selector, a
Reload button, a Bypass switch, a status light, and a slider per macro under
whatever labels the script declares. Click a control to focus it, then scroll
or swipe - sideways adjusts it, up and down moves between controls.
Identical to Windows, including the plug-in's own editor: patch pads, FINE,
Bypass and Reload buttons, an engine light, and an encoder per macro under
whatever labels the script declares. Drag an encoder up to turn it, or click
a control to focus it, then scroll or swipe - sideways adjusts it, up and
down moves between controls.

The editor is a plain X11 child window driven by the host's own run loop, so a
host that provides no `IRunLoop` gets no editor and the generic parameter
Expand Down
10 changes: 5 additions & 5 deletions docs/guides/windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ not in the library yet. With no script pointed at it that slot produces no
audio, so a slot you did not choose is obvious rather than sounding like an
instrument you meant to load.

The plug-in has an editor of its own: a patch selector, a Reload button, a
Bypass switch, an engine-status light, and a slider per macro, labelled with
whatever names the script declares. Click a control to focus it, then scroll
or swipe - sideways adjusts the focused control, up and down moves between
them. A drag or a burst of scrolling is recorded as one automation edit.
The plug-in has an editor of its own: patch pads, FINE, Bypass and Reload
buttons, an engine light, and an encoder with an LED ring per macro, labelled
with whatever names the script declares. Drag an encoder up to turn it, or
click a control to focus it, then scroll or swipe - sideways adjusts the
focused control, up and down moves between them. A drag or a burst of scrolling is recorded as one automation edit.

The same parameters are all there in the host's generic editor: `Bypass`,
`Reload Script`, `Patch`, and `Macro 01` through `Macro 16` (or the script's
Expand Down
8 changes: 4 additions & 4 deletions docs/how-it-works/editor.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# The editor

Every instance can show a panel of its own: a patch selector, a bypass switch, a reload button, an engine-status light and a slider per macro, labelled with whatever the script declares. This page is how that panel works and what happens when it breaks. It is one generic panel; per-script panels, a shared knob widget, meters and resizable editors aren't built.
Every instance can show a panel of its own, laid out like a hardware instrument. It has sixteen patch pads in eight banks, an encoder with a ring of LEDs for each macro, a dot-matrix display, an engine light, and FINE, BYPASS and RELOAD buttons, labelled with whatever the script declares. This page is how that panel works and what happens when it breaks. It is one generic panel, the `controlpanel` package, frozen into the engine. Per-script panels, meters and resizable editors aren't built.

## Who draws what

Expand All @@ -12,13 +12,13 @@ When no editor is open the engine does no UI work at all: no timers, no painting

## Input

The panel takes pointer and wheel input, not keyboard. Click a control to focus it. The wheel (or a trackpad swipe) acts on the focused control: sideways adjusts it, rightward increasing, and up and down move focus to the previous or next control.
The panel takes pointer and wheel input, not keyboard. Drag an encoder up or right to turn it up. Click a control to focus it: the wheel (or a trackpad swipe) acts on the focused control, sideways adjusting it (rightward increasing) and up and down moving focus to the previous or next control. Double-click an encoder to return it to the value the patch gave it. FINE makes drags and the wheel finer. A pad picks a patch; the bank buttons show another bank's pads.

## Gestures become automation

A slider drag is reported to your host as one begin, a stream of changes, and one end, so it records as a single automation edit. A burst of wheel notches on a focused control is one gesture too: it begins on the first notch and ends after a short quiet. The host echoes each change back through the normal parameter path, and the panel treats the host's value as the truth.
An encoder drag is reported to your host as one begin, a stream of changes, and one end, so it records as a single automation edit. A burst of wheel notches on a focused control is one gesture too: it begins on the first notch and ends after a short quiet. The host echoes each change back through the normal parameter path, and the panel treats the host's value as the truth.

The panel edits the patch selector, the 16 macros, bypass and reload. All of them are ordinary automatable parameters your host's generic editor shows as well.
The panel edits the patch, the 16 macros, bypass and reload. When the patch changes from the host, every encoder and LED ring moves to the new patch's macros, and nothing is sent back. All of them are ordinary automatable parameters your host's generic editor shows as well.

## Reload

Expand Down
2 changes: 1 addition & 1 deletion docs/how-it-works/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ It does not interpret the widgets or reconstruct the UI. The seam is:
[`usermods/vstui/modvstui.c`](../../usermods/vstui/modvstui.c) exposes the shared framebuffer, input, open-state,
edit, and error operations. [`lib/mpvst_board_config.py`](../../lib/mpvst_board_config.py) adapts those operations
to the normal PyDevices display contract, and [`lib/mpvst_editor.py`](../../lib/mpvst_editor.py) and
[`lib/mpvst_panel/`](../../lib/mpvst_panel/) implement the product panel. [The editor](editor.md) covers what the panel does and how it fails.
[`lib/mpvst_panel/`](../../lib/mpvst_panel/) connect the engine to the panel, the `controlpanel` package frozen into the engine. [The editor](editor.md) covers what the panel does and how it fails.

## Parameters, scripts, and state

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/known-limitations.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Known limitations

- There is no host-visible diagnostic string. Both editors show only the ready and error parameters; the bounded diagnostic text is available through the transport API.
- The editor is one generic panel. Per-script panels, a shared knob widget, meters, and resizable or zoomable editors are not built.
- The editor is one generic panel. Per-script panels, meters, and resizable or zoomable editors are not built.
- State embeds one source file, not a dependency bundle. Imports must resolve in the sidecar's own MicroPython environment.
- `MPVST_SCRIPT_PATH` is process-wide, so two instances that follow a developer file cannot follow different scripts; they re-read it on restart and on save. A project that needs per-instance scripts embeds them in state instead, which [`reaper/matrix/build_effect_project.py`](../../reaper/matrix/build_effect_project.py) shows by writing the chunks directly.
- The 2,080 hidden MIDI parameters are standards-compliant and pass the VST3 validator, but their cost at scan and project load hasn't been measured in real DAWs.
Expand Down
4 changes: 3 additions & 1 deletion lib/mpvst_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ def _build_panel(self):
self._adapter = EngineAdapter(vstui, self._script_path)
self._adapter.note_loaded(self._load_error)
vstaudio.observe(self._observe)
self._panel = build(self._adapter, group=group)
# The wheel mapping above moves focus with one wheel, so the panel
# keeps its encoders in edit mode for the other.
self._panel = build(self._adapter, group=group, two_wheels=True)
vstui.error(vstui.ERROR_NONE)

def _observe(self, event_type, channel, note_id, data0, value0, value1,
Expand Down
12 changes: 7 additions & 5 deletions lib/mpvst_panel/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""The editor's Python half: a generic panel and the adapter it edits through.
"""The editor's Python half: the panel and the adapter it edits through.

Kept host-neutral on purpose. `panel` imports only LVGL and `panel_adapter` imports
nothing at all from the engine, so the pair can graduate to a sibling repo as
a portable PyDevices example without surgery. It starts here.
The panel is `controlpanel`, a package of its own beside pydevices' other
LVGL code, frozen into the engine. This package keeps the half that knows
the engine: `panel_adapter.EngineAdapter`, which the panel edits through
and which imports nothing from the engine itself.
"""

from controlpanel import Panel, build

from .panel_adapter import EngineAdapter
from .panel import Panel, build

__all__ = ["EngineAdapter", "Panel", "build"]
Loading