Skip to content

Commit 268afd4

Browse files
committed
Similar fixes and tests for space deletion (see the previous commit for insertion fixes)
1 parent f9a4e10 commit 268afd4

File tree

4 files changed

+275
-85
lines changed

4 files changed

+275
-85
lines changed

modules/tracktion_engine/model/edit/tracktion_Edit.test.cpp

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,180 @@ TEST_SUITE("tracktion_engine")
4949
// Point should move from 5s to 7s
5050
CHECK (curve.getPointTime (0) == 7_tp);
5151
}
52+
53+
TEST_CASE ("insertSpaceIntoEdit: moves global macro parameter automation")
54+
{
55+
auto& engine = *Engine::getEngines()[0];
56+
auto edit = Edit::createSingleTrackEdit (engine, Edit::EditRole::forRendering);
57+
auto um = &edit->getUndoManager();
58+
59+
auto& globalMacros = edit->getGlobalMacros();
60+
auto macroParam = globalMacros.getMacroParameterListForWriting().createMacroParameter();
61+
REQUIRE (macroParam != nullptr);
62+
63+
auto& curve = macroParam->getCurve();
64+
curve.addPoint (5_tp, 0.5f, 0.0f, um);
65+
66+
CHECK (curve.getPointTime (0) == 5_tp);
67+
68+
insertSpaceIntoEdit (*edit, { 2_tp, 2_td });
69+
70+
// Point should move from 5s to 7s
71+
CHECK (curve.getPointTime (0) == 7_tp);
72+
}
73+
74+
TEST_CASE ("insertSpaceIntoEdit: moves rack plugin automation")
75+
{
76+
auto& engine = *Engine::getEngines()[0];
77+
auto edit = Edit::createSingleTrackEdit (engine, Edit::EditRole::forRendering);
78+
auto um = &edit->getUndoManager();
79+
80+
auto rackType = edit->getRackList().addNewRack();
81+
auto volumePlugin = edit->getPluginCache().createNewPlugin (VolumeAndPanPlugin::xmlTypeName, {});
82+
rackType->addPlugin (volumePlugin, {}, true);
83+
84+
auto volAndPan = dynamic_cast<VolumeAndPanPlugin*> (volumePlugin.get());
85+
REQUIRE (volAndPan != nullptr);
86+
87+
auto volParam = volAndPan->volParam.get();
88+
auto& curve = volParam->getCurve();
89+
curve.addPoint (5_tp, 0.5f, 0.0f, um);
90+
91+
CHECK (curve.getPointTime (0) == 5_tp);
92+
93+
insertSpaceIntoEdit (*edit, { 2_tp, 2_td });
94+
95+
// Point should move from 5s to 7s
96+
CHECK (curve.getPointTime (0) == 7_tp);
97+
}
98+
99+
TEST_CASE ("deleteRegionOfTracks: removes and shifts master volume automation")
100+
{
101+
auto& engine = *Engine::getEngines()[0];
102+
auto edit = Edit::createSingleTrackEdit (engine, Edit::EditRole::forRendering);
103+
auto um = &edit->getUndoManager();
104+
105+
auto masterVol = edit->getMasterVolumePlugin();
106+
REQUIRE (masterVol != nullptr);
107+
108+
auto volParam = masterVol->volParam.get();
109+
REQUIRE (volParam != nullptr);
110+
111+
auto& curve = volParam->getCurve();
112+
// Add points: point within deleted region will be removed, point after will shift
113+
curve.addPoint (2_tp, 0.5f, 0.0f, um);
114+
curve.addPoint (10_tp, 0.5f, 0.0f, um);
115+
116+
CHECK (curve.getNumPoints() == 2);
117+
118+
// Delete region from 3s to 6s
119+
deleteRegionOfTracks (*edit, { 3_tp, 6_tp }, false, CloseGap::yes, nullptr);
120+
121+
CHECK (curve.getNumPoints() == 2);
122+
CHECK (curve.getPointTime (0) == 2_tp);
123+
// Point at 10s should shift back by 3s to 7s
124+
CHECK (curve.getPointTime (1) == 7_tp);
125+
}
126+
127+
TEST_CASE ("deleteRegionOfTracks: removes and shifts global macro automation")
128+
{
129+
auto& engine = *Engine::getEngines()[0];
130+
auto edit = Edit::createSingleTrackEdit (engine, Edit::EditRole::forRendering);
131+
auto um = &edit->getUndoManager();
132+
133+
auto& globalMacros = edit->getGlobalMacros();
134+
auto macroParam = globalMacros.getMacroParameterListForWriting().createMacroParameter();
135+
REQUIRE (macroParam != nullptr);
136+
137+
auto& curve = macroParam->getCurve();
138+
// Use same values to avoid boundary preservation points being added
139+
curve.addPoint (2_tp, 0.5f, 0.0f, um);
140+
curve.addPoint (10_tp, 0.5f, 0.0f, um);
141+
142+
CHECK (curve.getNumPoints() == 2);
143+
144+
// Delete region from 3s to 6s
145+
deleteRegionOfTracks (*edit, { 3_tp, 6_tp }, false, CloseGap::yes, nullptr);
146+
147+
CHECK (curve.getNumPoints() == 2);
148+
CHECK (curve.getPointTime (0) == 2_tp);
149+
CHECK (curve.getPointTime (1) == 7_tp);
150+
}
151+
152+
TEST_CASE ("deleteRegionOfTracks: removes and shifts rack plugin automation")
153+
{
154+
auto& engine = *Engine::getEngines()[0];
155+
auto edit = Edit::createSingleTrackEdit (engine, Edit::EditRole::forRendering);
156+
auto um = &edit->getUndoManager();
157+
158+
auto rackType = edit->getRackList().addNewRack();
159+
auto volumePlugin = edit->getPluginCache().createNewPlugin (VolumeAndPanPlugin::xmlTypeName, {});
160+
rackType->addPlugin (volumePlugin, {}, true);
161+
162+
auto volAndPan = dynamic_cast<VolumeAndPanPlugin*> (volumePlugin.get());
163+
REQUIRE (volAndPan != nullptr);
164+
165+
auto volParam = volAndPan->volParam.get();
166+
auto& curve = volParam->getCurve();
167+
// Use same values to avoid boundary preservation points being added
168+
curve.addPoint (2_tp, 0.5f, 0.0f, um);
169+
curve.addPoint (10_tp, 0.5f, 0.0f, um);
170+
171+
CHECK (curve.getNumPoints() == 2);
172+
173+
deleteRegionOfTracks (*edit, { 3_tp, 6_tp }, false, CloseGap::yes, nullptr);
174+
175+
CHECK (curve.getNumPoints() == 2);
176+
CHECK (curve.getPointTime (0) == 2_tp);
177+
CHECK (curve.getPointTime (1) == 7_tp);
178+
}
179+
180+
TEST_CASE ("deleteRegionOfTracks: shifts pitch sequence")
181+
{
182+
auto& engine = *Engine::getEngines()[0];
183+
auto edit = Edit::createSingleTrackEdit (engine, Edit::EditRole::forRendering);
184+
185+
auto& pitchSeq = edit->pitchSequence;
186+
187+
// Add pitch changes at various positions
188+
pitchSeq.insertPitch (2_tp);
189+
pitchSeq.insertPitch (5_tp);
190+
pitchSeq.insertPitch (10_tp);
191+
192+
const int numPitchesBefore = pitchSeq.getNumPitches();
193+
CHECK (numPitchesBefore == 4); // Including initial pitch at 0
194+
195+
// Delete region from 3s to 6s
196+
deleteRegionOfTracks (*edit, { 3_tp, 6_tp }, false, CloseGap::yes, nullptr);
197+
198+
// Pitch at 5s should be removed, pitch at 10s should shift back
199+
CHECK (pitchSeq.getNumPitches() == 3);
200+
}
201+
202+
TEST_CASE ("deleteRegionOfTracks: CloseGap::no does not shift automation")
203+
{
204+
auto& engine = *Engine::getEngines()[0];
205+
auto edit = Edit::createSingleTrackEdit (engine, Edit::EditRole::forRendering);
206+
auto um = &edit->getUndoManager();
207+
208+
auto masterVol = edit->getMasterVolumePlugin();
209+
REQUIRE (masterVol != nullptr);
210+
211+
auto volParam = masterVol->volParam.get();
212+
auto& curve = volParam->getCurve();
213+
curve.addPoint (2_tp, 0.3f, 0.0f, um);
214+
curve.addPoint (10_tp, 0.7f, 0.0f, um);
215+
216+
CHECK (curve.getNumPoints() == 2);
217+
218+
// Delete region but don't close gap
219+
deleteRegionOfTracks (*edit, { 3_tp, 6_tp }, false, CloseGap::no, nullptr);
220+
221+
// Points should remain unchanged
222+
CHECK (curve.getNumPoints() == 2);
223+
CHECK (curve.getPointTime (0) == 2_tp);
224+
CHECK (curve.getPointTime (1) == 10_tp);
225+
}
52226
}
53227

54228
//==============================================================================

modules/tracktion_engine/model/edit/tracktion_EditUtilities.cpp

Lines changed: 71 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,50 @@
1010

1111
namespace tracktion::inline engine {
1212

13+
namespace
14+
{
15+
struct EditSpaceTargets
16+
{
17+
std::vector<ClipTrack*> clipTracks;
18+
std::vector<AutomatableEditItem*> automatableItems;
19+
20+
EditSpaceTargets (Edit& edit, const juce::Array<Track*>& onlyTheseTracks)
21+
{
22+
const bool allTracks = onlyTheseTracks.isEmpty();
23+
24+
if (allTracks)
25+
{
26+
for (auto ct : getTracksOfType<ClipTrack> (edit, true))
27+
clipTracks.push_back (ct);
28+
29+
automatableItems = getAllAutomatableEditItems (edit);
30+
}
31+
else
32+
{
33+
for (auto t : onlyTheseTracks)
34+
{
35+
if (auto ct = dynamic_cast<ClipTrack*> (t))
36+
clipTracks.push_back (ct);
37+
38+
for (auto sub : t->getAllSubTracks (true))
39+
if (auto ct = dynamic_cast<ClipTrack*> (sub))
40+
clipTracks.push_back (ct);
41+
42+
auto trackItems = t->getAllAutomatableEditItems();
43+
automatableItems.insert (automatableItems.end(), trackItems.begin(), trackItems.end());
44+
}
45+
}
46+
47+
// Deduplicate
48+
std::sort (clipTracks.begin(), clipTracks.end());
49+
clipTracks.erase (std::unique (clipTracks.begin(), clipTracks.end()), clipTracks.end());
50+
51+
std::sort (automatableItems.begin(), automatableItems.end());
52+
automatableItems.erase (std::unique (automatableItems.begin(), automatableItems.end()), automatableItems.end());
53+
}
54+
};
55+
}
56+
1357
Project::Ptr getProjectForEdit (const Edit& e)
1458
{
1559
return e.engine.getProjectManager().getProject (e);
@@ -48,49 +92,18 @@ void insertSpaceIntoEdit (Edit& edit, TimeRange timeRange, const juce::Array<Tra
4892
const auto time = timeRange.getStart();
4993
const auto length = timeRange.getLength();
5094

51-
std::vector<ClipTrack*> clipTracks;
52-
std::vector<AutomatableEditItem*> automatableItems;
53-
54-
if (allTracks)
55-
{
56-
for (auto ct : getTracksOfType<ClipTrack> (edit, true))
57-
clipTracks.push_back (ct);
58-
59-
automatableItems = getAllAutomatableEditItems (edit);
60-
}
61-
else
62-
{
63-
for (auto t : onlyTheseTracks)
64-
{
65-
if (auto ct = dynamic_cast<ClipTrack*> (t))
66-
clipTracks.push_back (ct);
67-
68-
for (auto sub : t->getAllSubTracks (true))
69-
if (auto ct = dynamic_cast<ClipTrack*> (sub))
70-
clipTracks.push_back (ct);
71-
72-
auto trackItems = t->getAllAutomatableEditItems();
73-
automatableItems.insert (automatableItems.end(), trackItems.begin(), trackItems.end());
74-
}
75-
}
76-
77-
// Deduplicate
78-
std::sort (clipTracks.begin(), clipTracks.end());
79-
clipTracks.erase (std::unique (clipTracks.begin(), clipTracks.end()), clipTracks.end());
80-
81-
std::sort (automatableItems.begin(), automatableItems.end());
82-
automatableItems.erase (std::unique (automatableItems.begin(), automatableItems.end()), automatableItems.end());
95+
EditSpaceTargets targets (edit, onlyTheseTracks);
8396

8497
if (allTracks && doTempoTrackFirst)
8598
{
8699
edit.pitchSequence.insertSpaceIntoSequence (time, length, false);
87100
edit.tempoSequence.insertSpaceIntoSequence (time, length, false);
88101
}
89102

90-
for (auto ct : clipTracks)
103+
for (auto ct : targets.clipTracks)
91104
ct->insertSpace (time, length);
92105

93-
for (auto item : automatableItems)
106+
for (auto item : targets.automatableItems)
94107
for (auto param : item->getAutomatableParameters())
95108
param->getCurve().insertSpace (*param, time, length);
96109

@@ -460,70 +473,43 @@ void deleteRegionOfTracks (Edit& edit, TimeRange rangeToDelete, bool onlySelecte
460473
selectionManager->deselectAll();
461474
}
462475

463-
Plugin::Array pluginsInRacks;
464-
465-
auto addPluginsInRack = [&] (RackInstance& r)
466-
{
467-
if (r.type != nullptr)
468-
for (auto p : r.type->getPlugins())
469-
pluginsInRacks.addIfNotAlreadyThere (p);
470-
};
471-
472-
auto removeAutomationRangeOfPlugin = [&] (Plugin& p)
473-
{
474-
for (auto param : p.getAutomatableParameters())
475-
param->getCurve().removeRegionAndCloseGap (*param, rangeToDelete, &edit.getUndoManager());
476-
};
477-
478-
auto removeAutomationRangeFindingRackPlugins = [&] (Track& t)
479-
{
480-
for (auto p : t.pluginList)
481-
{
482-
removeAutomationRangeOfPlugin (*p);
483-
484-
// find all the plugins in racks
485-
if (auto rf = dynamic_cast<RackInstance*> (p))
486-
addPluginsInRack (*rf);
487-
}
488-
};
476+
EditSpaceTargets targets (edit, onlySelected ? tracks : juce::Array<Track*>{});
489477

490-
for (int i = tracks.size(); --i >= 0;)
478+
for (auto ct : targets.clipTracks)
491479
{
492-
if (auto t = dynamic_cast<ClipTrack*> (tracks[i]))
493-
{
494-
t->deleteRegion (rangeToDelete, selectionManager);
480+
ct->deleteRegion (rangeToDelete, selectionManager);
495481

496-
// Remove any tiny clips that might be left over
497-
juce::Array<Clip*> clipsToRemove;
482+
// Remove any tiny clips that might be left over
483+
juce::Array<Clip*> clipsToRemove;
498484

499-
for (auto& c : t->getClips())
500-
if (c->getPosition().getLength() < TimeDuration::fromSeconds (0.0001))
501-
clipsToRemove.add (c);
485+
for (auto& c : ct->getClips())
486+
if (c->getPosition().getLength() < TimeDuration::fromSeconds (0.0001))
487+
clipsToRemove.add (c);
502488

503-
for (auto c : clipsToRemove)
504-
c->removeFromParent();
489+
for (auto c : clipsToRemove)
490+
c->removeFromParent();
505491

506-
if (closeGap == CloseGap::yes)
507-
{
508-
for (auto& c : t->getClips())
509-
if (c->getPosition().getStart() > rangeToDelete.getCentre())
510-
c->setStart (c->getPosition().getStart() - rangeToDelete.getLength(), false, true);
511-
512-
removeAutomationRangeFindingRackPlugins (*t);
513-
}
514-
}
515-
else if (auto ft = dynamic_cast<FolderTrack*> (tracks[i]))
492+
if (closeGap == CloseGap::yes)
516493
{
517-
removeAutomationRangeFindingRackPlugins (*ft);
494+
for (auto& c : ct->getClips())
495+
if (c->getPosition().getStart() > rangeToDelete.getCentre())
496+
c->setStart (c->getPosition().getStart() - rangeToDelete.getLength(), false, true);
518497
}
519498
}
520499

521-
for (auto p : pluginsInRacks)
522-
removeAutomationRangeOfPlugin (*p);
500+
if (closeGap == CloseGap::yes)
501+
{
502+
for (auto item : targets.automatableItems)
503+
for (auto param : item->getAutomatableParameters())
504+
param->getCurve().removeRegionAndCloseGap (*param, rangeToDelete, &edit.getUndoManager());
505+
}
523506

524-
// N.B. Delete tempo last
507+
// N.B. Delete tempo/pitch last
525508
if (! onlySelected || tracks.contains (edit.getTempoTrack()))
509+
{
510+
edit.pitchSequence.deleteRegion (rangeToDelete);
526511
edit.tempoSequence.deleteRegion (rangeToDelete);
512+
}
527513
}
528514

529515
void moveSelectedClips (const SelectableList& selectedObjectsIn, Edit& edit, MoveClipAction mode, bool automationLocked)

0 commit comments

Comments
 (0)