tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_ExternalAutomatableParameter.h
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
16 private juce::AsyncUpdater
17{
18public:
20 const juce::String& name,
21 ExternalPlugin& owner, int parameterIndex_,
22 juce::Range<float> valueRangeToUse)
23 : AutomatableParameter (parameterID, name, owner, valueRangeToUse),
24 parameterIndex (parameterIndex_)
25 {
26 if (auto vstXML = owner.getVSTXML())
27 {
28 param = vstXML->getParamForID (parameterIndex, nullptr);
29
30 if (param != nullptr)
31 {
32 displayName = param->name;
33
34 for (auto group = param->parent; group != nullptr; group = group->parent)
35 displayName = group->name + " >> " + displayName;
36
37 valueType = vstXML->getValueType (param->type);
38 }
39 }
40
41 registerAsListener();
42 }
43
45 {
48 unregisterAsListener();
49 notifyListenersOfDeletion();
50 }
51
52 void registerAsListener()
53 {
54 if (auto p = getParam())
55 p->addListener (this);
56 else
58 }
59
60 void unregisterAsListener()
61 {
62 if (auto p = getParam())
63 p->removeListener (this);
64 }
65
66 std::optional<float> getDefaultValue() const override
67 {
68 if (auto p = getParam())
69 return p->getDefaultValue();
70
71 return {};
72 }
73
74 void parameterChanged (float newValue, bool byAutomation) override
75 {
76 if (auto p = getParam())
77 {
78 if (p->getValue() != newValue)
79 {
80 if (! byAutomation)
81 markAsChanged();
82
83 p->setValue (newValue);
84 }
85 }
86 }
87
88 void beginParameterChangeGesture() override
89 {
90 if (auto p = getParam())
91 p->beginChangeGesture();
92 }
93
94 void endParameterChangeGesture() override
95 {
96 if (auto p = getParam())
97 p->endChangeGesture();
98 }
99
100 juce::String valueToString (float value) override
101 {
102 if (auto p = getParam())
103 return p->getText (value, 16);
104
105 return juce::String (value, 2);
106 }
107
108 float stringToValue (const juce::String& str) override
109 {
110 if (auto p = getParam())
111 return p->getValueForText (str);
112
113 return dbStringToDb (str);
114 }
115
116 juce::String getCurrentValueAsString() override
117 {
118 if (auto p = getParam())
119 return p->getCurrentValueAsText();
120
121 return {};
122 }
123
124 juce::String getLabel() override
125 {
126 if (auto p = getParam())
127 return p->getLabel();
128
129 return {};
130 }
131
132 void valueChangedByPlugin()
133 {
134 if (auto p = getParam())
135 setParameter (p->getValue(), juce::sendNotification);
136 }
137
138 juce::String getParameterName() const override
139 {
140 if (displayName.isNotEmpty())
141 return displayName;
142
143 if (auto p = getParam())
144 {
145 auto name = p->getName (1024);
146 if (name.isNotEmpty())
147 return name;
148 }
149
150 return TRANS("Unnamed") + " " + juce::String (parameterIndex + 1);
151 }
152
153 int getParameterIndex() const noexcept
154 {
155 return parameterIndex;
156 }
157
158 juce::String getParameterShortName (int suggestedLength) const override
159 {
160 if (auto p = getParam())
161 return p->getName (suggestedLength);
162
163 return {};
164 }
165
166 void setDisplayName (const juce::String& str)
167 {
168 displayName = str;
169 }
170
171 bool isDiscrete() const override
172 {
173 return param != nullptr
174 && (param->numberOfStates >= 2 || param->type == "switch");
175 }
176
177 int getNumberOfStates() const override
178 {
179 if (isDiscrete())
180 return (param->numberOfStates >= 2) ? param->numberOfStates : 2;
181
182 return 0;
183 }
184
185 float getValueForState (int state) const override
186 {
187 jassert (state >= 0 && state < getNumberOfStates());
188
189 if (valueType != nullptr && valueType->entries.size() > 0)
190 if (const VSTXML::Entry* e = valueType->entries[state])
191 return (e->range.high + e->range.low) / 2.0f;
192
193 return juce::jlimit (0.0f, 1.0f, 1.0f / float (getNumberOfStates() - 1) * state);
194 }
195
196 juce::String getLabelForValue (float val) const override
197 {
198 if (param != nullptr && param->type == "switch")
199 return (val >= 0.0f && val < 0.5f) ? TRANS("Off")
200 : TRANS("On");
201
202 if (valueType != nullptr)
203 {
204 for (const auto& v : valueType->entries)
205 if (v->range.contains (val))
206 return v->name;
207 }
208
209 return {};
210 }
211
212 juce::StringArray getAllLabels() const override
213 {
214 juce::StringArray labels;
215
216 if (param->type == "switch")
217 {
218 labels.add (TRANS("Off"));
219 labels.add (TRANS("On"));
220 }
221 else if (valueType && valueType->entries.size() > 0)
222 {
223 for (int i = 0; i < valueType->entries.size(); ++i)
224 labels.add (valueType->entries[i]->name);
225
226 labels.removeEmptyStrings();
227 }
228
229 return labels;
230 }
231
232 float snapToState (float val) const override
233 {
234 if (isDiscrete())
235 {
236 if (valueType != nullptr)
237 {
238 for (const auto& v : valueType->entries)
239 if (v->range.contains (val))
240 return (v->range.high + v->range.low) / 2;
241 }
242
243 int state = juce::jlimit (0, getNumberOfStates() - 1,
244 (int) (val * getNumberOfStates()));
245 return getValueForState (state);
246 }
247
248 return val;
249 }
250
251 bool hasLabels() const override
252 {
253 return (param != nullptr && param->type == "switch")
254 || (valueType != nullptr && valueType->entries.size() > 0
255 && valueType->entries[0]->name.isNotEmpty());
256 }
257
258 int getStateForValue (float val) const override
259 {
260 if (isDiscrete())
261 {
262 if (valueType != nullptr)
263 {
264 for (int i = 0; i < valueType->entries.size(); ++i)
265 if (valueType->entries.getUnchecked (i)->range.contains (val))
266 return i;
267 }
268
269 return juce::roundToInt (std::floor (val * getNumberOfStates()));
270 }
271
272 return 0;
273 }
274
275private:
276 const int parameterIndex;
277 juce::String displayName;
278
279 const VSTXML::Param* param = nullptr;
280 const VSTXML::ValueType* valueType = nullptr;
281
282 juce::AudioPluginInstance* getPlugin() const noexcept
283 {
284 jassert (plugin != nullptr);
285 return static_cast<ExternalPlugin&> (*plugin).getAudioPluginInstance();
286 }
287
288 juce::AudioProcessorParameter* getParam() const noexcept
289 {
290 if (auto pi = getPlugin())
291 return pi->getParameters()[parameterIndex];
292
293 return {};
294 }
295
296 void markAsChanged() const noexcept
297 {
298 getEdit().pluginChanged (*plugin);
299 }
300
301 void parameterValueChanged (int index, float /*newValue*/) override
302 {
303 if (parameterIndex == index)
305 else
307 }
308
309 void parameterGestureChanged (int index, bool gestureIsStarting) override
310 {
311 if (! juce::MessageManager::existsAndIsCurrentThread())
312 return;
313
314 if (parameterIndex == index)
315 {
316 if (gestureIsStarting)
317 {
319 }
320 else
321 {
323 markAsChanged();
324 }
325 }
326 else
327 {
329 }
330 }
331
332 void handleAsyncUpdate() override
333 {
334 // Some plugins send spurious parameter value changes (e.g. when their UI is opened)
335 // which can reset modifier base values so disable these from coming through if there
336 // are active modifiers etc.
337 // It would be nice to be able to rely on the begin/end gestures to figure out if the user
338 // is actively changing the parameter but as this isn't mandated by the plguin APIs, we can't
339 // do this and we just have to disable control from the plugin UI when there are active Modifiers
340 if (automatableEditElement.edit.isLoading() || hasActiveModifierAssignments())
341 return;
342
343 valueChangedByPlugin();
344 }
345
347};
348
349}} // namespace tracktion { inline namespace engine
void cancelPendingUpdate() noexcept
void removeEmptyStrings(bool removeWhitespaceStrings=true)
void add(String stringToAdd)
bool isNotEmpty() const noexcept
void parameterChangeGestureBegin()
Call to indicate this parameter is about to be changed.
bool hasActiveModifierAssignments() const
Returns true if any ModifierSources are currently in use by assignments.
void parameterChangeGestureEnd()
Call to indicate this parameter has stopped being to be changed.
void pluginChanged(Plugin &) noexcept
Plugins should call this when one of their parameters or state changes to mark the edit as unsaved.
bool isLoading() const
Returns true if the Edit's not yet fully loaded.
T floor(T... args)
#define TRANS(stringLiteral)
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
#define jassertfalse
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
sendNotification
int roundToInt(const FloatType value) noexcept
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.