11namespace tracktion {
inline namespace engine
16 :
TrackItem (targetParent.getClipOwnerEdit(), id, t),
17 state (v), parent (&targetParent),
18 sourceFileReference (edit, state, IDs::source)
22 edit.clipCache.addItem (*
this);
26 clipStart.referTo (
state, IDs::start, um);
27 length.referTo (
state, IDs::length, um);
28 offset.referTo (
state, IDs::offset, um);
33 showingTakes.
referTo (
state, IDs::showingTakes,
nullptr,
false);
34 groupID.referTo (
state, IDs::groupID, um);
40 convertPropertyToType<bool> (
state, IDs::disabled);
42 if (! (length >= 0_td && length < 1.0e10s))
55 edit.clipCache.removeItem (*
this);
68 if (sourceFileReference.isUsingProjectReference())
75 if (
auto f = track->getParentFolderTrack())
86 return &
edit.getUndoManager();
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;
107 auto sourceItem = edit.engine.getProjectManager().getProjectItem (sourceItemID);
110 if (sourceItem !=
nullptr && sourceItem->getLength() > 0.0)
112 if (
auto snapshot = EditSnapshot::getEditSnapshot (edit.engine, sourceItemID))
115 auto referencedEdits = snapshot->getNestedEditObjects();
116 referencedEdits.remove (0);
118 if (referencedEdits.contains (snapshot))
119 warning =
NEEDS_TRANS(
"You can't add an Edit which contains this Edit as a clip to itself");
123 warning =
NEEDS_TRANS(
"Unable to load Edit from source file");
128 if (warning.
isEmpty() || ! sourceItemID.isValid())
129 return new EditClip (v, newClipID, targetParent, sourceItemID);
131 edit.engine.getUIBehaviour().showWarningAlert (
TRANS(
"Can't Import Edit"),
TRANS(warning));
135static Clip::Ptr createNewClipObject (
const juce::ValueTree& v, EditItemID newClipID, ClipOwner& targetParent)
137 auto type = v.getType();
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);
163 || v.getParent().hasType (IDs::CLIPLIST)
164 || v.getParent().hasType (IDs::CLIPSLOT));
167 auto newClipID = EditItemID::readOrCreateNewID (
edit, v);
174 c = createNewClipObject (v, newClipID, targetParent);
175 jassert (c->getParent() == &targetParent);
180 c->Selectable::changed();
181 c->cancelAnyPendingUpdates();
191 p->flushPluginStateToValueTree();
197 if (clipName != newName)
201 if (
edit.engine.getPropertyStorage().getProperty (SettingID::renameClipRenamesSource))
205 if (wc !=
nullptr && wc->hasAnyTakes())
209 for (
int i = 0; i < takes.size(); ++i)
211 if (
auto source =
edit.engine.getProjectManager().getProjectItem (takes.getReference (i)))
214 source->setName (newName, ProjectItem::SetNameMode::doDefault);
216 source->setName (newName +
" #" +
juce::String (i + 1), ProjectItem::SetNameMode::doDefault);
222 if (
auto sourceItem = sourceFileReference.getSourceProjectItem())
223 sourceItem->setName (newName, ProjectItem::SetNameMode::doDefault);
230void Clip::setParent (
ClipOwner* newParent)
232 if (parent == newParent)
236 if (
auto f = track->getParentFolderTrack())
242 if (
auto f = track->getParentFolderTrack())
258 if (
auto t =
dynamic_cast<Track*
> (parent))
269 return dynamic_cast<ClipSlot*
> (parent);
280 if (! cloneInProgress &&
isLinked() && !
edit.isLoading())
293 const auto maxEnd = Edit::getMaximumEditEnd();
294 const auto start =
juce::jlimit (0_tp, maxEnd, newPosition.
time.getStart());
295 newPosition.
time = { start,
299 clipStart = newPosition.
time.getStart();
300 length = newPosition.
time.getLength();
301 offset = newPosition.
offset;
307 auto delta =
juce::jlimit (0_tp, Edit::getMaximumEditEnd(), newStart) - pos.time.getStart();
310 pos.time = pos.time + delta;
312 pos.time = pos.time.withStart (pos.time.getStart() + delta);
315 pos.offset =
juce::jmax (0_td, pos.offset + delta);
328 auto delta =
juce::jlimit (pos.time.getStart(), Edit::getMaximumEditEnd(), newEnd) - pos.time.getEnd();
331 pos.offset = pos.offset - delta;
333 pos.time = pos.time.withEnd (pos.time.getEnd() + delta);
349 times.add (pos.time.getStart());
352 times.add (m + toDuration (pos.time.getStart()));
354 times.add (pos.time.getEnd());
364 if (
auto sourceItem = sourceFileReference.getSourceProjectItem())
365 for (
auto t : sourceItem->getMarkedPoints())
366 times.add (TimePosition::fromSeconds (t.inSeconds() / speedRatio - offsetSecs));
380 return juce::jlimit (0_tp, toPosition (pos.time.getLength()),
381 marks.getFirst() - pos.offset);
388 if (! pos.time.intersects (r))
391 if (r.getEnd() < pos.time.getEnd())
393 else if (pos.time.getStart() < r.getStart())
394 setEnd (r.getStart(),
true);
405 if (parent == &newParent)
411 if (
auto to =
dynamic_cast<ClipTrack*
> (&newParent))
420 else if (
auto cs =
dynamic_cast<ClipSlot*
> (&newParent))
443 if (pivotTimeInEdit + ((pos.getStart() - pivotTimeInEdit) * factor) <
TimePosition())
444 factor = -(toDuration (pivotTimeInEdit) / (pos.getStart() - pivotTimeInEdit));
446 if (factor > 0.01 && factor != 1.0)
448 bool canRescale =
false;
451 canRescale = ! acb->getAutoTempo();
457 setPosition (pos.rescaled (pivotTimeInEdit, factor));
467 return ct->getCollectionClip (
const_cast<Clip*
> (
this));
475 if (currentSourceFile != f)
477 currentSourceFile = f;
487 if (
id == IDs::start ||
id == IDs::length ||
id == IDs::offset)
491 if (
auto f = track->getParentFolderTrack())
497 else if (
id == IDs::source)
502 else if (
id == IDs::colour ||
id == IDs::speed
503 ||
id == IDs::sync ||
id == IDs::linkID)
507 else if (
id == IDs::name)
513 if (weakRef !=
nullptr)
514 SelectionManager::refreshAllPropertyPanels();
519 else if (
id == IDs::followActionDurationType)
524 else if (
id == IDs::followActionBeats)
535 else if (
id == IDs::followActionNumLoops)
555void Clip::updateParent()
557 TRACKTION_ASSERT_MESSAGE_THREAD
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))
573 clipName .
setValue (c->clipName,
nullptr);
574 speedRatio .
setValue (c->speedRatio,
nullptr);
575 sourceFileReference.source .
setValue (c->sourceFileReference.source,
nullptr);
577 syncType .
setValue (c->syncType,
nullptr);
578 showingTakes .
setValue (c->showingTakes,
nullptr);
585 for (
auto c :
edit.findClipsInLinkGroup (linkID))
588 jassert (
typeid (*c) ==
typeid (*
this));
590 if (c !=
this &&
typeid (*c) ==
typeid (*
this))
600 return edit.engine.getEngineBehaviour().getReferencedItems (*
this);
605 edit.engine.getEngineBehaviour().reassignReferencedItem (*
this, itm, newID, newStartTime);
610 auto s = clipStart.get();
611 return { { s, s + length.get() }, offset.get() };
631 groupID = newGroupID;
644 if (listeners.isEmpty())
645 edit.restartPlayback();
652 listeners.remove (l);
654 if (listeners.isEmpty())
655 edit.restartPlayback();
676#if TRACKTION_UNIT_TESTS && ENGINE_UNIT_TESTS_CLIPS
678namespace tracktion::inline engine
681TEST_SUITE(
"tracktion_engine")
683 TEST_CASE(
"Trim away overlap")
686 auto edit = Edit::createSingleTrackEdit (engine);
688 auto clip =
getAudioTracks (*edit)[0]->insertMIDIClip ({ 4_tp, 8_tp },
nullptr);
689 auto positionBeforeTrim = clip->getPosition();
691 SUBCASE (
"Overlap at start")
693 clip->trimAwayOverlap ({ 2_tp, 6_tp });
694 CHECK (clip->getPosition().time ==
TimeRange (6_tp, 8_tp));
697 SUBCASE (
"Overlap at end")
699 clip->trimAwayOverlap ({ 6_tp, 10_tp });
700 CHECK (clip->getPosition().time ==
TimeRange (4_tp, 6_tp));
703 SUBCASE (
"No overlap at start")
705 clip->trimAwayOverlap ({ 2_tp, 4_tp });
706 CHECK (clip->getPosition().time == positionBeforeTrim.time);
709 SUBCASE (
"No overlap at end")
711 clip->trimAwayOverlap ({ 10_tp, 12_tp });
712 CHECK (clip->getPosition().time == positionBeforeTrim.time);
void triggerAsyncUpdate()
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.
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.
@ beats
A number of beats.
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.
#define TRANS(stringLiteral)
#define NEEDS_TRANS(stringLiteral)
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.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.