Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
host_parameter_edit.hpp
Go to the documentation of this file.
1#pragma once
2
3// host_parameter_edit.hpp — format-neutral provenance for editor parameter
4// writes (WAH-5).
5//
6// The problem this replaces: the VST3 adapter decided a StateStore write came
7// from the editor by checking whether a GESTURE was open. That is an inference,
8// not a fact, and it is wrong in a case users hit routinely — host automation
9// arriving while the user holds a knob. The automation write landed in the
10// store, the "is a gesture open?" test said yes, and the adapter echoed the
11// host's own value back at it through performEdit(). It also read
12// `get_normalized(id)` twice per reported edit, so a write landing between the
13// two reads produced a performEdit and a setParamNormalized carrying DIFFERENT
14// values for one logical change.
15//
16// The fix is to record provenance where it is known — at the write — instead of
17// guessing at it afterwards:
18//
19// * host-originated writes run inside `ScopedHostParameterWrite`;
20// * processor-originated writes run inside `ScopedProcessorParameterWrite`;
21// * everything else on the editor thread is, by elimination, an editor write.
22//
23// This is the model CLAP already used (thread id + explicit host/process
24// flags), lifted out of clap_adapter.cpp so VST3 gets it too. CLAP keeps its
25// own outbound SPSC queue for DELIVERY — the protocol needs events placed in
26// the process block — and only shares the provenance DECISION. That split is
27// deliberate: absorbing CLAP's queue here would weaken its ordering guarantees
28// for no gain.
29
31#include <pulp/state/store.hpp>
32
33#include <functional>
34#include <thread>
35
36namespace pulp::format {
37
50public:
51 ScopedHostParameterWrite() noexcept { ++depth(); }
54 ~ScopedHostParameterWrite() noexcept { --depth(); }
55
56 static bool active() noexcept { return depth() > 0; }
57
58private:
59 static int& depth() noexcept {
60 thread_local int d = 0;
61 return d;
62 }
63};
64
70public:
71 ScopedProcessorParameterWrite() noexcept { ++depth(); }
74 ~ScopedProcessorParameterWrite() noexcept { --depth(); }
75
76 static bool active() noexcept { return depth() > 0; }
77
78private:
79 static int& depth() noexcept {
80 thread_local int d = 0;
81 return d;
82 }
83};
84
91
115public:
118 struct Callbacks {
120 std::function<void(state::ParamID)> begin;
124 std::function<void(state::ParamID, float normalized, float plain)> value;
126 std::function<void(state::ParamID)> end;
127 };
128
131
134
137 std::thread::id editor_thread() const noexcept { return editor_thread_; }
138
142 bool would_report() const noexcept;
143
144private:
145 void on_begin(state::ParamID id);
146 void on_end(state::ParamID id);
147 void on_value(state::ParamID id, float plain);
148 void report(state::ParamID id, float plain);
149
150 state::StateStore& store_;
151 Callbacks callbacks_;
152 std::thread::id editor_thread_;
153 state::ListenerToken listener_;
154};
155
156} // namespace pulp::format
HostParameterEditBridge(const HostParameterEditBridge &)=delete
HostParameterEditBridge & operator=(const HostParameterEditBridge &)=delete
HostParameterEditBridge(state::StateStore &store, Callbacks callbacks)
bool would_report() const noexcept
Whether a call on THIS thread right now would be reported.
std::thread::id editor_thread() const noexcept
The thread this bridge treats as the editor.
Definition host_parameter_edit.hpp:137
~ScopedHostParameterWrite() noexcept
Definition host_parameter_edit.hpp:54
ScopedHostParameterWrite() noexcept
Definition host_parameter_edit.hpp:51
static bool active() noexcept
Definition host_parameter_edit.hpp:56
ScopedHostParameterWrite & operator=(const ScopedHostParameterWrite &)=delete
ScopedHostParameterWrite(const ScopedHostParameterWrite &)=delete
ScopedProcessorParameterWrite(const ScopedProcessorParameterWrite &)=delete
ScopedProcessorParameterWrite & operator=(const ScopedProcessorParameterWrite &)=delete
ScopedProcessorParameterWrite() noexcept
Definition host_parameter_edit.hpp:71
static bool active() noexcept
Definition host_parameter_edit.hpp:76
~ScopedProcessorParameterWrite() noexcept
Definition host_parameter_edit.hpp:74
Centralized parameter storage with lock-free value access.
Definition store.hpp:164
What the product grants a device: the quota table indexed by capability tier and live thermal state.
Definition aax_editor.hpp:31
bool in_non_editor_parameter_write() noexcept
True when the calling thread is inside a host- or processor-originated parameter write,...
Definition host_parameter_edit.hpp:87
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48
uint32_t ParamID
Unique parameter identifier, stable across plugin versions.
Definition parameter.hpp:55
STL namespace.
Protocol callbacks, in the order they are invoked for a gesture: begin → value(one or more) → value →...
Definition host_parameter_edit.hpp:118
std::function< void(state::ParamID, float normalized, float plain)> value
Report a value.
Definition host_parameter_edit.hpp:124
std::function< void(state::ParamID)> begin
Host gesture open (VST3 beginEdit, CLAP GestureBegin).
Definition host_parameter_edit.hpp:120
std::function< void(state::ParamID)> end
Host gesture close (VST3 endEdit, CLAP GestureEnd).
Definition host_parameter_edit.hpp:126