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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_AppFunctions.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
14namespace AppFunctions
15{
16 inline UIBehaviour& getCurrentUIBehaviour()
17 {
18 auto e = Engine::getEngines()[0];
19 jassert (e != nullptr);
20 return e->getUIBehaviour();
21 }
22
23 inline PropertyStorage& getCurrentPropertyStorage()
24 {
25 auto e = Engine::getEngines()[0];
26 jassert (e != nullptr);
27 return e->getPropertyStorage();
28 }
29
30 inline Edit* getCurrentlyFocusedEdit()
31 {
32 if (auto e = getCurrentUIBehaviour().getCurrentlyFocusedEdit())
33 return e;
34
35 return nullptr;
36 }
37
38 inline TransportControl* getActiveTransport()
39 {
40 if (auto ed = getCurrentlyFocusedEdit())
41 return &ed->getTransport();
42
43 return {};
44 }
45
46 inline SelectionManager* getCurrentlyFocusedSelectionManagerWithValidEdit()
47 {
48 if (auto sm = getCurrentUIBehaviour().getCurrentlyFocusedSelectionManager())
49 if (sm->edit != nullptr)
50 return sm;
51
52 return nullptr;
53 }
54
55 inline SelectableList getSelectedItems()
56 {
57 SelectableList items;
58
59 if (auto ed = getCurrentlyFocusedEdit())
60 for (SelectionManager::Iterator sm; sm.next();)
61 if (sm->getEdit() == ed)
62 for (auto s : sm->getSelectedObjects())
63 items.addIfNotAlreadyThere (s);
64
65 return items;
66 }
67
68 bool pasteIntoProject (Project& p)
69 {
70 if (auto content = Clipboard::getInstance()->getContentWithType<Clipboard::ProjectItems>())
71 return content->pasteIntoProject (p);
72
73 return false;
74 }
75
76 inline void nudgeSelected (const juce::String& commandDesc)
77 {
78 getCurrentUIBehaviour().nudgeSelected (commandDesc);
79 }
80
81 inline void zoomHorizontal (float increment)
82 {
83 getCurrentUIBehaviour().zoomHorizontal (increment);
84 }
85
86 inline void zoomVertical (float amount)
87 {
88 getCurrentUIBehaviour().zoomVertical (amount);
89 }
90
91 void cut()
92 {
93 auto& ui = getCurrentUIBehaviour();
94
95 if (auto sm = ui.getCurrentlyFocusedSelectionManager())
96 if (sm->cutSelected())
97 ui.showInfoMessage (TRANS("Copied to clipboard"));
98 }
99
100 void copy()
101 {
102 auto& ui = getCurrentUIBehaviour();
103
104 if (auto sm = ui.getCurrentlyFocusedSelectionManager())
105 if (sm->copySelected())
106 ui.showInfoMessage (TRANS("Copied to clipboard"));
107 }
108
109 void paste()
110 {
111 auto& ui = getCurrentUIBehaviour();
112
113 if (ui.paste (*Clipboard::getInstance()))
114 return;
115
116 if (auto p = ui.getCurrentlyFocusedProject())
117 if (pasteIntoProject (*p))
118 return;
119
120 ui.showWarningMessage (TRANS("No suitable target for pasting the items on the clipboard!"));
121 }
122
123 void insertPaste()
124 {
125 if (auto sm = getCurrentlyFocusedSelectionManagerWithValidEdit())
126 if (sm->insertPoint != nullptr)
127 if (auto clips = Clipboard::getInstance()->getContentWithType<Clipboard::Clips>())
128 clips->pasteInsertingAtCursorPos (*sm->getEdit(), *sm->insertPoint, *sm);
129 }
130
131 void deleteSelected()
132 {
133 if (! juce::Component::isMouseButtonDownAnywhere())
134 if (auto sm = getCurrentUIBehaviour().getCurrentlyFocusedSelectionManager())
135 sm->deleteSelected();
136 }
137
138 void deleteRegion()
139 {
140 if (auto sm = getCurrentlyFocusedSelectionManagerWithValidEdit())
141 {
142 if (sm->containsType<Track>())
143 deleteRegionOfTracks (*sm->getEdit(), getCurrentUIBehaviour().getEditingRange (*sm->getEdit()), true, CloseGap::no, sm);
144 else
145 deleteRegionOfSelectedClips (*sm, getCurrentUIBehaviour().getEditingRange (*sm->getEdit()), CloseGap::no, false);
146 }
147 }
148
149 void deleteRegionAndCloseGapFromSelected()
150 {
151 if (auto sm = getCurrentlyFocusedSelectionManagerWithValidEdit())
152 {
153 if (sm->containsType<Track>())
154 deleteRegionOfTracks (*sm->getEdit(), getCurrentUIBehaviour().getEditingRange (*sm->getEdit()), true, CloseGap::yes, sm);
155 else
156 deleteRegionOfSelectedClips (*sm, getCurrentUIBehaviour().getEditingRange (*sm->getEdit()), CloseGap::yes, false);
157 }
158 }
159
160 void deleteRegionAndCloseGap()
161 {
162 if (auto sm = getCurrentlyFocusedSelectionManagerWithValidEdit())
163 {
164 if (sm->containsType<Track>())
165 deleteRegionOfTracks (*sm->getEdit(), getCurrentUIBehaviour().getEditingRange (*sm->getEdit()), true, CloseGap::yes, sm);
166 else
167 deleteRegionOfSelectedClips (*sm, getCurrentUIBehaviour().getEditingRange (*sm->getEdit()), CloseGap::yes, true);
168 }
169 }
170
171 void goToStart()
172 {
173 if (auto transport = getActiveTransport())
174 {
175 toStart (*transport, getSelectedItems());
176 transport->startPosition = transport->getPosition();
177 }
178 }
179
180 void goToEnd()
181 {
182 if (auto transport = getActiveTransport())
183 {
184 toEnd (*transport, getSelectedItems());
185 transport->startPosition = transport->getPosition();
186 }
187 }
188
189 void markIn (bool forceAtCursor)
190 {
191 if (auto transport = getActiveTransport())
192 transport->setLoopIn (forceAtCursor ? transport->position.get()
193 : getCurrentUIBehaviour().getEditingPosition (transport->edit));
194 }
195
196 void markOut (bool forceAtCursor)
197 {
198 if (auto transport = getActiveTransport())
199 transport->setLoopOut (forceAtCursor ? transport->position.get()
200 : getCurrentUIBehaviour().getEditingPosition (transport->edit));
201 }
202
203 void start()
204 {
205 if (auto edit = getCurrentlyFocusedEdit())
206 edit->getTransport().play (true);
207 }
208
209 void stop()
210 {
211 if (auto transport = getActiveTransport())
212 {
213 if (transport->isPlaying())
214 {
215 transport->stop (false, false, true);
216 }
217 else
218 {
219 transport->startPosition = 0_tp;
220 transport->setPosition (0_tp);
221 }
222 }
223 }
224
225 void startStopPlay()
226 {
227 if (auto transport = getActiveTransport())
228 {
229 if (transport->isPlaying())
230 {
231 stop();
232 }
233 else
234 {
235 if (getCurrentPropertyStorage().getProperty (SettingID::resetCursorOnPlay))
236 transport->playFromStart (true);
237 else
238 transport->play (true);
239 }
240 }
241 }
242
243 void continueStopPlay()
244 {
245 if (auto transport = getActiveTransport())
246 {
247 if (transport->isPlaying())
248 stop();
249 else
250 transport->play (true);
251 }
252 }
253
254 void record()
255 {
256 if (auto transport = getActiveTransport())
257 {
258 const bool wasRecording = transport->isRecording();
259
260 if (wasRecording)
261 {
262 transport->stop (false, false);
263 }
264 else
265 {
266 getCurrentUIBehaviour().stopPreviewPlayback();
267
268 if (! transport->isPlaying())
269 if (getCurrentPropertyStorage().getProperty (SettingID::resetCursorOnPlay))
270 transport->setPosition (transport->startPosition);
271
272 transport->record (true, true);
273 }
274 }
275 }
276
277 void toggleTimecode()
278 {
279 if (auto ed = getCurrentlyFocusedEdit())
280 ed->toggleTimecodeMode();
281 }
282
283 void toggleLoop()
284 {
285 if (auto tc = getActiveTransport())
286 tc->looping = ! tc->looping;
287 }
288
289 void togglePunch()
290 {
291 if (auto ed = getCurrentlyFocusedEdit())
292 {
293 if (ed->getTransport().isRecording())
294 ed->getTransport().stop (false, false);
295
296 ed->recordingPunchInOut = ! ed->recordingPunchInOut;
297 }
298 }
299
300 void toggleSnap()
301 {
302 if (auto tc = getActiveTransport())
303 tc->snapToTimecode = ! tc->snapToTimecode;
304 }
305
306 void toggleClick()
307 {
308 if (auto ed = getCurrentlyFocusedEdit())
309 ed->clickTrackEnabled = ! ed->clickTrackEnabled;
310 }
311
312 void toggleMidiChase()
313 {
314 if (auto ed = getCurrentlyFocusedEdit())
315 ed->enableTimecodeSync (! ed->isTimecodeSyncEnabled());
316 }
317
318 void tabBack()
319 {
320 if (auto transport = getActiveTransport())
321 tabBack (*transport);
322 }
323
324 void tabForward()
325 {
326 if (auto transport = getActiveTransport())
327 tabForward (*transport);
328 }
329
330 void nudgeUp() { nudgeSelected ("up"); }
331 void nudgeDown() { nudgeSelected ("down"); }
332 void nudgeLeft() { nudgeSelected ("left"); }
333 void nudgeRight() { nudgeSelected ("right"); }
334
335 void zoomIn() { zoomHorizontal (0.2f); }
336 void zoomOut() { zoomHorizontal (-0.2f); }
337
338 void scrollTracksUp()
339 {
340 getCurrentUIBehaviour().scrollTracksUp();
341 }
342
343 void scrollTracksDown()
344 {
345 getCurrentUIBehaviour().scrollTracksDown();
346 }
347
348 void scrollTracksLeft()
349 {
350 getCurrentUIBehaviour().scrollTracksLeft();
351 }
352
353 void scrollTracksRight()
354 {
355 getCurrentUIBehaviour().scrollTracksRight();
356 }
357
358 void toggleEndToEnd()
359 {
360 if (auto ed = getCurrentlyFocusedEdit())
361 ed->playInStopEnabled = ! ed->playInStopEnabled;
362 }
363
364 bool saveEdit()
365 {
366 if (auto ed = getCurrentlyFocusedEdit())
367 return EditFileOperations (*ed).save (true, true, false);
368
369 return false;
370 }
371
372 bool saveEditAs()
373 {
374 if (auto ed = getCurrentlyFocusedEdit())
375 return EditFileOperations (*ed).saveAs();
376
377 return false;
378 }
379
380 void armOrDisarmAllInputs()
381 {
382 if (auto ed = getCurrentlyFocusedEdit())
383 {
384 int numArmed = 0;
385 int numDisarmed = 0;
386
387 for (auto in : ed->getAllInputDevices())
388 {
389 if (isAttached (*in))
390 {
391 for (auto t : getTargetTracks (*in))
392 {
393 if (in->isRecordingEnabled (t->itemID))
394 ++numArmed;
395 else
396 ++numDisarmed;
397 }
398 }
399 }
400
401 for (auto in : ed->getAllInputDevices())
402 if (isAttached (*in))
403 for (auto t : getTargetTracks (*in))
404 in->setRecordingEnabled (t->itemID, numArmed <= numDisarmed);
405 }
406 }
407
408 void goToMarkIn()
409 {
410 if (auto transport = getActiveTransport())
411 transport->setPosition (transport->getLoopRange().getStart());
412 }
413
414 void goToMarkOut()
415 {
416 if (auto transport = getActiveTransport())
417 transport->setPosition (transport->getLoopRange().getEnd());
418 }
419
420 void zoomToSelection()
421 {
422 getCurrentUIBehaviour().zoomToSelection();
423 }
424
425 void zoomTracksIn() { zoomVertical (1.1f); }
426 void zoomTracksOut() { zoomVertical (1.0f / 1.1f); }
427
428 void zoomToFitHorizontally()
429 {
430 getCurrentUIBehaviour().zoomToFitHorizontally();
431 }
432
433 void zoomToFitVertically()
434 {
435 getCurrentUIBehaviour().zoomToFitVertically();
436 }
437
438 void zoomToFitAll()
439 {
440 zoomToFitHorizontally();
441 zoomToFitVertically();
442 }
443
444 void moveToNextMarker()
445 {
446 if (auto transport = getActiveTransport())
447 if (auto clip = transport->edit.getMarkerManager().getNextMarker (transport->getPosition()))
448 transport->setPosition (clip->getPosition().getStart());
449 }
450
451 void moveToPrevMarker()
452 {
453 if (auto transport = getActiveTransport())
454 if (auto clip = transport->edit.getMarkerManager().getPrevMarker (transport->getPosition()))
455 transport->setPosition (clip->getPosition().getStart());
456 }
457
458 void redo()
459 {
460 if (auto ed = getCurrentlyFocusedEdit())
461 ed->redo();
462 }
463
464 void undo()
465 {
466 if (auto ed = getCurrentlyFocusedEdit())
467 ed->undo();
468 }
469
470 void toggleScroll()
471 {
472 getCurrentUIBehaviour().toggleScroll();
473 }
474
475 bool isScrolling()
476 {
477 return getCurrentUIBehaviour().isScrolling();
478 }
479
480 void stopRecordingAndDiscard()
481 {
482 if (auto transport = getActiveTransport())
483 transport->stop (true, false);
484 }
485
486 void stopRecordingAndRestart()
487 {
488 if (auto transport = getActiveTransport())
489 {
490 if (transport->isRecording())
491 {
492 transport->stop (true, true);
493 transport->record (true, true);
494 }
495 }
496 }
497
498 void insertTempoChange()
499 {
500 if (auto sm = getCurrentlyFocusedSelectionManagerWithValidEdit())
501 {
502 auto& edit = *sm->getEdit();
503 auto& tempoSequence = edit.tempoSequence;
504
505 if (tempoSequence.getNumTempos() >= 128)
506 {
507 edit.engine.getUIBehaviour().showWarningAlert (TRANS("Can't insert tempo"),
508 TRANS("There are already too many tempos in this edit!"));
509 }
510 else
511 {
512 auto nearestBeat = TimecodeSnapType::get1BeatSnapType()
513 .roundTimeNearest (getCurrentUIBehaviour().getEditingPosition (edit), tempoSequence);
514
515 if (auto newTempo = tempoSequence.insertTempo (nearestBeat))
516 sm->selectOnly (*newTempo);
517 else
518 edit.engine.getUIBehaviour().showWarningMessage (TRANS("Tempo changes must be further than 1 beat apart") + "...");
519 }
520 }
521 }
522
523 void insertPitchChange()
524 {
525 if (auto sm = getCurrentlyFocusedSelectionManagerWithValidEdit())
526 if (auto newPitch = sm->getEdit()->pitchSequence.insertPitch (getCurrentUIBehaviour().getEditingPosition (*sm->getEdit())))
527 sm->selectOnly (*newPitch);
528 }
529
530 void insertTimeSigChange()
531 {
532 if (auto sm = getCurrentlyFocusedSelectionManagerWithValidEdit())
533 {
534 auto& edit = *sm->getEdit();
535 auto& tempoSequence = edit.tempoSequence;
536
537 if (tempoSequence.getNumTimeSigs() >= 128)
538 {
539 edit.engine.getUIBehaviour().showWarningAlert (TRANS("Can't insert time signature"),
540 TRANS("There are already too many time signatures in this edit!"));
541 }
542 else
543 {
544 auto nearestBeat = TimecodeSnapType::get1BeatSnapType()
545 .roundTimeNearest (getCurrentUIBehaviour().getEditingPosition (edit), tempoSequence);
546
547 if (auto newTempo = tempoSequence.insertTimeSig (nearestBeat))
548 sm->selectOnly (*newTempo);
549 else
550 edit.engine.getUIBehaviour().showWarningMessage (TRANS("Time signature changes must be further than 1 beat apart")+ "...");
551 }
552 }
553 }
554
555 void insertChord()
556 {
557 if (auto sm = getCurrentlyFocusedSelectionManagerWithValidEdit())
558 {
559 auto& edit = *sm->getEdit();
560
561 if (auto ct = edit.getChordTrack())
562 {
563 const auto start = edit.getTransport().getPosition();
564 auto& tempo = edit.tempoSequence.getTempoAt (start);
565 auto approxOneBarLength = TimeDuration::fromSeconds (tempo.getMatchingTimeSig().numerator * tempo.getApproxBeatLength().inSeconds());
566
567 ct->insertNewClip (TrackItem::Type::chord, { start, start + approxOneBarLength }, sm);
568 }
569 }
570 }
571
572 void showHideVideo()
573 {
574 getCurrentUIBehaviour().showHideVideo();
575 }
576
577 void showHideMixer (bool fs)
578 {
579 getCurrentUIBehaviour().showHideMixer (fs);
580 }
581
582 void showHideMidiEditor (bool fs)
583 {
584 getCurrentUIBehaviour().showHideMidiEditor (fs);
585 }
586
587 void showHideTrackEditor (bool zoom)
588 {
589 getCurrentUIBehaviour().showHideTrackEditor (zoom);
590 }
591
592 void showHideBrowser()
593 {
594 getCurrentUIBehaviour().showHideBrowser();
595 }
596
597 void showHideActions()
598 {
599 getCurrentUIBehaviour().showHideActions();
600 }
601
602 void showHideAllPanes()
603 {
604 getCurrentUIBehaviour().showHideAllPanes();
605 }
606
607 void performUserAction (int a)
608 {
609 getCurrentUIBehaviour().performUserAction (a);
610 }
611
612 void split()
613 {
614 if (auto sm = getCurrentlyFocusedSelectionManagerWithValidEdit())
615 {
616 auto selected = sm->getSelectedObjects();
617 selected.mergeArray (splitClips (getCurrentUIBehaviour().getAssociatedClipsToEdit (selected),
618 getCurrentUIBehaviour().getEditingPosition (*sm->getEdit())));
619 sm->select (selected);
620 }
621 }
622
623 void toggleAutomationReadMode()
624 {
625 if (auto ed = getCurrentlyFocusedEdit())
626 {
627 auto& arm = ed->getAutomationRecordManager();
628 arm.setReadingAutomation (! arm.isReadingAutomation());
629 }
630 }
631
632 void toggleAutomationWriteMode()
633 {
634 if (auto ed = getCurrentlyFocusedEdit())
635 ed->getAutomationRecordManager().toggleWriteAutomationMode();
636 }
637
638 void showHideBigMeters()
639 {
640 auto& ui = getCurrentUIBehaviour();
641 ui.setBigInputMetersMode (! ui.getBigInputMetersMode());
642 }
643
644 void showHideInputs()
645 {
646 getCurrentUIBehaviour().showHideInputs();
647 }
648
649 void showHideOutputs()
650 {
651 getCurrentUIBehaviour().showHideOutputs();
652 }
653
654 void showProjectScreen() { getCurrentUIBehaviour().showProjectScreen(); }
655 void showSettingsScreen() { getCurrentUIBehaviour().showSettingsScreen(); }
656 void showEditScreen() { getCurrentUIBehaviour().showEditScreen(); }
657
658 void resetOverloads()
659 {
660 getCurrentUIBehaviour().resetOverloads();
661 }
662
663 void resetPeaks()
664 {
665 getCurrentUIBehaviour().resetPeaks();
666 }
667
668 void toggleTrackFreeze()
669 {
670 if (auto sm = getCurrentlyFocusedSelectionManagerWithValidEdit())
671 {
672 auto tracks = sm->getItemsOfType<AudioTrack>();
673
674 int freezeCount = 0;
675
676 for (auto t : tracks)
677 if (t->isFrozen (Track::anyFreeze))
678 freezeCount++;
679
680 if (freezeCount == 0)
681 {
682 for (auto t : tracks)
683 if (! t->isFrozen (Track::anyFreeze))
684 t->setFrozen (true, Track::individualFreeze);
685 }
686 else
687 {
688 for (auto t : tracks)
689 {
690 if (t->isFrozen (Track::groupFreeze))
691 t->setFrozen (false, Track::groupFreeze);
692 else if (t->isFrozen (Track::individualFreeze))
693 t->setFrozen (false, Track::individualFreeze);
694 }
695 }
696 }
697 }
698}
699
700}} // namespace tracktion { inline namespace engine
static juce::Array< Engine * > getEngines()
Returns the list of currently active engines.
@ individualFreeze
Freezes a track in to a single audio file.
@ groupFreeze
Freezes multiple tracks together in to a single file.
#define TRANS(stringLiteral)
#define jassert(expression)
SelectableList splitClips(const SelectableList &clips, TimePosition time)
Splits the clips at a given time.
@ no
Don't move up subsequent track content.
@ yes
Do move up subsequent track content.
bool isAttached(InputDeviceInstance &instance)
Returns true if this input is assigned to a target.
juce::Array< Clip * > deleteRegion(ClipOwner &parent, TimeRange range)
Removes a region of a ClipOwner and returns any newly created clips.
void markOut(TransportControl &tc)
Sets the mark out position to the current transport position.
void deleteRegionOfTracks(Edit &edit, TimeRange rangeToDelete, bool onlySelected, CloseGap closeGap, SelectionManager *selectionManager)
Deletes a time range of an Edit, optionally closing the gap.
void toStart(TransportControl &tc, const SelectableList &items)
Moves the transport to the start of the selected objects.
void toEnd(TransportControl &tc, const SelectableList &items)
Moves the transport to the end of the selected objects.
void deleteRegionOfSelectedClips(SelectionManager &selectionManager, TimeRange rangeToDelete, CloseGap closeGap, bool moveAllSubsequentClipsOnTrack)
Deletes a time range of a Clip selection, optionally closing the gap.
void markIn(TransportControl &tc)
Sets the mark in position to the current transport position.
void tabBack(TransportControl &tc)
Moves the transport back to the previous point of interest.
void tabForward(TransportControl &tc)
Moves the transport forwards to the next point of interest.
juce::Array< AudioTrack * > getTargetTracks(InputDeviceInstance &instance)
Returns the AudioTracks this instance is assigned to.
juce::Array< Clip * > split(ClipOwner &parent, TimePosition time)
Splits the given clp owner at the time and returns any newly created clips.