Writes editor-store changes back to the AAX parameter manager without the two mirrors chasing each other. More...
#include <aax_editor.hpp>
Public Types | |
| using | ValueReader = std::function<bool(const char* aax_param_id, float& out_value)> |
| Reads the value the AAX parameter manager currently holds. | |
| using | ValueWriter = std::function<bool(const char* aax_param_id, float value)> |
| Requests value for aax_param_id — AAX_IParameter::SetValueWithFloat. | |
Public Member Functions | |
| ParameterMirror (const PluginDefinition &definition, ValueReader reader, ValueWriter writer) | |
| bool | on_store_value_changed (state::ParamID id, float store_value) |
| Editor store → AAX parameter manager. | |
| std::uint64_t | suppressed_count () const |
| Echoes absorbed. | |
| std::uint64_t | refused_count () const |
| Corrective writes the fuse refused. | |
| bool | is_fused (state::ParamID id) const |
| Whether id's fuse is currently open, i.e. | |
Static Public Attributes | |
| static constexpr std::uint32_t | kCorrectionFuseLimit = 8 |
| Consecutive corrective writes of the same store value before the fuse opens on a parameter. | |
Writes editor-store changes back to the AAX parameter manager without the two mirrors chasing each other.
The manager is the value authority; the editor's store is a mirror. Both directions are live at once, so the write-back needs a rule that terminates:
The rule is a statement about the two values rather than an "I am syncing" flag, so it holds wherever and whenever the store listener lands. It is load-bearing rather than a mere optimization: StateStore::set_value() notifies unconditionally, even when the value did not change, so the echo has no fixed point of its own and would ping-pong at the host's dispatch rate.
The two sides are not in the same coordinate system, so the rule cannot compare them directly. Both legs are lossy, in different ways:
So for, say, an Integer parameter with no explicit step, the two are structurally different values and aax_value == store_value is never true. Nor does writing the store's value fix that: SetValueWithFloat does not store what it is given (see above), and denormalize(normalize(k)) != k in float for many k, so the correction lands somewhere else again and the next echo re-triggers it. That is an unbounded automation write stream, not a mirror.
The rule therefore never compares the two reals. It asks whether there is anything for a write to do, which is two separate questions — both needed, neither implying the other:
Exact float comparison is right here rather than an epsilon: the taper is a pure function of the range, so two values that normalize to the same float are the same parameter value as far as AAX is concerned. An epsilon would only invent a second, arbitrary notion of "close enough" on top of the one AAX already defines.
Free of AAX types: the manager is reached through two sinks, so the whole contract is exercised by tests that never see the SDK.
| using pulp::format::aax::ParameterMirror::ValueReader = std::function<bool(const char* aax_param_id, float& out_value)> |
Reads the value the AAX parameter manager currently holds.
Returns false — leaving out_value untouched — when the value could not be read.
| using pulp::format::aax::ParameterMirror::ValueWriter = std::function<bool(const char* aax_param_id, float value)> |
Requests value for aax_param_id — AAX_IParameter::SetValueWithFloat.
Returns false when the request was refused.
|
inline |
|
inline |
Whether id's fuse is currently open, i.e.
the next repeat of the same value would be refused.
|
inline |
Editor store → AAX parameter manager.
Wire to a StateStore value listener. Returns true when a write was issued.
|
inline |
Corrective writes the fuse refused.
Non-zero means some parameter's round trip does not converge — a bug here or a host quirk, never routine.
|
inline |
Echoes absorbed.
Non-zero is the healthy case — it counts the write-backs the rule swallowed rather than bouncing at the host.
|
staticconstexpr |
Consecutive corrective writes of the same store value before the fuse opens on a parameter.
A converging correction takes exactly one; a knob drag carries a new value each time and resets the count. Only a value that is written, echoed, and written again unchanged accumulates — which is precisely the shape of a mirror that is not converging.