Files
reasampler/src/core/instrument/param/param_units.h
T
daniel de5654fb6f Service both VST3 parameter channels, and promote pitch key-track and Trigger length so all 44 ids issue
The SDK's own single-component sample drains inputParameterChanges in
process() and implements setParamNormalized; automation was reading the
GUI channel alone. The audio thread now patches a block it solely owns.
2026-08-02 16:22:17 -04:00

76 lines
3.7 KiB
C++

// 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);
// WHERE a control's value actually lives. The host's read and write paths branch on this, and
// the exposed set is asserted against it: a control promoted into the list with no home would
// otherwise no-op silently in BOTH directions, with nothing to catch it at compile time.
enum class ValueHome {
None, // not a scalar control at all — a toggle, a radio, a curve-popup cell
ParamSetNorm, // the filter's four: the stored double IS the normalized position
ParamSet, // every other knob the parameter set carries
InstanceScalar, // beside the parameter set: master gain, and the pitch key-track scalar
};
ValueHome valueHomeFor(DeckParam deck);
// 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