Skip to content

Macros are replayed at 0.5 on load, so a script's own defaults never survive #7

Description

@bdbarnett

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 :41macros_ 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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions