What happens
Every macro reaches the script as EVENT_PARAMETER with normalized 0.5
immediately after load, so anything the script set for itself — a constructor
kwarg like Limiter(..., gain_db=...), or a value applied from PATCHES — is
overwritten before the first block is rendered.
Observed directly: a script that logs its dispatch prints EV 6 0 0.5,
EV 6 1 0.5, … for every declared macro, in a smoke-host run that sends no
parameter changes at all.
It is not the host
Worth stating, because it was first reported as "the VST host resets every
macro". No host is involved. It is ours, in three places:
src/plugin/source/controller.cpp:72 — each macro parameter is registered
with defaultNormalizedValue 0.5.
src/plugin/source/processor.cpp:25 and :41 — macros_ is initialised
to 0.5f.
src/plugin/source/processor.cpp:478-488 — on load, reload and state
restore, all 16 macro values are replayed as parameter events at sample 0.
The replay is deliberate and its comment explains why: a host does not resend
parameters on load, so without it an automated or reopened instance would
diverge from the script's idea of its own macros. The replay is right. The
value it replays is wrong.
Why 0.5 is the wrong default
The README already says so, about patches: an unset macro should resolve to
what the instrument's own defaults describe, "not the middle of its range,
which is not 'off' and not anything intended". PATCHES exists for exactly
this, and tools/derive_patches.py generates it by measuring the
instrument rather than guessing. Then load throws it away and sends 0.5.
Suggested fix
Seed macros_ from patch 0 of the loaded script instead of 0.5f, so the
replay carries the instrument's own defaults. The residual wart is the
controller's registered default, which VST3 fixes at registration time and
which the controller cannot know before it learns its class from state — so
a host's own "reset parameter to default" would still land on 0.5 until that
is addressed separately.
Until then
Macros are host-owned. A value a script wants to keep belongs in PATCHES,
not in a constructor kwarg, because the kwarg loses to the replay every time.
Found while root-causing a related symptom of the same mechanism: the load-time
0.5 push is also what silenced Phaser and Vibrato, whose rewritten classes
rebuild their node graph on a macro change and hand back a different output
object (fixed in d15a84a).
What happens
Every macro reaches the script as
EVENT_PARAMETERwith normalized 0.5immediately after load, so anything the script set for itself — a constructor
kwarg like
Limiter(..., gain_db=...), or a value applied fromPATCHES— isoverwritten before the first block is rendered.
Observed directly: a script that logs its dispatch prints
EV 6 0 0.5,EV 6 1 0.5, … for every declared macro, in a smoke-host run that sends noparameter changes at all.
It is not the host
Worth stating, because it was first reported as "the VST host resets every
macro". No host is involved. It is ours, in three places:
src/plugin/source/controller.cpp:72— each macro parameter is registeredwith
defaultNormalizedValue0.5.src/plugin/source/processor.cpp:25and:41—macros_is initialisedto
0.5f.src/plugin/source/processor.cpp:478-488— on load, reload and staterestore, all 16 macro values are replayed as parameter events at sample 0.
The replay is deliberate and its comment explains why: a host does not resend
parameters on load, so without it an automated or reopened instance would
diverge from the script's idea of its own macros. The replay is right. The
value it replays is wrong.
Why 0.5 is the wrong default
The README already says so, about patches: an unset macro should resolve to
what the instrument's own defaults describe, "not the middle of its range,
which is not 'off' and not anything intended".
PATCHESexists for exactlythis, and
tools/derive_patches.pygenerates it by measuring theinstrument rather than guessing. Then load throws it away and sends 0.5.
Suggested fix
Seed
macros_from patch 0 of the loaded script instead of0.5f, so thereplay carries the instrument's own defaults. The residual wart is the
controller's registered default, which VST3 fixes at registration time and
which the controller cannot know before it learns its class from state — so
a host's own "reset parameter to default" would still land on 0.5 until that
is addressed separately.
Until then
Macros are host-owned. A value a script wants to keep belongs in
PATCHES,not in a constructor kwarg, because the kwarg loses to the replay every time.
Found while root-causing a related symptom of the same mechanism: the load-time
0.5 push is also what silenced Phaser and Vibrato, whose rewritten classes
rebuild their node graph on a macro change and hand back a different
outputobject (fixed in
d15a84a).