11namespace tracktion {
inline namespace engine
14EditClip::EditClip (
const juce::ValueTree& v, EditItemID clipID, ClipOwner& targetParent, ProjectItemID sourceEditID)
15 : AudioClipBase (v, clipID, Type::edit, targetParent),
16 waveInfo (getAudioFile().getInfo())
18 renderOptions = RenderOptions::forEditClip (*
this);
20 sourceIdUpdater.setFunction ([
this] { sourceMediaChanged(); });
21 sourceFileReference.setToProjectFileReference (sourceEditID);
23 auto um = getUndoManager();
24 copyColourFromMarker.referTo (state, IDs::copyColour, um,
false);
25 trimToMarker.referTo (state, IDs::trimToMarker, um,
false);
26 renderEnabled.referTo (state, IDs::renderEnabled, um,
true);
31 for (
auto e : referencedEdits)
32 e->removeListener (this);
34 notifyListenersOfDeletion();
39 if (isTracktionEditFile (getCurrentSourceFile()))
42 return AudioClipBase::getAudioFile();
46void EditClip::initialise()
48 AudioClipBase::initialise();
50 if (waveInfo.sampleRate <= 0 || waveInfo.lengthInSamples <= 0)
56 sourceIdUpdater.triggerAsyncUpdate();
59void EditClip::cloneFrom (
Clip* c)
61 if (
auto other =
dynamic_cast<EditClip*
> (c))
63 AudioClipBase::cloneFrom (other);
65 copyColourFromMarker .setValue (other->copyColourFromMarker,
nullptr);
66 trimToMarker .setValue (other->trimToMarker,
nullptr);
67 renderEnabled .setValue (other->renderEnabled,
nullptr);
79 jassert (editSnapshot ==
nullptr || editSnapshot->getFile() == sourceFileReference.getFile());
80 return editSnapshot !=
nullptr ? editSnapshot->getFile() :
juce::File();
83void EditClip::setLoopDefaults()
86 if (loopInfo.getNumerator() == 0
87 && loopInfo.getDenominator() == 0
88 && loopInfo.getNumBeats() == 0)
89 updateLoopInfoBasedOnSource (
true);
93bool EditClip::needsRender()
const
95 if (! renderEnabled || editSnapshot ==
nullptr)
98 return editSnapshot->getLength() > 0.0s;
103 TRACKTION_ASSERT_MESSAGE_THREAD
106 if (
auto existing = edit.engine.getRenderManager().getRenderJobWithoutCreating (destFile))
109 auto j = EditRenderJob::getOrCreateRenderJob (edit.engine,
110 destFile, *renderOptions,
111 sourceFileReference.getSourceProjectItemID(),
112 true, getIsReversed());
113 j->setName (
TRANS(
"Creating Edit Clip") +
": " +
getName());
119void EditClip::renderComplete()
123 AudioClipBase::renderComplete();
128 TRACKTION_ASSERT_MESSAGE_THREAD
130 if (renderJob ==
nullptr || getCurrentSourceFile().existsAsFile())
133 auto numRenderingJobs = edit.engine.getRenderManager().getNumJobs();
136 if (numRenderingJobs > 0)
137 remainderMessage <<
" (" <<
juce::String (numRenderingJobs) <<
" " <<
TRANS(
"remaining") <<
")";
139 if (renderJob ==
nullptr)
140 return TRANS(
"Rendering referenced Edits") +
"..." + remainderMessage;
142 const float progress = renderJob->getCurrentTaskProgress();
144 if (progress <= 0.0f)
145 return TRANS(
"Rendering referenced Edits") +
"..." + remainderMessage;
152 if (! sourceFileReference.getSourceProjectItemID().isValid())
153 return TRANS(
"No source set");
155 if (renderOptions->getNumTracks() == 0)
156 return TRANS(
"No tracks selected to render");
159 return TRANS(
"Rendering disabled");
161 return AudioClipBase::getClipMessage();
165void EditClip::sourceMediaChanged()
167 if (sourceMediaReEntrancyCheck)
172 auto newID = sourceFileReference.getSourceProjectItemID();
176 if (isInitialised && lastSourceId == newID)
179 const bool resetTracksToDefault = (! edit.isLoading() && ! lastSourceId.isValid());
181 lastSourceId = newID;
182 editSnapshot = EditSnapshot::getEditSnapshot (edit.engine, newID);
183 const bool invalidSource = editSnapshot ==
nullptr || ! editSnapshot->isValid();
187 if (renderJob !=
nullptr)
188 renderJob->removeListener (
this);
190 setCurrentSourceFile ({});
194 if (resetTracksToDefault)
196 if (editSnapshot !=
nullptr)
197 renderOptions->setTrackIDs (editSnapshot->getTracks());
199 renderOptions->setTrackIDs ({});
202 updateReferencedEdits();
212 updateLoopInfoBasedOnSource (
true);
215void EditClip::changed()
219 AudioClipBase::changed();
224 if (AudioClipBase::isUsingFile (af))
227 auto audioFile = RenderManager::getAudioFileForHash (edit.engine, edit.getTempDirectory (
false), getHash());
237 if (v == state && i == IDs::renderEnabled)
239 TRACKTION_ASSERT_MESSAGE_THREAD
240 renderEnabled.forceUpdateOfCachedValue();
242 setUsesProxy (renderEnabled);
248 cancelCurrentRender();
252 AudioClipBase::valueTreePropertyChanged (v, i);
256ProjectItem::Ptr EditClip::createUniqueCopy()
258 if (
auto item = sourceFileReference.getSourceProjectItem())
259 return item->createCopy();
264void EditClip::updateWaveInfo()
268 jassert ((! needsRender()) || getSourceLength() > 0s);
269 const auto sourceLength = getSourceLength() == 0s ? 5.0 : getSourceLength().inSeconds();
271 waveInfo.bitsPerSample = renderOptions->getBitDepth();
272 waveInfo.sampleRate = renderOptions->getSampleRate();
273 waveInfo.numChannels = renderOptions->getStereo() ? 2 : 1;
274 waveInfo.lengthInSamples =
static_cast<SampleCount
> (sourceLength * waveInfo.sampleRate);
276 updateLoopInfoBasedOnSource (
false);
279HashCode EditClip::generateHash()
286 HashCode editClipHash = 0;
288 for (
auto snapshot : referencedEdits)
289 editClipHash ^= snapshot->getHash();
291 HashCode newHash = editClipHash
292 ^ renderOptions->getHash()
293 ^
static_cast<HashCode
> (getIsReversed() * 768);
306 if (renderOptions !=
nullptr)
308 renderOptions->setTrackIDs (trackIDs);
313void EditClip::updateReferencedEdits()
319 if (editSnapshot !=
nullptr)
320 current = editSnapshot->getNestedEditObjects();
323 for (
int i = current.size(); --i >= 0;)
325 auto snapshot = current.getUnchecked (i);
327 if (referencedEdits.contains (snapshot))
330 referencedEdits.add (snapshot);
331 added.add (snapshot);
335 for (
int i = referencedEdits.size(); --i >= 0;)
337 auto snapshot = referencedEdits.getUnchecked (i);
339 if (current.contains (snapshot))
342 removed.add (snapshot);
343 referencedEdits.remove (i);
347 for (
auto snapshot : removed)
348 snapshot->removeListener (this);
350 for (
auto snapshot : added)
351 snapshot->addListener (this);
354void EditClip::updateLoopInfoBasedOnSource (
bool updateLength)
356 if (editSnapshot ==
nullptr || ! editSnapshot->isValid())
360 auto tempo = editSnapshot->getTempo();
361 auto timeSigNum = editSnapshot->getTimeSigNumerator();
362 auto timeSigDenom = editSnapshot->getTimeSigDenominator();
363 auto pitch = editSnapshot->getPitch();
364 double clipNumBeats = 1.0;
368 clipNumBeats = (tempo * getSourceLength().inSeconds()) / 60.0;
369 loopInfo.setNumBeats (clipNumBeats);
370 loopInfo.setBpm (tempo, waveInfo);
374 if (timeSigNum > 0 && timeSigDenom > 0)
376 loopInfo.setNumerator (timeSigNum);
377 loopInfo.setDenominator (timeSigDenom);
381 auto& ts = edit.tempoSequence;
382 auto clipPos = getPosition();
384 if (loopInfo.getNumerator() == 0 || loopInfo.getDenominator() == 0)
386 loopInfo.setNumerator (ts.getTimeSigAt (clipPos.getStart()).numerator);
387 loopInfo.setDenominator (ts.getTimeSigAt (clipPos.getStart()).denominator);
390 if (loopInfo.getNumBeats() == 0)
391 loopInfo.setNumBeats (length.get().inSeconds() * (ts.getTempoAt (clipPos.getStart()).getBpm() / 60.0));
396 if (! (loopInfo.getNumerator() == 0
397 && loopInfo.getDenominator() == 0
398 && loopInfo.getNumBeats() == 0))
400 auto editBpm = ts.getTempoAt (clipPos.getStart()).getBpm();
403 tempo = loopInfo.getBpm (getWaveInfo());
405 if (editBpm > 0.0 && tempo > 0.0 && tempo < 400)
407 auto bpmRatio = tempo / editBpm;
408 jassert (bpmRatio > 0.1 && bpmRatio < 10.0);
409 auto newLength = getSourceLength() * bpmRatio;
410 setLength (newLength,
true);
413 loopInfo.setNumBeats (clipNumBeats);
418 if (updateLength && pitch > 0)
419 loopInfo.setRootNote (pitch);
422double EditClip::getCurrentStretchRatio()
const
424 if (audioSegmentList !=
nullptr && ! audioSegmentList->getSegments().isEmpty())
425 return audioSegmentList->getSegments().getReference (0).getStretchRatio();
427 return getSpeedRatio();
431void EditClip::editChanged (EditSnapshot&)
434 updateReferencedEdits();
This is the main source of an Edit clip and is responsible for managing its properties.
#define TRANS(stringLiteral)
int roundToInt(const FloatType value) noexcept
juce::String getName(LaunchQType t)
Retuns the name of a LaunchQType for display purposes.
size_t hash(size_t seed, const T &v)
Hashes a type with a given seed and returns the new hash value.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.