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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_CustomControlSurface.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
14void CustomControlSurface::CustomControlSurfaceManager::registerSurface (CustomControlSurface* item)
15{
16 surfaces.addIfNotAlreadyThere (item);
17}
18
19void CustomControlSurface::CustomControlSurfaceManager::unregisterSurface (CustomControlSurface* item)
20{
21 surfaces.removeAllInstancesOf (item);
22}
23
24void CustomControlSurface::CustomControlSurfaceManager::saveAllSettings (Engine& e)
25{
26 juce::XmlElement root ("MIDICUSTOMCONTROLSURFACES");
27
28 for (auto s : surfaces)
29 root.addChildElement (s->createXml());
30
31 e.getPropertyStorage().setXmlProperty (SettingID::customMidiControllers, root);
32}
33
34//==============================================================================
35CustomControlSurface::CustomControlSurface (ExternalControllerManager& ecm,
36 const juce::String& name,
37 ExternalControllerManager::Protocol protocol_)
38 : ControlSurface (ecm),
39 protocol (protocol_)
40{
41 init();
42
43 deviceDescription = name;
44 manager->saveAllSettings (engine);
45
46 loadFunctions();
47}
48
49CustomControlSurface::CustomControlSurface (ExternalControllerManager& ecm, const juce::XmlElement& xml)
50 : ControlSurface (ecm)
51{
52 protocol = xml.getStringAttribute ("protocol") == "osc" ? ExternalControllerManager::osc :
53 ExternalControllerManager::midi;
54
55 init();
56
57 deviceDescription = xml.getStringAttribute ("name");
58
59 loadFromXml (xml);
60
61 loadFunctions();
62}
63
64void CustomControlSurface::init()
65{
66 manager->registerSurface (this);
67
68 if (protocol == ExternalControllerManager::osc)
69 {
70 needsMidiChannel = false;
71 needsMidiBackChannel = false;
72 needsOSCSocket = true;
73 }
74 else
75 {
76 needsMidiChannel = true;
77 needsMidiBackChannel = true;
78 needsOSCSocket = false;
79 }
80 numberOfFaderChannels = 8;
81 numberOfTrackPads = 8;
82 numCharactersForTrackNames = needsOSCSocket ? 12 : 0;
83 numParameterControls = 18;
84 numCharactersForParameterLabels = needsOSCSocket ? 12 : 0;
85 wantsDummyParams = false;
86 deletable = true;
87 listeningOnRow = -1;
88 pluginMoveMode = true;
89 eatsAllMidi = false;
90 wantsClock = false;
91 allowBankingOffEnd = true;
92}
93
94CustomControlSurface::~CustomControlSurface()
95{
96 manager->unregisterSurface (this);
97 clearCommandGroups();
98}
99
100void CustomControlSurface::clearCommandGroups()
101{
102 for (auto& s : commandGroups)
103 delete s.second;
104
105 commandGroups.clear();
106}
107
108bool CustomControlSurface::isPendingEventAssignable()
109{
110 if (auto ed = getEdit())
111 return ed->getParameterChangeHandler().isActionFunctionPending();
112
113 return false;
114}
115
116void CustomControlSurface::updateOrCreateMappingForID (int id, juce::String addr,
117 int channel, int noteNum, int controllerID)
118{
119 Mapping* mappingToUpdate = nullptr;
120
121 // first try to find existing mapping
122 for (auto mapping : mappings)
123 {
124 if (mapping->function == id)
125 {
126 mappingToUpdate = mapping;
127 break;
128 }
129 }
130
131 if (mappingToUpdate == nullptr)
132 mappingToUpdate = mappings.add (new Mapping());
133
134 mappingToUpdate->id = controllerID;
135 mappingToUpdate->addr = addr;
136 mappingToUpdate->note = noteNum;
137 mappingToUpdate->channel = channel;
138 mappingToUpdate->function = id;
139
140 sendChangeMessage();
141}
142
143void CustomControlSurface::addMappingSetsForID (ActionID id, juce::Array<MappingSet>& mappingSet)
144{
145 if (owner == nullptr)
146 return;
147
148 auto selectionColour = owner->getSelectionColour();
149
150 for (int i = mappings.size(); --i >= 0;)
151 {
152 auto mapping = mappings.getUnchecked (i);
153
154 if (mapping->function == id)
155 {
156 MappingSet matchedMapping;
157
158 matchedMapping.id = id;
159 matchedMapping.controllerID = mapping->id;
160 matchedMapping.addr = mapping->addr;
161 matchedMapping.note = mapping->note;
162 matchedMapping.channel = mapping->channel;
163 matchedMapping.colour = selectionColour;
164 matchedMapping.surfaceName = owner->getName();
165
166 mappingSet.add (matchedMapping);
167 }
168 }
169}
170
171juce::String CustomControlSurface::getNameForActionID (ExternalControllerManager& ecm, ActionID id)
172{
174
175 for (auto ec : ecm.getControllers())
176 if (auto ccs = ec->getControlSurfaceIfType<CustomControlSurface>())
177 return ccs->getFunctionName (id);
178
179 return {};
180}
181
182juce::Array<ControlSurface*> CustomControlSurface::getCustomSurfaces (ExternalControllerManager& ecm)
183{
185
186 if (auto n = ecm.engine.getPropertyStorage().getXmlProperty (SettingID::customMidiControllers))
187 {
188 for (auto controllerXml : n->getChildIterator())
189 {
190 if (controllerXml->hasTagName ("MIDICUSTOMCONTROLSURFACE"))
191 surfaces.add (new CustomControlSurface (ecm, *controllerXml));
192 else if (auto ccs = ecm.engine.getEngineBehaviour().getCustomControlSurfaceForXML (ecm, *controllerXml))
193 surfaces.add (ccs);
194 }
195 }
196
197 return surfaces;
198}
199
200juce::Array<CustomControlSurface::MappingSet> CustomControlSurface::getMappingSetsForID (ExternalControllerManager& ecm, ActionID id)
201{
202 juce::Array<MappingSet> mappingSet;
203
204 for (auto ec : ecm.getControllers())
205 if (ec->isEnabled())
206 if (auto ccs = ec->getControlSurfaceIfType<CustomControlSurface>())
207 ccs->addMappingSetsForID (id, mappingSet);
208
209 return mappingSet;
210}
211
212int CustomControlSurface::getControllerNumberFromId (int id) noexcept
213{
214 return id == 0x40000 ? -1 : (id - 0x10000);
215}
216
217juce::XmlElement* CustomControlSurface::createXml()
218{
219 auto element = new juce::XmlElement ("MIDICUSTOMCONTROLSURFACE");
220 element->setAttribute ("name", deviceDescription);
221 element->setAttribute ("protocol", protocol == ExternalControllerManager::osc ? "osc" : "midi");
222 element->setAttribute ("eatsMidi", eatsAllMidi);
223 element->setAttribute ("channels", numberOfFaderChannels);
224 element->setAttribute ("parameters", numParameterControls);
225 element->setAttribute ("pickUpMode", pickUpMode);
226 element->setAttribute ("followsSelection", followsTrackSelection);
227
228 for (auto m : mappings)
229 {
230 auto mapping = element->createNewChildElement ("MAPPING");
231 mapping->setAttribute ("id", m->id);
232 mapping->setAttribute ("addr", m->addr);
233 mapping->setAttribute ("channel", m->channel);
234 mapping->setAttribute ("function", m->function);
235 mapping->setAttribute ("note", m->note);
236 }
237
238 return element;
239}
240
241bool CustomControlSurface::loadFromXml (const juce::XmlElement& xml)
242{
243 eatsAllMidi = xml.getBoolAttribute ("eatsMidi", false);
244 numberOfFaderChannels = xml.getIntAttribute ("channels", 8);
245 numParameterControls = xml.getIntAttribute ("parameters", 18);
246 pickUpMode = xml.getBoolAttribute ("pickUpMode", false);
247 followsTrackSelection = xml.getBoolAttribute ("followsSelection", false);
248
249 mappings.clear();
250
251 for (auto node : xml.getChildWithTagNameIterator ("MAPPING"))
252 {
253 auto mapping = mappings.add (new Mapping());
254 mapping->id = node->getIntAttribute ("id");
255 mapping->addr = node->getStringAttribute ("addr");
256 mapping->channel = node->getIntAttribute ("channel");
257 mapping->function = node->getIntAttribute ("function");
258 mapping->note = node->getIntAttribute ("note", -1);
259 }
260
261 return true;
262}
263
264void CustomControlSurface::importSettings (const juce::File& file)
265{
266 importSettings (file.loadFileAsString());
267}
268
269void CustomControlSurface::importSettings (const juce::String& xmlText)
270{
271 bool ok = false;
272 mappings.clearQuick (true);
273
274 if (auto xml = juce::parseXML (xmlText))
275 {
276 loadFromXml (*xml);
277 loadFunctions();
278 ok = true;
279 }
280
281 manager->saveAllSettings (engine);
282
283 if (! ok)
284 engine.getUIBehaviour().showWarningAlert (TRANS("Import"), TRANS("Import failed"));
285}
286
287void CustomControlSurface::exportSettings (const juce::File& file)
288{
289 bool ok = false;
290 std::unique_ptr<juce::XmlElement> xml (createXml());
291
292 if (xml != nullptr)
293 {
294 file.deleteFile();
295 file.create();
296 file.appendText (xml->toString());
297
298 if (file.getSize() > 10)
299 ok = true;
300 }
301
302 if (! ok)
303 engine.getUIBehaviour().showWarningAlert (TRANS("Export"), TRANS("Export failed"));
304}
305
306void CustomControlSurface::initialiseDevice (bool online_)
307{
308 online = online_;
309 recreateOSCSockets();
310}
311
312void CustomControlSurface::shutDownDevice()
313{
314 online = false;
315 recreateOSCSockets();
316}
317
318void CustomControlSurface::updateOSCSettings (int in, int out, juce::String addr)
319{
320 oscInputPort = in;
321 oscOutputPort = out;
322 oscOutputAddr = addr;
323
324 recreateOSCSockets();
325}
326
327void CustomControlSurface::recreateOSCSockets()
328{
329 oscSender.reset();
330 oscReceiver.reset();
331
332 if (online && oscInputPort > 0)
333 {
335 if (! oscReceiver->connect (oscInputPort))
336 {
337 oscReceiver.reset();
338 engine.getUIBehaviour().showInfoMessage (TRANS("Failed to open OSC input port"));
339 }
340 else
341 {
342 oscReceiver->addListener (this);
343 }
344 }
345
346 if (online && oscOutputPort > 0 && oscOutputAddr.isNotEmpty())
347 {
349 if (! oscSender->connect (oscOutputAddr, oscOutputPort))
350 {
351 oscSender.reset();
352 engine.getUIBehaviour().showInfoMessage (TRANS("Failed to open OSC output port"));
353 }
354 }
355}
356
357void CustomControlSurface::saveAllSettings (Engine& e)
358{
360 manager->saveAllSettings (e);
361}
362
363void CustomControlSurface::updateMiscFeatures()
364{
365}
366
367bool CustomControlSurface::wantsMessage (int, const juce::MidiMessage& m)
368{
369 // in T5 there will always be a current list box so we don't wan't to eat all the time
370 if (eatsAllMidi || listeningOnRow != -1 || ((m.isNoteOn() || m.isController())
371 && isPendingEventAssignable()))
372 return true;
373
374 // only eat messages that are mapped, audio engine gets the rest
375 if (m.isController() || m.isNoteOnOrOff())
376 {
377 int note = -1;
378 int id = 0;
379 int channel = 0;
380
381 if (m.isController())
382 {
383 int val;
384 rpnParser.parseControllerMessage (m, id, channel, val);
385 }
386 else
387 {
388 note = m.getNoteNumber();
389 channel = m.getChannel();
390 }
391
392 if (getEdit() != nullptr)
393 {
394 for (auto mapping : mappings)
395 {
396 if (((id == mapping->id && id != 0) || (note == mapping->note && note != -1))
397 && channel == mapping->channel
398 && mapping->function)
399 {
400 return true;
401 }
402 }
403 }
404 }
405
406 return false;
407}
408
409void CustomControlSurface::oscMessageReceived (const juce::OSCMessage& m)
410{
411 packetsIn++;
412
413 float val = 1.0f;
414
415 if (m.size() >= 1)
416 {
417 auto arg = m[0];
418
419 if (arg.isFloat32())
420 val = arg.getFloat32();
421 else if (arg.isInt32())
422 val = float (arg.getInt32());
423 }
424
425 auto addr = m.getAddressPattern().toString();
426
427 if (addr.endsWith ("/z"))
428 {
429 // Track control touches and releases
430 addr = addr.dropLastCharacters (2);
431 if (val != 0.0f)
432 {
433 oscControlTouched[addr] = true;
434 oscControlTapsWhileTouched[addr] = 0;
435 }
436 else
437 {
438 oscControlTouched[addr] = false;
439 oscControlTapsWhileTouched[addr] = 0;
440
441 auto itr = oscLastValue.find (addr);
442 if (itr != oscLastValue.end())
443 {
444 if (oscSender)
445 {
446 try
447 {
448 juce::OSCMessage mo (addr);
449 mo.addFloat32 (itr->second);
450
451 if (oscSender->send (mo))
452 packetsOut++;
453 }
454 catch ([[maybe_unused]] juce::OSCException& err)
455 {
456 DBG("OSC Error: " + err.description);
457 }
458 }
459
460 oscLastValue.erase (itr);
461 }
462 }
463 }
464 else
465 {
466 // Track control values
467 lastControllerAddr = addr;
468 lastControllerValue = val;
469
470 if (listeningOnRow >= 0)
471 triggerAsyncUpdate();
472
473 oscControlTapsWhileTouched[addr]++;
474 oscActiveAddr = addr;
475
476 if (getEdit() != nullptr)
477 {
478 for (auto* mapping : mappings)
479 {
480 if (lastControllerAddr == mapping->addr)
481 {
482 for (auto* actionFunction : actionFunctionList)
483 {
484 if (actionFunction->id == mapping->function)
485 {
486 auto actionFunc = actionFunction->actionFunc;
487 (this->*actionFunc) (lastControllerValue, actionFunction->param);
488 }
489 }
490 }
491 }
492 }
493 }
494}
495
496void CustomControlSurface::oscBundleReceived (const juce::OSCBundle& b)
497{
498 for (auto e : b)
499 if (e.isMessage())
500 oscMessageReceived (e.getMessage());
501}
502
503bool CustomControlSurface::isTextAction (ActionID id)
504{
505 switch (id)
506 {
507 case nameTrackId:
508 case numberTrackId:
509 case volTextTrackId:
510 case timecodeId:
511 case masterVolumeTextId:
512 case emptyTextId:
513 case auxTextTrackId:
514 case panTextTrackId:
515 case paramNameTrackId:
516 case paramTextTrackId:
517 case selectedPluginNameId:
518 return true;
519 case playId:
520 case stopId:
521 case recordId:
522 case homeId:
523 case endId:
524 case rewindId:
525 case fastForwardId:
526 case markInId:
527 case markOutId:
528 case automationReadId:
529 case automationRecordId:
530 case addMarkerId:
531 case nextMarkerId:
532 case previousMarkerId:
533 case nudgeLeftId:
534 case nudgeRightId:
535 case abortId:
536 case abortRestartId:
537 case jogId:
538 case jumpToMarkInId:
539 case jumpToMarkOutId:
540 case toggleBeatsSecondsModeId:
541 case toggleLoopId:
542 case togglePunchId:
543 case toggleClickId:
544 case toggleSnapId:
545 case toggleSlaveId:
546 case toggleEtoEId:
547 case toggleScrollId:
548 case toggleAllArmId:
549 case masterVolumeId:
550 case masterPanId:
551 case quickParamId:
552 case paramTrackId:
553 case clearAllSoloId:
554 case volTrackId:
555 case panTrackId:
556 case muteTrackId:
557 case soloTrackId:
558 case armTrackId:
559 case selectTrackId:
560 case auxTrackId:
561 case zoomInId:
562 case zoomOutId:
563 case scrollTracksUpId:
564 case scrollTracksDownId:
565 case scrollTracksLeftId:
566 case scrollTracksRightId:
567 case zoomTracksInId:
568 case zoomTracksOutId:
569 case toggleSelectionModeId:
570 case selectLeftId:
571 case selectRightId:
572 case selectUpId:
573 case selectDownId:
574 case selectClipInTrackId:
575 case selectPluginInTrackId:
576 case faderBankLeftId:
577 case faderBankLeft1Id:
578 case faderBankLeft4Id:
579 case faderBankLeft8Id:
580 case faderBankLeft16Id:
581 case faderBankRightId:
582 case faderBankRight1Id:
583 case faderBankRight4Id:
584 case faderBankRight8Id:
585 case faderBankRight16Id:
586 case paramBankLeftId:
587 case paramBankLeft1Id:
588 case paramBankLeft4Id:
589 case paramBankLeft8Id:
590 case paramBankLeft16Id:
591 case paramBankLeft24Id:
592 case paramBankRightId:
593 case paramBankRight1Id:
594 case paramBankRight4Id:
595 case paramBankRight8Id:
596 case paramBankRight16Id:
597 case paramBankRight24Id:
598 case userAction1Id:
599 case userAction2Id:
600 case userAction3Id:
601 case userAction4Id:
602 case userAction5Id:
603 case userAction6Id:
604 case userAction7Id:
605 case userAction8Id:
606 case userAction9Id:
607 case userAction10Id:
608 case userAction11Id:
609 case userAction12Id:
610 case userAction13Id:
611 case userAction14Id:
612 case userAction15Id:
613 case userAction16Id:
614 case userAction17Id:
615 case userAction18Id:
616 case userAction19Id:
617 case userAction20Id:
618 case clip1TrackId:
619 case clip2TrackId:
620 case clip3TrackId:
621 case clip4TrackId:
622 case clip5TrackId:
623 case clip6TrackId:
624 case clip7TrackId:
625 case clip8TrackId:
626 case stopClipsTrackId:
627 case sceneId:
628 case clipBankUp1Id:
629 case clipBankUp4Id:
630 case clipBankUp8Id:
631 case clipBankDown1Id:
632 case clipBankDown4Id:
633 case clipBankDown8Id:
634 case marker1Id:
635 case marker2Id:
636 case marker3Id:
637 case marker4Id:
638 case marker5Id:
639 case marker6Id:
640 case marker7Id:
641 case marker8Id:
642 case none:
643 default:
644 return false;
645 }
646}
647
648void CustomControlSurface::timerCallback()
649{
650 stopTimer();
651
652 auto i = listeningOnRow;
653 while (i < mappings.size())
654 {
655 i++;
656 if (auto map = mappings[i]; map && ! map->isControllerAssigned())
657 {
658 setLearntParam (false);
659 listenToRow (i);
660 break;
661 }
662 }
663}
664
665void CustomControlSurface::acceptMidiMessage (int, const juce::MidiMessage& m)
666{
667 if (! m.isController() && ! m.isNoteOn())
668 return;
669
670 if (m.isController())
671 {
672 int val;
673 lastControllerNote = -1;
674 rpnParser.parseControllerMessage (m, lastControllerID, lastControllerChannel, val);
675 lastControllerValue = val / 127.0f;
676 }
677
678 if (m.isNoteOn())
679 {
680 lastControllerID = 0;
681 lastControllerNote = m.getNoteNumber();
682 lastControllerChannel = m.getChannel();
683 lastControllerValue = 1.0f;
684 }
685
686 if (listeningOnRow >= 0)
687 {
688 triggerAsyncUpdate();
689 startTimer (666);
690 }
691
692 if (auto ed = getEdit())
693 {
694 if (engine.getMidiLearnState().isActive()
695 && ed->getParameterChangeHandler().isActionFunctionPending())
696 {
697 const MidiLearnState::ScopedChangeCaller changeCaller (engine.getMidiLearnState(), MidiLearnState::added);
698 updateOrCreateMappingForID (ed->getParameterChangeHandler().getPendingActionFunctionId (true),
699 lastControllerAddr, lastControllerChannel, lastControllerNote, lastControllerID);
700 }
701 else
702 {
703 for (auto mapping : mappings)
704 {
705 if (((lastControllerID == mapping->id && lastControllerID != 0) ||
706 (lastControllerNote == mapping->note && lastControllerNote != -1)) &&
707 lastControllerChannel == mapping->channel)
708 {
709 for (auto actionFunction : actionFunctionList)
710 {
711 if (actionFunction->id == mapping->function)
712 {
713 auto actionFunc = actionFunction->actionFunc;
714 (this->*actionFunc) (lastControllerValue, actionFunction->param);
715 }
716 }
717 }
718 }
719 }
720 }
721}
722
723void CustomControlSurface::moveFader (int faderIndex, float v)
724{
725 ControlSurface::moveFader (faderIndex, v);
726
727 sendCommandToControllerForActionID (volTrackId + faderIndex, v);
728
729 auto dbText = juce::Decibels::toString (volumeFaderPositionToDB (v));
730 sendCommandToControllerForActionID (volTextTrackId + faderIndex, dbText);
731}
732
733void CustomControlSurface::moveMasterLevelFader (float newSliderPos)
734{
735 ControlSurface::moveMasterLevelFader (newSliderPos);
736
737 sendCommandToControllerForActionID (masterVolumeId, newSliderPos);
738
739 auto dbText = juce::Decibels::toString (volumeFaderPositionToDB (newSliderPos));
740 sendCommandToControllerForActionID (masterVolumeTextId, dbText);
741}
742
743void CustomControlSurface::moveMasterPanPot (float newPos)
744{
745 ControlSurface::moveMasterPanPot (newPos);
746
747 sendCommandToControllerForActionID (masterPanId, (newPos + 1.0f) / 2.0f);
748}
749
750void CustomControlSurface::movePanPot (int faderIndex, float v)
751{
752 ControlSurface::movePanPot (faderIndex, v);
753 sendCommandToControllerForActionID (panTrackId + faderIndex, (v * 0.5f) + 0.5f);
754
755 juce::String panText;
756 auto p = juce::roundToInt (v * 100);
757
758 if (p == 0)
759 panText = "C";
760 else if (p < 0)
761 panText = juce::String (-p) + "L";
762 else
763 panText = juce::String (p) + "R";
764
765 sendCommandToControllerForActionID (panTextTrackId + faderIndex, panText);
766}
767
768void CustomControlSurface::moveAux (int faderIndex, int num, const char* bus, float v)
769{
770 ControlSurface::moveAux (faderIndex, num, bus, v);
771
772 sendCommandToControllerForActionID (auxTrackId + faderIndex, v);
773
774 auto dbText = juce::Decibels::toString (volumeFaderPositionToDB (v));
775 sendCommandToControllerForActionID (auxTextTrackId + faderIndex, dbText);
776}
777
778void CustomControlSurface::updateSoloAndMute (int faderIndex, Track::MuteAndSoloLightState state, bool isBright)
779{
780 const bool muteLit = (state & Track::muteLit) != 0;
781 const bool muteFlashing = (state & Track::muteFlashing) != 0;
782 const bool soloLit = (state & Track::soloLit) != 0;
783 const bool soloFlashing = (state & Track::soloFlashing) != 0;
784 sendCommandToControllerForActionID (muteTrackId + faderIndex, muteLit || (isBright && muteFlashing) ? 1.0f : 0.0f);
785 sendCommandToControllerForActionID (soloTrackId + faderIndex, soloLit || (isBright && soloFlashing) ? 1.0f : 0.0f);
786}
787
788void CustomControlSurface::soloCountChanged (bool anySolo)
789{
790 sendCommandToControllerForActionID (clearAllSoloId, anySolo);
791}
792
793void CustomControlSurface::playStateChanged (bool isPlaying)
794{
795 sendCommandToControllerForActionID (playId, isPlaying);
796}
797
798void CustomControlSurface::recordStateChanged (bool isRecording)
799{
800 sendCommandToControllerForActionID (recordId, isRecording);
801}
802
803void CustomControlSurface::automationReadModeChanged (bool isReading)
804{
805 sendCommandToControllerForActionID (automationReadId, isReading);
806}
807
808void CustomControlSurface::automationWriteModeChanged (bool isWriting)
809{
810 sendCommandToControllerForActionID (automationRecordId, isWriting);
811}
812
813void CustomControlSurface::faderBankChanged (int newStartChannelNumber, const juce::StringArray& trackNames)
814{
815 int idx = 0;
816
817 for (auto name : trackNames)
818 {
819 sendCommandToControllerForActionID (nameTrackId + idx, name);
820 sendCommandToControllerForActionID (numberTrackId + idx, juce::String (newStartChannelNumber + idx + 1));
821 idx++;
822 }
823}
824
825void CustomControlSurface::channelLevelChanged (int, float, float) {}
826
827void CustomControlSurface::trackSelectionChanged (int faderIndex, bool isSelected)
828{
829 sendCommandToControllerForActionID (selectTrackId + faderIndex, isSelected);
830}
831
832void CustomControlSurface::trackRecordEnabled (int faderIndex, bool recordEnabled)
833{
834 sendCommandToControllerForActionID (armTrackId + faderIndex, recordEnabled);
835}
836
837void CustomControlSurface::masterLevelsChanged (float, float) {}
838
839void CustomControlSurface::timecodeChanged (int barsOrHours, int beatsOrMinutes,
840 int ticksOrSeconds, int millisecs, bool isBarsBeats, bool isFrames)
841{
842 char d[32];
843
844 if (isBarsBeats)
845 sprintf (d, sizeof (d), "%3d %02d %03d", barsOrHours, beatsOrMinutes, ticksOrSeconds);
846 else if (isFrames)
847 sprintf (d, sizeof (d), "%02d:%02d:%02d.%02d", barsOrHours, beatsOrMinutes, ticksOrSeconds, millisecs);
848 else
849 sprintf (d, sizeof (d), "%02d:%02d:%02d.%03d", barsOrHours, beatsOrMinutes, ticksOrSeconds, millisecs);
850
851 sendCommandToControllerForActionID (timecodeId, juce::String (d));
852 sendCommandToControllerForActionID (emptyTextId, juce::String());
853}
854
855void CustomControlSurface::clickOnOffChanged (bool enabled)
856{
857 sendCommandToControllerForActionID (toggleClickId, enabled);
858}
859
860void CustomControlSurface::snapOnOffChanged (bool enabled)
861{
862 sendCommandToControllerForActionID (toggleSnapId, enabled);
863}
864
865void CustomControlSurface::loopOnOffChanged (bool enabled)
866{
867 sendCommandToControllerForActionID (toggleLoopId, enabled);
868}
869
870void CustomControlSurface::slaveOnOffChanged (bool enabled)
871{
872 sendCommandToControllerForActionID (toggleSlaveId, enabled);
873}
874
875void CustomControlSurface::punchOnOffChanged (bool enabled)
876{
877 sendCommandToControllerForActionID (togglePunchId, enabled);
878}
879
880void CustomControlSurface::scrollOnOffChanged (bool enabled)
881{
882 sendCommandToControllerForActionID (toggleScrollId, enabled);
883}
884
885void CustomControlSurface::parameterChanged (int paramIndex, const ParameterSetting& setting)
886{
887 ControlSurface::parameterChanged (paramIndex, setting);
888
889 if (paramIndex >= 0)
890 {
891 sendCommandToControllerForActionID (paramTrackId + paramIndex, setting.value);
892 sendCommandToControllerForActionID (paramNameTrackId + paramIndex, juce::String (setting.label));
893 sendCommandToControllerForActionID (paramTextTrackId + paramIndex, juce::String (setting.valueDescription));
894 }
895}
896
897void CustomControlSurface::clearParameter (int paramIndex)
898{
899 if (paramIndex >= 0)
900 {
901 sendCommandToControllerForActionID (paramTrackId + paramIndex, 0.0f);
902 sendCommandToControllerForActionID (paramNameTrackId + paramIndex, juce::String());
903 sendCommandToControllerForActionID (paramTextTrackId + paramIndex, juce::String());
904 }
905}
906
907void CustomControlSurface::currentSelectionChanged (juce::String pluginName)
908{
909 sendCommandToControllerForActionID (selectedPluginNameId, pluginName);
910}
911
912bool CustomControlSurface::canChangeSelectedPlugin()
913{
914 return true;
915}
916
917void CustomControlSurface::deleteController()
918{
919 manager->unregisterSurface (this);
920 manager->saveAllSettings (engine);
921}
922
923void CustomControlSurface::sendCommandToControllerForActionID (int actionID, bool value)
924{
925 sendCommandToControllerForActionID (actionID, value ? 1.0f : 0.0f);
926}
927
928void CustomControlSurface::sendCommandToControllerForActionID (int actionID, float value)
929{
930 for (auto mapping : mappings)
931 {
932 if (mapping->function == actionID)
933 {
934 const auto oscAddr = mapping->addr;
935 const int midiController = getControllerNumberFromId (mapping->id); // MIDI CC
936 const int midiNote = mapping->note; // MIDI Note
937 const int midiChannel = mapping->channel; // MIDI Channel
938
939 if (needsOSCSocket && oscAddr.isNotEmpty())
940 {
941 // Only send a value if the control is currently not being touched
942 auto itr = oscControlTouched.find (oscAddr);
943 if (itr == oscControlTouched.end() || ! itr->second)
944 {
945 if (oscSender)
946 {
947 try
948 {
949 juce::OSCMessage m (oscAddr);
950 m.addFloat32 (value);
951
952 if (oscSender->send (m))
953 packetsOut++;
954 }
955 catch ([[maybe_unused]] juce::OSCException& err)
956 {
957 DBG("OSC Error: " + err.description);
958 }
959 }
960 }
961 else
962 {
963 oscLastValue[oscAddr] = value;
964 }
965 }
966 else if (needsMidiBackChannel && midiChannel != -1)
967 {
968 if (midiNote != -1)
969 {
970 if (value <= 0.0f) sendMidiCommandToController (0, juce::MidiMessage::noteOff (midiChannel, midiNote, value));
971 else sendMidiCommandToController (0, juce::MidiMessage::noteOn (midiChannel, midiNote, value));
972 }
973
974 if (midiController != -1)
975 sendMidiCommandToController (0, juce::MidiMessage::controllerEvent (midiChannel, midiController,
977 }
978 }
979 }
980}
981
982void CustomControlSurface::sendCommandToControllerForActionID (int actionID, juce::String value)
983{
984 for (auto mapping : mappings)
985 {
986 if (mapping->function == actionID)
987 {
988 const auto oscAddr = mapping->addr;
989
990 if (needsOSCSocket && oscAddr.isNotEmpty())
991 {
992 if (oscSender)
993 {
994 try
995 {
996 juce::OSCMessage m (oscAddr);
997 m.addString (value);
998
999 if (oscSender->send (m))
1000 packetsOut++;
1001 }
1002 catch ([[maybe_unused]] juce::OSCException& err)
1003 {
1004 DBG("OSC Error: " + err.description);
1005 }
1006 }
1007 }
1008 }
1009 }
1010}
1011
1012//==============================================================================
1013bool CustomControlSurface::removeMapping (ActionID id, int controllerID, int note, int channel)
1014{
1015 for (int i = mappings.size(); --i >= 0;)
1016 {
1017 if (auto m = mappings.getUnchecked (i))
1018 {
1019 if (m->function == id && m->id == controllerID
1020 && m->note == note && m->channel == channel)
1021 {
1022 removeMapping (i);
1023 return true;
1024 }
1025 }
1026 }
1027
1028 return false;
1029}
1030
1031//==============================================================================
1032void CustomControlSurface::showMappingsEditor (juce::DialogWindow::LaunchOptions& o)
1033{
1034 #if JUCE_MODAL_LOOPS_PERMITTED
1035 if (needsMidiChannel && owner->getMidiInputDevice (0).isEmpty())
1036 {
1037 engine.getUIBehaviour().showWarningAlert (TRANS("Error"),
1038 TRANS("You must set a MIDI input device!"));
1039 return;
1040 }
1041
1042 listenToRow (-1);
1043 o.runModal();
1044 setLearntParam (false);
1045 listenToRow (-1);
1046
1047 manager->saveAllSettings (engine);
1048 #else
1050 #endif
1051}
1052
1053int CustomControlSurface::getNumMappings() const
1054{
1055 return mappings.size();
1056}
1057
1058void CustomControlSurface::listenToRow (int row)
1059{
1060 listeningOnRow = row;
1061 lastControllerValue = 0;
1062 lastControllerID = 0;
1063 lastControllerNote = -1;
1064 lastControllerChannel = 0;
1065
1066 sendChangeMessage();
1067}
1068
1069int CustomControlSurface::getRowBeingListenedTo() const
1070{
1071 return listeningOnRow;
1072}
1073
1074void CustomControlSurface::showMappingsListForRow (int row)
1075{
1076 #if JUCE_MODAL_LOOPS_PERMITTED
1077 int r = 0;
1079 if (auto underMouse = mouse.getComponentUnderMouse())
1080 {
1081 auto pt = mouse.getScreenPosition();
1082 auto opts = juce::PopupMenu::Options().withTargetComponent (underMouse)
1083 .withTargetScreenArea ({ int (pt.x), int (pt.y), 1, 1 });
1084 r = contextMenu.showMenu (opts);
1085 }
1086 else
1087 {
1088 r = contextMenu.show();
1089 }
1090 #else
1091 int r = 0;
1092 #endif
1093
1094 auto cg = commandGroups.find (r);
1095
1096 if (cg != commandGroups.end())
1097 {
1098 auto set = cg->second;
1099
1100 for (int i = 0; i < set->size(); ++i)
1101 {
1102 if (row >= mappings.size())
1103 mappings.add (new Mapping());
1104
1105 mappings[row]->function = (*set)[i];
1106 row++;
1107 }
1108
1109 sendChangeMessage();
1110 }
1111 else if (r != 0)
1112 {
1113 if (row >= mappings.size())
1114 mappings.add (new Mapping());
1115
1116 mappings[row]->function = r;
1117
1118 sendChangeMessage();
1119 }
1120}
1121
1122CustomControlSurface::Mapping* CustomControlSurface::getMappingForRow (int row) const
1123{
1124 return mappings[row];
1125}
1126
1127std::pair<juce::String, juce::String> CustomControlSurface::getTextForRow (int rowNumber) const
1128{
1129 const int numMappings = mappings.size();
1130 jassert (juce::isPositiveAndNotGreaterThan (rowNumber, numMappings));
1131
1132 auto mappingForRow = mappings[rowNumber];
1133
1134 auto getLeftText = [&] () -> juce::String
1135 {
1136 if (rowNumber == listeningOnRow)
1137 {
1138 if (lastControllerAddr.isNotEmpty())
1139 return lastControllerAddr;
1140
1141 if (lastControllerID > 0 && lastControllerNote == -1)
1142 return controllerIDToString (lastControllerID, lastControllerChannel)
1143 + ": " + juce::String (juce::roundToInt (lastControllerValue * 100.0f)) + "%";
1144
1145 if (lastControllerNote != -1 && lastControllerID == 0)
1146 return noteIDToString (lastControllerNote, lastControllerChannel);
1147
1148 if (needsOSCSocket)
1149 return "(" + TRANS("Move a controller, double click to type OSC path") + ")";
1150
1151 return "(" + TRANS("Move a controller") + ")";
1152 }
1153
1154 if (rowNumber < numMappings && mappingForRow->addr.isNotEmpty())
1155 return mappingForRow->addr;
1156
1157 if (rowNumber < numMappings && mappingForRow->id != 0)
1158 return controllerIDToString (mappingForRow->id, mappingForRow->channel);
1159
1160 if (rowNumber < numMappings && mappingForRow->note != -1)
1161 return noteIDToString (mappingForRow->note, mappingForRow->channel);
1162
1163 if (rowNumber < numMappings && needsOSCSocket)
1164 return TRANS("Click here to choose a controller, double click to type OSC path");
1165
1166 return TRANS("Click here to choose a controller");
1167 };
1168
1169 return { getLeftText(), mappingForRow != nullptr ? getFunctionName (mappingForRow->function) : juce::String() };
1170}
1171
1172juce::String CustomControlSurface::noteIDToString (int note, int channelid) const
1173{
1174 auto text = TRANS("Note On") + " "
1175 + juce::MidiMessage::getMidiNoteName (note, true, true,
1176 engine.getEngineBehaviour().getMiddleCOctave());
1177
1178 auto channel = juce::String::formatted (" [%d]", channelid);
1179
1180 return text + channel;
1181}
1182
1183juce::String CustomControlSurface::controllerIDToString (int id, int channelid) const
1184{
1185 auto channel = juce::String::formatted (" [%d]", channelid);
1186
1187 if (id >= 0x40000)
1188 return TRANS("Channel Pressure Controller") + channel;
1189
1190 if (id >= 0x30000)
1191 return "RPN #" + juce::String (id & 0x7fff) + channel;
1192
1193 if (id >= 0x20000)
1194 return "NRPN #" + juce::String (id & 0x7fff) + channel;
1195
1196 if (id >= 0x10000)
1197 {
1198 auto name = TRANS(juce::MidiMessage::getControllerName (id & 0x7f));
1199
1200 if (name.isNotEmpty())
1201 name = " (" + name + ")";
1202
1203 return TRANS("Controller") + " #" + juce::String (id & 0x7f) + name + channel;
1204 }
1205
1206 return {};
1207}
1208
1209juce::String CustomControlSurface::getFunctionName (int id) const
1210{
1211 for (int i = 0; i < actionFunctionList.size(); ++i)
1212 if (actionFunctionList.getUnchecked (i)->id == id)
1213 return actionFunctionList.getUnchecked (i)->name;
1214
1215 return {};
1216}
1217
1218void CustomControlSurface::setLearntParam (bool keepListening)
1219{
1220 if (listeningOnRow >= 0)
1221 {
1222 if (lastControllerID > 0 || lastControllerNote != -1 || lastControllerAddr.isNotEmpty())
1223 {
1224 if (listeningOnRow >= mappings.size())
1225 mappings.add (new Mapping());
1226
1227 mappings[listeningOnRow]->id = lastControllerID;
1228 mappings[listeningOnRow]->addr = lastControllerAddr;
1229 mappings[listeningOnRow]->note = lastControllerNote;
1230 mappings[listeningOnRow]->channel = lastControllerChannel;
1231 }
1232
1233 if (! keepListening)
1234 {
1235 listeningOnRow = -1;
1236 lastControllerID = 0;
1237 lastControllerAddr = {};
1238 lastControllerNote = -1;
1239 lastControllerChannel = 0;
1240 }
1241
1242 sendChangeMessage();
1243 }
1244}
1245
1246void CustomControlSurface::removeMapping (int index)
1247{
1248 mappings.remove (index);
1249}
1250
1251void CustomControlSurface::handleAsyncUpdate()
1252{
1254 sendChangeMessage();
1255
1256 if (listeningOnRow >= 0 && listeningOnRow == mappings.size())
1257 setLearntParam (true);
1258}
1259
1260void CustomControlSurface::loadFunctions()
1261{
1263
1264 clearCommandGroups();
1265 contextMenu.clear();
1266 actionFunctionList.clear();
1267
1268
1269 juce::PopupMenu userFunctionSubMenu;
1270 auto userFunctionSubMenuSet = new juce::SortedSet<int>();
1271 addAllCommandItem (userFunctionSubMenu);
1272 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 1"), userAction1Id, &CustomControlSurface::userFunction1);
1273 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 2"), userAction2Id, &CustomControlSurface::userFunction2);
1274 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 3"), userAction3Id, &CustomControlSurface::userFunction3);
1275 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 4"), userAction4Id, &CustomControlSurface::userFunction4);
1276 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 5"), userAction5Id, &CustomControlSurface::userFunction5);
1277 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 6"), userAction6Id, &CustomControlSurface::userFunction6);
1278 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 7"), userAction7Id, &CustomControlSurface::userFunction7);
1279 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 8"), userAction8Id, &CustomControlSurface::userFunction8);
1280 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 9"), userAction9Id, &CustomControlSurface::userFunction9);
1281 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 10"), userAction10Id, &CustomControlSurface::userFunction10);
1282 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 11"), userAction11Id, &CustomControlSurface::userFunction11);
1283 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 12"), userAction12Id, &CustomControlSurface::userFunction12);
1284 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 13"), userAction13Id, &CustomControlSurface::userFunction13);
1285 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 14"), userAction14Id, &CustomControlSurface::userFunction14);
1286 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 15"), userAction15Id, &CustomControlSurface::userFunction15);
1287 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 16"), userAction16Id, &CustomControlSurface::userFunction16);
1288 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 17"), userAction17Id, &CustomControlSurface::userFunction17);
1289 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 18"), userAction18Id, &CustomControlSurface::userFunction18);
1290 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 19"), userAction19Id, &CustomControlSurface::userFunction19);
1291 addFunction (userFunctionSubMenu, *userFunctionSubMenuSet, TRANS("User Function"), TRANS("User Function 20"), userAction20Id, &CustomControlSurface::userFunction20);
1292
1293 commandGroups [nextCmdGroupIndex++] = userFunctionSubMenuSet;
1294
1295 juce::PopupMenu transportSubMenu;
1296 auto transportSubMenuSet = new juce::SortedSet<int>();
1297 addAllCommandItem (transportSubMenu);
1298 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Play"), playId, &CustomControlSurface::play);
1299 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Stop"), stopId, &CustomControlSurface::stop);
1300 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Record"), recordId, &CustomControlSurface::record);
1301 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Home"), homeId, &CustomControlSurface::home);
1302 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("End"), endId, &CustomControlSurface::end);
1303 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Rewind"), rewindId, &CustomControlSurface::rewind);
1304 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Fast Forward"), fastForwardId, &CustomControlSurface::fastForward);
1305 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Mark-In"), markInId, &CustomControlSurface::markIn);
1306 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Mark-Out"), markOutId, &CustomControlSurface::markOut);
1307 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Automation Read"), automationReadId, &CustomControlSurface::automationReading);
1308 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Automation Record"), automationRecordId, &CustomControlSurface::automationWriting);
1309 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Add Marker"), addMarkerId, &CustomControlSurface::addMarker);
1310 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Next Marker"), nextMarkerId, &CustomControlSurface::nextMarker);
1311 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Previous Marker"), previousMarkerId, &CustomControlSurface::prevMarker);
1312 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Marker 1"), marker1Id, &CustomControlSurface::marker1);
1313 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Marker 2"), marker2Id, &CustomControlSurface::marker2);
1314 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Marker 3"), marker3Id, &CustomControlSurface::marker3);
1315 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Marker 4"), marker4Id, &CustomControlSurface::marker4);
1316 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Marker 5"), marker5Id, &CustomControlSurface::marker5);
1317 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Marker 6"), marker6Id, &CustomControlSurface::marker6);
1318 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Marker 7"), marker7Id, &CustomControlSurface::marker7);
1319 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Marker 8"), marker8Id, &CustomControlSurface::marker8);
1320 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Nudge Left"), nudgeLeftId, &CustomControlSurface::nudgeLeft);
1321 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Nudge Right"), nudgeRightId, &CustomControlSurface::nudgeRight);
1322 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Abort"), abortId, &CustomControlSurface::abort);
1323 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Abort & Restart"), abortRestartId, &CustomControlSurface::abortRestart);
1324 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Jog"), jogId, &CustomControlSurface::jog);
1325 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Jump to the Mark-In Point"), jumpToMarkInId, &CustomControlSurface::jumpToMarkIn);
1326 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Jump to the Mark-Out Point"), jumpToMarkOutId, &CustomControlSurface::jumpToMarkOut);
1327 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Timecode"), timecodeId, &CustomControlSurface::null);
1328 addFunction (transportSubMenu, *transportSubMenuSet, TRANS("Transport"), TRANS("Clear all solos"), clearAllSoloId, &CustomControlSurface::clearAllSolo);
1329 commandGroups [nextCmdGroupIndex++] = transportSubMenuSet;
1330
1331 juce::PopupMenu optionsSubMenu;
1332 auto optionsSubMenuSet = new juce::SortedSet<int>();
1333 addAllCommandItem (optionsSubMenu);
1334 addFunction (optionsSubMenu, *optionsSubMenuSet, TRANS("Options"), TRANS("Toggle beats/seconds mode"), toggleBeatsSecondsModeId, &CustomControlSurface::toggleBeatsSecondsMode);
1335 addFunction (optionsSubMenu, *optionsSubMenuSet, TRANS("Options"), TRANS("Toggle loop"), toggleLoopId, &CustomControlSurface::toggleLoop);
1336 addFunction (optionsSubMenu, *optionsSubMenuSet, TRANS("Options"), TRANS("Toggle punch"), togglePunchId, &CustomControlSurface::togglePunch);
1337 addFunction (optionsSubMenu, *optionsSubMenuSet, TRANS("Options"), TRANS("Toggle click"), toggleClickId, &CustomControlSurface::toggleClick);
1338 addFunction (optionsSubMenu, *optionsSubMenuSet, TRANS("Options"), TRANS("Toggle snap"), toggleSnapId, &CustomControlSurface::toggleSnap);
1339 addFunction (optionsSubMenu, *optionsSubMenuSet, TRANS("Options"), TRANS("Toggle slave"), toggleSlaveId, &CustomControlSurface::toggleSlave);
1340 addFunction (optionsSubMenu, *optionsSubMenuSet, TRANS("Options"), TRANS("Toggle E-to-E"), toggleEtoEId, &CustomControlSurface::toggleEtoE);
1341 addFunction (optionsSubMenu, *optionsSubMenuSet, TRANS("Options"), TRANS("Toggle scroll"), toggleScrollId, &CustomControlSurface::toggleScroll);
1342 addFunction (optionsSubMenu, *optionsSubMenuSet, TRANS("Options"), TRANS("Toggle all arm"), toggleAllArmId, &CustomControlSurface::toggleAllArm);
1343 addFunction (optionsSubMenu, *optionsSubMenuSet, TRANS("Options"), TRANS("Empty Text"), emptyTextId, &CustomControlSurface::null);
1344 commandGroups [nextCmdGroupIndex++] = optionsSubMenuSet;
1345
1346 juce::PopupMenu pluginSubMenu;
1347 auto pluginSubMenuSet = new juce::SortedSet<int>();
1348 addAllCommandItem (pluginSubMenu);
1349 addFunction (pluginSubMenu, *pluginSubMenuSet, TRANS("Plugin"), TRANS("Master volume"), masterVolumeId, &CustomControlSurface::masterVolume);
1350 addFunction (pluginSubMenu, *pluginSubMenuSet, TRANS("Plugin"), TRANS("Master volume text"), masterVolumeTextId, &CustomControlSurface::null);
1351 addFunction (pluginSubMenu, *pluginSubMenuSet, TRANS("Plugin"), TRANS("Master pan"), masterPanId, &CustomControlSurface::masterPan);
1352 addFunction (pluginSubMenu, *pluginSubMenuSet, TRANS("Plugin"), TRANS("Quick control parameter"), quickParamId, &CustomControlSurface::quickParam);
1353 commandGroups [nextCmdGroupIndex++] = pluginSubMenuSet;
1354 addPluginFunction (pluginSubMenu, TRANS("Plugin"), TRANS("Automatable parameters"), paramTrackId, &CustomControlSurface::paramTrack);
1355 addPluginFunction (pluginSubMenu, TRANS("Plugin"), TRANS("Automatable parameter name"), paramNameTrackId, &CustomControlSurface::paramTrack);
1356 addPluginFunction (pluginSubMenu, TRANS("Plugin"), TRANS("Automatable parameter text"), paramTextTrackId, &CustomControlSurface::paramTrack);
1357
1358 juce::PopupMenu trackSubMenu;
1359 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Name"), nameTrackId, &CustomControlSurface::null);
1360 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Number text"), numberTrackId, &CustomControlSurface::null);
1361 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Volume"), volTrackId, &CustomControlSurface::volTrack);
1362 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Volume text"), volTextTrackId, &CustomControlSurface::null);
1363 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Pan"), panTrackId, &CustomControlSurface::panTrack);
1364 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Pan Text"), panTextTrackId, &CustomControlSurface::null);
1365 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Mute"), muteTrackId, &CustomControlSurface::muteTrack);
1366 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Solo"), soloTrackId, &CustomControlSurface::soloTrack);
1367 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Arm"), armTrackId, &CustomControlSurface::armTrack);
1368 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Select"), selectTrackId, &CustomControlSurface::selectTrack);
1369 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Aux"), auxTrackId, &CustomControlSurface::auxTrack);
1370 addTrackFunction (trackSubMenu, TRANS("Track"), TRANS("Aux Text"), auxTextTrackId, &CustomControlSurface::null);
1371
1372 juce::PopupMenu clipSubMenu;
1373 juce::PopupMenu clipBankSubMenu;
1374
1375 if (engine.getEngineBehaviour().areClipSlotsEnabled())
1376 {
1377 addTrackFunction (clipSubMenu, TRANS("Clip Launcher"), TRANS("Launch Clip #1"), clip1TrackId, &CustomControlSurface::launchClip1);
1378 addTrackFunction (clipSubMenu, TRANS("Clip Launcher"), TRANS("Launch Clip #2"), clip2TrackId, &CustomControlSurface::launchClip2);
1379 addTrackFunction (clipSubMenu, TRANS("Clip Launcher"), TRANS("Launch Clip #3"), clip3TrackId, &CustomControlSurface::launchClip3);
1380 addTrackFunction (clipSubMenu, TRANS("Clip Launcher"), TRANS("Launch Clip #4"), clip4TrackId, &CustomControlSurface::launchClip4);
1381 addTrackFunction (clipSubMenu, TRANS("Clip Launcher"), TRANS("Launch Clip #5"), clip5TrackId, &CustomControlSurface::launchClip5);
1382 addTrackFunction (clipSubMenu, TRANS("Clip Launcher"), TRANS("Launch Clip #6"), clip6TrackId, &CustomControlSurface::launchClip6);
1383 addTrackFunction (clipSubMenu, TRANS("Clip Launcher"), TRANS("Launch Clip #7"), clip7TrackId, &CustomControlSurface::launchClip7);
1384 addTrackFunction (clipSubMenu, TRANS("Clip Launcher"), TRANS("Launch Clip #8"), clip8TrackId, &CustomControlSurface::launchClip8);
1385 addTrackFunction (clipSubMenu, TRANS("Clip Launcher"), TRANS("Stop Clips"), stopClipsTrackId, &CustomControlSurface::stopClips);
1386 addSceneFunction (clipSubMenu, TRANS("Clip Launcher"), TRANS("Launch Scene"), sceneId, &CustomControlSurface::launchScene);
1387
1388 auto clipBankSubMenuSet = new juce::SortedSet<int>();
1389 addAllCommandItem (clipBankSubMenu);
1390 addFunction (clipBankSubMenu, *clipBankSubMenuSet, TRANS("Switch Clip Launch bank"), TRANS("Up") + " 1", clipBankUp1Id, &CustomControlSurface::clipBankUp1);
1391 addFunction (clipBankSubMenu, *clipBankSubMenuSet, TRANS("Switch Clip Launch bank"), TRANS("Up") + " 4", clipBankUp4Id, &CustomControlSurface::clipBankUp4);
1392 addFunction (clipBankSubMenu, *clipBankSubMenuSet, TRANS("Switch Clip Launch bank"), TRANS("Up") + " 8", clipBankUp8Id, &CustomControlSurface::clipBankUp8);
1393 addFunction (clipBankSubMenu, *clipBankSubMenuSet, TRANS("Switch Clip Launch bank"), TRANS("Down") + " 1", clipBankDown1Id, &CustomControlSurface::clipBankDown1);
1394 addFunction (clipBankSubMenu, *clipBankSubMenuSet, TRANS("Switch Clip Launch bank"), TRANS("Down") + " 4", clipBankDown4Id, &CustomControlSurface::clipBankDown4);
1395 addFunction (clipBankSubMenu, *clipBankSubMenuSet, TRANS("Switch Clip Launch bank"), TRANS("Down") + " 8", clipBankDown8Id, &CustomControlSurface::clipBankDown8);
1396 commandGroups [nextCmdGroupIndex++] = clipBankSubMenuSet;
1397 }
1398
1399 juce::PopupMenu navigationSubMenu;
1400 auto navigationSubMenuSet = new juce::SortedSet<int>();
1401 addAllCommandItem (navigationSubMenu);
1402 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Zoom in"), zoomInId, &CustomControlSurface::zoomIn);
1403 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Zoom out"), zoomOutId, &CustomControlSurface::zoomOut);
1404 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Scroll tracks up"), scrollTracksUpId, &CustomControlSurface::scrollTracksUp);
1405 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Scroll tracks down"), scrollTracksDownId, &CustomControlSurface::scrollTracksDown);
1406 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Scroll tracks left"), scrollTracksLeftId, &CustomControlSurface::scrollTracksLeft);
1407 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Scroll tracks right"), scrollTracksRightId, &CustomControlSurface::scrollTracksRight);
1408 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Zoom tracks in"), zoomTracksInId, &CustomControlSurface::zoomTracksIn);
1409 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Zoom tracks out"), zoomTracksOutId, &CustomControlSurface::zoomTracksOut);
1410 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Toggle selection mode"), toggleSelectionModeId, &CustomControlSurface::toggleSelectionMode);
1411 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Select left"), selectLeftId, &CustomControlSurface::selectLeft);
1412 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Select right"), selectRightId, &CustomControlSurface::selectRight);
1413 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Select up"), selectUpId, &CustomControlSurface::selectUp);
1414 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Select down"), selectDownId, &CustomControlSurface::selectDown);
1415 addFunction (navigationSubMenu, *navigationSubMenuSet, TRANS("Navigation"), TRANS("Selected plugin name"), selectedPluginNameId, &CustomControlSurface::null);
1416
1417 commandGroups [nextCmdGroupIndex++] = navigationSubMenuSet;
1418 addTrackFunction (navigationSubMenu, TRANS("Navigation"), TRANS("Select clip in track"), selectClipInTrackId, &CustomControlSurface::selectClipInTrack);
1419 addTrackFunction (navigationSubMenu, TRANS("Navigation"), TRANS("Select plugin in track"), selectPluginInTrackId, &CustomControlSurface::selectFilterInTrack);
1420
1421 juce::PopupMenu bankSubMenu;
1422 auto bankSubMenuSet = new juce::SortedSet<int>();
1423 addAllCommandItem (bankSubMenu);
1424 addFunction (bankSubMenu, *bankSubMenuSet, TRANS("Switch fader bank"), TRANS("Left"), faderBankLeftId, &CustomControlSurface::faderBankLeft);
1425 addFunction (bankSubMenu, *bankSubMenuSet, TRANS("Switch fader bank"), TRANS("Left") + " 1", faderBankLeft1Id, &CustomControlSurface::faderBankLeft1);
1426 addFunction (bankSubMenu, *bankSubMenuSet, TRANS("Switch fader bank"), TRANS("Left") + " 4", faderBankLeft4Id, &CustomControlSurface::faderBankLeft4);
1427 addFunction (bankSubMenu, *bankSubMenuSet, TRANS("Switch fader bank"), TRANS("Left") + " 8", faderBankLeft8Id, &CustomControlSurface::faderBankLeft8);
1428 addFunction (bankSubMenu, *bankSubMenuSet, TRANS("Switch fader bank"), TRANS("Left") + " 16", faderBankLeft16Id, &CustomControlSurface::faderBankLeft16);
1429 addFunction (bankSubMenu, *bankSubMenuSet, TRANS("Switch fader bank"), TRANS("Right"), faderBankRightId, &CustomControlSurface::faderBankRight);
1430 addFunction (bankSubMenu, *bankSubMenuSet, TRANS("Switch fader bank"), TRANS("Right") + " 1", faderBankRight1Id, &CustomControlSurface::faderBankRight1);
1431 addFunction (bankSubMenu, *bankSubMenuSet, TRANS("Switch fader bank"), TRANS("Right") + " 4", faderBankRight4Id, &CustomControlSurface::faderBankRight4);
1432 addFunction (bankSubMenu, *bankSubMenuSet, TRANS("Switch fader bank"), TRANS("Right") + " 8", faderBankRight8Id, &CustomControlSurface::faderBankRight8);
1433 addFunction (bankSubMenu, *bankSubMenuSet, TRANS("Switch fader bank"), TRANS("Right") + " 16", faderBankRight16Id, &CustomControlSurface::faderBankRight16);
1434 commandGroups [nextCmdGroupIndex++] = bankSubMenuSet;
1435
1436 juce::PopupMenu paramBankSubMenu;
1437 auto paramBankSubMenuSet = new juce::SortedSet<int>();
1438 addAllCommandItem (paramBankSubMenu);
1439 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Left"), paramBankLeftId, &CustomControlSurface::paramBankLeft);
1440 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Left") + " 1", paramBankLeft1Id, &CustomControlSurface::paramBankLeft1);
1441 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Left") + " 4", paramBankLeft4Id, &CustomControlSurface::paramBankLeft4);
1442 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Left") + " 8", paramBankLeft8Id, &CustomControlSurface::paramBankLeft8);
1443 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Left") + " 16", paramBankLeft16Id, &CustomControlSurface::paramBankLeft16);
1444 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Left") + " 24", paramBankLeft24Id, &CustomControlSurface::paramBankLeft24);
1445 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Right"), paramBankRightId, &CustomControlSurface::paramBankRight);
1446 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Right") + " 1", paramBankRight1Id, &CustomControlSurface::paramBankRight1);
1447 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Right") + " 4", paramBankRight4Id, &CustomControlSurface::paramBankRight4);
1448 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Right") + " 8", paramBankRight8Id, &CustomControlSurface::paramBankRight8);
1449 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Right") + " 16", paramBankRight16Id, &CustomControlSurface::paramBankRight16);
1450 addFunction (paramBankSubMenu, *paramBankSubMenuSet, TRANS("Switch param bank"), TRANS("Right") + " 24", paramBankRight24Id, &CustomControlSurface::paramBankRight24);
1451 commandGroups [nextCmdGroupIndex++] = paramBankSubMenuSet;
1452
1453 contextMenu.addSubMenu (TRANS("User Functions"), userFunctionSubMenu);
1454 contextMenu.addSubMenu (TRANS("Transport"), transportSubMenu);
1455 contextMenu.addSubMenu (TRANS("Options"), optionsSubMenu);
1456 contextMenu.addSubMenu (TRANS("Plugin"), pluginSubMenu);
1457 contextMenu.addSubMenu (TRANS("Track"), trackSubMenu);
1458
1459 if (engine.getEngineBehaviour().areClipSlotsEnabled())
1460 {
1461 contextMenu.addSubMenu (TRANS("Clip Launcher"), clipSubMenu);
1462 contextMenu.addSubMenu (TRANS("Clip Launcher Bank"),clipBankSubMenu);
1463 }
1464
1465 contextMenu.addSubMenu (TRANS("Navigation"), navigationSubMenu);
1466 contextMenu.addSubMenu (TRANS("Switch fader bank"), bankSubMenu);
1467 contextMenu.addSubMenu (TRANS("Switch param bank"), paramBankSubMenu);
1468
1469 #if JUCE_DEBUG
1470 // check for duplicate ids that some sloppy programmer put in
1471 for (int i = 0; i < actionFunctionList.size(); ++i)
1472 {
1474
1475 if (set.contains (actionFunctionList[i]->id))
1477
1478 set.add (actionFunctionList[i]->id);
1479 }
1480 #endif
1481}
1482
1483void CustomControlSurface::addAllCommandItem (juce::PopupMenu& menu)
1484{
1485 menu.addItem (nextCmdGroupIndex, TRANS("Add all commands"));
1486 menu.addSeparator();
1487}
1488
1489void CustomControlSurface::addFunction (juce::PopupMenu& menu, juce::SortedSet<int>& commandSet,
1490 const juce::String& group, const juce::String& name,
1491 ActionID aid, ActionFunction actionFunc)
1492{
1493 if (isTextAction (aid) && ! needsOSCSocket)
1494 return;
1495
1496 int id = (int) aid;
1497
1498 ActionFunctionInfo* afi = new ActionFunctionInfo();
1499
1500 afi->name = name;
1501 afi->group = group;
1502 afi->id = id;
1503 afi->actionFunc = actionFunc;
1504 afi->param = 0;
1505
1506 actionFunctionList.add(afi);
1507 menu.addItem (afi->id, afi->name);
1508 commandSet.add (afi->id);
1509}
1510
1511void CustomControlSurface::addPluginFunction (juce::PopupMenu& menu,
1512 const juce::String& group, const juce::String& name,
1513 ActionID aid, ActionFunction actionFunc)
1514{
1515 if (isTextAction (aid) && ! needsOSCSocket)
1516 return;
1517
1518 int id = (int) aid;
1519
1520 juce::PopupMenu subMenu;
1521 addAllCommandItem (subMenu);
1522
1523 auto subMenuSet = new juce::SortedSet<int>();
1524
1525 for (int i = 0; i < numParameterControls; ++i)
1526 {
1527 ActionFunctionInfo* afi = new ActionFunctionInfo();
1528
1529 afi->name = name + " " + TRANS("Parameter") + " #" + juce::String (i + 1);
1530 afi->group = group;
1531 afi->id = id + i;
1532 afi->actionFunc = actionFunc;
1533 afi->param = i;
1534
1535 actionFunctionList.add(afi);
1536 subMenu.addItem (afi->id, TRANS("Parameter") + " #" + juce::String (i + 1));
1537 subMenuSet->add(afi->id);
1538 }
1539
1540 menu.addSubMenu(name, subMenu);
1541 commandGroups[nextCmdGroupIndex++] = subMenuSet;
1542}
1543
1544void CustomControlSurface::addTrackFunction (juce::PopupMenu& menu,
1545 const juce::String& group, const juce::String& name,
1546 ActionID aid, ActionFunction actionFunc)
1547{
1548 if (isTextAction (aid) && ! needsOSCSocket)
1549 return;
1550
1551 int id = (int) aid;
1552
1553 juce::PopupMenu subMenu;
1554 addAllCommandItem (subMenu);
1555
1556 auto subMenuSet = new juce::SortedSet<int>();
1557
1558 for (int i = 0; i < numberOfFaderChannels; ++i)
1559 {
1560 ActionFunctionInfo* afi = new ActionFunctionInfo();
1561
1562 afi->name = name + " " + TRANS("Track") + " #" + juce::String (i + 1);
1563 afi->group = group;
1564 afi->id = id + i;
1565 afi->actionFunc = actionFunc;
1566 afi->param = i;
1567
1568 actionFunctionList.add (afi);
1569 subMenu.addItem (afi->id, TRANS("Track") + " #" + juce::String (i + 1));
1570 subMenuSet->add (afi->id);
1571 }
1572
1573 menu.addSubMenu (name, subMenu);
1574 commandGroups[nextCmdGroupIndex++] = subMenuSet;
1575}
1576
1577void CustomControlSurface::addSceneFunction (juce::PopupMenu& menu,
1578 const juce::String& group, const juce::String& name,
1579 ActionID aid, ActionFunction actionFunc)
1580{
1581 if (isTextAction (aid) && ! needsOSCSocket)
1582 return;
1583
1584 int id = (int) aid;
1585
1586 juce::PopupMenu subMenu;
1587 addAllCommandItem (subMenu);
1588
1589 auto subMenuSet = new juce::SortedSet<int>();
1590
1591 for (int i = 0; i < 8; ++i)
1592 {
1593 ActionFunctionInfo* afi = new ActionFunctionInfo();
1594
1595 afi->name = name + " " + TRANS("Scene") + " #" + juce::String (i + 1);
1596 afi->group = group;
1597 afi->id = id + i;
1598 afi->actionFunc = actionFunc;
1599 afi->param = i;
1600
1601 actionFunctionList.add (afi);
1602 subMenu.addItem (afi->id, TRANS("Scene") + " #" + juce::String (i + 1));
1603 subMenuSet->add (afi->id);
1604 }
1605
1606 menu.addSubMenu (name, subMenu);
1607 commandGroups[nextCmdGroupIndex++] = subMenuSet;
1608}
1609
1610bool CustomControlSurface::shouldActOnValue (float val)
1611{
1612 if (needsOSCSocket)
1613 {
1614 // If the control doesn't track touches, try and debounce via time
1615 if (oscControlTouched.find (oscActiveAddr) == oscControlTouched.end())
1616 {
1617 double now = juce::Time::getMillisecondCounterHiRes() / 1000.0;
1618 double lastUsed = oscLastUsedTime[oscActiveAddr];
1619
1620 if (now - lastUsed > 0.75)
1621 {
1622 oscLastUsedTime[oscActiveAddr] = now;
1623 return true;
1624 }
1625 return false;
1626 }
1627
1628 // Otherwise, only act on press
1629 if (oscControlTapsWhileTouched[oscActiveAddr] == 1)
1630 return true;
1631
1632 return false;
1633 }
1634
1635 return val > 0.001f;
1636}
1637
1638static bool isValueNonZero (float val) noexcept
1639{
1640 return val > 0.001f;
1641}
1642
1643void CustomControlSurface::play (float val, int) { if (shouldActOnValue (val)) userPressedPlay(); }
1644void CustomControlSurface::stop (float val, int) { if (shouldActOnValue (val)) userPressedStop(); }
1645void CustomControlSurface::record (float val, int) { if (shouldActOnValue (val)) userPressedRecord(); }
1646void CustomControlSurface::home (float val, int) { if (shouldActOnValue (val)) userPressedHome(); }
1647void CustomControlSurface::end (float val, int) { if (shouldActOnValue (val)) userPressedEnd(); }
1648
1649void CustomControlSurface::rewind (float val, int) { userChangedRewindButton (isValueNonZero (val)); }
1650void CustomControlSurface::fastForward (float val, int) { userChangedFastForwardButton (isValueNonZero (val)); }
1651
1652void CustomControlSurface::masterVolume (float val, int) { userMovedMasterLevelFader (val); }
1653void CustomControlSurface::masterPan (float val, int) { userMovedMasterPanPot (val * 2.0f - 1.0f); }
1654
1655void CustomControlSurface::quickParam (float val, int) { userMovedQuickParam (val); }
1656void CustomControlSurface::volTrack (float val, int param) { userMovedFader (param, val); }
1657
1658void CustomControlSurface::panTrack (float val, int param) { userMovedPanPot (param, val * 2.0f - 1.0f); }
1659void CustomControlSurface::muteTrack (float val, int param) { if (shouldActOnValue (val)) userPressedMute (param, false); }
1660void CustomControlSurface::soloTrack (float val, int param) { if (shouldActOnValue (val)) userPressedSolo (param); }
1661
1662void CustomControlSurface::armTrack (float val, int param) { if (shouldActOnValue (val)) userPressedRecEnable (param, false); }
1663void CustomControlSurface::selectTrack (float val, int param) { if (shouldActOnValue (val)) userSelectedTrack (param); }
1664
1665void CustomControlSurface::auxTrack (float val, int param) { userMovedAux (param, 0, val); }
1666void CustomControlSurface::selectClipInTrack (float val, int param) { if (shouldActOnValue (val)) userSelectedClipInTrack (param); }
1667void CustomControlSurface::selectFilterInTrack (float val, int param) { if (shouldActOnValue (val)) userSelectedPluginInTrack (param); }
1668
1669void CustomControlSurface::launchClip1 (float val, int param) { if (shouldActOnValue (val)) userLaunchedClip (param, 0); }
1670void CustomControlSurface::launchClip2 (float val, int param) { if (shouldActOnValue (val)) userLaunchedClip (param, 1); }
1671void CustomControlSurface::launchClip3 (float val, int param) { if (shouldActOnValue (val)) userLaunchedClip (param, 2); }
1672void CustomControlSurface::launchClip4 (float val, int param) { if (shouldActOnValue (val)) userLaunchedClip (param, 3); }
1673void CustomControlSurface::launchClip5 (float val, int param) { if (shouldActOnValue (val)) userLaunchedClip (param, 4); }
1674void CustomControlSurface::launchClip6 (float val, int param) { if (shouldActOnValue (val)) userLaunchedClip (param, 5); }
1675void CustomControlSurface::launchClip7 (float val, int param) { if (shouldActOnValue (val)) userLaunchedClip (param, 6); }
1676void CustomControlSurface::launchClip8 (float val, int param) { if (shouldActOnValue (val)) userLaunchedClip (param, 7); }
1677void CustomControlSurface::stopClips (float val, int param) { if (shouldActOnValue (val)) userStoppedClip (param); }
1678void CustomControlSurface::launchScene (float val, int param) { if (shouldActOnValue (val)) userLaunchedScene (param); }
1679void CustomControlSurface::clipBankUp1 (float val, int) { if (shouldActOnValue (val)) userChangedPadBanks (-1); }
1680void CustomControlSurface::clipBankUp4 (float val, int) { if (shouldActOnValue (val)) userChangedPadBanks (-4); }
1681void CustomControlSurface::clipBankUp8 (float val, int) { if (shouldActOnValue (val)) userChangedPadBanks (-8); }
1682void CustomControlSurface::clipBankDown1 (float val, int) { if (shouldActOnValue (val)) userChangedPadBanks (1); }
1683void CustomControlSurface::clipBankDown4 (float val, int) { if (shouldActOnValue (val)) userChangedPadBanks (4); }
1684void CustomControlSurface::clipBankDown8 (float val, int) { if (shouldActOnValue (val)) userChangedPadBanks (8); }
1685
1686void CustomControlSurface::markIn (float val, int) { if (shouldActOnValue (val)) userPressedMarkIn(); }
1687void CustomControlSurface::markOut (float val, int) { if (shouldActOnValue (val)) userPressedMarkOut(); }
1688void CustomControlSurface::automationReading (float val, int) { if (shouldActOnValue (val)) userPressedAutomationReading(); }
1689void CustomControlSurface::automationWriting (float val, int) { if (shouldActOnValue (val)) userPressedAutomationWriting(); }
1690void CustomControlSurface::toggleBeatsSecondsMode (float val, int) { if (shouldActOnValue (val)) userToggledBeatsSecondsMode(); }
1691void CustomControlSurface::toggleLoop (float val, int) { if (shouldActOnValue (val)) userToggledLoopOnOff(); }
1692void CustomControlSurface::togglePunch (float val, int) { if (shouldActOnValue (val)) userToggledPunchOnOff(); }
1693void CustomControlSurface::toggleClick (float val, int) { if (shouldActOnValue (val)) userToggledClickOnOff(); }
1694void CustomControlSurface::toggleSnap (float val, int) { if (shouldActOnValue (val)) userToggledSnapOnOff(); }
1695void CustomControlSurface::toggleSlave (float val, int) { if (shouldActOnValue (val)) userToggledSlaveOnOff(); }
1696void CustomControlSurface::toggleEtoE (float val, int) { if (shouldActOnValue (val)) userToggledEtoE(); }
1697void CustomControlSurface::toggleScroll (float val, int) { if (shouldActOnValue (val)) userToggledScroll(); }
1698void CustomControlSurface::toggleAllArm (float val, int) { if (shouldActOnValue (val)) userPressedArmAll(); }
1699void CustomControlSurface::zoomIn (float val, int) { if (shouldActOnValue (val)) userZoomedIn(); }
1700void CustomControlSurface::zoomOut (float val, int) { if (shouldActOnValue (val)) userZoomedOut(); }
1701void CustomControlSurface::scrollTracksUp (float val, int) { if (shouldActOnValue (val)) userScrolledTracksUp(); }
1702void CustomControlSurface::scrollTracksDown (float val, int) { if (shouldActOnValue (val)) userScrolledTracksDown(); }
1703void CustomControlSurface::scrollTracksLeft (float val, int) { if (shouldActOnValue (val)) userScrolledTracksLeft(); }
1704void CustomControlSurface::scrollTracksRight (float val, int) { if (shouldActOnValue (val)) userScrolledTracksRight(); }
1705void CustomControlSurface::zoomTracksIn (float val, int) { if (shouldActOnValue (val)) userZoomedTracksIn(); }
1706void CustomControlSurface::zoomTracksOut (float val, int) { if (shouldActOnValue (val)) userZoomedTracksOut(); }
1707
1708void CustomControlSurface::toggleSelectionMode (float val, int) { if (shouldActOnValue (val)) pluginMoveMode = ! pluginMoveMode; }
1709
1710void CustomControlSurface::selectLeft (float val, int) { if (shouldActOnValue (val)) selectOtherObject (SelectableClass::Relationship::moveLeft, pluginMoveMode); }
1711void CustomControlSurface::selectRight (float val, int) { if (shouldActOnValue (val)) selectOtherObject (SelectableClass::Relationship::moveRight, pluginMoveMode); }
1712void CustomControlSurface::selectUp (float val, int) { if (shouldActOnValue (val)) selectOtherObject (SelectableClass::Relationship::moveUp, pluginMoveMode); }
1713void CustomControlSurface::selectDown (float val, int) { if (shouldActOnValue (val)) selectOtherObject (SelectableClass::Relationship::moveDown, pluginMoveMode); }
1714
1715void CustomControlSurface::faderBankLeft (float val, int) { if (shouldActOnValue (val)) userChangedFaderBanks (-numberOfFaderChannels); }
1716void CustomControlSurface::faderBankLeft1 (float val, int) { if (shouldActOnValue (val)) userChangedFaderBanks (-1); }
1717void CustomControlSurface::faderBankLeft4 (float val, int) { if (shouldActOnValue (val)) userChangedFaderBanks (-4); }
1718void CustomControlSurface::faderBankLeft8 (float val, int) { if (shouldActOnValue (val)) userChangedFaderBanks (-8); }
1719void CustomControlSurface::faderBankLeft16 (float val, int) { if (shouldActOnValue (val)) userChangedFaderBanks (-16); }
1720
1721void CustomControlSurface::faderBankRight (float val, int) { if (shouldActOnValue (val)) userChangedFaderBanks (numberOfFaderChannels); }
1722void CustomControlSurface::faderBankRight1 (float val, int) { if (shouldActOnValue (val)) userChangedFaderBanks (1); }
1723void CustomControlSurface::faderBankRight4 (float val, int) { if (shouldActOnValue (val)) userChangedFaderBanks (4); }
1724void CustomControlSurface::faderBankRight8 (float val, int) { if (shouldActOnValue (val)) userChangedFaderBanks (8); }
1725void CustomControlSurface::faderBankRight16 (float val, int) { if (shouldActOnValue (val)) userChangedFaderBanks (16); }
1726
1727void CustomControlSurface::paramBankLeft (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (-numParameterControls); }
1728void CustomControlSurface::paramBankLeft1 (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (-1); }
1729void CustomControlSurface::paramBankLeft4 (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (-4); }
1730void CustomControlSurface::paramBankLeft8 (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (-8); }
1731void CustomControlSurface::paramBankLeft16 (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (-16); }
1732void CustomControlSurface::paramBankLeft24 (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (-24); }
1733
1734void CustomControlSurface::paramBankRight (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (numParameterControls); }
1735void CustomControlSurface::paramBankRight1 (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (1); }
1736void CustomControlSurface::paramBankRight4 (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (4); }
1737void CustomControlSurface::paramBankRight8 (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (8); }
1738void CustomControlSurface::paramBankRight16 (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (16); }
1739void CustomControlSurface::paramBankRight24 (float val, int) { if (shouldActOnValue (val)) userChangedParameterBank (24); }
1740
1741void CustomControlSurface::userFunction1 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (0); }
1742void CustomControlSurface::userFunction2 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (1); }
1743void CustomControlSurface::userFunction3 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (2); }
1744void CustomControlSurface::userFunction4 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (3); }
1745void CustomControlSurface::userFunction5 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (4); }
1746void CustomControlSurface::userFunction6 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (5); }
1747void CustomControlSurface::userFunction7 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (6); }
1748void CustomControlSurface::userFunction8 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (7); }
1749void CustomControlSurface::userFunction9 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (8); }
1750void CustomControlSurface::userFunction10 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (9); }
1751void CustomControlSurface::userFunction11 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (10); }
1752void CustomControlSurface::userFunction12 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (11); }
1753void CustomControlSurface::userFunction13 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (12); }
1754void CustomControlSurface::userFunction14 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (13); }
1755void CustomControlSurface::userFunction15 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (14); }
1756void CustomControlSurface::userFunction16 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (15); }
1757void CustomControlSurface::userFunction17 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (16); }
1758void CustomControlSurface::userFunction18 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (17); }
1759void CustomControlSurface::userFunction19 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (18); }
1760void CustomControlSurface::userFunction20 (float val, int) { if (shouldActOnValue (val)) userPressedUserAction (19); }
1761
1762void CustomControlSurface::addMarker (float val, int)
1763{
1764 if (shouldActOnValue (val))
1765 if (auto e = getEdit())
1766 e->getMarkerManager().createMarker (-1, e->getTransport().getPosition(), {}, externalControllerManager.getSelectionManager());
1767}
1768
1769void CustomControlSurface::prevMarker (float val, int) { if (shouldActOnValue (val)) userPressedPreviousMarker(); }
1770void CustomControlSurface::nextMarker (float val, int) { if (shouldActOnValue (val)) userPressedNextMarker(); }
1771void CustomControlSurface::nudgeLeft (float val, int) { if (shouldActOnValue (val)) userNudgedLeft(); }
1772void CustomControlSurface::nudgeRight (float val, int) { if (shouldActOnValue (val)) userNudgedRight(); }
1773
1774void CustomControlSurface::marker1 (float val, int) { if (shouldActOnValue (val)) userPressedGoToMarker (0); }
1775void CustomControlSurface::marker2 (float val, int) { if (shouldActOnValue (val)) userPressedGoToMarker (1); }
1776void CustomControlSurface::marker3 (float val, int) { if (shouldActOnValue (val)) userPressedGoToMarker (2); }
1777void CustomControlSurface::marker4 (float val, int) { if (shouldActOnValue (val)) userPressedGoToMarker (3); }
1778void CustomControlSurface::marker5 (float val, int) { if (shouldActOnValue (val)) userPressedGoToMarker (4); }
1779void CustomControlSurface::marker6 (float val, int) { if (shouldActOnValue (val)) userPressedGoToMarker (5); }
1780void CustomControlSurface::marker7 (float val, int) { if (shouldActOnValue (val)) userPressedGoToMarker (6); }
1781void CustomControlSurface::marker8 (float val, int) { if (shouldActOnValue (val)) userPressedGoToMarker (7); }
1782
1783void CustomControlSurface::paramTrack (float val, int param)
1784{
1785 userMovedParameterControl (param, val);
1786}
1787
1788void CustomControlSurface::abort (float val, int) { if (shouldActOnValue (val)) userPressedAbort(); }
1789void CustomControlSurface::abortRestart (float val, int) { if (shouldActOnValue (val)) userPressedAbortRestart(); }
1790
1791void CustomControlSurface::jumpToMarkIn (float val, int) { if (shouldActOnValue (val)) userPressedJumpToMarkIn(); }
1792void CustomControlSurface::jumpToMarkOut (float val, int) { if (shouldActOnValue (val)) userPressedJumpToMarkOut(); }
1793
1794void CustomControlSurface::clearAllSolo (float val, int) { if (shouldActOnValue (val)) userPressedClearAllSolo(); }
1795
1796void CustomControlSurface::jog (float val, int)
1797{
1798 int x = juce::roundToInt (val * 127.0f);
1799
1800 if (x <= 64)
1801 userMovedJogWheel (float(x) / 2);
1802 else
1803 userMovedJogWheel (float(x - 128) / 2);
1804}
1805
1806bool CustomControlSurface::eatsAllMessages()
1807{
1808 return eatsAllMidi;
1809}
1810
1811bool CustomControlSurface::canSetEatsAllMessages()
1812{
1813 return true;
1814}
1815
1816void CustomControlSurface::setEatsAllMessages (bool eatAll)
1817{
1818 eatsAllMidi = eatAll;
1819 manager->saveAllSettings (engine);
1820}
1821
1822void CustomControlSurface::markerChanged (int, const MarkerSetting&)
1823{
1824}
1825
1826void CustomControlSurface::clearMarker (int)
1827{
1828}
1829
1830}} // namespace tracktion { inline namespace engine
void add(const ElementType &newElement)
static String toString(Type decibels, int decimalPlaces=2, Type minusInfinityDb=Type(defaultMinusInfinitydB), bool shouldIncludeSuffix=true, StringRef customMinusInfinityString={})
MouseInputSource getMainMouseSource() const noexcept
static Desktop &JUCE_CALLTYPE getInstance()
const String & toString() const noexcept
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
static MidiMessage controllerEvent(int channel, int controllerType, int value) noexcept
static uint8 floatValueToMidiByte(float valueBetween0and1) noexcept
static String getMidiNoteName(int noteNumber, bool useSharps, bool includeOctaveNumber, int octaveNumForMiddleC)
static MidiMessage noteOff(int channel, int noteNumber, float velocity) noexcept
static const char * getControllerName(int controllerNumber)
Point< float > getScreenPosition() const noexcept
Options withTargetScreenArea(Rectangle< int > targetArea) const
Options withTargetComponent(Component *targetComponent) const
void addSubMenu(String subMenuName, PopupMenu subMenu, bool isEnabled=true)
void addItem(Item newItem)
bool add(const ElementType &newElement) noexcept
bool contains(const ElementType &elementToLookFor) const noexcept
String dropLastCharacters(int numberToDrop) const
static String formatted(const String &formatStr, Args... args)
bool endsWith(StringRef text) const noexcept
bool isNotEmpty() const noexcept
static double getMillisecondCounterHiRes() noexcept
String toString(const TextFormat &format={}) const
bool getBoolAttribute(StringRef attributeName, bool defaultReturnValue=false) const
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
const String & getStringAttribute(StringRef attributeName) const noexcept
The Engine is the central class for all tracktion sessions.
sprintf
T is_pointer_v
#define TRANS(stringLiteral)
#define jassert(expression)
#define DBG(textToWrite)
#define jassertfalse
typedef int
typedef float
std::unique_ptr< XmlElement > parseXML(const String &textToParse)
void ignoreUnused(Types &&...) noexcept
bool isPositiveAndNotGreaterThan(Type1 valueToTest, Type2 upperLimit) noexcept
int roundToInt(const FloatType value) noexcept
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.