11namespace tracktion {
inline namespace engine
14TrackCompManager::CompSection::CompSection (
const juce::ValueTree& v) : state (v)
20TrackCompManager::CompSection::~CompSection()
24TrackCompManager::CompSection* TrackCompManager::CompSection::createAndIncRefCount (
const juce::ValueTree& v)
26 auto cs =
new CompSection (v);
27 cs->incReferenceCount();
35 if (i == IDs::track) updateTrack();
36 else if (i == IDs::end) updateEnd();
44void TrackCompManager::CompSection::updateTrack() { track = EditItemID::fromProperty (state, IDs::track); }
45void TrackCompManager::CompSection::updateEnd() {
end =
static_cast<double> (state[IDs::end]); }
48TrackCompManager::TrackComp* TrackCompManager::TrackComp::createAndIncRefCount (Edit& edit,
const juce::ValueTree& v)
50 auto tc =
new TrackComp (edit, v);
51 tc->incReferenceCount();
55TrackCompManager::TrackComp::TrackComp (Edit& e,
const juce::ValueTree& v)
58 auto um = &edit.getUndoManager();
59 name.referTo (state, IDs::name, um);
60 timeFormat.referTo (state, IDs::timecodeFormat, um);
61 includedColour.referTo (state, IDs::includedColour, um, juce::Colours::green);
62 excludedColour.referTo (state, IDs::excludedColour, um, juce::Colours::red);
67TrackCompManager::TrackComp::~TrackComp()
69 notifyListenersOfDeletion();
74juce::String TrackCompManager::TrackComp::getSelectableDescription() {
return TRANS(
"Track Comp Group"); }
76void TrackCompManager::TrackComp::setTimeFormat (TimeFormat t)
78 const TimeFormat current = timeFormat.get();
84 convertTimes (current, t);
99 for (
auto r : nonMuteTimes)
101 muteTimes.
add ({ lastTime, r.getStart() });
102 lastTime = r.getEnd();
111juce::Array<TimeRange> TrackCompManager::TrackComp::getNonMuteTimes (Track& t, TimeDuration crossfadeTime)
const
113 auto halfCrossfade = crossfadeTime / 2.0;
116 auto& ts = edit.tempoSequence;
117 const bool convertFromBeats = timeFormat == beats;
119 for (
const auto& sec : getSectionsForTrack (&t))
121 auto s = sec.timeRange.getStart();
122 auto e = sec.timeRange.getEnd();
124 if (convertFromBeats)
126 s = ts.toTime (BeatPosition::fromBeats (s)).inSeconds();
127 e = ts.toTime (BeatPosition::fromBeats (e)).inSeconds();
130 nonMuteTimes.
add ({ TimePosition::fromSeconds (s) - halfCrossfade,
131 TimePosition::fromSeconds (e) + halfCrossfade });
135 for (
int i = 0; i < nonMuteTimes.
size() - 1; ++i)
140 if (r1.getEnd() > r2.getStart())
142 auto diff = (r1.getEnd() - r2.getStart()) / 2.0;
143 r1 = r1.withEnd (r1.getEnd() - diff);
144 jassert (r1.getEnd() > r1.getStart());
145 r2 = r2.withStart (r2.getStart() + diff);
146 jassert (r2.getEnd() > r2.getStart());
153TimeRange TrackCompManager::TrackComp::getTimeRange()
const
157 const auto crossfadeTimeMs = edit.engine.getPropertyStorage().getProperty (SettingID::compCrossfadeMs, 20.0);
158 const auto crossfadeTime = TimeDuration::fromSeconds (
static_cast<double> (crossfadeTimeMs) / 1000.0);
159 const auto halfCrossfade = crossfadeTime / 2.0;
161 auto& ts = edit.tempoSequence;
162 bool convertFromBeats = timeFormat == beats;
164 for (
const auto& sec : getSectionsForTrack ({}))
166 if (! sec.compSection->getTrack().isValid())
169 auto s = sec.timeRange.getStart();
170 auto e = sec.timeRange.getEnd();
172 if (convertFromBeats)
174 s = ts.toTime (BeatPosition::fromBeats (s)).inSeconds();
175 e = ts.toTime (BeatPosition::fromBeats (e)).inSeconds();
178 TimeRange secTime (TimePosition::fromSeconds (s) - halfCrossfade,
179 TimePosition::fromSeconds (e) + halfCrossfade);
180 time =
time.isEmpty() ? secTime :
time.getUnionWith (secTime);
186void TrackCompManager::TrackComp::setName (
const juce::String& n)
191EditItemID TrackCompManager::TrackComp::getSourceTrackID (
const juce::ValueTree& v)
193 return EditItemID::fromProperty (v, IDs::track);
198 v.setProperty (IDs::track, newID, um);
201void TrackCompManager::TrackComp::setSectionTrack (CompSection& cs,
const Track::Ptr& t)
203 jassert (cs.state.getParent() == state);
204 auto um = &edit.getUndoManager();
205 setSourceTrackID (cs.state, t !=
nullptr ? t->itemID : EditItemID(), um);
208void TrackCompManager::TrackComp::removeSection (CompSection& cs)
210 auto um = &edit.getUndoManager();
211 auto previous = cs.state.getSibling (-1);
212 auto next = cs.state.getSibling (1);
217 if (getSourceTrackID (next).isValid())
218 setSourceTrackID (cs.state, {}, um);
222 else if (previous.isValid())
224 if (! getSourceTrackID (previous).isValid())
225 state.removeChild (previous, um);
231 state.removeChild (cs.state, um);
234TrackCompManager::CompSection* TrackCompManager::TrackComp::moveSectionToEndAt (CompSection* cs,
double newEndTime)
239 return moveSection (cs, newEndTime - cs->getEnd());
242TrackCompManager::CompSection* TrackCompManager::TrackComp::moveSection (CompSection* cs,
double timeDelta)
244 if (timeDelta == 0.0 || cs ==
nullptr)
247 bool needToAddSectionAtStart =
false;
248 const int sectionIndex = objects.indexOf (cs);
251 if (sectionIndex == 0)
254 needToAddSectionAtStart =
true;
260 auto um = &edit.getUndoManager();
262 juce::Range<double> oldSectionTimes (prevCs ==
nullptr ? 0.0 : prevCs->getEnd(), cs->getEnd());
263 auto newSectionTimes = oldSectionTimes + timeDelta;
265 if (newSectionTimes.getStart() < 0.0)
268 if (oldSectionTimes == newSectionTimes)
271 removeSectionsWithinRange (oldSectionTimes.
getUnionWith (newSectionTimes), cs);
272 prevCs = objects[objects.indexOf (cs) - 1];
274 if (prevCs !=
nullptr)
275 prevCs->state.setProperty (IDs::end, newSectionTimes.getStart(), um);
277 cs->state.setProperty (IDs::end, newSectionTimes.getEnd(), um);
279 if (needToAddSectionAtStart)
280 addSection (
nullptr, timeDelta);
285TrackCompManager::CompSection* TrackCompManager::TrackComp::moveSectionEndTime (CompSection* cs,
double newTime)
287 const auto minSectionLength = 0.01;
290 juce::Range<double> oldSectionTimes (prevCs ==
nullptr ? 0.0 : prevCs->getEnd(), cs->getEnd());
291 auto newSectionTimes = oldSectionTimes.
withEnd (newTime);
292 auto um = &edit.getUndoManager();
294 if (newSectionTimes.getEnd() >= oldSectionTimes.
getEnd())
297 removeSectionsWithinRange (newSectionTimes, cs);
298 cs->state.setProperty (IDs::end, newSectionTimes.getEnd(), um);
302 if (newSectionTimes.getLength() >= minSectionLength)
304 cs->state.setProperty (IDs::end, newSectionTimes.getEnd(), um);
308 if (newSectionTimes.getLength() < minSectionLength)
310 newSectionTimes = oldSectionTimes.
withLength (minSectionLength);
311 cs->state.setProperty (IDs::end, newSectionTimes.getEnd(), um);
318int TrackCompManager::TrackComp::removeSectionsWithinRange (
juce::Range<double> timeRange, CompSection* sectionToKeep)
321 auto sections = getSectionsForTrack ({});
323 for (
int i = sections.size(); --i >= 0;)
325 auto& section = sections.getReference (i);
326 auto sectionTime = section.timeRange;
328 if (section.compSection != sectionToKeep
329 && (timeRange.
contains (sectionTime) || sectionTime.getLength() < 0.0))
331 state.removeChild (section.compSection->state, &edit.getUndoManager());
335 if (sectionTime.getStart() < timeRange.
getStart())
346 return double (f[IDs::end]) <
double (s[IDs::end]) ? -1 : 1;
353 auto newSection = createValueTree (IDs::COMPSECTION,
358 const int numSections = state.getNumChildren();
360 for (
int i = numSections; --i >= 0;)
362 auto v = state.getChild (i);
364 if (
double (v[IDs::end]) < endTime)
370 state.addChild (newSection, insertIndex, um);
373 state.sort (sorter, &edit.getUndoManager(),
false);
378TrackCompManager::CompSection* TrackCompManager::TrackComp::addSection (Track::Ptr t,
double endTime)
380 auto trackID = t ==
nullptr ? EditItemID() : t->itemID;
381 auto cs = getCompSectionFor (addSection (trackID, endTime, &edit.getUndoManager()));
386TrackCompManager::CompSection* TrackCompManager::TrackComp::splitSectionAtTime (
double time)
390 if (
auto cs = findSectionAtTime (
nullptr, time))
391 trackID = cs->getTrack();
393 return getCompSectionFor (addSection (trackID, time, &edit.getUndoManager()));
399 double lastTime = 0.0;
400 auto trackId = track ==
nullptr ? EditItemID() : track->itemID;
402 for (
auto cs : objects)
404 const auto t = cs->getEnd();
406 if (! trackId.isValid() || cs->getTrack() == trackId)
407 sections.
add (Section { cs, { lastTime, t } });
415TrackCompManager::CompSection* TrackCompManager::TrackComp::findSectionWithEdgeTimeWithin (
const Track::Ptr& track,
417 bool& startEdge)
const
419 for (
const auto& section : getSectionsForTrack (track))
421 if (timeRange.
contains (section.timeRange.getStart()))
424 return objects[objects.indexOf (section.compSection) - 1];
427 if (timeRange.
contains (section.timeRange.getEnd()))
430 return section.compSection;
437TrackCompManager::CompSection* TrackCompManager::TrackComp::findSectionAtTime (
const Track::Ptr& track,
double time)
const
439 auto trackID = track !=
nullptr ? track->itemID : EditItemID();
441 for (
const auto& section : getSectionsForTrack (track))
443 auto* cs = section.compSection;
445 if ((track ==
nullptr || cs->getTrack() == trackID)
446 && section.timeRange.contains (time))
453TrackCompManager::CompSection* TrackCompManager::TrackComp::createNewObject (
const juce::ValueTree& v)
455 return CompSection::createAndIncRefCount (v);
458bool TrackCompManager::TrackComp::isSuitableType (
const juce::ValueTree& v)
const {
return v.hasType (IDs::COMPSECTION); }
459void TrackCompManager::TrackComp::deleteObject (CompSection* cs) { cs->decReferenceCount(); }
460void TrackCompManager::TrackComp::newObjectAdded (CompSection*) {}
461void TrackCompManager::TrackComp::objectRemoved (CompSection*) {}
462void TrackCompManager::TrackComp::objectOrderChanged() {}
466 if (v.hasType (IDs::COMPSECTION))
467 if (CompSection* cs = getCompSectionFor (v))
468 cs->updateFrom (v, i);
471TrackCompManager::CompSection* TrackCompManager::TrackComp::getCompSectionFor (
const juce::ValueTree& v)
473 jassert (v.hasType (IDs::COMPSECTION));
475 for (
auto cs : objects)
482void TrackCompManager::TrackComp::convertFromSecondsToBeats()
484 auto& ts = edit.tempoSequence;
485 auto um = &edit.getUndoManager();
487 for (
auto cs : objects)
489 const auto s = cs->getEnd();
490 const auto b = ts.toBeats (TimePosition::fromSeconds (s)).inBeats();
491 cs->state.setProperty (IDs::end, b, um);
496void TrackCompManager::TrackComp::convertFromBeatsToSeconds()
498 auto& ts = edit.tempoSequence;
499 auto um = &edit.getUndoManager();
501 for (
auto cs : objects)
503 const auto b = cs->getEnd();
504 const auto s = ts.toTime (BeatPosition::fromBeats (b)).inSeconds();
505 cs->state.setProperty (IDs::end, s, um);
510void TrackCompManager::TrackComp::convertTimes (TimeFormat o, TimeFormat n)
517 convertFromSecondsToBeats();
521 convertFromBeatsToSeconds();
537 bool isSuitableType (
const juce::ValueTree& v)
const override {
return v.hasType (IDs::TRACKCOMP); }
538 TrackComp* createNewObject (
const juce::ValueTree& v)
override {
return TrackComp::createAndIncRefCount (edit, v); }
540 void newObjectAdded (
TrackComp*)
override {}
541 void objectRemoved (
TrackComp*)
override {}
542 void objectOrderChanged()
override {}
551TrackCompManager::TrackCompManager (
Edit& e) : edit (e) {}
552TrackCompManager::~TrackCompManager() {}
556 jassert (v.hasType (IDs::TRACKCOMPS));
561int TrackCompManager::getNumGroups()
const
571 for (
int i = 0; i < numComps; ++i)
574 jassert (v.hasType (IDs::TRACKCOMP));
576 auto name = v.getProperty (IDs::name).
toString();
590 return "<" +
TRANS(
"None") +
">";
592 auto name = getCompNames()[index];
594 if (name.isNotEmpty())
601void TrackCompManager::setCompName (
int index,
const juce::String& name)
608 v.setProperty (IDs::name, name, &edit.getUndoManager());
611int TrackCompManager::addGroup (
const juce::String& name)
613 auto v = createValueTree (IDs::TRACKCOMP,
616 state.
addChild (v, -1, &edit.getUndoManager());
618 return getNumGroups() - 1;
621void TrackCompManager::removeGroup (
int index)
624 if (at->getCompGroup() == index)
625 at->setCompGroup (-1);
635 if (at->getCompGroup() == index)
641TrackCompManager::TrackComp::Ptr TrackCompManager::getTrackComp (AudioTrack* at)
643 return at ==
nullptr ? nullptr : trackCompList->objects[at->getCompGroup()];
bool isEmpty() const noexcept
void ensureStorageAllocated(int minNumElements)
int size() const noexcept
ElementType getFirst() const noexcept
void add(const ElementType &newElement)
ElementType & getReference(int index) noexcept
bool isValid() const noexcept
const String & toString() const noexcept
constexpr ValueType getStart() const noexcept
constexpr ValueType getEnd() const noexcept
constexpr Range withEnd(const ValueType newEnd) const noexcept
constexpr Range getUnionWith(Range other) const noexcept
constexpr Range movedToStartAt(const ValueType newStart) const noexcept
constexpr Range withLength(const ValueType newLength) const noexcept
constexpr bool contains(const ValueType position) const noexcept
void decReferenceCount() noexcept
void add(String stringToAdd)
void removeChild(const ValueTree &child, UndoManager *undoManager)
ValueTree getChild(int index) const
int getNumChildren() const noexcept
void addChild(const ValueTree &child, int index, UndoManager *undoManager)
The Tracktion Edit class!
#define TRANS(stringLiteral)
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
juce::Array< AudioTrack * > getAudioTracks(const Edit &edit)
Returns all the AudioTracks in an Edit.
RangeType< TimePosition > TimeRange
A RangeType based on real time (i.e.
ID for objects of type EditElement - e.g.