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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_EditUtilities.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
14Project::Ptr getProjectForEdit (const Edit& e)
15{
16 return e.engine.getProjectManager().getProject (e);
17}
18
19ProjectItem::Ptr getProjectItemForEdit (const Edit& e)
20{
22}
23
25{
26 if (auto item = getProjectItemForEdit (edit))
27 return item->getSourceFile();
28
29 return {};
30}
31
33{
34 for (auto e : Exportable::addAllExportables (edit))
35 for (auto& i : e->getReferencedItems())
36 if (i.itemID == itemID)
37 return true;
38
39 return false;
40}
41
42//==============================================================================
43//==============================================================================
44void insertSpaceIntoEdit (Edit& edit, TimeRange timeRange)
45{
46 const bool doTempoTrackFirst = ! edit.getTimecodeFormat().isBarsBeats();
47 const auto time = timeRange.getStart();
48 const auto length = timeRange.getLength();
49
50 if (doTempoTrackFirst)
51 edit.getTempoTrack()->insertSpaceIntoTrack (time, length);
52
53 for (auto ct : getTracksOfType<ClipTrack> (edit, true))
54 {
55 ct->splitAt (time);
56 ct->insertSpaceIntoTrack (time, length);
57 }
58
59 if (! doTempoTrackFirst)
60 edit.getTempoTrack()->insertSpaceIntoTrack (time, length);
61}
62
63void insertSpaceIntoEditFromBeatRange (Edit& edit, BeatRange beatRange)
64{
65 auto& ts = edit.tempoSequence;
66 const auto timeToInsertAt = ts.toTime (beatRange.getStart());
67 auto& tempoAtInsertionPoint = ts.getTempoAt (timeToInsertAt - TimeDuration::fromSeconds (0.0001));
68
69 const auto lengthInTimeToInsert = TimeDuration::fromSeconds (beatRange.getLength().inBeats() * tempoAtInsertionPoint.getApproxBeatLength().inSeconds());
70 insertSpaceIntoEdit (edit, TimeRange (timeToInsertAt, lengthInTimeToInsert));
71}
72
73//==============================================================================
75{
77 edit.visitAllTracks ([&] (Track& t) { tracks.add (&t); return true; }, true);
78 return tracks;
79}
80
82{
84 edit.visitAllTracks ([&] (Track& t) { tracks.add (&t); return true; }, false);
85 return tracks;
86}
87
89{
90 return getTracksOfType<AudioTrack> (edit, true);
91}
92
94{
95 return getTracksOfType<ClipTrack> (edit, true);
96}
97
98int getTotalNumTracks (const Edit& edit)
99{
100 int count = 0;
101 edit.visitAllTracksRecursive ([&] (Track&) { ++count; return true; });
102 return count;
103}
104
106{
107 return findTrackForPredicate (edit, [id] (Track& t) { return t.itemID == id; });
108}
109
111{
112 return dynamic_cast<AudioTrack*> (findTrackForID (edit, id));
113}
114
116{
117 juce::Array<Track*> tracks;
118
119 edit.visitAllTracksRecursive ([&] (Track& t)
120 {
121 if (ids.contains (t.itemID))
122 tracks.add (&t);
123
124 return true;
125 });
126
127 return tracks;
128}
129
131{
132 return findTrackForPredicate (edit, [&] (Track& t) { return t.state == v; });
133}
134
136{
137 return getAudioTracks (edit).getFirst();
138}
139
140bool containsTrack (const Edit& edit, const Track& track)
141{
142 return findTrackForPredicate (edit, [&track] (Track& t) { return &track == &t; }) != nullptr;
143}
144
146{
147 if (auto t = dynamic_cast<AudioTrack*> (&track))
148 return &t->getOutput();
149
150 if (auto t = dynamic_cast<FolderTrack*> (&track))
151 return t->getOutput();
152
153 return {};
154}
155
157{
158 juce::BigInteger bitset;
159
160 if (auto first = tracks[0])
161 {
162 auto allTracks = getAllTracks (first->edit);
163
164 for (auto t : allTracks)
165 if (int index = allTracks.indexOf (t); index >= 0)
166 bitset.setBit (index);
167 }
168
169 return bitset;
170}
171
173{
174 juce::Array<Track*> tracks;
175
176 auto allTracks = getAllTracks (edit);
177
178 for (auto bit = tracksToAdd.findNextSetBit (0); bit != -1; bit = tracksToAdd.findNextSetBit (bit + 1))
179 tracks.add (allTracks[bit]);
180
181 return tracks;
182}
183
185{
186 auto tracks = items.getItemsOfType<Track>();
187
188 if (tracks.isEmpty())
189 for (auto& i : items.getItemsOfType<TrackItem>())
190 if (auto t = i->getTrack())
191 tracks.addIfNotAlreadyThere (t);
192
193 if (tracks.isEmpty())
194 for (auto& cs : items.getItemsOfType<ClipSlot>())
195 tracks.addIfNotAlreadyThere (&cs->track);
196
197 std::sort (tracks.begin(), tracks.end(),
198 [] (Track* t1, Track* t2)
199 {
200 return t1->getIndexInEditTrackList() < t2->getIndexInEditTrackList();
201 });
202
203 return tracks;
204}
205
207{
208 auto clipTracks = items.getItemsOfType<ClipTrack>();
209
210 if (clipTracks.isEmpty())
211 for (auto& clip : items.getItemsOfType<Clip>())
212 clipTracks.add (clip->getClipTrack());
213
214 ClipTrack* firstTrack = nullptr;
215 auto firstIndex = std::numeric_limits<int>::max();
216
217 for (auto& t : clipTracks)
218 {
219 auto index = t->getIndexInEditTrackList();
220
221 if (index < firstIndex)
222 {
223 firstIndex = index;
224 firstTrack = t;
225 }
226 }
227
228 return firstTrack;
229}
230
231Clip::Ptr duplicateClip (const Clip& c)
232{
233 auto n = c.state.createCopy();
234 EditItemID::remapIDs (n, nullptr, c.edit);
235 auto newClipID = EditItemID::fromID (n);
236 jassert (newClipID != EditItemID::fromID (c.state));
237 jassert (c.edit.clipCache.findItem (newClipID) == nullptr);
238
239 if (auto t = c.getClipTrack())
240 {
241 jassert (! t->state.getChildWithProperty (IDs::id, newClipID).isValid());
242 t->state.appendChild (n, c.getUndoManager());
243
244 if (auto newClip = t->findClipForID (newClipID))
245 return newClip;
246 }
247 else
248 {
250 }
251
252 return {};
253}
254
256{
257 SelectableList newClips;
258
259 for (auto c : getClipSelectionWithCollectionClipContents (clips).getItemsOfType<Clip>())
260 if (c->getEditTimeRange().contains (time))
261 if (auto ct = c->getClipTrack())
262 if (auto newClip = ct->splitClip (*c, time))
263 newClips.add (newClip);
264
265 return newClips;
266}
267
268void deleteRegionOfClip (Clip& c, TimeRange timeRangeToDelete)
269{
271
272 if (auto track = c.getClipTrack())
273 {
274 track->setFrozen (false, Track::groupFreeze);
275 track->setFrozen (false, Track::individualFreeze);
276
277 const auto clipTimeRange = c.getEditTimeRange();
278
279 if (timeRangeToDelete.contains (clipTimeRange))
280 {
281 c.removeFromParent();
282 }
283 else if (clipTimeRange.getStart() < timeRangeToDelete.getStart() && clipTimeRange.getEnd() > timeRangeToDelete.getEnd())
284 {
285 if (auto newClip = track->splitClip (c, timeRangeToDelete.getStart()))
286 {
287 newClip->setStart (timeRangeToDelete.getEnd(), true, false);
288 c.setEnd (timeRangeToDelete.getStart(), true);
289 }
290 }
291 else
292 {
293 c.trimAwayOverlap (timeRangeToDelete);
294 }
295 }
296}
297
298void deleteRegionOfSelectedClips (SelectionManager& selectionManager, TimeRange rangeToDelete,
299 CloseGap closeGap, bool moveAllSubsequentClipsOnTrack)
300{
301 Clip::Array selectedClips;
302
303 for (auto c : selectionManager.getItemsOfType<Clip>())
304 selectedClips.add (c);
305
306 for (auto cc : selectionManager.getItemsOfType<CollectionClip>())
307 for (int j = 0; j < cc->getNumClips(); ++j)
308 selectedClips.add (cc->getClip(j));
309
310 if (selectedClips.isEmpty())
311 return;
312
313 auto& edit = selectedClips[0]->edit;
314
315 for (auto c : edit.engine.getUIBehaviour().getAssociatedClipsToEdit (selectedClips).getItemsOfType<Clip>())
316 selectedClips.addIfNotAlreadyThere (c);
317
319
320 #if JUCE_DEBUG
321 // A clip should always have a track. Crash reports are
322 // showing this is sometimes not the case
323 for (auto c : selectedClips)
324 jassert (c->getClipTrack() != nullptr);
325 #endif
326
327 for (auto c : selectedClips)
328 if (c->getPosition().time.overlaps (rangeToDelete))
329 if (auto t = c->getClipTrack())
330 tracks.addIfNotAlreadyThere (t);
331
332 if (tracks.isEmpty())
333 {
334 selectionManager.engine.getUIBehaviour()
335 .showWarningMessage (TRANS("None of the selected clips lay between the in/out marks"));
336
337 return;
338 }
339
340 for (auto c : selectedClips)
341 if (c->getPosition().time.overlaps (rangeToDelete))
342 if (auto t = c->getClipTrack())
343 t->deleteRegionOfClip (c, rangeToDelete, &selectionManager);
344
345 if (closeGap == CloseGap::yes)
346 {
347 const auto centreTime = rangeToDelete.getCentre();
348
349 if (moveAllSubsequentClipsOnTrack)
350 {
351 for (auto& t : tracks)
352 for (auto c : t->getClips())
353 if (c->getPosition().getStart() > centreTime)
354 c->setStart (c->getPosition().getStart() - rangeToDelete.getLength(), false, true);
355 }
356 else
357 {
358 for (auto c : selectionManager.getItemsOfType<Clip>())
359 if (c->getPosition().getStart() > centreTime)
360 c->setStart (c->getPosition().getStart() - rangeToDelete.getLength(), false, true);
361 }
362 }
363}
364
365void deleteRegionOfTracks (Edit& edit, TimeRange rangeToDelete, bool onlySelected, CloseGap closeGap, SelectionManager* selectionManager)
366{
367 juce::Array<Track*> tracks;
368
369 if (onlySelected)
370 {
371 jassert (selectionManager != nullptr);
372
373 if (selectionManager != nullptr)
374 {
375 for (auto track : selectionManager->getItemsOfType<Track>())
376 {
377 tracks.addIfNotAlreadyThere (track);
378 for (auto t : track->getAllSubTracks (true))
379 tracks.addIfNotAlreadyThere (t);
380 }
381 }
382 }
383 else
384 {
385 tracks = getAllTracks (edit);
386 }
387
388 if (tracks.size() == 0 || rangeToDelete.getLength() <= TimeDuration::fromSeconds (0.0001))
389 return;
390
392
393 if (selectionManager != nullptr)
394 {
395 selectionState = std::make_unique<SelectionManager::ScopedSelectionState> (*selectionManager);
396 selectionManager->deselectAll();
397 }
398
399 Plugin::Array pluginsInRacks;
400
401 auto addPluginsInRack = [&] (RackInstance& r)
402 {
403 if (r.type != nullptr)
404 for (auto p : r.type->getPlugins())
405 pluginsInRacks.addIfNotAlreadyThere (p);
406 };
407
408 auto removeAutomationRangeOfPlugin = [&] (Plugin& p)
409 {
410 for (auto param : p.getAutomatableParameters())
411 param->getCurve().removeRegionAndCloseGap (rangeToDelete);
412 };
413
414 auto removeAutomationRangeFindingRackPlugins = [&] (Track& t)
415 {
416 for (auto p : t.pluginList)
417 {
418 removeAutomationRangeOfPlugin (*p);
419
420 // find all the plugins in racks
421 if (auto rf = dynamic_cast<RackInstance*> (p))
422 addPluginsInRack (*rf);
423 }
424 };
425
426 for (int i = tracks.size(); --i >= 0;)
427 {
428 if (auto t = dynamic_cast<ClipTrack*> (tracks[i]))
429 {
430 t->deleteRegion (rangeToDelete, selectionManager);
431
432 // Remove any tiny clips that might be left over
433 juce::Array<Clip*> clipsToRemove;
434
435 for (auto& c : t->getClips())
436 if (c->getPosition().getLength() < TimeDuration::fromSeconds (0.0001))
437 clipsToRemove.add (c);
438
439 for (auto c : clipsToRemove)
440 c->removeFromParent();
441
442 if (closeGap == CloseGap::yes)
443 {
444 for (auto& c : t->getClips())
445 if (c->getPosition().getStart() > rangeToDelete.getCentre())
446 c->setStart (c->getPosition().getStart() - rangeToDelete.getLength(), false, true);
447
448 removeAutomationRangeFindingRackPlugins (*t);
449 }
450 }
451 else if (auto ft = dynamic_cast<FolderTrack*> (tracks[i]))
452 {
453 removeAutomationRangeFindingRackPlugins (*ft);
454 }
455 }
456
457 for (auto p : pluginsInRacks)
458 removeAutomationRangeOfPlugin (*p);
459
460 // N.B. Delete tempo last
461 if (! onlySelected || tracks.contains (edit.getTempoTrack()))
462 edit.tempoSequence.deleteRegion (rangeToDelete);
463}
464
465void moveSelectedClips (const SelectableList& selectedObjectsIn, Edit& edit, MoveClipAction mode, bool automationLocked)
466{
467 auto selectedObjects = edit.engine.getUIBehaviour().getAssociatedClipsToEdit (selectedObjectsIn);
468 auto expandedList = getClipSelectionWithCollectionClipContents (selectedObjects);
469
470 auto firstClip = selectedObjects.getFirstOfType<Clip>();
471 auto firstCClip = selectedObjects.getFirstOfType<CollectionClip>();
472
473 if (firstClip == nullptr && firstCClip == nullptr)
474 return;
475
476 auto& ed = firstClip ? firstClip->edit
477 : firstCClip->edit;
478
479 auto moveClipsAndAutomation = [automationLocked] (const juce::Array<Clip*>& clips, TimeDuration delta)
480 {
481 if (delta == TimeDuration())
482 return;
483
485
486 for (auto c : clips)
487 {
488 if (automationLocked)
489 {
490 if (auto cc = c->getGroupClip())
491 {
493 ts.position = cc->getPosition().time;
494 ts.src = cc->getTrack();
495 ts.dst = cc->getTrack();
496 sections.add (ts);
497 }
498 else
499 {
500 sections.add (TrackAutomationSection (*c));
501 }
502 }
503
504 c->setStart (c->getPosition().getStart() + delta, false, true);
505 }
506
507 if (sections.size() > 0 && delta != TimeDuration())
508 moveAutomation (sections, delta, false);
509 };
510
511 auto moveCollectionClipAutomation = [automationLocked] (const juce::Array<CollectionClip*>& clips, TimeDuration delta)
512 {
513 if (delta == TimeDuration())
514 return;
515
517
518 if (automationLocked)
519 for (auto c : clips)
520 sections.add (TrackAutomationSection (*c));
521
522 if (sections.size() > 0 && delta != TimeDuration())
523 moveAutomation (sections, delta, false);
524 };
525
526 if (mode == MoveClipAction::moveStartToCursor || mode == MoveClipAction::moveEndToCursor)
527 {
528 auto selectedRange = getTimeRangeForSelectedItems (selectedObjects);
529
530 auto delta = edit.getTransport().getPosition() - (mode == MoveClipAction::moveEndToCursor ? selectedRange.getEnd()
531 : selectedRange.getStart());
532
533 moveClipsAndAutomation (expandedList.getItemsOfType<Clip>(), delta);
534 moveCollectionClipAutomation (selectedObjects.getItemsOfType<CollectionClip>(), delta);
535 }
536 else
537 {
538 for (auto track : getClipTracks (ed))
539 {
540 juce::Array<Clip*> clipsInTrack;
541
542 for (auto s : expandedList)
543 if (auto c = dynamic_cast<Clip*> (s))
544 if (c->getTrack() == track)
545 clipsInTrack.add (c);
546
547 if (! clipsInTrack.isEmpty())
548 {
549 TrackItem::sortByTime (clipsInTrack);
550
551 if (mode == MoveClipAction::moveToEndOfLast)
552 {
553 if (auto previousClip = track->getClips()[track->getClips().indexOf (clipsInTrack.getFirst()) - 1])
554 moveClipsAndAutomation (clipsInTrack, previousClip->getPosition().getEnd() - clipsInTrack.getFirst()->getPosition().getStart());
555 }
556 else if (mode == MoveClipAction::moveToStartOfNext)
557 {
558 if (auto nextClip = track->getClips()[track->getClips().indexOf (clipsInTrack.getLast()) + 1])
559 moveClipsAndAutomation (clipsInTrack, nextClip->getPosition().getStart() - clipsInTrack.getLast()->getPosition().getEnd());
560 }
561 }
562 }
563 }
564}
565
567{
568 SelectableList result;
569
570 if (in.size() > 1000)
571 {
572 // For large sizes it's much quicker to add all the items and then remove duplicates
573 std::vector<Selectable*> selectables;
574 selectables.reserve (static_cast<size_t> (in.size()));
575
576 for (auto ti : in.getItemsOfType<TrackItem>())
577 {
578 if (auto clip = dynamic_cast<Clip*> (ti))
579 selectables.push_back (clip);
580
581 if (auto cc = dynamic_cast<CollectionClip*> (ti))
582 for (auto c : cc->getClips())
583 selectables.push_back (c);
584 }
585
586 return stable_remove_duplicates (selectables);
587 }
588 else
589 {
590 for (auto ti : in.getItemsOfType<TrackItem>())
591 {
592 if (auto clip = dynamic_cast<Clip*> (ti))
593 result.addIfNotAlreadyThere (clip);
594
595 if (auto cc = dynamic_cast<CollectionClip*> (ti))
596 for (auto c : cc->getClips())
597 result.addIfNotAlreadyThere (c);
598 }
599 }
600
601 return result;
602}
603
605{
607
608 for (auto audioTrack : getAudioTracks (edit))
609 for (auto clip : audioTrack->getClips())
610 if (auto waveClip = dynamic_cast<AudioClipBase*> (clip))
611 if (auto effects = waveClip->getClipEffects())
612 for (auto effect : *effects)
613 res.add (effect);
614
615 return res;
616}
617
618
619//==============================================================================
621{
622 if (auto clipTrack = dynamic_cast<ClipTrack*> (findTrackForID (edit, id)))
623 return clipTrack;
624
625 if (auto clipSlot = findClipSlotForID (edit, id))
626 return clipSlot;
627
628 if (auto containerClip = dynamic_cast<ContainerClip*> (findClipForID (edit, id)))
629 return containerClip;
630
631 return nullptr;
632}
633
634//==============================================================================
636{
637 ClipSlot* result = nullptr;
638
639 edit.visitAllTracksRecursive ([&] (Track& t)
640 {
641 if (auto at = dynamic_cast<AudioTrack*> (&t))
642 {
643 for (auto cs : at->getClipSlotList().getClipSlots())
644 {
645 if (cs->itemID == id)
646 {
647 result = cs;
648 return false;
649 }
650 }
651 }
652
653 return true;
654 });
655
656 return result;
657}
658
660{
661 if (auto at = dynamic_cast<AudioTrack*> (&slot.track))
662 return at->getClipSlotList().getClipSlots().indexOf (&slot);
663
664 jassertfalse; // This should never happen
665 return -1;
666}
667
668
669//==============================================================================
670Clip* findClipForID (const Edit& edit, EditItemID clipID)
671{
672 Clip* result = nullptr;
673
674 edit.visitAllTracksRecursive ([&] (Track& t)
675 {
676 if (auto c = t.findClipForID (clipID))
677 {
678 result = c;
679 return false;
680 }
681
682 for (auto cc : getTrackItemsOfType<ContainerClip> (t))
683 {
684 if (auto c = findClipForID (*cc, clipID))
685 {
686 result = c;
687 return false;
688 }
689 }
690
691 return true;
692 });
693
694 return result;
695}
696
698{
699 Clip* result = nullptr;
700
701 visitAllTrackItems (edit, [&] (TrackItem& t)
702 {
703 if (auto c = dynamic_cast<Clip*> (&t))
704 {
705 if (c->state == v)
706 {
707 result = c;
708 return false;
709 }
710 }
711
712 return true;
713 });
714
715 return result;
716}
717
718bool containsClip (const Edit& edit, Clip* clip)
719{
720 return findTrackForPredicate (edit,
721 [clip] (Track& t)
722 {
723 if (t.indexOfTrackItem (clip) >= 0)
724 return true;
725
726 if (auto at = dynamic_cast<AudioTrack*> (&t))
727 for (auto slot : at->getClipSlotList().getClipSlots())
728 if (clip == slot->getClip())
729 return true;
730
731 for (auto cc : getTrackItemsOfType<ContainerClip> (t))
732 if (cc->getClips().contains (clip))
733 return true;
734
735 return false;
736 }) != nullptr;
737}
738
739void visitAllTrackItems (const Edit& edit, std::function<bool (TrackItem&)> f)
740{
741 edit.visitAllTracksRecursive ([&] (Track& t)
742 {
743 auto numItems = t.getNumTrackItems();
744
745 for (int i = 0; i < numItems; ++i)
746 {
747 if (auto ti = t.getTrackItem (i))
748 if (! f (*ti))
749 return false;
750 }
751
752 if (auto at = dynamic_cast<AudioTrack*> (&t))
753 for (auto slot : at->getClipSlotList().getClipSlots())
754 if (auto c = slot->getClip())
755 if (! f (*c))
756 return false;
757
758 for (auto cc : getTrackItemsOfType<ContainerClip> (t))
759 for (auto c : cc->getClips())
760 if (! f (*c))
761 return false;
762
763 return true;
764 });
765}
766
768{
769 auto items = selected.getItemsOfType<TrackItem>();
770
771 if (items.isEmpty())
772 return {};
773
774 auto range = items.getFirst()->getEditTimeRange();
775
776 for (auto& i : items)
777 range = range.getUnionWith (i->getEditTimeRange());
778
779 return range;
780}
781
782//==============================================================================
784{
785 MidiNote* result = nullptr;
786
787 visitAllTrackItems (edit, [&] (TrackItem& t)
788 {
789 if (auto c = dynamic_cast<MidiClip*> (&t))
790 {
791 if (auto n = c->getSequence().getNoteFor (v))
792 {
793 result = n;
794 return false;
795 }
796 }
797
798 return true;
799 });
800
801 return result;
802}
803
805{
806 for (auto c : clips)
807 if (c->getClipTrack() == nullptr || c->getClipTrack()->isFrozen (Track::anyFreeze))
808 return juce::Result::fail (TRANS("Unable to merge clips on frozen tracks"));
809
810 TrackItem::sortByTime (clips);
811
812 if (auto first = clips.getFirst())
813 {
814 if (auto track = first->getClipTrack())
815 {
816 if (auto newClip = track->insertMIDIClip (first->getName(), first->getPosition().time, sm))
817 {
818 newClip->setQuantisation (first->getQuantisation());
819 newClip->setGrooveTemplate (first->getGrooveTemplate());
820 newClip->setMidiChannel (first->getMidiChannel());
821
822 auto startBeat = BeatPosition::fromBeats (1.0e10);
823 auto startTime = TimePosition::fromSeconds (1.0e10);
824 auto endTime = TimePosition();
825
826 for (auto c : clips)
827 {
828 startBeat = juce::jmin (startBeat, c->getStartBeat());
829 startTime = juce::jmin (startTime, c->getPosition().getStart());
830 endTime = juce::jmax (endTime, c->getPosition().getEnd());
831 }
832
833 MidiList destinationList;
834 destinationList.setMidiChannel (newClip->getMidiChannel());
835
836 for (auto c : clips)
837 {
838 MidiList sourceList;
839 sourceList.copyFrom (c->getSequenceLooped(), nullptr);
840
841 const auto offset = BeatDuration::fromBeats (c->getPosition().getOffset().inSeconds() * c->edit.tempoSequence.getBeatsPerSecondAt (c->getPosition().getStart(), true));
842
843 sourceList.trimOutside (toPosition (offset), toPosition (offset + c->getLengthInBeats()), nullptr);
844 sourceList.moveAllBeatPositions (c->getStartBeat() - startBeat - offset, nullptr);
845
846 destinationList.addFrom (sourceList, nullptr);
847 }
848
849 newClip->setPosition ({ { startTime, endTime }, TimeDuration() });
850 newClip->getSequence().addFrom (destinationList, &track->edit.getUndoManager());
851
852 for (int i = clips.size(); --i >= 0;)
853 clips.getUnchecked (i)->removeFromParent();
854 }
855
856 return juce::Result::ok();
857 }
858 }
859
860 return juce::Result::fail (TRANS("No clips to merge"));
861}
862
863juce::OwnedArray<MidiList> readFileToMidiList (juce::File midiFile, bool importAsNoteExpression)
864{
867 juce::Array<BeatPosition> tempoChangeBeatNumbers;
869 juce::Array<int> numerators, denominators;
870 BeatDuration len;
871
872 if (MidiList::readSeparateTracksFromFile (midiFile, lists,
873 tempoChangeBeatNumbers, bpms,
874 numerators, denominators, len,
875 importAsNoteExpression))
876 {
877 return lists;
878 }
879
880 return {};
881}
882
883MidiClip::Ptr createClipFromFile (juce::File midiFile, ClipOwner& owner, bool importAsNoteExpression)
884{
885 auto lists = readFileToMidiList (std::move (midiFile), importAsNoteExpression);
886
887 if (auto l = lists.getFirst())
888 {
889 const auto& ts = owner.getClipOwnerEdit().tempoSequence;
890 const auto endTime = ts.toTime (l->getLastBeatNumber());
891 const auto barsBeats = ts.toBarsAndBeats (endTime);
892 const auto totalBars = barsBeats.getTotalBars();
893 const auto nextBar = static_cast<int> (std::ceil (totalBars));
894 const auto clipEndTime = ts.toTime ({ nextBar });
895
896 if (auto c = insertMIDIClip (owner, { 0_tp, clipEndTime }))
897 {
898 c->setName (l->getImportedFileName ());
899 c->getSequence ().copyFrom (*l, &owner.getClipOwnerEdit().getUndoManager());
900
901 return c;
902 }
903 }
904
905 return {};
906}
907
908
909//==============================================================================
910Plugin::Array getAllPlugins (const Edit& edit, bool includeMasterVolume)
911{
913 Plugin::Array list;
914 edit.visitAllTracksRecursive ([&] (Track& t)
915 {
916 list.addArray (t.getAllPlugins());
917
918 if (auto at = dynamic_cast<AudioTrack*> (&t))
919 {
920 for (auto clip : at->getClips())
921 {
922 if (auto abc = dynamic_cast<AudioClipBase*> (clip))
923 {
924 if (auto pluginList = abc->getPluginList())
925 list.addArray (pluginList->getPlugins());
926
927 if (auto clipEffects = abc->getClipEffects())
928 for (auto effect : *clipEffects)
929 if (auto pluginEffect = dynamic_cast<PluginEffect*> (effect))
930 if (pluginEffect->plugin != nullptr)
931 list.add (pluginEffect->plugin);
932 }
933 }
934 }
935
936 return true;
937 });
938
939 // Master plugins may have been found by the MasterTrack plugnList
940 for (auto p : edit.getMasterPluginList().getPlugins())
941 list.addIfNotAlreadyThere (p);
942
943 for (auto r : edit.getRackList().getTypes())
944 for (auto p : r->getPlugins())
945 list.add (p);
946
947 if (includeMasterVolume)
948 list.add (edit.getMasterVolumePlugin().get());
949
950 return list;
951}
952
953Plugin::Ptr findPluginForState (const Edit& edit, const juce::ValueTree& v)
954{
955 for (auto p : getAllPlugins (edit, true))
956 if (p->state == v)
957 return p;
958
959 return {};
960}
961
962Plugin::Ptr findPluginForID (const Edit& edit, EditItemID id)
963{
964 for (auto p : getAllPlugins (edit, true))
965 if (p->itemID == id)
966 return p;
967
968 return {};
969}
970
972{
973 return findTrackForPredicate (edit, [p] (Track& t) { return t.containsPlugin (p); });
974}
975
976bool areAnyPluginsMissing (const Edit& edit)
977{
978 for (auto p : getAllPlugins (edit, false))
979 if (p->isMissing())
980 return true;
981
982 return false;
983}
984
986{
988
989 for (auto p : getAllPlugins (rt.edit, false))
990 if (auto ri = dynamic_cast<RackInstance*> (p))
991 if (ri->type == &rt)
992 instances.add (ri);
993
994 return instances;
995}
996
997void muteOrUnmuteAllPlugins (const Edit& edit)
998{
999 auto allPlugins = getAllPlugins (edit, true);
1000
1001 int numEnabled = 0;
1002
1003 for (auto p : allPlugins)
1004 if (p->isEnabled())
1005 ++numEnabled;
1006
1007 for (auto p : allPlugins)
1008 p->setEnabled (numEnabled == 0);
1009}
1010
1012{
1013 for (auto p : getAllPlugins (edit, false))
1014 if (p->isSynth())
1015 if (auto at = dynamic_cast<AudioTrack*> (p->getOwnerTrack()))
1016 for (auto& m : messages)
1017 at->injectLiveMidiMessage (m, MidiMessageArray::notMPE);
1018}
1019
1020void midiPanic (Edit& edit, bool resetPlugins)
1021{
1022 juce::Timer::callAfterDelay (100, [weakRef = makeSafeRef (edit), resetPlugins]
1023 {
1024 if (weakRef)
1025 {
1027 messages.reserve (16);
1028
1029 for (auto chan = 1; chan <= 16; ++chan)
1030 messages.push_back (juce::MidiMessage::allNotesOff (chan));
1031
1032 injectMIDIToAllPlugins (*weakRef, messages);
1033
1034 if (resetPlugins)
1035 for (auto p : getAllPlugins (*weakRef, false))
1036 p->reset();
1037 }
1038 });
1039
1040 Edit::ScopedRenderStatus srd (edit, true);
1041}
1042
1043
1044//==============================================================================
1046{
1049
1050 if (auto mpl = edit.getGlobalMacros().getMacroParameterList())
1051 destArray.add (mpl);
1052
1053 edit.visitAllTracksRecursive ([&] (Track& t)
1054 {
1055 if (auto m = dynamic_cast<MacroParameterElement*> (&t))
1056 if (auto mpl = m->getMacroParameterList())
1057 destArray.add (mpl);
1058
1059 destArray.addArray (t.getAllAutomatableEditItems());
1060 return true;
1061 });
1062
1063 for (auto p : edit.getMasterPluginList())
1064 {
1065 if (auto mpl = p->getMacroParameterList())
1066 destArray.add (mpl);
1067
1068 destArray.add (p);
1069 }
1070
1071 for (auto r : edit.getRackList().getTypes())
1072 {
1073 for (auto p : r->getPlugins())
1074 {
1075 destArray.add (p);
1076
1077 if (auto mpl = p->getMacroParameterList())
1078 destArray.add (mpl);
1079 }
1080
1081 if (auto mpl = r->getMacroParameterList())
1082 destArray.add (mpl);
1083
1084 destArray.addArray (r->getModifierList().getModifiers());
1085 }
1086
1087 destArray.add (edit.getMasterVolumePlugin().get());
1088
1089 return destArray;
1090}
1091
1092void deleteAutomation (const SelectableList& selectedClips)
1093{
1094 if (selectedClips.containsType<Clip>())
1095 {
1096 for (auto& section : TrackSection::findSections (selectedClips.getItemsOfType<TrackItem>()))
1097 {
1098 for (auto plugin : section.track->pluginList)
1099 {
1100 for (auto& param : plugin->getAutomatableParameters())
1101 {
1102 auto& curve = param->getCurve();
1103
1104 if (curve.countPointsInRegion (section.range.expanded (TimeDuration::fromSeconds (0.0001))))
1105 {
1106 auto start = curve.getValueAt (section.range.getStart());
1107 auto end = curve.getValueAt (section.range.getEnd());
1108
1109 curve.removePointsInRegion (section.range.expanded (TimeDuration::fromSeconds (0.0001)));
1110 curve.addPoint (section.range.getStart(), start, 1.0f);
1111 curve.addPoint (section.range.getEnd(), end, 0.0f);
1112 }
1113 }
1114 }
1115 }
1116 }
1117}
1118
1119//==============================================================================
1121{
1123
1124 sources.addArray (getAllModifiers (edit));
1125
1126 for (auto mpe : getAllMacroParameterElements (edit))
1127 sources.addArray (mpe->getMacroParameters());
1128
1129 return sources;
1130}
1131
1133{
1135
1136 for (auto r : edit.getRackList().getTypes())
1137 modifiers.addArray (r->getModifierList().getModifiers());
1138
1139 edit.visitAllTracksRecursive ([&] (Track& t)
1140 {
1141 if (auto modifierList = t.getModifierList())
1142 modifiers.addArray (modifierList->getModifiers());
1143
1144 return true;
1145 });
1146
1147 return modifiers;
1148}
1149
1150Modifier::Ptr findModifierForID (const Edit& edit, EditItemID id)
1151{
1152 for (auto modifier : getAllModifiers (edit))
1153 if (modifier->itemID == id)
1154 return modifier;
1155
1156 return {};
1157}
1158
1159Modifier::Ptr findModifierForID (const RackType& rack, EditItemID id)
1160{
1161 for (auto modifier : rack.getModifierList().getModifiers())
1162 if (modifier->itemID == id)
1163 return modifier;
1164
1165 return {};
1166}
1167
1168Track* getTrackContainingModifier (const Edit& edit, const Modifier::Ptr& m)
1169{
1170 if (m == nullptr)
1171 return nullptr;
1172
1173 return findTrackForPredicate (edit, [m] (Track& t)
1174 {
1175 if (auto list = t.getModifierList())
1176 return list->getModifiers().contains (m);
1177
1178 return false;
1179 });
1180}
1181
1182//==============================================================================
1184{
1187
1188 for (auto ae : getAllAutomatableEditItems (edit))
1189 if (auto m = dynamic_cast<MacroParameterList*> (ae))
1190 destArray.add (m);
1191
1192 return destArray;
1193}
1194
1196{
1198
1199 elements.add (&edit.getGlobalMacros());
1200 elements.addArray (edit.getRackList().getTypes());
1201 elements.addArray (getAudioTracks (edit));
1202 elements.addArray (getAllPlugins (edit, false));
1203
1204 return elements;
1205}
1206
1207InputDeviceInstance::RecordingParameters getDefaultRecordingParameters (const EditPlaybackContext& context,
1208 TimePosition playStart,
1209 TimePosition punchIn)
1210{
1211 InputDeviceInstance::RecordingParameters params;
1212
1213 const auto& edit = context.edit;
1214
1215 params.punchRange = { punchIn, Edit::getMaximumEditTimeRange().getEnd() };
1216
1217 if (edit.recordingPunchInOut)
1218 {
1219 const auto loopRange = context.transport.getLoopRange();
1220 params.punchRange = params.punchRange.withStart (std::max (params.punchRange.getStart(), loopRange.getStart() - 0.5s));
1221
1222 if (edit.getNumCountInBeats() > 0 && context.getLoopTimes().getStart() > loopRange.getStart())
1223 params.punchRange = params.punchRange.withStart (context.getLoopTimes().getStart());
1224
1225 // Ignore punch out if we start recording very close to the end of the punch markers
1226 if (playStart < loopRange.getEnd() - 0.5s)
1227 params.punchRange = params.punchRange.withEnd (loopRange.getEnd());
1228 }
1229 else if (context.isLooping())
1230 {
1231 params.punchRange = params.punchRange.withStart (context.getLoopTimes().getStart());
1232 }
1233
1234 return params;
1235}
1236
1238{
1240 TRACKTION_ASSERT_MESSAGE_THREAD
1241 InputDeviceInstance::Destination* dest = nullptr;
1242
1243 for (auto inputDest : instance.destinations)
1244 {
1245 if (inputDest->targetID == targetID)
1246 {
1247 dest = inputDest;
1248 break;
1249 }
1250 }
1251
1252 if (! dest)
1253 return juce::Result::fail (TRANS("Input not assigned to this destination"));
1254
1255 if (! instance.edit.getTransport().isRecording())
1256 return juce::Result::fail (TRANS("Transport must be recording to punch record"));
1257
1258 if (! dest->recordEnabled)
1259 return juce::Result::fail (TRANS("Input must be armed to punch record"));
1260
1261 // Punch in at the current play time don't punch out until recording is stopped
1262 InputDeviceInstance::RecordingParameters params;
1263 params.punchRange = { instance.context.getPosition(), Edit::getMaximumEditTimeRange().getEnd() };
1264
1265 if (auto [recContexts, errors] = extract (instance.prepareToRecord (params)); errors.isEmpty())
1266 instance.startRecording (std::move (recContexts));
1267 else
1268 return juce::Result::fail (errors[0]);
1269
1270 return juce::Result::ok();
1271}
1272
1273tl::expected<Clip::Array, juce::String> punchOutRecording (InputDeviceInstance& instance)
1274{
1275 auto& transport = instance.edit.getTransport();
1276 jassert (transport.isPlaying()); // If this isn't playing the getUnloopedPosition call below will be incorrect
1277
1278 InputDeviceInstance::StopRecordingParameters params;
1279
1280 for (auto targetID : instance.getTargets())
1281 params.targetsToStop.push_back (targetID);
1282
1283 if (auto epc = transport.getCurrentPlaybackContext())
1284 params.unloopedTimeToEndRecording = epc->getUnloopedPosition();
1285
1286 params.isLooping = transport.looping;
1287 params.markedRange = transport.getLoopRange();
1288 params.discardRecordings = false;
1289
1290 return instance.stopRecording (params);
1291}
1292
1294{
1295 for (auto input : epc.getAllInputs())
1296 if (input->isRecording())
1297 return true;
1298
1299 return false;
1300}
1301
1302
1303}} // namespace tracktion { inline namespace engine
T ceil(T... args)
ElementType getUnchecked(int index) const
bool isEmpty() const noexcept
void addArray(const Type *elementsToAdd, int numElementsToAdd)
int size() const noexcept
ElementType getFirst() const noexcept
void add(const ElementType &newElement)
bool contains(ParameterType elementToLookFor) const
bool addIfNotAlreadyThere(ParameterType newElement)
ElementType getLast() const noexcept
int findNextSetBit(int startIndex) const noexcept
BigInteger & setBit(int bitNumber)
bool isValid() const noexcept
static MidiMessage allNotesOff(int channel) noexcept
void addArray(const ReferenceCountedArray &arrayToAddFrom, int startIndex=0, int numElementsToAdd=-1) noexcept
ReferencedType * get() const noexcept
static Result fail(const String &errorMessage) noexcept
static Result ok() noexcept
static void JUCE_CALLTYPE callAfterDelay(int milliseconds, std::function< void()> functionToCall)
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.
Track & track
The Track this ClipSlot belongs to.
A clip in an edit.
A clip that can contain multiple other clips and mix their output together.
The Tracktion Edit class!
VolumeAndPanPlugin::Ptr getMasterVolumePlugin() const
Returns the master VolumeAndPanPlugin.
static TimeRange getMaximumEditTimeRange()
Returns the maximum length an Edit can be.
TransportControl & getTransport() const noexcept
Returns the TransportControl which is used to stop/stop/position playback and recording.
TimecodeDisplayFormat getTimecodeFormat() const
Returns the current TimecodeDisplayFormat.
TempoSequence tempoSequence
The global TempoSequence of this Edit.
MacroParameterElement & getGlobalMacros() const
Returns global MacroParameterElement.
TempoTrack * getTempoTrack() const
Returns the global TempoTrack.
int getNumCountInBeats() const
Returns the number of beats of the count in.
void visitAllTracksRecursive(std::function< bool(Track &)>) const
Visits all tracks in the Edit with the given function.
juce::CachedValue< bool > recordingPunchInOut
Whether recoridng only happens within the in/out markers.
juce::UndoManager & getUndoManager() noexcept
Returns the juce::UndoManager used for this Edit.
PluginList & getMasterPluginList() const noexcept
Returns the master PluginList.
void visitAllTracks(std::function< bool(Track &)>, bool recursive) const
Visits all tracks in the Edit with the given function.
RackTypeList & getRackList() const noexcept
Returns the RackTypeList which contains all the RackTypes for the Edit.
Engine & engine
A reference to the Engine.
UIBehaviour & getUIBehaviour() const
Returns the UIBehaviour class.
ProjectManager & getProjectManager() const
Returns the ProjectManager instance.
static juce::Array< Exportable * > addAllExportables(Edit &)
Returns all the Exportables contained in an Edit.
An instance of an InputDevice that's available to an Edit.
Edit & edit
The Edit this instance belongs to.
virtual std::vector< std::unique_ptr< RecordingContext > > startRecording(std::vector< std::unique_ptr< RecordingContext > >)=0
Starts a recording.
juce::Array< EditItemID > getTargets() const
Returns the targets this instance is assigned to.
virtual PreparedContext prepareToRecord(RecordingParameters)=0
Prepares a recording operation.
EditPlaybackContext & context
The EditPlaybackContext this instance belongs to.
DestinationList destinations
The list of assigned destinations.
virtual tl::expected< Clip::Array, juce::String > stopRecording(StopRecordingParameters)=0
Stops a recording.
Base class for elements which can contain macro parameters.
MacroParameterList * getMacroParameterList()
If no parameters have been created, this may return nullptr.
void copyFrom(const MidiList &, juce::UndoManager *)
Clears the current list and copies the others contents and properties.
void setMidiChannel(MidiChannel chanNum)
Gives the list a channel number that it'll use when generating real midi messages.
void addFrom(const MidiList &, juce::UndoManager *)
Adds copies of the events in another list to this one.
An ID representing one of the items in a Project.
ProjectItem::Ptr getProjectItem(ProjectItemID)
tries to find the project that contains an id, and open it as a ProjectItem.
Manages a list of items that are currently selected.
TimePosition toTime(BeatPosition) const
Converts a number of beats a time.
void deleteRegion(TimeRange)
Removes a region in a sequence, shifting TempoSettings and TimeSigs.
void insertSpaceIntoTrack(TimePosition, TimeDuration) override
Should insert empty space in to the track, shuffling down any items after the time.
Base class for EditItems that live in a Track, e.g.
static void sortByTime(ArrayType &items)
Helper function to sort an array of TrackItem[s] by their start time.
TimeRange getEditTimeRange() const
Returns the time range of this item.
Represents the destination output device(s) for a track.
Base class for tracks which contain clips and plugins and can be added to Edit[s].
@ individualFreeze
Freezes a track in to a single audio file.
@ groupFreeze
Freezes multiple tracks together in to a single file.
@ anyFreeze
Either a group or individual freeze.
TimePosition getPosition() const
Returns the current transport position.
bool isRecording() const
Returns true if recording is in progress.
TimeRange getLoopRange() const noexcept
Returns the loop range.
virtual SelectableList getAssociatedClipsToEdit(const SelectableList &items)
If your UI has the concept of edit groups, you should return an expanded list of selected items that ...
virtual void showWarningMessage(const juce::String &message)
Should display a temporary warning message.
T is_pointer_v
#define TRANS(stringLiteral)
#define jassert(expression)
#define jassertfalse
T max(T... args)
constexpr Type jmin(Type a, Type b)
constexpr Type jmax(Type a, Type b)
juce::File getEditFileFromProjectManager(Edit &edit)
Uses the ProjectManager to look up the file for an Edit.
juce::Array< MacroParameterElement * > getAllMacroParameterElements(const Edit &edit)
Returns all the MacroParameterElement in an Edit.
bool referencesProjectItem(Edit &edit, ProjectItemID itemID)
Returns true if the ProjectItemID is being used for any of the Edit's elements.
AudioTrack * getFirstAudioTrack(const Edit &edit)
Returns the first audio track in an Edit.
void insertSpaceIntoEdit(Edit &edit, TimeRange timeRange)
Inserts blank space in to an Edit, splitting clips if necessary.
TrackOutput * getTrackOutput(Track &track)
Returns the TrackOutput if the given track has one.
SelectableList splitClips(const SelectableList &clips, TimePosition time)
Splits the clips at a given time.
SelectableList getClipSelectionWithCollectionClipContents(const SelectableList &in)
Returns a list of clips.
Track * findTrackForState(const Edit &edit, const juce::ValueTree &v)
Returns the Track with a given state if contained in the Edit.
void deleteAutomation(const SelectableList &selectedClips)
Deletes the automation covered by the selected clips.
Clip * findClipForState(ClipOwner &co, const juce::ValueTree &v)
Returns a clip with the given state if the ClipOwner contains it.
juce::Array< Track * > toTrackArray(Edit &edit, const juce::BigInteger &tracksToAdd)
Returns an Array of Track[s] corresponding to the set bits of all tracks in an Edit.
juce::Array< Track * > getTopLevelTracks(const Edit &edit)
Returns all of the non-foldered tracks in an Edit.
void insertSpaceIntoEditFromBeatRange(Edit &edit, BeatRange beatRange)
Inserts a number of blank beats in to the Edit.
juce::Result mergeMidiClips(juce::Array< MidiClip * > clips, SelectionManager *sm)
Merges a set of MIDI clips in to one new one.
TimeRange getTimeRangeForSelectedItems(const SelectableList &selected)
Returns the time range covered by the given items.
Clip::Ptr duplicateClip(const Clip &c)
Creates a unique copy of this clip with a new EditItemID.
Clip * findClipForID(ClipOwner &co, EditItemID id)
Returns a clip with the given ID if the ClipOwner contains it.
CloseGap
An enum to specify if gaps deleted should be closed or not.
@ yes
Do move up subsequent track content.
ClipTrack * findFirstClipTrackFromSelection(const SelectableList &items)
Returns the first ClipTrack from the selected tracks or clips.
bool areAnyPluginsMissing(const Edit &edit)
Returns true if any plugins couldn't be loaded beacuse their files are missing.
juce::Array< MacroParameterList * > getAllMacroParameterLists(const Edit &edit)
Returns all the MacroParameterLists in an Edit.
juce::Array< Track * > getAllTracks(const Edit &edit)
Returns all the tracks in an Edit.
int findClipSlotIndex(ClipSlot &slot)
Returns the index of the ClipSlot in the list it is owned by.
ClipOwner * findClipOwnerForID(const Edit &edit, EditItemID id)
Returns the ClipOwner with a given ID if it can be found in the Edit.
bool containsTrack(const Edit &edit, const Track &track)
Returns true if the Edit contains this Track.
juce::Array< Track * > findTracksForIDs(const Edit &edit, const juce::Array< EditItemID > &ids)
Returns the Tracks for the given IDs in the Edit.
tl::expected< Clip::Array, juce::String > punchOutRecording(InputDeviceInstance &instance)
If the instance is currently recording, this will stop it and return any created clips or an error me...
juce::Array< AudioTrack * > getAudioTracks(const Edit &edit)
Returns all the AudioTracks in an Edit.
void visitAllTrackItems(const Edit &edit, std::function< bool(TrackItem &)> f)
Calls a function for all TrackItems in an Edit.
Track * getTrackContainingPlugin(const Edit &edit, const Plugin *p)
Returns the track for the track which the plugin is located on.
juce::BigInteger toBitSet(const juce::Array< Track * > &tracks)
Returns the set of tracks as a BigInteger with each bit corresponding to the array of all tracks in a...
juce::Array< AutomatableParameter::ModifierSource * > getAllModifierSources(const Edit &edit)
Returns all the ModifierSources in an Edit.
MidiNote * findNoteForState(const Edit &edit, const juce::ValueTree &v)
Returns the MidiNote with a given state.
Plugin::Ptr findPluginForState(const Edit &edit, const juce::ValueTree &v)
Returns the plugin with given state.
ClipSlot * findClipSlotForID(const Edit &edit, EditItemID id)
Returns the ClipSlot for the given ID.
void deleteRegionOfClip(Clip &c, TimeRange timeRangeToDelete)
Deletes a time range of a Clip.
ProjectItem::Ptr getProjectItemForEdit(const Edit &e)
Tries to find the project item that refers to this edit (but may return nullptr!)
juce::Array< ClipTrack * > getClipTracks(const Edit &edit)
Returns all the ClipTracks in an Edit.
juce::ReferenceCountedArray< Modifier > getAllModifiers(const Edit &edit)
Returns all the Modifiers in an Edit.
Plugin::Ptr findPluginForID(const Edit &edit, EditItemID id)
Returns the plugin with given EditItemID.
void deleteRegionOfTracks(Edit &edit, TimeRange rangeToDelete, bool onlySelected, CloseGap closeGap, SelectionManager *selectionManager)
Deletes a time range of an Edit, optionally closing the gap.
juce::Array< RackInstance * > getRackInstancesInEditForType(const RackType &rt)
Returns all of the instances of a specific RackType in an Edit.
Modifier::Ptr findModifierForID(ModifierList &ml, EditItemID modifierID)
Returns a Modifier if it can be found in the list.
void moveAutomation(const juce::Array< TrackAutomationSection > &origSections, TimeDuration offset, bool copy)
Moves a set of automation optionally applying an offset and copying the automation (rather than movin...
int getTotalNumTracks(const Edit &edit)
Returns the total number of Tracks in an Edit.
juce::Array< AutomatableEditItem * > getAllAutomatableEditItems(const Edit &edit)
Returns all AutomatableEditItems in an Edit.
void deleteRegionOfSelectedClips(SelectionManager &selectionManager, TimeRange rangeToDelete, CloseGap closeGap, bool moveAllSubsequentClipsOnTrack)
Deletes a time range of a Clip selection, optionally closing the gap.
std::pair< std::vector< std::unique_ptr< InputDeviceInstance::RecordingContext > >, juce::StringArray > extract(InputDeviceInstance::PreparedContext &&pc)
Splits the PreparedContext in to valid RecordingContexts and an array of error messages.
juce::Result prepareAndPunchRecord(InputDeviceInstance &instance, EditItemID targetID)
Starts an InputDeviceInstance recording to the given target without any count-in etc.
Track * getTrackContainingModifier(const Edit &edit, const Modifier::Ptr &m)
Returns the Track containing a Modifier.
juce::OwnedArray< MidiList > readFileToMidiList(juce::File midiFile, bool importAsNoteExpression)
Helper function to read a file to a number of MidiLists.
void injectMIDIToAllPlugins(const Edit &edit, const std::span< juce::MidiMessage > &messages)
Sends a list of MIDI messages to all the plugins in this edit.
MidiClip::Ptr insertMIDIClip(ClipOwner &parent, const juce::String &name, TimeRange position)
Inserts a new MidiClip into the ClipOwner's clip list.
Track * findTrackForID(const Edit &edit, EditItemID id)
Returns the Track with a given ID if contained in the Edit.
void muteOrUnmuteAllPlugins(const Edit &edit)
Toggles the enabled state of all plugins in an Edit.
Project::Ptr getProjectForEdit(const Edit &e)
Tries to find the project that contains this edit (but may return nullptr!)
SafeSelectable< SelectableType > makeSafeRef(SelectableType &selectable)
Creates a SafeSelectable for a given selectable object.
void moveSelectedClips(const SelectableList &selectedObjectsIn, Edit &edit, MoveClipAction mode, bool automationLocked)
Moves the selected clips within their track.
juce::Array< Track * > findAllTracksContainingSelectedItems(const SelectableList &items)
Returns all the tracks containing the selected tracks or TrackItems.
bool isRecording(EditPlaybackContext &epc)
Returns true if any inputs are currently recording.
void midiPanic(Edit &edit, bool resetPlugins)
Performs a "MIDI panic" on the edit, by resetting playback, and sending some all-note-off messages to...
InputDeviceInstance::RecordingParameters getDefaultRecordingParameters(const EditPlaybackContext &context, TimePosition playStart, TimePosition punchIn)
Returns the default set of recording parameters.
AudioTrack * findAudioTrackForID(const Edit &edit, EditItemID id)
Returns the AudioTrack with a given ID if contained in the Edit.
MoveClipAction
Enum to dictate move clip behaviour.
MidiClip::Ptr createClipFromFile(juce::File midiFile, ClipOwner &owner, bool importAsNoteExpression)
Helper function to read a MIDI file and create a MidiClip from it.
bool containsClip(const Edit &edit, Clip *clip)
Returns true if an Edit contains a given clip.
Plugin::Array getAllPlugins(const Edit &edit, bool includeMasterVolume)
Returns all the plugins in a given Edit.
juce::Array< ClipEffect * > getAllClipEffects(Edit &edit)
Returns all clip effects.
T push_back(T... args)
T reserve(T... args)
T sort(T... args)
Represents a duration in beats.
Represents a duration in real-life time.
Represents a position in real-life time.
ID for objects of type EditElement - e.g.
A list of Selectables, similar to a juce::Array but contains a cached list of the SelectableClasses f...
Holds a reference to a section of automation for a given Track.
Track::Ptr src
The time range of the automation section.
static juce::Array< TrackSection > findSections(const TrackItemArray &trackItems)
Returns a set of TrackSections for the given TrackItems.
time
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.