11namespace tracktion {
inline namespace engine
14ExternalController::ExternalController (
Engine& e, ControlSurface* c) : engine (e), controlSurface (c)
17 controlSurface->owner =
this;
19 auto& cs = getControlSurface();
20 auto& storage = engine.getPropertyStorage();
22 maxTrackNameChars = cs.numCharactersForTrackNames;
23 needsBackChannel = cs.needsMidiBackChannel;
24 needsChannel = cs.needsMidiChannel;
25 needsOSC = cs.needsOSCSocket;
26 wantsClock = cs.wantsClock;
27 followsTrackSelection = cs.followsTrackSelection;
28 deletable = cs.deletable;
29 auxBank = cs.wantsAuxBanks || cs.auxMode == AuxPosition::byPosition ? 0 : -1;
30 allowBankingOffEnd = cs.allowBankingOffEnd;
32 numDevices = engine.getPropertyStorage().getPropertyItem (SettingID::externControlNum,
getName(), 1);
33 mainDevice = engine.getPropertyStorage().getPropertyItem (SettingID::externControlMain,
getName(), 0);
35 inputDeviceNames.add (storage.getPropertyItem (SettingID::externControlIn,
getName()));
36 outputDeviceNames.add (storage.getPropertyItem (SettingID::externControlOut,
getName()));
38 for (
int i = 1; i < maxDevices; i++)
40 inputDeviceNames.add (storage.getPropertyItem (SettingID::externControlIn,
getName() +
juce::String (i)));
41 outputDeviceNames.add (storage.getPropertyItem (SettingID::externControlOut,
getName() +
juce::String (i)));
44 inputDevices.resize ((
size_t) maxDevices);
45 outputDevices.resize ((
size_t) maxDevices);
47 oscInputPort = storage.getPropertyItem (SettingID::externOscInputPort,
getName());
48 oscOutputPort = storage.getPropertyItem (SettingID::externOscOutputPort,
getName());
49 oscOutputAddr = storage.getPropertyItem (SettingID::externOscOutputAddr,
getName());
51 showTrackSelection = storage.getPropertyItem (SettingID::externControlShowSelection,
getName());
52 showClipSlotSelection = storage.getPropertyItem (SettingID::externControlShowSelection,
getName());
54 juce::Colours::red.withHue (0.0f).withSaturation (0.7f).toString()).toString());
55 enabled = storage.getPropertyItem (SettingID::externControlEnable,
getName());
57 midiInOutDevicesChanged();
60 cs.initialiseDevice (isEnabled());
62 cs.numExtendersChanged (numDevices - 1, mainDevice);
67 auto& dm = engine.getDeviceManager();
68 dm.addChangeListener (
this);
71ExternalController::~ExternalController()
75 if (
auto af = getCurrentPlugin())
76 for (
auto p : af->getAutomatableParameters())
77 p->removeListener (this);
79 getControlSurface().shutDownDevice();
80 controlSurface =
nullptr;
82 auto& dm = engine.getDeviceManager();
83 dm.removeChangeListener (
this);
85 for (
auto& d : dm.getMidiInDevices())
86 if (auto
min = dynamic_cast<PhysicalMidiInputDevice*> (d.get()))
88 min->removeExternalController (this);
90 if (lastRegisteredSelectable !=
nullptr)
92 lastRegisteredSelectable->removeSelectableListener (
this);
93 lastRegisteredSelectable =
nullptr;
104 if (
auto cs = controlSurface.get())
105 return cs->deviceDescription;
110bool ExternalController::wantsDevice (
const MidiID& m)
112 if (
auto cs = controlSurface.get())
113 return cs->wantsDevice (m);
118juce::String ExternalController::getDesiredMidiChannel()
const
120 if (
auto cs = controlSurface.get())
121 return cs->midiChannelName;
126juce::String ExternalController::getDesiredMidiBackChannel()
const
128 if (
auto cs = controlSurface.get())
129 return cs->midiBackChannelName;
134Plugin* ExternalController::getCurrentPlugin()
const
136 return dynamic_cast<Plugin*
> (currentParamSource.get());
139void ExternalController::currentEditChanged (Edit* edit)
141 if (controlSurface !=
nullptr)
144 getControlSurface().currentEditChanged (edit);
148void ExternalController::currentSelectionManagerChanged (SelectionManager* sm)
150 if (controlSurface !=
nullptr)
153 getControlSurface().currentSelectionManagerChanged (sm);
157bool ExternalController::isEnabled()
const
161 if (hasMidiInput.has_value())
162 return *hasMidiInput;
164 hasMidiInput = getMidiInputDevice (0).
isNotEmpty();
166 return *hasMidiInput;
172void ExternalController::setEnabled (
bool e)
174 if (controlSurface !=
nullptr && ! needsChannel)
179 engine.getPropertyStorage().setPropertyItem (SettingID::externControlEnable,
getName(), e);
181 getControlSurface().initialiseDevice (isEnabled());
187int ExternalController::getNumDevices()
const
192void ExternalController::setNumDevices (
int num)
197 controlSurface->numExtendersChanged (num - 1, mainDevice);
199 engine.getPropertyStorage().setPropertyItem (SettingID::externControlNum,
getName(), num);
202int ExternalController::getMainDevice()
const
207void ExternalController::setMainDevice (
int num)
211 controlSurface->numExtendersChanged (num - 1, mainDevice);
213 engine.getPropertyStorage().setPropertyItem (SettingID::externControlMain,
getName(), mainDevice);
216juce::String ExternalController::getMidiInputDevice (
int idx)
const
218 auto name = inputDeviceNames[idx];
220 if (name.isNotEmpty() && getMidiInputPorts().contains (name))
226void ExternalController::setMidiInputDevice (
int idx,
const juce::String& nameOfMidiInput)
231 for (
auto c : getExternalControllerManager().getControllers())
232 for (
int i = 0; i < maxDevices; i++)
233 if (c !=
this && c->getMidiInputDevice (idx) == nameOfMidiInput)
234 c->setMidiInputDevice (idx, {});
237 inputDeviceNames.set (idx, nameOfMidiInput);
238 engine.getPropertyStorage().setPropertyItem (SettingID::externControlIn,
242 midiInOutDevicesChanged();
245juce::String ExternalController::getBackChannelDevice (
int idx)
const
247 auto name = outputDeviceNames[idx];
249 if (name.isNotEmpty() && getMidiOutputPorts().contains (name))
255void ExternalController::deleteController()
257 if (controlSurface !=
nullptr)
258 getControlSurface().deleteController();
263 return { channelStart, channelStart + getNumFaderChannels() };
268 return { startParamNumber, startParamNumber + getNumParameterControls() };
271int ExternalController::getFaderIndexInActiveRegion (
int i)
const noexcept
277int ExternalController::getNumFaderChannels() const noexcept
279 if (
auto cs = controlSurface.get())
280 return cs->numberOfFaderChannels;
285int ExternalController::getNumParameterControls() const noexcept
287 if (
auto cs = controlSurface.get())
288 return cs->numParameterControls;
293void ExternalController::midiInOutDevicesChanged()
295 if (! needsMidiChannel())
298 auto& dm = engine.getDeviceManager();
300 auto oldInputDevices = inputDevices;
301 auto oldOutputDevices = outputDevices;
303 for (
auto& i : inputDevices)
306 for (
auto& d : dm.getMidiInDevices())
310 if (
auto min =
dynamic_cast<PhysicalMidiInputDevice*
> (d.get()))
312 if (
min->isEnabled())
316 for (
int j = 0; j < numDevices; j++)
318 if (
min->getName().equalsIgnoreCase (inputDeviceNames[j]))
320 inputDevices[(
size_t) j] = min;
326 min->setExternalController (
this);
328 min->removeExternalController (
this);
333 for (
auto& o : outputDevices)
336 for (
int i = dm.getNumMidiOutDevices(); --i >= 0;)
339 auto mo = dm.getMidiOutDevice (i);
342 for (
int j = 0; j < numDevices; j++)
344 if (mo !=
nullptr && mo->isEnabled() && mo->getName().equalsIgnoreCase (outputDeviceNames[j]))
346 outputDevices[(
size_t) j] = mo;
347 mo->setSendControllerMidiClock (wantsClock);
353 mo->setExternalController (
this);
355 mo->removeExternalController (
this);
358 if (oldInputDevices != inputDevices || oldOutputDevices != outputDevices)
362void ExternalController::timerCallback()
367 if (controlSurface !=
nullptr)
368 getControlSurface().initialiseDevice (isEnabled());
374void ExternalController::oscSettingsChanged()
376 if (! needsOSCSocket())
380 if (controlSurface !=
nullptr)
381 getControlSurface().initialiseDevice (isEnabled());
383 getControlSurface().updateOSCSettings (oscInputPort, oscOutputPort, oscOutputAddr);
389void ExternalController::setBackChannelDevice (
int idx,
const juce::String& nameOfMidiOutput)
395 for (
auto c : getExternalControllerManager().getControllers())
396 for (
int i = 0; i < maxDevices; i++)
397 if (c !=
this && c->getBackChannelDevice (i) == nameOfMidiOutput)
398 c->setBackChannelDevice (i, {});
401 outputDeviceNames.set (idx, nameOfMidiOutput);
402 engine.getPropertyStorage().setPropertyItem (SettingID::externControlOut,
406 midiInOutDevicesChanged();
409bool ExternalController::isUsingMidiOutputDevice (
const MidiOutputDevice* d)
const noexcept
411 for (
auto od : outputDevices)
418void ExternalController::setOSCInputPort (
int port)
422 engine.getPropertyStorage().setPropertyItem (SettingID::externOscInputPort,
getName(), oscInputPort);
423 oscSettingsChanged();
426void ExternalController::setOSCOutputPort (
int port)
428 oscOutputPort = port;
430 engine.getPropertyStorage().setPropertyItem (SettingID::externOscOutputPort,
getName(), oscOutputPort);
431 oscSettingsChanged();
434void ExternalController::setOSCOutputAddress (
const juce::String addr)
436 oscOutputAddr = addr;
438 engine.getPropertyStorage().setPropertyItem (SettingID::externOscOutputAddr,
getName(), oscOutputAddr);
439 oscSettingsChanged();
442void ExternalController::setSelectionColour (
juce::Colour c)
444 if (selectionColour != c)
447 engine.getPropertyStorage().setPropertyItem (SettingID::externControlSelectionColour,
getName(), c.
toString());
448 getControlSurface().changed();
452void ExternalController::setShowTrackSelectionColour (
bool b)
454 if (showTrackSelection != b)
456 showTrackSelection = b;
457 engine.getPropertyStorage().setPropertyItem (SettingID::externControlShowSelection,
getName(), b);
458 getControlSurface().changed();
462void ExternalController::setShowClipSlotSelectionColour (
bool b)
464 if (showClipSlotSelection != b)
466 showClipSlotSelection = b;
467 engine.getPropertyStorage().setPropertyItem (SettingID::externControlShowClipSlotSelection,
getName(), b);
468 getControlSurface().changed();
472void ExternalController::moveFader (
int channelNum,
float newSliderPos)
474 int i = getFaderIndexInActiveRegion (channelNum);
477 getControlSurface().moveFader (i, newSliderPos);
480void ExternalController::moveMasterFader (
float newPos)
483 if (controlSurface !=
nullptr)
484 getControlSurface().moveMasterLevelFader (newPos);
487void ExternalController::movePanPot (
int channelNum,
float newPan)
489 int i = getFaderIndexInActiveRegion (channelNum);
492 getControlSurface().movePanPot (i, newPan);
495void ExternalController::moveMasterPanPot (
float newPan)
497 getControlSurface().moveMasterPanPot (newPan);
500void ExternalController::updateSoloAndMute (
int channelNum, Track::MuteAndSoloLightState state,
bool isBright)
502 int i = getFaderIndexInActiveRegion (channelNum);
505 getControlSurface().updateSoloAndMute (i, state, isBright);
508void ExternalController::soloCountChanged (
bool anySoloTracks)
510 if (controlSurface !=
nullptr)
511 getControlSurface().soloCountChanged (anySoloTracks);
514void ExternalController::playStateChanged (
bool isPlaying)
516 if (controlSurface !=
nullptr)
517 getControlSurface().playStateChanged (isPlaying);
520void ExternalController::recordStateChanged (
bool isRecording)
522 if (controlSurface !=
nullptr)
523 getControlSurface().recordStateChanged (isRecording);
526void ExternalController::automationModeChanged (
bool isReading,
bool isWriting)
528 if (controlSurface !=
nullptr)
530 getControlSurface().automationReadModeChanged (isReading);
531 getControlSurface().automationWriteModeChanged (isWriting);
535void ExternalController::snapChanged (
bool isOn)
537 if (controlSurface !=
nullptr)
538 getControlSurface().snapOnOffChanged (isOn);
541void ExternalController::loopChanged (
bool isOn)
543 if (controlSurface !=
nullptr)
544 getControlSurface().loopOnOffChanged (isOn);
547void ExternalController::clickChanged (
bool isOn)
549 if (controlSurface !=
nullptr)
550 getControlSurface().clickOnOffChanged (isOn);
553void ExternalController::channelLevelChanged (
int channelNum,
float l,
float r)
555 int i = getFaderIndexInActiveRegion (channelNum);
558 getControlSurface().channelLevelChanged (i, l, r);
561void ExternalController::masterLevelsChanged (
float leftLevel,
float rightLevel)
563 if (controlSurface !=
nullptr)
564 getControlSurface().masterLevelsChanged (leftLevel, rightLevel);
567void ExternalController::timecodeChanged (
int barsOrHours,
574 if (controlSurface !=
nullptr)
575 getControlSurface().timecodeChanged (barsOrHours, beatsOrMinutes, ticksOrSeconds,
576 millisecs, isBarsBeats, isFrames);
579void ExternalController::trackSelected (
int channelNum,
bool isSelected)
581 int i = getFaderIndexInActiveRegion (channelNum);
584 getControlSurface().trackSelectionChanged (i, isSelected);
587void ExternalController::selectOtherObject (SelectableClass::Relationship relationship,
bool moveFromCurrentPlugin)
589 if (
auto sm = getExternalControllerManager().getSelectionManager())
591 if (moveFromCurrentPlugin
592 && currentParamSource !=
nullptr
593 && ! sm->isSelected (currentParamSource))
595 sm->selectOnly (currentParamSource);
598 sm->selectOtherObjects (relationship,
false);
602void ExternalController::muteOrUnmutePlugin()
604 if (
auto p = getCurrentPlugin())
605 p->setEnabled (! p->isEnabled());
608void ExternalController::changePluginPreset (
int delta)
610 if (
auto ep =
dynamic_cast<ExternalPlugin*
> (getCurrentPlugin()))
611 if (ep->getNumPrograms() > 1)
613 ep->getNumPrograms() - 1,
614 ep->getCurrentProgram() + delta),
618void ExternalController::soloPluginTrack()
620 if (
auto p = getCurrentPlugin())
621 if (
auto t = p->getOwnerTrack())
622 t->setSolo (! t->isSolo (
false));
625void ExternalController::muteOrUnmutePluginsInTrack()
627 if (
auto p = getCurrentPlugin())
628 if (
auto t = p->getOwnerTrack())
629 t->flipAllPluginsEnablement();
632void ExternalController::changeFaderBank (
int delta,
bool moveSelection)
634 if (controlSurface !=
nullptr)
636 if (getEdit() !=
nullptr)
641 auto& ecm = getExternalControllerManager();
643 for (
int i = channelStart; i < (channelStart + getNumFaderChannels()); ++i)
644 selectedChannels.add(i);
647 std::max (0, ecm.getNumChannelTracks()
648 - (allowBankingOffEnd ? 1 : getNumFaderChannels())));
650 for (
int i = channelStart; i < (channelStart + getNumFaderChannels()); ++i)
652 if (selectedChannels.contains(i))
653 selectedChannels.removeValue(i);
655 selectedChannels.add(i);
660 if (selectedChannels.size() > 0 && getShowTrackSelectionColour() && isEnabled())
661 for (
int i = 0; i < selectedChannels.size(); ++i)
662 ecm.repaintTrack (selectedChannels[i]);
666 if (
auto sm = ecm.getSelectionManager())
667 if (
auto t = ecm.getChannelTrack (channelStart))
668 if (! sm->isSelected (t) || sm->getNumObjectsSelected() != 1)
675void ExternalController::changePadBank (
int delta)
677 if (controlSurface !=
nullptr)
679 padStart =
std::max (0, padStart + delta);
682 auto& ecm = getExternalControllerManager();
684 if (getShowClipSlotSelectionColour() && isEnabled())
686 for (
int i = channelStart; i < (channelStart + getNumFaderChannels()); ++i)
687 ecm.repaintSlots (i);
692void ExternalController::changeParamBank (
int delta)
694 if (controlSurface !=
nullptr)
697 startParamNumber += delta;
703void ExternalController::updateParamList()
707 if (controlSurface !=
nullptr)
709 currentParams.clear();
711 if (
auto plugin = getCurrentPlugin())
713 auto params (plugin->getFlattenedParameterTree());
714 AutomatableParameter::Array possibleParams;
716 #if TRACKTION_ENABLE_CONTROL_SURFACES
717 if ((getControlSurfaceIfType<NovationRemoteSl>() !=
nullptr
718 || getControlSurfaceIfType<RemoteSLCompact>() !=
nullptr)
719 &&
dynamic_cast<ExternalPlugin*
> (plugin) !=
nullptr)
721 for (
int i = 0; i < 6; ++i)
722 possibleParams.add (
nullptr);
727 if (getControlSurface().wantsDummyParams)
728 for (
int i = 0; i < 2; ++i)
729 possibleParams.add (
nullptr);
732 for (
auto p : params)
733 possibleParams.add (p);
735 if (controlSurface !=
nullptr)
738 std::max (0, possibleParams.size() - getControlSurface().numParameterControls),
741 for (
int i = 0; i < getControlSurface().numParameterControls && i + startParamNumber < possibleParams.size(); ++i)
742 currentParams.add (possibleParams[startParamNumber + i]);
748void ExternalController::userMovedParameterControl (
int paramNumber,
float newValue,
bool delta)
750 if (
auto p = currentParams[paramNumber])
753 p->midiControllerMoved (
std::clamp (p->getCurrentNormalisedValue() + newValue, 0.0f, 1.0f));
755 p->midiControllerMoved (newValue);
759void ExternalController::userPressedParameterControl (
int paramNumber)
761 if (
auto p = currentParams[paramNumber])
762 p->midiControllerPressed();
765void ExternalController::userPressedGoToMarker (
int marker)
767 if (
auto tc = getTransport())
768 if (
auto ed = getEdit())
769 if (
auto mc = ed->getMarkerManager().getMarkers().getObjectPointer (marker + startMarkerNumber))
770 tc->setPosition (mc->getPosition().getStart());
773void ExternalController::updateParameters()
777 if (controlSurface ==
nullptr || ! isEnabled())
780 auto& cs = getControlSurface();
782 if (lastRegisteredSelectable != currentParamSource)
784 if (
auto* p = getCurrentPlugin())
785 for (
auto* param : p->getAutomatableParameters())
786 param->removeListener (this);
788 if (cs.showingPluginParams())
789 repaintParamSource();
791 currentParamSource = lastRegisteredSelectable;
794 if (cs.showingPluginParams())
795 repaintParamSource();
797 if (
auto* p = getCurrentPlugin())
798 for (
auto* param : p->getAutomatableParameters())
799 param->addListener (this);
802 auto numAvailableParams = currentParams.size();
804 auto paramSourcePlugin = getCurrentPlugin();
805 cs.pluginBypass (paramSourcePlugin !=
nullptr && ! paramSourcePlugin->isEnabled());
807 if (numAvailableParams > 0)
809 for (
int i = 0; i < numAvailableParams; ++i)
811 ParameterSetting param;
813 if (
auto p = currentParams[i])
815 auto pn = p->getParameterShortName (cs.numCharactersForParameterLabels);
817 if (pn.length() > cs.numCharactersForParameterLabels)
818 pn = shortenName (pn, 7);
820 pn.copyToUTF8 (param.label, (
size_t)
std::min (cs.numCharactersForParameterLabels,
821 (
int) sizeof (param.label) - 1));
823 param.value =
juce::jlimit (0.0f, 1.0f, p->valueRange.convertTo0to1 (p->getCurrentBaseValue()));
825 auto s = p->getLabelForValue (p->getCurrentBaseValue());
828 s = p->getCurrentValueAsString();
832 if (s.toLowerCase().endsWith(
" dB"))
833 s = s.dropLastCharacters(3);
834 else if (s.toLowerCase().endsWith(
"dB"))
835 s = s.dropLastCharacters(2);
838 if (s.length() > cs.numCharactersForParameterLabels)
839 s = shortenName (s, 7);
846 cs.parameterChanged (i, param);
851 param.valueDescription[0] = 0;
854 if (
auto plugin = getCurrentPlugin())
856 auto t = plugin->getOwnerTrack();
858 if (startParamNumber + i == 0)
861 shortenName (t->getName(), 7)
862 .copyToUTF8 (param.label, (
size_t)
std::min (cs.numCharactersForParameterLabels,
863 (
int) sizeof (param.label) - 1));
865 cs.parameterChanged (i, param);
867 else if (startParamNumber + i == 1)
869 shortenName (plugin->getName(), 7)
870 .copyToUTF8 (param.label, (
size_t)
std::min (cs.numCharactersForParameterLabels,
871 (
int) sizeof (param.label) - 1));
873 cs.parameterChanged (i, param);
877 cs.clearParameter(i);
885 startParamNumber = 0;
888 for (
int i = numAvailableParams; i < cs.numParameterControls; ++i)
889 cs.clearParameter (i);
892void ExternalController::selectedPluginChanged()
894 if (controlSurface !=
nullptr)
896 if (getControlSurface().canChangeSelectedPlugin() || lastRegisteredSelectable ==
nullptr)
898 if (lastRegisteredSelectable !=
nullptr)
899 lastRegisteredSelectable->removeSelectableListener (
this);
901 lastRegisteredSelectable =
nullptr;
903 if (
auto sm = getExternalControllerManager().getSelectionManager())
904 lastRegisteredSelectable = sm->getSelectedObject (0);
906 if (lastRegisteredSelectable !=
nullptr)
907 lastRegisteredSelectable->addSelectableListener (
this);
910 if (
auto plugin =
dynamic_cast<Plugin*
> (lastRegisteredSelectable.get()))
911 pluginName = plugin->getName();
913 getControlSurface().currentSelectionChanged (pluginName);
915 updateTrackSelectLights();
920void ExternalController::selectableObjectChanged (Selectable*)
926void ExternalController::selectableObjectAboutToBeDeleted (Selectable* s)
928 if (currentParamSource == s)
930 if (
auto* p = getCurrentPlugin())
931 for (
auto* param : p->getAutomatableParameters())
932 param->removeListener (this);
934 currentParamSource =
nullptr;
938 if (lastRegisteredSelectable == s)
940 s->removeSelectableListener (
this);
941 lastRegisteredSelectable =
nullptr;
950 triggerAsyncUpdate();
956 triggerAsyncUpdate();
959void ExternalController::updateTrackSelectLights()
961 for (
int chan = channelStart; chan < (channelStart + getNumFaderChannels()); ++chan)
963 bool selected =
false;
965 if (getEdit() !=
nullptr)
966 if (
auto sm = getExternalControllerManager().getSelectionManager())
967 if (
auto t = getExternalControllerManager().getChannelTrack (chan))
968 selected = sm->isSelected (t);
970 trackSelected (chan, selected);
974void ExternalController::updateTrackRecordLights()
976 if (
auto ed = getEdit())
978 auto& ecm = getExternalControllerManager();
980 for (
int chan = channelStart; chan < (channelStart + getNumFaderChannels()); ++chan)
982 if (
auto t = ecm.getChannelTrack (chan))
984 bool isRecording =
false;
986 for (
auto in : ed->getAllInputDevices())
988 if (
auto at =
dynamic_cast<AudioTrack*
> (t))
990 if (in->isRecordingActive (at->itemID) && in->getTargets().contains (at->itemID))
998 getControlSurface().trackRecordEnabled (chan - channelStart, isRecording);
1002 getControlSurface().trackRecordEnabled (chan - channelStart,
false);
1008 for (
int chan = channelStart; chan < (channelStart + getNumFaderChannels()); ++chan)
1009 getControlSurface().trackRecordEnabled (chan - channelStart,
false);
1013void ExternalController::updatePunchLights()
1015 if (
auto ed = getEdit())
1016 if (
auto cs = controlSurface.get())
1017 cs->punchOnOffChanged (ed->recordingPunchInOut);
1020void ExternalController::updateScrollLights()
1022 if (
auto cs = controlSurface.get())
1023 cs->scrollOnOffChanged (AppFunctions::isScrolling());
1026void ExternalController::updateUndoLights()
1028 if (
auto ed = getEdit())
1029 if (
auto cs = controlSurface.get())
1030 cs->undoStatusChanged (ed->getUndoManager().canUndo(),
1031 ed->getUndoManager().canRedo());
1034void ExternalController::clearPadColours()
1036 auto& cs = getControlSurface();
1038 if (cs.numberOfTrackPads > 0)
1040 for (
auto track = 0; track < cs.numberOfFaderChannels; track++)
1042 cs.clipsPlayingStateChanged (track,
false);
1043 for (
auto scene = 0; scene < cs.numberOfTrackPads; scene++)
1044 cs.padStateChanged (track, scene, 0, 0);
1049void ExternalController::updatePadColours()
1051 auto& ecm = getExternalControllerManager();
1052 auto& cs = getControlSurface();
1054 if (cs.numberOfTrackPads > 0)
1056 for (
auto track = 0; track < cs.numberOfFaderChannels; track++)
1059 bool isPlaying =
false;
1061 if (
auto at =
dynamic_cast<AudioTrack*
> (ecm.getChannelTrack (track + channelStart)))
1063 for (
auto slot : at->getClipSlotList().getClipSlots())
1065 if (
auto dest = slot->getInputDestination(); dest && dest->input.isRecording (dest->targetID))
1068 if (
auto c = slot->getClip())
1069 if (
auto lh = c->getLaunchHandle())
1070 if (lh->getPlayingStatus() == LaunchHandle::PlayState::playing || lh->getQueuedStatus() == LaunchHandle::QueueState::playQueued)
1078 cs.clipsPlayingStateChanged (track, isPlaying);
1081 for (
auto scene = 0; scene < cs.numberOfTrackPads; scene++)
1086 if (
auto ft =
dynamic_cast<FolderTrack*
> (ecm.getChannelTrack (track + channelStart)))
1088 for (
auto at : ft->getAllAudioSubTracks (true))
1090 if (
auto slot = at->getClipSlotList().getClipSlots()[padStart + scene])
1092 if (
auto c = slot->getClip())
1094 auto col = c->getColour();
1096 if (! col.isTransparent())
1098 auto numColours = 19;
1099 auto newHue = col.getHue();
1108 else if (
auto at =
dynamic_cast<AudioTrack*
> (ecm.getChannelTrack (track + channelStart)))
1110 if (
auto slot = at->getClipSlotList().getClipSlots()[padStart + scene])
1112 if (
auto dest = slot->getInputDestination(); dest && dest->input.isRecording (dest->targetID))
1114 auto epc = dest->input.edit.getTransport().getCurrentPlaybackContext();
1115 const auto currentTime = epc ? epc->getUnloopedPosition() : dest->input.edit.getTransport().getPosition();
1117 if (currentTime < dest->input.getPunchInTime (dest->targetID))
1119 colourIdx = cs.limitedPadColours ? 3 : 1;
1124 colourIdx = cs.limitedPadColours ? 3 : 1;
1128 else if (
auto c = slot->getClip())
1130 auto col = c->getColour();
1132 if (! col.isTransparent())
1134 auto numColours = 19;
1135 auto newHue = col.getHue();
1140 if (
auto tc = getTransport())
1142 if (
auto lh = c->getLaunchHandle())
1144 if (lh->getPlayingStatus() == LaunchHandle::PlayState::playing)
1146 colourIdx = cs.limitedPadColours ? 2 : 8;
1147 state = tc->isPlaying() ? 2 : 1;
1149 else if (lh->getQueuedStatus() == LaunchHandle::QueueState::playQueued)
1151 colourIdx = cs.limitedPadColours ? 2 : 8;;
1157 else if (cs.recentlyPressedPads.contains ({track + channelStart, padStart + scene}))
1159 auto anyPlaying = [&]()
1161 if (
auto tc = getTransport())
1163 for (
auto slot_ : at->getClipSlotList().getClipSlots())
1165 if (
auto c_ = slot_->getClip())
1167 if (
auto lh = c_->getLaunchHandle())
1169 if (lh->getPlayingStatus() == LaunchHandle::PlayState::playing && tc->isPlaying())
1171 else if (lh->getQueuedStatus() == LaunchHandle::QueueState::playQueued)
1182 colourIdx = cs.limitedPadColours ? 3 : 1;
1187 cs.recentlyPressedPads.erase ({track + channelStart, padStart + scene});
1193 cs.padStateChanged (track, scene, colourIdx, state);
1199void ExternalController::updateDeviceState()
1201 if (controlSurface !=
nullptr)
1203 if (
auto edit = getEdit())
1205 auto& ecm = getExternalControllerManager();
1206 auto& cs = getControlSurface();
1210 bool anySoloTracks = edit->areAnyTracksSolo();
1212 for (
int chan = channelStart; chan < (channelStart + getNumFaderChannels()); ++chan)
1214 if (
auto t = ecm.getChannelTrack (chan))
1216 auto at =
dynamic_cast<AudioTrack*
> (t);
1217 auto ft =
dynamic_cast<FolderTrack*
> (t);
1219 if (
auto vp = at !=
nullptr ? at->getVolumePlugin() :
nullptr)
1221 moveFader (chan, vp->getSliderPos());
1222 movePanPot (chan, vp->getPan());
1224 else if (
auto vca = ft !=
nullptr ? ft->getVCAPlugin() : nullptr)
1226 moveFader (chan, vca->getSliderPos());
1227 movePanPot (chan, 0.0f);
1231 moveFader (chan, decibelsToVolumeFaderPosition (0.0f));
1232 movePanPot (chan, 0.0f);
1235 updateSoloAndMute (chan, t->getMuteAndSoloLightState(),
true);
1237 channelLevelChanged (chan, 0.0f, 0.0f);
1239 if (
auto sm = ecm.getSelectionManager())
1240 trackSelected (chan, sm->isSelected (t));
1244 moveFader (chan, decibelsToVolumeFaderPosition (0.0f));
1245 movePanPot (chan, 0.0f);
1246 updateSoloAndMute (chan, {},
false);
1247 channelLevelChanged (chan, 0.0f, 0.0f);
1248 trackSelected (chan,
false);
1252 updateTrackSelectLights();
1253 updateTrackRecordLights();
1254 soloCountChanged (anySoloTracks);
1260 if (
auto masterVol = edit->getMasterVolumePlugin())
1262 moveMasterFader (masterVol->getSliderPos());
1263 moveMasterPanPot (masterVol->getPan());
1268 for (
int i = 0; i < getNumFaderChannels(); ++i)
1272 if (
auto track = ecm.getChannelTrack (i + channelStart))
1276 if (trackName.startsWithIgnoreCase (
TRANS(
"Track") +
" ") && trackName.length() > maxTrackNameChars)
1277 trackName =
juce::String (trackName.getTrailingIntValue());
1278 else if (trackName.length() > maxTrackNameChars)
1279 trackName = shortenName (trackName, maxTrackNameChars);
1281 name = trackName.substring (0, maxTrackNameChars);
1284 trackNames.
add (name);
1287 cs.faderBankChanged (channelStart, trackNames);
1291 if (cs.showingMarkers())
1292 ecm.updateMarkers();
1295 if (
auto tc = getTransport())
1298 playStateChanged (tc->isPlaying());
1299 recordStateChanged (tc->isRecording());
1301 automationModeChanged (edit->getAutomationRecordManager().isReadingAutomation(),
1302 edit->getAutomationRecordManager().isWritingAutomation());
1304 snapChanged (tc->snapToTimecode);
1305 loopChanged (tc->looping);
1306 clickChanged (edit->clickTrackEnabled);
1307 cs.scrollOnOffChanged (AppFunctions::isScrolling());
1308 cs.punchOnOffChanged (edit->recordingPunchInOut);
1309 cs.slaveOnOffChanged (edit->isTimecodeSyncEnabled());
1311 masterLevelsChanged (0.0f, 0.0f);
1313 updateTrackRecordLights();
1314 cs.auxBankChanged (auxBank);
1315 auxSendLevelsChanged();
1317 cs.updateMiscFeatures();
1326 updateTrackSelectLights();
1327 updateTrackRecordLights();
1333void ExternalController::auxSendLevelsChanged()
1335 if (controlSurface !=
nullptr)
1337 auto& ecm = getExternalControllerManager();
1338 auto& cs = getControlSurface();
1340 for (
int chan = channelStart; chan < (channelStart + getNumFaderChannels()); ++chan)
1342 if (
auto t = ecm.getChannelTrack (chan))
1344 auto at =
dynamic_cast<AudioTrack*
> (t);
1348 for (
auto i = 0; i < cs.numAuxes; i++)
1349 cs.clearAux (chan - channelStart, i);
1351 else if (cs.auxMode == AuxPosition::byPosition)
1353 for (
auto i = 0; i < cs.numAuxes; i++)
1355 if (
auto aux = at->getAuxSendPlugin (auxBank + i, AuxPosition::byPosition))
1357 auto nm = aux->getBusName();
1359 if (nm.length() > cs.numCharactersForAuxLabels)
1360 nm = shortenName (nm, cs.numCharactersForAuxLabels);
1362 cs.moveAux (chan - channelStart, i, nm.toRawUTF8(), decibelsToVolumeFaderPosition (aux->getGainDb()));
1366 cs.clearAux (chan - channelStart, i);
1372 for (
auto i = 0; i < cs.numAuxes; i++)
1374 if (
auto aux = at->getAuxSendPlugin (auxBank + i, AuxPosition::byBus))
1376 auto nm = aux->getBusName();
1378 if (nm.length() > cs.numCharactersForAuxLabels)
1379 nm = shortenName (nm, cs.numCharactersForAuxLabels);
1381 cs.moveAux (chan - channelStart, i, nm.toRawUTF8(), decibelsToVolumeFaderPosition (aux->getGainDb()));
1385 cs.clearAux (chan - channelStart, i);
1392 for (
auto i = 0; i < cs.numAuxes; i++)
1393 cs.clearAux (chan - channelStart, i);
1399void ExternalController::acceptMidiMessage (MidiInputDevice& d,
const juce::MidiMessage& m)
1406 for (
size_t i = 0; i < inputDevices.size(); ++i)
1407 if (inputDevices[i] == &d)
1410 pendingMidiMessages.add ({ idx, m });
1412 triggerAsyncUpdate();
1415bool ExternalController::wantsMessage (MidiInputDevice& d,
const juce::MidiMessage& m)
1419 for (
size_t i = 0; i < inputDevices.size(); ++i)
1420 if (inputDevices[i] == &d)
1423 return controlSurface !=
nullptr && getControlSurface().wantsMessage (idx, m);
1426bool ExternalController::eatsAllMessages()
const
1428 return controlSurface !=
nullptr && getControlSurface().eatsAllMessages();
1431void ExternalController::setEatsAllMessages (
bool eatAll)
1433 if (controlSurface !=
nullptr)
1434 getControlSurface().setEatsAllMessages (eatAll);
1437void ExternalController::handleAsyncUpdate()
1441 processMidi =
false;
1443 if (controlSurface !=
nullptr)
1452 messages.swapWith (pendingMidiMessages);
1455 for (
auto& m : messages)
1456 getControlSurface().acceptMidiMessage (m.first, m.second);
1462 updateParams =
false;
1467juce::String ExternalController::getNoDeviceSelectedMessage()
1469 return "<" +
TRANS(
"No Device Selected") +
">";
1476 inputNames.
add (getNoDeviceSelectedMessage());
1478 auto& dm = engine.getDeviceManager();
1480 auto wantsDevice = [] ([[maybe_unused]] MidiInputDevice* in)
1483 if (
dynamic_cast<VirtualMidiInputDevice*
> (in))
1490 for (
auto& m : dm.getMidiInDevices())
1491 if (m->isEnabled() && wantsDevice (m.get()))
1492 inputNames.add (m->
getName());
1501 outputNames.
add (getNoDeviceSelectedMessage());
1502 auto& dm = engine.getDeviceManager();
1504 for (
int i = 0; i < dm.getNumMidiOutDevices(); ++i)
1505 if (
auto m = dm.getMidiOutDevice (i))
1507 outputNames.add(m->getName());
1512bool ExternalController::shouldTrackBeColoured (
int channelNum)
1514 return channelNum >= channelStart
1515 && channelNum < (channelStart + getNumFaderChannels())
1516 && getControlSurface().showingTracks()
1517 && getShowTrackSelectionColour()
1521void ExternalController::getTrackColour (
int channelNum,
juce::Colour& color)
1523 if (channelNum >= channelStart
1524 && channelNum < channelStart + getNumFaderChannels()
1525 && getControlSurface().showingTracks()
1526 && getShowTrackSelectionColour()
1530 color = getSelectionColour();
1532 color = color.
overlaidWith (getSelectionColour().withAlpha (0.8f));
1538 if (&e != getEdit())
1541 if (controlSurface ==
nullptr || ! getControlSurface().showingClipSlots() || ! getShowClipSlotSelectionColour() || ! isEnabled())
1544 auto& ecm = getExternalControllerManager();
1546 auto t1 = ecm.getChannelTrack (channelStart);
1549 for (
auto i = 0; i < controlSurface->numberOfFaderChannels; i++)
1550 if (
auto t = t2 = ecm.getChannelTrack (channelStart + i))
1555 ColourArea c = {getSelectionColour(), *t1, *t2, padStart, padStart + controlSurface->numberOfTrackPads - 1};
1562bool ExternalController::shouldPluginBeColoured (Plugin* p)
1564 return controlSurface !=
nullptr
1565 && (getControlSurface().isPluginSelected (p)
1566 || (p == currentParamSource && getControlSurface().showingPluginParams()))
1567 && getShowTrackSelectionColour()
1571void ExternalController::getPluginColour (Plugin* plugin,
juce::Colour& color)
1573 if (shouldPluginBeColoured (plugin) && getShowTrackSelectionColour() && isEnabled())
1576 color = getSelectionColour();
1578 color = color.
overlaidWith (getSelectionColour().withAlpha (0.8f));
1582void ExternalController::repaintParamSource()
1586 if (
auto plugin = getCurrentPlugin())
1587 getExternalControllerManager().repaintPlugin (*plugin);
1590void ExternalController::redrawTracks()
1594 auto& ecm = getExternalControllerManager();
1595 auto numTracks = ecm.getNumChannelTracks();
1597 for (
int i = 0; i < numTracks; ++i)
1598 ecm.repaintTrack (i);
1601void ExternalController::changeMarkerBank (
int delta)
1603 if (controlSurface !=
nullptr)
1605 startMarkerNumber += delta;
1610void ExternalController::updateMarkers()
1612 if (controlSurface !=
nullptr)
1614 auto& cs = getControlSurface();
1615 int numMarkersUsed = 0;
1617 if (
auto ed = getEdit())
1619 auto allMarkers = ed->getMarkerManager().getMarkers();
1621 if (allMarkers.size() > 0)
1624 std::max (0, allMarkers.size() - cs.numMarkers),
1627 for (
int i = 0; (i < cs.numMarkers) && (i + startMarkerNumber < allMarkers.size()); ++i)
1629 if (
auto mc = allMarkers.getObjectPointer (i + startMarkerNumber))
1631 juce::String pn (mc->getName().replace (
"marker",
"mk",
true));
1635 else if (pn.length() > cs.numCharactersForMarkerLabels)
1636 pn = shortenName (pn, 7);
1639 pn.copyToUTF8 (ms.label, (
size_t)
std::min (cs.numCharactersForMarkerLabels,
1640 (
int) sizeof (ms.label) - 1));
1641 ms.number = mc->getMarkerID();
1642 ms.absolute = mc->isSyncAbsolute();
1644 cs.markerChanged (i, ms);
1645 numMarkersUsed = i + 1;
1651 for (
int i = numMarkersUsed; i < cs.numMarkers; ++i)
1656void ExternalController::changeAuxBank (
int delta)
1658 if (controlSurface !=
nullptr)
1660 if (getControlSurface().auxMode == AuxPosition::byPosition)
1665 getControlSurface().auxBankChanged (auxBank);
1666 auxSendLevelsChanged();
1670void ExternalController::setAuxBank (
int num)
1672 if (controlSurface !=
nullptr)
1674 if (getControlSurface().auxMode == AuxPosition::byPosition)
1679 getControlSurface().auxBankChanged (auxBank);
1680 auxSendLevelsChanged();
1694 bool hasSeenConsonant =
false;
1697 for (
int i = 0; i < s.
length(); ++i)
1701 hasSeenConsonant = (hasSeenConsonant || ! isVowel)
1704 if (! (hasSeenConsonant && isVowel))
void ensureStorageAllocated(int minNumElements)
static bool isWhitespace(char character) noexcept
static Colour fromString(StringRef encodedColourString)
uint32 getARGB() const noexcept
Colour overlaidWith(Colour foregroundColour) const noexcept
const String & toString() const noexcept
void add(String stringToAdd)
int length() const noexcept
bool containsChar(juce_wchar character) const noexcept
size_t copyToUTF8(CharPointer_UTF8::CharType *destBuffer, size_t maxBufferSizeBytes) const noexcept
String replace(StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase=false) const
String substring(int startIndex, int endIndex) const
bool isNotEmpty() const noexcept
#define TRANS(stringLiteral)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
int roundToInt(const FloatType value) noexcept
juce::String getName(LaunchQType t)
Retuns the name of a LaunchQType for display purposes.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.