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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_Track.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
14Track::Track (Edit& ed, const juce::ValueTree& v, bool hasModifierList)
15 : EditItem (EditItemID::readOrCreateNewID (ed, v), ed),
16 state (v),
17 pluginList (ed)
18{
19 edit.trackCache.addItem (*this);
20 auto um = &edit.getUndoManager();
21
22 trackName.referTo (state, IDs::name, um, {});
23 colour.referTo (state, IDs::colour, um, juce::Colour());
24 tags.referTo (state, IDs::tags, um);
25 hidden.referTo (state, IDs::hidden, um);
26 processing.referTo (state, IDs::process, um, true);
27
28 currentAutoParamPlugin.referTo (state, IDs::currentAutoParamPluginID, um, EditItemID());
29 currentAutoParamID.referTo (state, IDs::currentAutoParamTag, um, {});
30
31 if (hasModifierList)
32 modifierList = std::make_unique<ModifierList> (edit, state.getOrCreateChildWithName (IDs::MODIFIERS, um));
33
34 state.addListener (this);
35}
36
38{
39 edit.trackCache.removeItem (*this);
40 cachedParentTrack = nullptr;
41 cachedParentFolderTrack = nullptr;
42 masterReference.clear();
43 state.removeListener (this);
44
45 pluginList.releaseObjects();
46 currentAutoParam = {};
47}
48
50{
51 for (auto p : getAllPlugins())
52 p->flushPluginStateToValueTree();
53}
54
56{
58
59 auto um = &edit.getUndoManager();
60
61 if (canShowImage())
62 {
63 imageIdOrData.referTo (state, IDs::imageIdOrData, um, {});
64
65 if (imageIdOrData.get().isNotEmpty())
66 imageChanged = true;
67 }
68
69 tagsArray = juce::StringArray::fromTokens (tags.get().replace ("_", " "), "|", "\"");
70
71 pluginList.setTrackAndClip (this, nullptr);
72
74 pluginList.initialise (state);
75
76 updateTrackList();
77 updateCachedParent();
78}
79
81{
82 auto newName = n.substring (0, 64);
83
84 if (trackName != newName)
85 trackName = newName;
86}
87
89{
90 trackName.resetToDefault();
91}
92
94{
95 int index = 0, result = -1;
96
97 edit.visitAllTracksRecursive ([&] (Track& t)
98 {
99 if (this == &t)
100 {
101 result = index;
102 return false;
103 }
104
105 ++index;
106 return true;
107 });
108
109 return result;
110}
111
113{
115
116 if (trackList != nullptr)
117 {
118 trackList->visitAllTracks ([&] (Track& t)
119 {
120 if (auto at = dynamic_cast<AudioTrack*> (&t))
121 results.addIfNotAlreadyThere (at);
122
123 return true;
124 }, recursive);
125 }
126
127 return results;
128}
129
131{
132 juce::Array<Track*> results;
133
134 if (trackList != nullptr)
135 trackList->visitAllTracks ([&] (Track& t) { results.add (&t); return true; }, recursive);
136
137 return results;
138}
139
141{
142 for (auto track : getAllSubTracks (false))
143 if (auto c = track->findClipForID (id))
144 return c;
145
146 return {};
147}
148
149Track* Track::getSiblingTrack (int delta, bool keepWithinSameParent) const
150{
151 juce::Array<Track*> tracks;
152
153 if (keepWithinSameParent)
154 {
155 if (auto ft = getParentFolderTrack())
156 tracks = ft->getAllSubTracks (false);
157 else
158 tracks = getTopLevelTracks (edit);
159 }
160 else
161 {
162 tracks = getAllTracks (edit);
163 }
164
165 if (auto index = tracks.indexOf (const_cast<Track*> (this)); index >= 0)
166 return tracks[index + delta];
167
168 return {};
169}
170
171//==============================================================================
172bool Track::isProcessing (bool includeParents) const
173{
174 if (includeParents)
175 for (auto p = getParentTrack(); p != nullptr; p = p->getParentTrack())
176 if (! p->processing)
177 return false;
178
179 return processing;
180}
181
182bool Track::isTrackAudible (bool areAnyTracksSolo) const
183{
184 if (areAnyTracksSolo && ! (isSolo (true) || isSoloIsolate (true)))
185 return false;
186
187 return ! isMuted (true);
188}
189
190void Track::updateAudibility (bool areAnyTracksSolo)
191{
192 isAudible = isTrackAudible (areAnyTracksSolo);
193}
194
196{
197 int depth = 0;
198
199 for (auto p = getParentTrack(); p != nullptr; p = p->getParentTrack())
200 ++depth;
201
202 return depth;
203}
204
206{
207 for (auto p = getParentFolderTrack(); p != nullptr; p = p->getParentFolderTrack())
208 if (p->isSubmixFolder())
209 return true;
210
211 return false;
212}
213
215{
216 int flags = 0;
217
218 if (isMuted (false))
219 flags |= muteLit;
220 else if (isMuted (true))
221 flags |= muteFlashing;
222
223 if (isSolo (false))
224 flags |= soloLit;
225 else if (isSolo (true))
226 flags |= soloFlashing;
227 else if (isSoloIsolate (false))
228 {
229 flags |= soloIsolate;
230
231 if (! isMuted (true) && edit.areAnyTracksSolo())
232 flags |= soloLit;
233 }
234 else if (isSoloIsolate (true) && edit.areAnyTracksSolo())
235 {
236 flags |= soloIsolate;
237
238 if (! isMuted (true))
239 flags |= soloFlashing;
240 }
241
242 return (MuteAndSoloLightState) flags;
243}
244
245bool Track::containsPlugin (const Plugin* p) const
246{
247 return pluginList.contains (p);
248}
249
251{
252 for (auto p : pluginList)
253 if (p->getPluginType() == "freezePoint")
254 return true;
255
256 return false;
257}
258
260{
262 auto plugins = getAllPlugins();
263 destArray.addArray (plugins);
264
265 if (modifierList != nullptr)
266 for (auto m : modifierList->getModifiers())
267 if (auto aei = dynamic_cast<AutomatableEditItem*> (m))
268 destArray.add (aei);
269
270 for (auto p : plugins)
271 if (auto mpl = p->getMacroParameterList())
272 destArray.add (mpl);
273
274 return destArray;
275}
276
278{
279 return pluginList.getPlugins();
280}
281
283{
284 pluginList.sendMirrorUpdateToAllPlugins (p);
285}
286
288{
289 auto isVolPanVCA = [] (Plugin* p)
290 {
291 return dynamic_cast<VolumeAndPanPlugin*> (p) != nullptr
292 || dynamic_cast<VCAPlugin*> (p) != nullptr;
293 };
294
295 auto areAnyOn = [&isVolPanVCA] (const PluginList& pl) -> bool
296 {
297 for (auto p : pl)
298 if (! isVolPanVCA (p) && p->canBeDisabled() && p->isEnabled())
299 return true;
300
301 return false;
302 };
303
304 const bool enable = ! areAnyOn (pluginList);
305
306 for (auto p : pluginList)
307 if (! isVolPanVCA (p) && p->canBeDisabled())
308 p->setEnabled (enable);
309}
310
311//==============================================================================
313{
315
316 for (auto p : getAllPlugins())
317 {
318 for (int j = 0; j < p->getNumAutomatableParameters(); ++j)
319 params.add (p->getAutomatableParameter (j).get());
320
321 params.addArray (p->getMacroParameters());
322 }
323
324 if (modifierList != nullptr)
325 for (auto m : modifierList->getModifiers())
326 params.addArray (m->getAutomatableParameters());
327
328 return params;
329}
330
332{
333 for (auto p : getAllPlugins())
334 {
335 if (auto mpl = p->getMacroParameterList())
336 mpl->visitAllAutomatableParams (visit);
337
338 p->visitAllAutomatableParams (visit);
339 }
340
341 if (modifierList != nullptr)
342 for (auto m : modifierList->getModifiers())
343 m->visitAllAutomatableParams (visit);
344}
345
346static AutomatableParameter::Ptr findAutomatableParam (Edit& edit, EditItemID pluginID, const juce::String& paramID)
347{
348 if (pluginID.isValid() && paramID.isNotEmpty())
349 {
351
353 {
354 if (p.paramID == paramID && p.getOwnerID() == pluginID)
355 found = p;
356 });
357
358 if (found)
359 return found;
360
361 if (auto p = edit.getPluginCache().getPluginFor (pluginID))
362 {
363 p->initialiseFully();
364
365 if (auto ap = p->getAutomatableParameterByID (paramID))
366 return ap;
367
368 // Check for param name as a fallback for old Edits
369 for (auto ap : p->getAutomatableParameters())
370 if (ap->paramName == paramID)
371 return ap;
372
373 return {};
374 }
375
376 for (auto m : getAllModifiers (edit))
377 if (auto aei = dynamic_cast<AutomatableEditItem*> (m))
378 for (auto p : aei->getAutomatableParameters())
379 if (p->getOwnerID() == pluginID && p->paramID == paramID)
380 return p;
381
382 for (auto mpl : getAllMacroParameterLists (edit))
383 for (auto p : mpl->getMacroParameters())
384 if (p->getOwnerID() == pluginID && p->paramID == paramID)
385 return p;
386 }
387
388 return {};
389}
390
392{
393 currentAutoParamPlugin.forceUpdateOfCachedValue();
394 currentAutoParamID.forceUpdateOfCachedValue();
395
396 auto newParam = findAutomatableParam (edit, currentAutoParamPlugin.get(), currentAutoParamID.get()).get();
397
398 if (currentAutoParam != newParam)
399 {
400 currentAutoParam = newParam;
401 changed();
402 }
403}
404
406{
407 return dynamic_cast<AutomatableParameter*> (currentAutoParam.get());
408}
409
411{
412 currentAutoParamPlugin = param == nullptr ? EditItemID() : param->getOwnerID();
413 currentAutoParamID = param == nullptr ? juce::String() : param->paramID;
414}
415
417{
418 if (currentAutoParamPlugin == pluginOrParameterID
419 || currentAutoParamID == pluginOrParameterID.toString())
420 {
421 if (isAutomationTrack())
422 edit.deleteTrack (this);
423 else
425 }
426}
427
429{
430 auto parent = state.getParent();
431
432 if (TrackList::isTrack (parent))
433 return parent;
434
435 return {};
436}
437
438bool Track::isAChildOf (const Track& t) const
439{
440 for (auto p = getParentTrack(); p != nullptr; p = p->getParentTrack())
441 if (p == &t)
442 return true;
443
444 return false;
445}
446
448{
449 // shift up any automation curves too..
450 for (auto p : pluginList)
451 {
452 for (int j = p->getNumAutomatableParameters(); --j >= 0;)
453 {
454 if (auto param = p->getAutomatableParameter (j))
455 {
456 auto& curve = param->getCurve();
457 auto valueAtInsertionTime = curve.getValueAt (time);
458
459 for (int k = curve.getNumPoints(); --k >= 0;)
460 if (curve.getPointTime (k) >= time)
461 curve.movePoint (k,
462 curve.getPointTime (k) + amountOfSpace,
463 curve.getPointValue (k), false);
464
465 if (! juce::approximatelyEqual (valueAtInsertionTime, curve.getValueAt (time)))
466 curve.addPoint (time, valueAtInsertionTime, 0.0f);
467
468 if (! juce::approximatelyEqual (valueAtInsertionTime, curve.getValueAt (time + amountOfSpace)))
469 curve.addPoint (time + amountOfSpace, valueAtInsertionTime, 0.0f);
470 }
471 }
472 }
473}
474
475void Track::updateTrackList()
476{
478
480 {
481 if (trackList == nullptr)
482 trackList = std::make_unique<TrackList> (edit, state);
483 }
484 else
485 {
486 trackList.reset();
487 }
488
489 changed();
490}
491
492void Track::updateCachedParent()
493{
495 if (auto parent = getParentTrackTree(); parent.isValid())
496 cachedParentTrack = dynamic_cast<Track*> (edit.trackCache.findItem (EditItemID::fromID (parent)));
497 else
498 cachedParentTrack = nullptr;
499
500 auto newFolder = dynamic_cast<FolderTrack*> (cachedParentTrack.get());
501
502 if (cachedParentFolderTrack != newFolder)
503 {
504 if (cachedParentFolderTrack != nullptr)
505 cachedParentFolderTrack->setDirtyClips();
506
507 if (newFolder != nullptr)
508 newFolder->setDirtyClips();
509
510 cachedParentFolderTrack = newFolder;
511 changed();
512 }
513}
514
515//==============================================================================
517{
518 return isAudioTrack() || isFolderTrack();
519}
520
521void Track::setTrackImage (const juce::String& idOrData)
522{
523 if (canShowImage())
524 imageIdOrData = idOrData;
525}
526
528{
529 const juce::ScopedValueSetter<bool> svs (imageChanged, imageChanged, false);
530 return imageChanged;
531}
532
534{
535 tags = s.joinIntoString ("|").replace (" ", "_");
536}
537
538void Track::valueTreePropertyChanged (juce::ValueTree& v, const juce::Identifier& i)
539{
540 if (v == state)
541 {
542 if (i == IDs::tags)
543 {
545 tagsArray = juce::StringArray::fromTokens (tags.get().replace ("_", " "), "|", "\"");
546 changed();
547 }
548 else if (i == IDs::name)
549 {
550 changed();
551
552 if (! edit.isLoading())
553 juce::MessageManager::callAsync ([trackRef = makeSafeRef (*this)]
554 {
555 if (auto t = trackRef.get())
556 SelectionManager::refreshAllPropertyPanelsShowing (*t);
557 });
558
560 }
561 else if (i == IDs::colour)
562 {
563 changed();
565 }
566 else if (i == IDs::imageIdOrData)
567 {
568 imageChanged = true;
569 changed();
570 }
571 else if (i == IDs::currentAutoParamPluginID || i == IDs::currentAutoParamTag)
572 {
574 }
575 else if (i == IDs::expanded)
576 {
577 changed();
578 }
579 else if (i == IDs::process)
580 {
581 juce::Array<Track*> tracks;
582 tracks.add (this);
583 tracks.addArray (getAllSubTracks (true));
584
585 for (auto t : tracks)
586 for (auto p : t->getAllPlugins())
587 p->setProcessingEnabled (p->getOwnerTrack()->isProcessing (true));
588 }
589 }
590}
591
592void Track::valueTreeChildAdded (juce::ValueTree& p, juce::ValueTree& c)
593{
594 if (p == state)
595 if (TrackList::isTrack (c))
596 updateTrackList();
597}
598
599void Track::valueTreeChildRemoved (juce::ValueTree& p, juce::ValueTree& c, int)
600{
601 if (p == state)
602 if (TrackList::isTrack (c))
603 updateTrackList();
604}
605
606void Track::valueTreeParentChanged (juce::ValueTree& v)
607{
608 if (v == state)
609 updateCachedParent();
610}
611
612void Track::handleAsyncUpdate()
613{
614 pluginList.updateTrackProperties();
615}
616
617}} // namespace tracktion { inline namespace engine
void addArray(const Type *elementsToAdd, int numElementsToAdd)
int indexOf(ParameterType elementToLookFor) const
void add(const ElementType &newElement)
bool addIfNotAlreadyThere(ParameterType newElement)
void forceUpdateOfCachedValue()
void referTo(ValueTree &tree, const Identifier &property, UndoManager *um)
Type get() const noexcept
bool isValid() const noexcept
String joinIntoString(StringRef separatorString, int startIndex=0, int numberOfElements=-1) const
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
String replace(StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase=false) const
String substring(int startIndex, int endIndex) const
bool isValid() const noexcept
void addListener(Listener *listener)
ValueTree getParent() const noexcept
void removeListener(Listener *listener)
ValueTree getOrCreateChildWithName(const Identifier &type, UndoManager *undoManager)
Base class for elements that have some kind of automatable parameters.
A clip in an edit.
Base class for objects that live inside an edit - e.g.
The Tracktion Edit class!
void visitAllAutomatableParams(bool includeTrackParams, const std::function< void(AutomatableParameter &)> &) const
Returns all automatable parameters in an Edit.
void deleteTrack(Track *)
Deletes a Track.
bool areAnyTracksSolo() const
Returns true if any tracks are soloed.
PluginCache & getPluginCache() noexcept
Returns the PluginCache which manages all active Plugin[s] for this Edit.
bool isLoading() const
Returns true if the Edit's not yet fully loaded.
void visitAllTracksRecursive(std::function< bool(Track &)>) const
Visits all tracks in the Edit with the given function.
EditItemCache< Track > trackCache
Quick way to find and iterate all Track[s] in the Edit.
juce::UndoManager & getUndoManager() noexcept
Returns the juce::UndoManager used for this Edit.
Holds a sequence of plugins.
virtual void changed()
This should be called to send a change notification to any SelectableListeners that are registered wi...
Base class for tracks which contain clips and plugins and can be added to Edit[s].
int getTrackDepth() const
Returns the number of parents within which this track is nested.
virtual bool isAutomationTrack() const
Returns true if this is an AutomationTrack.
virtual Clip * findClipForID(EditItemID) const
Returns a clip one with a matching ID can be found on this Track.
juce::ValueTree getParentTrackTree() const
Returns the state of the parent Track.
void hideAutomatableParametersForSource(EditItemID pluginOrParameterID)
Hides a shown parameter if it matches the given ID.
PluginList pluginList
The Track's PluginList.
bool isProcessing(bool includeParents) const
Returns true if this track should be included in playback.
juce::Array< Track * > getAllSubTracks(bool recursive) const
Returns all nested tracks.
void setName(const juce::String &)
Sets the name of the Track.
AutomatableParameter * getCurrentlyShownAutoParam() const noexcept
Returns the parameter whos curve should be shown on this Track.
void setTrackImage(const juce::String &idOrData)
Sets some image data to use.
bool isAChildOf(const Track &possibleParent) const
Tests whether this is a child of a given Track.
virtual bool isSolo(bool) const
Returns true if this track is soloed.
Track(Edit &, const juce::ValueTree &, bool hasModifierList)
Creates a track with a given state.
Track * getSiblingTrack(int delta, bool keepWithinSameParent) const
Returns a sibling Track to this one.
virtual bool containsPlugin(const Plugin *) const
Tests whether this Track or a clip on it contains the given plugin.
bool canShowImage() const
Tests whether this Track can show an image.
virtual void flushStateToValueTree()
Flushes all plugin states on the track to the state object.
virtual bool isFolderTrack() const
Returns true if this is a FolderTrack.
void setTags(const juce::StringArray &)
Sets an array of Strings to use as tags.
int getIndexInEditTrackList() const
Returns the index of this track in a flat list of tracks contained in an Edit.
bool canContainPlugins() const
Returns true if this Track can contain Plugin[s].
bool isPartOfSubmix() const
Tests whether this nested within a submix FolderTrack.
void setCurrentlyShownAutoParam(const AutomatableParameter::Ptr &)
Sets a parameter to display on this Track.
Track * getParentTrack() const
Returns the parent Track if this is a nested track.
virtual bool isSoloIsolate(bool) const
Returns true if this track is solo isolated.
juce::ValueTree state
The state of this Track.
juce::Array< AutomatableEditItem * > getAllAutomatableEditItems() const
Returns all AutomatableEditItem[s] on this Track.
juce::Array< AutomatableParameter * > getAllAutomatableParams() const
Returns all the parameters for this track's Plugin[s] and Modifier[s].
juce::Array< AudioTrack * > getAllAudioSubTracks(bool recursive) const
Returns all nested AudioTrack[s].
bool hasFreezePointPlugin() const
Tests whether this Track contains a FreezePointPlugin.
virtual Plugin::Array getAllPlugins() const
Returns all pugins on this Track.
virtual bool isAudioTrack() const
Returns true if this is an AudioTrack.
~Track() override
Destructor.
virtual bool isMuted(bool) const
Returns true if this track is muted.
void flipAllPluginsEnablement()
Toggles the Plugin::isEnabled state for all Plugin[s] on this Track.
void visitAllAutomatableParams(const std::function< void(AutomatableParameter &)> &) const
Visits all the parameters for this track's Plugin[s] and Modifier[s].
void resetName()
Sets the name of the Track to an empty string.
virtual void initialise()
Initialises the Track.
MuteAndSoloLightState getMuteAndSoloLightState() const
Returns the mute a solo status.
FolderTrack * getParentFolderTrack() const
Returns the parent FolderTrack if this is nested in one.
MuteAndSoloLightState
Determines the status of the mute and solo indicators.
@ muteFlashing
Track is implicitly muted.
@ muteLit
Track is explicitly muted.
@ soloLit
Track is explicitly soloed.
@ soloIsolate
Track is explicitly solo isolated.
@ soloFlashing
Track is implicitly soloed.
void refreshCurrentAutoParam()
Updates the current parameter bases on the set IDs.
virtual void insertSpaceIntoTrack(TimePosition, TimeDuration)
Should insert empty space in to the track, shuffling down any items after the time.
virtual bool isTrackAudible(bool areAnyTracksSolo) const
Returns whether this Track should be audible.
bool imageHasChanged()
Tests and resets a flag internally kept when the image changes.
virtual void sendMirrorUpdateToAllPlugins(Plugin &changedPlugin) const
Sends a message to all plugins that the given plugin has changed.
void updateAudibility(bool areAnyTracksSolo)
Updates the audibility state of the Track.
The VCA plugin sits on a folder track to control the overall level of all the volume/pan plugins in i...
The built-in Tracktion volume/pan plugin.
T is_pointer_v
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
juce::Array< Track * > getTopLevelTracks(const Edit &edit)
Returns all of the non-foldered tracks in an Edit.
juce::Array< MacroParameterList * > getAllMacroParameterLists(const Edit &edit)
Returns all the MacroParameterLists in an Edit.
juce::Array< Track * > getAllTracks(const Edit &edit)
Returns all the tracks in an Edit.
juce::ReferenceCountedArray< Modifier > getAllModifiers(const Edit &edit)
Returns all the Modifiers in an Edit.
SafeSelectable< SelectableType > makeSafeRef(SelectableType &selectable)
Creates a SafeSelectable for a given selectable object.
Represents a duration in real-life time.
Represents a position in real-life time.
ID for objects of type EditElement - e.g.
static bool isTrack(const juce::ValueTree &) noexcept
Returns true if the given ValeTree is for a known Track type.
static bool hasAnySubTracks(const juce::ValueTree &)
Returns true if the track has any sub tracks.
time
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.