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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_AutomatableEditItem.cpp
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
14AutomatableEditItem::AutomatableEditItem (Edit& ed, const juce::ValueTree& v)
15 : EditItem (EditItemID::readOrCreateNewID (ed, v), ed),
16 elementState (v)
17{
18 remapOnTempoChange.referTo (elementState, IDs::remapOnTempoChange, &edit.getUndoManager(), false);
19}
20
21AutomatableEditItem::~AutomatableEditItem()
22{
23}
24
25//==============================================================================
26void AutomatableEditItem::flushPluginStateToValueTree()
27{
28 saveChangedParametersToState();
29}
30
31//==============================================================================
32juce::Array<AutomatableParameter*> AutomatableEditItem::getAutomatableParameters() const
33{
35 params.addArray (automatableParams);
36 return params;
37}
38
39int AutomatableEditItem::getNumAutomatableParameters() const
40{
41 return automatableParams.size();
42}
43
44void AutomatableEditItem::visitAllAutomatableParams (const std::function<void(AutomatableParameter&)>& visit) const
45{
46 for (auto p : automatableParams)
47 visit (*p);
48}
49
50AutomatableParameter::Ptr AutomatableEditItem::getAutomatableParameterByID (const juce::String& paramID) const
51{
52 for (auto p : automatableParams)
53 if (p->paramID == paramID)
54 return p;
55
56 return {};
57}
58
59void AutomatableEditItem::deleteParameter (AutomatableParameter* p)
60{
61 automatableParams.removeObject (p);
62 rebuildParameterTree();
63}
64
65void AutomatableEditItem::deleteAutomatableParameters()
66{
68 auto& ech = edit.getParameterChangeHandler();
69 auto& pcm = edit.getParameterControlMappings();
70
71 for (auto& ap : automatableParams)
72 {
73 if (ech.getPendingParam (false).get() == ap)
74 ech.getPendingParam (true);
75
76 if (pcm.isParameterMapped (*ap))
77 pcm.removeParameterMapping (*ap);
78 }
79
80 automatableParams.clear();
81 parameterTree.clear();
82
83 {
84 // N.B. swap under the lock here to minimise the time held
86
87 {
88 const std::scoped_lock sl (activeParameterLock);
89 std::swap (activeParameters, nowActiveParams);
90 }
91 }
92
93 sendListChangeMessage();
94}
95
96int AutomatableEditItem::indexOfAutomatableParameter (const AutomatableParameter::Ptr& param) const
97{
98 return automatableParams.indexOf (param);
99}
100
101AutomatableParameterTree& AutomatableEditItem::getParameterTree() const
102{
103 if (setIfDifferent (parameterTreeBuilt, true))
104 {
105 buildParameterTree();
106 sendListChangeMessage();
107 }
108
109
110 return parameterTree;
111}
112
113juce::ReferenceCountedArray<AutomatableParameter> AutomatableEditItem::getFlattenedParameterTree() const
114{
116
117 for (auto node : getParameterTree().rootNode->subNodes)
118 {
119 if (node->type == AutomatableParameterTree::Parameter)
120 params.add (node->parameter);
121 else if (node->type == AutomatableParameterTree::Group)
122 params.addArray (getFlattenedParameterTree (*node));
123 }
124
125 jassert (! params.contains (nullptr));
126
127 return params;
128}
129
130//==============================================================================
131void AutomatableEditItem::setAutomatableParamPosition (TimePosition time)
132{
133 if (setIfDifferent (lastTime, time))
134 if (edit.getAutomationRecordManager().isReadingAutomation())
135 updateAutomatableParamPosition (time);
136}
137
138bool AutomatableEditItem::isBeingActivelyPlayed() const
139{
140 return juce::Time::getApproximateMillisecondCounter() < (unsigned int) (systemTimeOfLastPlayedBlock + 150);
141}
142
143void AutomatableEditItem::updateAutomatableParamPosition (TimePosition time)
144{
145 for (auto p : automatableParams)
146 if (p->isAutomationActive())
147 p->updateToFollowCurve (time);
148}
149
150void AutomatableEditItem::updateParameterStreams (TimePosition time)
151{
152 const std::scoped_lock sl (activeParameterLock);
153
154 for (auto p : activeParameters)
155 p->updateFromAutomationSources (time);
156}
157
158void AutomatableEditItem::resetRecordingStatus()
159{
160 for (auto p : automatableParams)
161 p->resetRecordingStatus();
162}
163
164//==============================================================================
165void AutomatableEditItem::buildParameterTree() const
166{
167 for (auto p : automatableParams)
168 parameterTree.rootNode->addSubNode (new AutomatableParameterTree::TreeNode (p));
169}
170
171void AutomatableEditItem::updateLastPlaybackTime()
172{
173 systemTimeOfLastPlayedBlock = juce::Time::getApproximateMillisecondCounter();
174}
175
176void AutomatableEditItem::clearParameterList()
177{
178 automatableParams.clear();
179 rebuildParameterTree();
180}
181
182void AutomatableEditItem::addAutomatableParameter (const AutomatableParameter::Ptr& param)
183{
184 jassert (param != nullptr);
185 automatableParams.add (param);
186 rebuildParameterTree();
187}
188
189void AutomatableEditItem::rebuildParameterTree()
190{
191 parameterTree.clear();
192 parameterTreeBuilt = false;
193}
194
195juce::ReferenceCountedArray<AutomatableParameter> AutomatableEditItem::getFlattenedParameterTree (AutomatableParameterTree::TreeNode& node) const
196{
198
199 for (auto subNode : node.subNodes)
200 {
201 if (subNode->type == AutomatableParameterTree::Parameter)
202 params.add (subNode->parameter);
203 else if (subNode->type == AutomatableParameterTree::Group)
204 params.addArray (getFlattenedParameterTree (*subNode));
205 }
206
207 return params;
208}
209
210void AutomatableEditItem::updateActiveParameters()
211{
214
215 for (auto ap : automatableParams)
216 if (ap->isAutomationActive())
217 nowActiveParams.add (ap);
218
219 {
220 const std::scoped_lock sl (activeParameterLock);
221 activeParameters.swapWith (nowActiveParams);
222 automationActive.store (! activeParameters.isEmpty(), std::memory_order_relaxed);
223 }
224
225 lastTime = -1.0s;
226}
227
228void AutomatableEditItem::saveChangedParametersToState()
229{
231
232 for (auto ap : automatableParams)
233 {
234 if (ap->getCurrentValue() != ap->getCurrentExplicitValue())
235 {
236 stream.writeString (ap->paramID);
237 stream.writeFloat (ap->getCurrentExplicitValue());
238 }
239 }
240
241 stream.flush();
242 auto um = &edit.getUndoManager();
243
244 if (stream.getDataSize() > 0)
245 elementState.setProperty (IDs::parameters, stream.getMemoryBlock(), um);
246 else
247 elementState.removeProperty (IDs::parameters, um);
248}
249
250void AutomatableEditItem::restoreChangedParametersFromState()
251{
252 if (auto mb = elementState[IDs::parameters].getBinaryData())
253 {
254 juce::MemoryInputStream stream (*mb, false);
255
256 while (! stream.isExhausted())
257 {
258 // N.B. we must read both the string and the float here as they always both written, otherwise the stream will get out of sync
259 auto paramID = stream.readString();
260 auto value = stream.readFloat();
261
262 if (auto ap = getAutomatableParameterByID (paramID))
263 ap->setParameter (value, juce::dontSendNotification);
264 }
265 }
266}
267
268AutomatableEditItem::ParameterChangeListeners::ParameterChangeListeners (AutomatableEditItem& e) : owner (e) {}
269
270void AutomatableEditItem::ParameterChangeListeners::handleAsyncUpdate()
271{
272 listeners.call ([this] (ParameterListChangeListener& l) { l.parameterListChanged (owner); });
273}
274
275void AutomatableEditItem::sendListChangeMessage() const
276{
277 if (parameterChangeListeners != nullptr)
278 parameterChangeListeners->triggerAsyncUpdate();
279}
280
281void AutomatableEditItem::addParameterListChangeListener (ParameterListChangeListener* l)
282{
283 if (parameterChangeListeners == nullptr)
284 parameterChangeListeners = std::make_unique<ParameterChangeListeners> (*this);
285
286 parameterChangeListeners->listeners.add (l);
287}
288
289void AutomatableEditItem::removeParameterListChangeListener (ParameterListChangeListener* l)
290{
291 if (parameterChangeListeners != nullptr)
292 parameterChangeListeners->listeners.remove (l);
293}
294
295}} // namespace tracktion { inline namespace engine
void addArray(const Type *elementsToAdd, int numElementsToAdd)
int size() const noexcept
virtual float readFloat()
virtual String readString()
bool isExhausted() override
void flush() override
size_t getDataSize() const noexcept
MemoryBlock getMemoryBlock() const
virtual bool writeFloat(float value)
virtual bool writeString(const String &text)
bool contains(const ObjectClass *objectToLookFor) const noexcept
void addArray(const ReferenceCountedArray &arrayToAddFrom, int startIndex=0, int numElementsToAdd=-1) noexcept
ObjectClass * add(ObjectClass *newObject)
static uint32 getApproximateMillisecondCounter() noexcept
Base class for elements that have some kind of automatable parameters.
T is_pointer_v
#define jassert(expression)
typedef int
dontSendNotification
Represents a position in real-life time.
T swap(T... args)
time
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.
T visit(T... args)