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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_Clip.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
14//==============================================================================
15Clip::Clip (const juce::ValueTree& v, ClipOwner& targetParent, EditItemID id, Type t)
16 : TrackItem (targetParent.getClipOwnerEdit(), id, t),
17 state (v), parent (&targetParent),
18 sourceFileReference (edit, state, IDs::source)
19{
21 jassert (getParent() == &targetParent);
22 edit.clipCache.addItem (*this);
23
24 auto um = getUndoManager();
25 clipName.referTo (state, IDs::name, um);
26 clipStart.referTo (state, IDs::start, um);
27 length.referTo (state, IDs::length, um);
28 offset.referTo (state, IDs::offset, um);
29 colour.referTo (state, IDs::colour, um);
30 disabled.referTo (state, IDs::disabled, um);
31 speedRatio.referTo (state, IDs::speed, um, 1.0);
32 syncType.referTo (state, IDs::sync, um);
33 showingTakes.referTo (state, IDs::showingTakes, nullptr, false);
34 groupID.referTo (state, IDs::groupID, um);
35 linkID.referTo (state, IDs::linkID, um);
36 followActionDurationType.referTo (state, IDs::followActionDurationType, um);
37 followActionBeats.referTo (state, IDs::followActionBeats, um);
38 followActionNumLoops.referTo (state, IDs::followActionNumLoops, um, 1.0);
39
40 convertPropertyToType<bool> (state, IDs::disabled);
41
42 if (! (length >= 0_td && length < 1.0e10s)) // reverse logic to check for NANs
43 {
45 length = 0_td;
46 }
47
48 state.addListener (this);
49
50 updateLinkedClipsCaller.setFunction ([this] { updateLinkedClips(); });
51}
52
54{
55 edit.clipCache.removeItem (*this);
56 state.removeListener (this);
57}
58
60{
61 jassert (parent != nullptr);
62
63 const juce::ScopedValueSetter<bool> initialiser (isInitialised, false, true);
64
67
68 if (sourceFileReference.isUsingProjectReference())
70
71 Selectable::changed(); // call superclass so not to mark the edit as altered
72
73 if (auto track = getTrack())
74 {
75 if (auto f = track->getParentFolderTrack())
76 f->setDirtyClips();
77
78 track->setFrozen (false, Track::groupFreeze);
79 }
80
82}
83
85{
86 return &edit.getUndoManager();
87}
88
89//==============================================================================
91{
92 return isClipState (v.getType());
93}
94
96{
97 return i == IDs::AUDIOCLIP || i == IDs::MIDICLIP || i == IDs::MARKERCLIP
98 || i == IDs::STEPCLIP || i == IDs::CHORDCLIP || i == IDs::ARRANGERCLIP
99 || i == IDs::EDITCLIP || i == IDs::CONTAINERCLIP;
100}
101
102//==============================================================================
103static Clip::Ptr createNewEditClip (const juce::ValueTree& v, EditItemID newClipID, ClipOwner& targetParent)
104{
105 auto& edit = targetParent.getClipOwnerEdit();
106 ProjectItemID sourceItemID (v.getProperty (IDs::source).toString());
107 auto sourceItem = edit.engine.getProjectManager().getProjectItem (sourceItemID);
108 juce::String warning;
109
110 if (sourceItem != nullptr && sourceItem->getLength() > 0.0)
111 {
112 if (auto snapshot = EditSnapshot::getEditSnapshot (edit.engine, sourceItemID))
113 {
114 // check for recursion
115 auto referencedEdits = snapshot->getNestedEditObjects();
116 referencedEdits.remove (0); // 0 will be the source so we need to remove it
117
118 if (referencedEdits.contains (snapshot))
119 warning = NEEDS_TRANS("You can't add an Edit which contains this Edit as a clip to itself");
120 }
121 else
122 {
123 warning = NEEDS_TRANS("Unable to load Edit from source file");
124 }
125 }
126
127 // If sourceItemID is invalid it means we're creating an empty EditClip
128 if (warning.isEmpty() || ! sourceItemID.isValid())
129 return new EditClip (v, newClipID, targetParent, sourceItemID);
130
131 edit.engine.getUIBehaviour().showWarningAlert (TRANS("Can't Import Edit"), TRANS(warning));
132 return {};
133}
134
135static Clip::Ptr createNewClipObject (const juce::ValueTree& v, EditItemID newClipID, ClipOwner& targetParent)
136{
137 auto type = v.getType();
138
139 // TODO: remove this legacy-style CLIP tag + type handling when definitely not needed
140 if (type.toString() == "CLIP")
141 {
143 type = TrackItem::clipTypeToXMLType (TrackItem::stringToType (v.getProperty (IDs::type).toString()));
144 }
145
146 if (type == IDs::AUDIOCLIP) return new WaveAudioClip (v, newClipID, targetParent);
147 if (type == IDs::MIDICLIP) return new MidiClip (v, newClipID, targetParent);
148 if (type == IDs::MARKERCLIP) return new MarkerClip (v, newClipID, targetParent);
149 if (type == IDs::STEPCLIP) return new StepClip (v, newClipID, targetParent);
150 if (type == IDs::CHORDCLIP) return new ChordClip (v, newClipID, targetParent);
151 if (type == IDs::ARRANGERCLIP) return new ArrangerClip (v, newClipID, targetParent);
152 if (type == IDs::CONTAINERCLIP) return new ContainerClip (v, newClipID, targetParent);
153 if (type == IDs::EDITCLIP) return createNewEditClip (v, newClipID, targetParent);
154
156 return {};
157}
158
160{
162 jassert (TrackList::isTrack (v.getParent())
163 || v.getParent().hasType (IDs::CLIPLIST)
164 || v.getParent().hasType (IDs::CLIPSLOT));
165
166 auto& edit = targetParent.getClipOwnerEdit();
167 auto newClipID = EditItemID::readOrCreateNewID (edit, v);
168
169 Clip::Ptr c = edit.clipCache.findItem (newClipID);
170 jassert (c == nullptr || &c->edit == &edit);
171
172 if (c == nullptr)
173 {
174 c = createNewClipObject (v, newClipID, targetParent);
175 jassert (c->getParent() == &targetParent); // If this is hit it means two clips share the same ID!
176
177 if (c != nullptr)
178 {
179 c->initialise();
180 c->Selectable::changed(); // call superclass so not to mark the edit as altered
181 c->cancelAnyPendingUpdates();
182 }
183 }
184
185 return c;
186}
187
189{
190 for (auto p : getAllPlugins())
191 p->flushPluginStateToValueTree();
192}
193
194//==============================================================================
195void Clip::setName (const juce::String& newName)
196{
197 if (clipName != newName)
198 {
199 clipName = newName;
200
201 if (edit.engine.getPropertyStorage().getProperty (SettingID::renameClipRenamesSource))
202 {
203 auto wc = dynamic_cast<WaveAudioClip*> (this);
204
205 if (wc != nullptr && wc->hasAnyTakes())
206 {
207 const auto& takes = wc->getTakes();
208
209 for (int i = 0; i < takes.size(); ++i)
210 {
211 if (auto source = edit.engine.getProjectManager().getProjectItem (takes.getReference (i)))
212 {
213 if (i == 0)
214 source->setName (newName, ProjectItem::SetNameMode::doDefault);
215 else
216 source->setName (newName + " #" + juce::String (i + 1), ProjectItem::SetNameMode::doDefault);
217 }
218 }
219 }
220 else
221 {
222 if (auto sourceItem = sourceFileReference.getSourceProjectItem())
223 sourceItem->setName (newName, ProjectItem::SetNameMode::doDefault);
224 }
225 }
226 }
227}
228
229//==============================================================================
230void Clip::setParent (ClipOwner* newParent)
231{
232 if (parent == newParent)
233 return;
234
235 if (auto track = getTrack())
236 if (auto f = track->getParentFolderTrack())
237 f->setDirtyClips();
238
239 parent = newParent;
240
241 if (auto track = getTrack())
242 if (auto f = track->getParentFolderTrack())
243 f->setDirtyClips();
244}
245
247{
248 return parent;
249}
250
252{
253 return dynamic_cast<ClipTrack*> (getTrack());
254}
255
257{
258 if (auto t = dynamic_cast<Track*> (parent))
259 return t;
260
261 if (auto cs = getClipSlot())
262 return &cs->track;
263
264 return nullptr;
265}
266
268{
269 return dynamic_cast<ClipSlot*> (parent);
270}
271
272//==============================================================================
274{
276
277 if (auto track = getTrack())
278 track->setFrozen (false, Track::groupFreeze);
279
280 if (! cloneInProgress && isLinked() && ! edit.isLoading())
281 updateLinkedClipsCaller.triggerAsyncUpdate();
282}
283
284//==============================================================================
286{
287 setCurrentSourceFile (sourceFileReference.getFile());
288}
289
290//==============================================================================
292{
293 const auto maxEnd = Edit::getMaximumEditEnd();
294 const auto start = juce::jlimit (0_tp, maxEnd, newPosition.time.getStart());
295 newPosition.time = { start,
296 juce::jlimit (start, maxEnd, newPosition.time.getEnd()) };
297 newPosition.offset = juce::jmax (0_td, newPosition.offset);
298
299 clipStart = newPosition.time.getStart();
300 length = newPosition.time.getLength();
301 offset = newPosition.offset;
302}
303
304void Clip::setStart (TimePosition newStart, bool preserveSync, bool keepLength)
305{
306 auto pos = getPosition();
307 auto delta = juce::jlimit (0_tp, Edit::getMaximumEditEnd(), newStart) - pos.time.getStart();
308
309 if (keepLength)
310 pos.time = pos.time + delta;
311 else
312 pos.time = pos.time.withStart (pos.time.getStart() + delta);
313
314 if (preserveSync)
315 pos.offset = juce::jmax (0_td, pos.offset + delta);
316
317 setPosition (pos);
318}
319
320void Clip::setLength (TimeDuration newLength, bool preserveSync)
321{
322 setEnd (getPosition().time.getStart() + newLength, preserveSync);
323}
324
325void Clip::setEnd (TimePosition newEnd, bool preserveSync)
326{
327 auto pos = getPosition();
328 auto delta = juce::jlimit (pos.time.getStart(), Edit::getMaximumEditEnd(), newEnd) - pos.time.getEnd();
329
330 if (! preserveSync)
331 pos.offset = pos.offset - delta;
332
333 pos.time = pos.time.withEnd (pos.time.getEnd() + delta);
334 setPosition (pos);
335}
336
338{
339 auto pos = getPosition();
340 pos.offset = juce::jmax (0_td, newOffset);
341 setPosition (pos);
342}
343
345{
347
348 auto pos = getPosition();
349 times.add (pos.time.getStart());
350
351 for (auto m : getRescaledMarkPoints())
352 times.add (m + toDuration (pos.time.getStart()));
353
354 times.add (pos.time.getEnd());
355
356 return times;
357}
358
360{
362 const auto offsetSecs = getPosition().offset.inSeconds();
363
364 if (auto sourceItem = sourceFileReference.getSourceProjectItem())
365 for (auto t : sourceItem->getMarkedPoints())
366 times.add (TimePosition::fromSeconds (t.inSeconds() / speedRatio - offsetSecs));
367
368 return times;
369}
370
372{
373 auto marks = getRescaledMarkPoints();
374
375 if (marks.isEmpty())
376 return {};
377
378 auto pos = getPosition();
379
380 return juce::jlimit (0_tp, toPosition (pos.time.getLength()),
381 marks.getFirst() - pos.offset);
382}
383
384void Clip::trimAwayOverlap (TimeRange r)
385{
386 auto pos = getPosition();
387
388 if (! pos.time.intersects (r))
389 return;
390
391 if (r.getEnd() < pos.time.getEnd())
392 setStart (r.getEnd(), true, false);
393 else if (pos.time.getStart() < r.getStart())
394 setEnd (r.getStart(), true);
395}
396
402
403bool Clip::moveTo (ClipOwner& newParent)
404{
405 if (parent == &newParent)
406 return false;
407
408 if (! canBeAddedTo (newParent))
409 return false;
410
411 if (auto to = dynamic_cast<ClipTrack*> (&newParent))
412 {
413 if (to->isFrozen (Track::anyFreeze))
414 return false;
415
416 Clip::Ptr refHolder (this);
417 to->addClip (this);
418 return true;
419 }
420 else if (auto cs = dynamic_cast<ClipSlot*> (&newParent))
421 {
422 Clip::Ptr refHolder (this);
423 cs->setClip (this);
424 }
425
426 return false;
427}
428
429//==============================================================================
430void Clip::setSpeedRatio (double r)
431{
433
434 if (speedRatio != r)
435 speedRatio = r;
436}
437
438void Clip::rescale (TimePosition pivotTimeInEdit, double factor)
439{
440 auto pos = getPosition();
441
442 // limit the start to 0
443 if (pivotTimeInEdit + ((pos.getStart() - pivotTimeInEdit) * factor) < TimePosition())
444 factor = -(toDuration (pivotTimeInEdit) / (pos.getStart() - pivotTimeInEdit));
445
446 if (factor > 0.01 && factor != 1.0)
447 {
448 bool canRescale = false;
449
450 if (auto acb = dynamic_cast<AudioClipBase*> (this))
451 canRescale = ! acb->getAutoTempo();
452 else if (type == Type::midi || type == Type::step)
453 canRescale = true;
454
455 if (canRescale)
456 {
457 setPosition (pos.rescaled (pivotTimeInEdit, factor));
458 setSpeedRatio (speedRatio / factor);
459 }
460 }
461}
462
463//==============================================================================
465{
466 if (auto ct = getClipTrack())
467 return ct->getCollectionClip (const_cast<Clip*> (this));
468
469 return {};
470}
471
472//==============================================================================
474{
475 if (currentSourceFile != f)
476 {
477 currentSourceFile = f;
478 changed();
479 }
480}
481
482//==============================================================================
483void Clip::valueTreePropertyChanged (juce::ValueTree& tree, const juce::Identifier& id)
484{
485 if (tree == state)
486 {
487 if (id == IDs::start || id == IDs::length || id == IDs::offset)
488 {
489 if (auto track = getTrack())
490 {
491 if (auto f = track->getParentFolderTrack())
492 f->setDirtyClips();
493
494 changed();
495 }
496 }
497 else if (id == IDs::source)
498 {
499 sourceFileReference.source.forceUpdateOfCachedValue();
501 }
502 else if (id == IDs::colour || id == IDs::speed
503 || id == IDs::sync || id == IDs::linkID)
504 {
505 changed();
506 }
507 else if (id == IDs::name)
508 {
509 clipName.forceUpdateOfCachedValue();
510
512 {
513 if (weakRef != nullptr)
514 SelectionManager::refreshAllPropertyPanels();
515 });
516
517 changed();
518 }
519 else if (id == IDs::followActionDurationType)
520 {
521 changed();
522 propertiesChanged();
523 }
524 else if (id == IDs::followActionBeats)
525 {
526 if (! getUndoManager()->isPerformingUndoRedo())
527 {
528 followActionBeats.forceUpdateOfCachedValue();
529
530 if (followActionBeats.get() > 0_bd)
531 if (auto fa = getFollowActions(); fa && fa->getActions().empty())
532 fa->addAction();
533 }
534 }
535 else if (id == IDs::followActionNumLoops)
536 {
537 if (! getUndoManager()->isPerformingUndoRedo())
538 {
540
541 if (followActionNumLoops.get() > 0.0)
542 if (auto fa = getFollowActions(); fa && fa->getActions().empty())
543 fa->addAction();
544 }
545 }
546 }
547}
548
549void Clip::valueTreeParentChanged (juce::ValueTree& v)
550{
551 if (v == state)
552 updateParent();
553}
554
555void Clip::updateParent()
556{
557 TRACKTION_ASSERT_MESSAGE_THREAD
558 auto parentState = state.getParent();
559
560 if (TrackList::isTrack (parentState))
561 setParent (dynamic_cast<ClipTrack*> (findTrackForID (edit, EditItemID::fromID (parentState))));
562 else if (parentState.hasType (IDs::CLIPLIST) && parentState.getParent().hasType (IDs::CONTAINERCLIP))
563 setParent (dynamic_cast<ContainerClip*> (findClipForID (edit, EditItemID::fromID (parentState.getParent()))));
564 else if (parentState.hasType (IDs::CLIPSLOT))
565 setParent (dynamic_cast<ClipSlot*> (findClipSlotForID (edit, EditItemID::fromID (parentState))));
566 else
567 setParent ({});
568}
569
570//==============================================================================
572{
573 clipName .setValue (c->clipName, nullptr);
574 speedRatio .setValue (c->speedRatio, nullptr);
575 sourceFileReference.source .setValue (c->sourceFileReference.source, nullptr);
576 colour .setValue (c->colour, nullptr);
577 syncType .setValue (c->syncType, nullptr);
578 showingTakes .setValue (c->showingTakes, nullptr);
579}
580
582{
584
585 for (auto c : edit.findClipsInLinkGroup (linkID))
586 {
588 jassert (typeid (*c) == typeid (*this));
589
590 if (c != this && typeid (*c) == typeid (*this))
591 {
592 const juce::ScopedValueSetter<bool> setter (c->cloneInProgress, true);
593 c->cloneFrom (this);
594 }
595 }
596}
597
599{
600 return edit.engine.getEngineBehaviour().getReferencedItems (*this);
601}
602
603void Clip::reassignReferencedItem (const ReferencedItem& itm, ProjectItemID newID, double newStartTime)
604{
605 edit.engine.getEngineBehaviour().reassignReferencedItem (*this, itm, newID, newStartTime);
606}
607
609{
610 auto s = clipStart.get();
611 return { { s, s + length.get() }, offset.get() };
612}
613
615{
616 return edit.tempoSequence.toBeats (t) - toDuration (getContentStartBeat());
617}
618
620{
621 return edit.tempoSequence.toTime (beat + toDuration (getContentStartBeat()));
622}
623
625{
626 return getGroupClip();
627}
628
629void Clip::setGroup (EditItemID newGroupID)
630{
631 groupID = newGroupID;
632 if (groupID == EditItemID())
633 state.removeProperty (IDs::groupID, getUndoManager());
634}
635
637{
638 return colour.get();
639}
640
641//==============================================================================
643{
644 if (listeners.isEmpty())
645 edit.restartPlayback();
646
647 listeners.add (l);
648}
649
651{
652 listeners.remove (l);
653
654 if (listeners.isEmpty())
655 edit.restartPlayback();
656}
657
658//==============================================================================
659namespace details
660{
661 Clip::FollowActionDurationType followActionDurationTypeFromString (juce::String s)
662 {
663 return magic_enum::enum_cast<Clip::FollowActionDurationType> (s.toRawUTF8()).value_or (Clip::FollowActionDurationType::beats);
664 }
665
667 {
668 return std::string (magic_enum::enum_name (t));
669 }
670}
671
672}} // namespace tracktion { inline namespace engine
673
674//==============================================================================
675//==============================================================================
676#if TRACKTION_UNIT_TESTS && ENGINE_UNIT_TESTS_CLIPS
677
678namespace tracktion::inline engine
679{
680
681TEST_SUITE("tracktion_engine")
682{
683 TEST_CASE("Trim away overlap")
684 {
685 auto& engine = *tracktion::engine::Engine::getEngines()[0];
686 auto edit = Edit::createSingleTrackEdit (engine);
687
688 auto clip = getAudioTracks (*edit)[0]->insertMIDIClip ({ 4_tp, 8_tp }, nullptr);
689 auto positionBeforeTrim = clip->getPosition();
690
691 SUBCASE ("Overlap at start")
692 {
693 clip->trimAwayOverlap ({ 2_tp, 6_tp });
694 CHECK (clip->getPosition().time == TimeRange (6_tp, 8_tp));
695 }
696
697 SUBCASE ("Overlap at end")
698 {
699 clip->trimAwayOverlap ({ 6_tp, 10_tp });
700 CHECK (clip->getPosition().time == TimeRange (4_tp, 6_tp));
701 }
702
703 SUBCASE ("No overlap at start")
704 {
705 clip->trimAwayOverlap ({ 2_tp, 4_tp });
706 CHECK (clip->getPosition().time == positionBeforeTrim.time);
707 }
708
709 SUBCASE ("No overlap at end")
710 {
711 clip->trimAwayOverlap ({ 10_tp, 12_tp });
712 CHECK (clip->getPosition().time == positionBeforeTrim.time);
713 }
714 }
715}
716
717} // namespace tracktion::inline engine
718
719#endif
bool isUsingDefault() const
void forceUpdateOfCachedValue()
void setValue(const Type &newValue, UndoManager *undoManagerToUse)
void referTo(ValueTree &tree, const Identifier &property, UndoManager *um)
Type get() const noexcept
const String & toString() const noexcept
static bool callAsync(std::function< void()> functionToCall)
bool isEmpty() const noexcept
const char * toRawUTF8() const
void removeChild(const ValueTree &child, UndoManager *undoManager)
bool isValid() const noexcept
void addListener(Listener *listener)
ValueTree getParent() const noexcept
void removeListener(Listener *listener)
void removeProperty(const Identifier &name, UndoManager *undoManager)
Base class for Clips that produce some kind of audio e.g.
Base class for items that can contain clips.
virtual Edit & getClipOwnerEdit()=0
Must return the Edit this ClipOwner belongs to.
Represents a slot on a track that a Clip can live in to be played as a launched clip.
A clip in an edit.
virtual juce::Array< TimePosition > getRescaledMarkPoints() const
Returns the mark points relative to the start of the clip, rescaled to the current speed.
virtual Plugin::Array getAllPlugins()
Returns all the plugins on the clip.
juce::Array< ReferencedItem > getReferencedItems() override
Returns an array of any ReferencedItem[s] e.g.
juce::CachedValue< BeatDuration > followActionBeats
Determines the time for which a launched clip will play before a follow action is taken.
ClipSlot * getClipSlot() const
Returns the parent ClipSlot this clip is on (if any).
static bool isClipState(const juce::ValueTree &)
Checks whether a ValueTree is some kind of clip state.
ClipTrack * getClipTrack() const
Returns the parent ClipTrack this clip is on (if any).
TimePosition getTimeOfContentBeat(BeatPosition) const
Returns time of a beat number.
virtual bool canBeAddedTo(ClipOwner &)=0
Tests whether this clip can go on the given parent.
void changed() override
This should be called to send a change notification to any SelectableListeners that are registered wi...
CollectionClip * getGroupClip() const
Returns this as a CollectionClip if it is one.
void setOffset(TimeDuration newOffset)
Sets the offset of the clip, i.e.
TimePosition getSpottingPoint() const
Returns the first marked time in the source file which can be used for syncronising newly added clips...
void trimAwayOverlap(TimeRange editRangeToTrim)
Trims away any part of the clip that overlaps this region.
juce::ValueTree state
The ValueTree of the Clip state.
virtual void flushStateToValueTree()
Can be overridden to ensure any state (e.g.
virtual void rescale(TimePosition pivotTimeInEdit, double factor)
stretches and scales this clip relative to a fixed point in the edit.
void setStart(TimePosition newStart, bool preserveSync, bool keepLength)
Sets the start time of the clip.
Track * getTrack() const override
Returns the parent Track this clip is on (if any).
void reassignReferencedItem(const ReferencedItem &, ProjectItemID, double) override
Should be implemented to change the underlying source to a new ProjectItemID.
virtual void initialise()
Initialises the Clip.
BeatPosition getContentBeatAtTime(TimePosition) const
Returns the beat number (with offset) at the given time.
ClipOwner * getParent() const
Returns the parent ClipOwner this clip is on.
virtual void sourceMediaChanged()
Called when the source media file reference (attribute "source") has changed - i.e.
virtual void cloneFrom(Clip *)
Clones the given clip to this clip.
void removeFromParent()
Removes this clip from the parent track or container clip.
void setLength(TimeDuration newLength, bool preserveSync)
Sets the length of the clip.
Clip(const juce::ValueTree &, ClipOwner &, EditItemID, Type)
Creates a clip of a given type from a ValueTree state.
virtual juce::Colour getColour() const
Returns the colour property of this clip.
bool moveTo(ClipOwner &)
Moves the clip to a new parent (if possible).
~Clip() override
Destructor.
void updateLinkedClips()
Triggers a call to cloneFrom for all clips with the same linkID.
juce::CachedValue< bool > disabled
Whether the Clip is disabled or not.
void addListener(Listener *)
Adds a Listener.
juce::CachedValue< FollowActionDurationType > followActionDurationType
The type of duration to use for when to trigger the follow action.
void setName(const juce::String &newName)
Sets a new name for a clip.
juce::CachedValue< double > followActionNumLoops
Determines the number of loops for which a launched clip will play before a follow action is taken.
virtual juce::Array< TimePosition > getInterestingTimes()
Returns times for snapping to, relative to the Edit.
virtual void setSpeedRatio(double)
Sets a speed ratio i.e.
juce::CachedValue< juce::Colour > colour
The colour property.
virtual FollowActions * getFollowActions()
Some clip types can be launched, if that's possible, this can be used to determine the action to perf...
virtual juce::Colour getDefaultColour() const =0
Returns the default colour for this clip.
juce::UndoManager * getUndoManager() const
Returns the UndoManager.
bool isLinked() const
Returns true if this clip is linked with any others.
ClipPosition getPosition() const override
Returns the ClipPosition on the parent Track.
void setEnd(TimePosition newEnd, bool preserveSync)
Sets the end of the clip.
FollowActionDurationType
Defines the types of duration follow actions can use.
static Ptr createClipForState(const juce::ValueTree &, ClipOwner &targetParent)
Creates a clip for a given ValueTree representation.
void setGroup(EditItemID newGroupID)
Sets the clip to be part of a group.
void setCurrentSourceFile(const juce::File &)
Sets a new source file for this clip.
void removeListener(Listener *)
Removes a Listener.
void setPosition(ClipPosition newPosition)
Sets the position of the clip.
TrackItem * getGroupParent() const override
Returns the parent TrackItem if part of a group.
static juce::Array< Engine * > getEngines()
Returns the list of currently active engines.
An ID representing one of the items in a Project.
virtual void changed()
This should be called to send a change notification to any SelectableListeners that are registered wi...
static bool isSelectableValid(const Selectable *) noexcept
checks whether this object has been deleted.
void cancelAnyPendingUpdates()
If changed() has been called, this will cancel any pending async change notificaions.
Base class for EditItems that live in a Track, e.g.
Type
Defines the types of item that can live on Track[s].
const Type type
The type of this item.
static juce::Identifier clipTypeToXMLType(Type)
Returns an Identifier version of a TrackItem::Type.
static TrackItem::Type stringToType(const juce::String &)
Returns the TrackItem::Type of a type string.
BeatPosition getContentStartBeat() const
Returns the start beat of the content in the Edit of this item.
Base class for tracks which contain clips and plugins and can be added to Edit[s].
@ groupFreeze
Freezes multiple tracks together in to a single file.
@ anyFreeze
Either a group or individual freeze.
An audio clip that uses an audio file as its source.
juce::Array< ProjectItemID > getTakes() const override
Returns the ProjectItemID of the clip's takes.
T empty(T... args)
#define TRANS(stringLiteral)
#define NEEDS_TRANS(stringLiteral)
#define jassert(expression)
#define jassertfalse
constexpr Type jmax(Type a, Type b)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
const double speedRatioMax
Maximum speed ratio.
const double speedRatioMin
Minimum speed ratio.
Clip * findClipForID(ClipOwner &co, EditItemID id)
Returns a clip with the given ID if the ClipOwner contains it.
juce::Array< AudioTrack * > getAudioTracks(const Edit &edit)
Returns all the AudioTracks in an Edit.
ClipSlot * findClipSlotForID(const Edit &edit, EditItemID id)
Returns the ClipSlot for the given ID.
Track * findTrackForID(const Edit &edit, EditItemID id)
Returns the Track with a given ID if contained in the Edit.
SafeSelectable< SelectableType > makeSafeRef(SelectableType &selectable)
Creates a SafeSelectable for a given selectable object.
RangeType< TimePosition > TimeRange
A RangeType based on real time (i.e.
Represents a position in beats.
Represents a duration in real-life time.
constexpr double inSeconds() const
Returns the TimeDuration as a number of seconds.
Represents a position in real-life time.
void setFunction(std::function< void()> f)
Sets the function to call.
Represents the position of a clip on a timeline.
TimeDuration offset
The offset this ClipPosition has.
TimeRange time
The TimeRange this ClipPosition occupies.
Listener interface to be notified of recorded MIDI being sent to the plugins.
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.
time
times
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.