Report the instrument's automatable parameters to the host under a frozen id table, in signal-flow order, with real units

42 of 44 ids issued: pitch key-track and Trigger length stay reserved
pending a live path. Master gain reclassified Live — it never reloaded.
This commit is contained in:
2026-08-02 15:14:16 -04:00
parent c7afa3a80f
commit bfaa0f2614
38 changed files with 1742 additions and 218 deletions
+64
View File
@@ -0,0 +1,64 @@
// param_units.h — the plain-value layer the host reads a parameter through: the unit category,
// the plain range, and the norm <-> plain pair. `toPlain` IS the taper's forward map and
// `toNormalized` its inverse, so the host's normalization, the knob's needle angle and the
// overlay node's position are the SAME function rather than three that agree today.
#pragma once
#include "core/instrument/ui/deck_groups.h" // DeckParam
namespace reasampler::instrument::param {
using ui::DeckParam;
// The eight DISPLAY categories. A category fixes the units string and the digit precision; the
// norm <-> plain LAW is per control, because three of the dimensionless controls (the curve
// exponents, Q, drive) share a display and share no law.
enum class UnitKind {
Time, // ms, 0..10000
Semitones, // st, -24..+24, always signed
PercentUnipolar, // %, 0..100
PercentKeyTrack, // %, 0..200
PercentBipolar, // %, -100..+100, always signed
PercentRate, // %, 50..200, one decimal
Decibels, // dB, -60..+24, always signed; norm 0 reads -inf
Hertz, // Hz, 20..20000
Dimensionless, // no unit, two decimals
};
struct PlainRange {
double min = 0.0;
double max = 1.0;
};
UnitKind unitKindFor(DeckParam deck);
// The units string ParameterInfo carries — "" for the dimensionless category. Carried SEPARATELY
// from the digits, which is the SDK's own convention (RangeParameter::toString prints the number;
// the Parameter constructor takes units as its own argument).
const char* unitStringFor(DeckParam deck);
PlainRange plainRangeFor(DeckParam deck);
// A straight line drawn in a host automation lane is NOT linear in these plain units, and that is
// deliberate: exponential in ms, linear in octaves on cutoff, linear in dB on master gain, a
// linear pitch glide on rate, and slow-near-zero on the two centre-expanded semitone throws. It
// follows from reporting real units over a musically-shaped taper; the remedy for a user who
// wants a literal-units ramp is the host's own curve tools, never a change to the taper.
double toPlain(DeckParam deck, double normalized);
double toNormalized(DeckParam deck, double plain);
// The filter's four tone controls STORE their normalized position (payload v9), so their default
// normalized value is that stored double verbatim and no taper participates in a host's
// reset-to-default. Reporting Hz / Q / drive depth for them means CALLING their frozen laws, not
// replacing them.
bool storesNormalized(DeckParam deck);
// The default, read off a default-constructed PlaySeconds — there is no second table of defaults,
// and no normalized default is ever written as a literal. defaultNormalized is COMPUTED as
// toNormalized(defaultPlain) for every tapered control, which is what makes a host's
// reset-to-default and the editor's double-click land on the same value.
double defaultPlain(DeckParam deck);
double defaultNormalized(DeckParam deck);
} // namespace reasampler::instrument::param