diff --git a/app/translates/zh-CN/main.txt b/app/translates/zh-CN/main.txt index 91d68f9b..0a679340 100644 --- a/app/translates/zh-CN/main.txt +++ b/app/translates/zh-CN/main.txt @@ -128,3 +128,6 @@ countries: cn "Parameter" = "参数" "CC Controller" = "CC控制器" "Scale Plugin Window" = "缩放插件窗口" +"MIDI Input" = "MIDI输入" +"Sequencer Track" = "音序器轨道" +"Mixer Track" = "混音器轨道" diff --git a/src/audioCore/quickAPI/QuickGet.cpp b/src/audioCore/quickAPI/QuickGet.cpp index 62625cfb..c7e5f3e4 100644 --- a/src/audioCore/quickAPI/QuickGet.cpp +++ b/src/audioCore/quickAPI/QuickGet.cpp @@ -381,7 +381,7 @@ namespace quickAPI { return false; } - static juce::Array getPluginParamCCLink(PluginHolder pointer) { + static const juce::Array getPluginParamCCLink(PluginHolder pointer) { if (pointer) { juce::Array result; @@ -408,6 +408,7 @@ namespace quickAPI { if (pointer) { return pointer->getParamNameList(); } + return {}; } int getInstrNum() { @@ -442,6 +443,27 @@ namespace quickAPI { return false; } + bool getInstrMIDIInputFromDevice(int index) { + if (auto graph = AudioCore::getInstance()->getGraph()) { + return graph->getInstrMidiInputFromDeviceConnections(index).size() > 0; + } + return false; + } + + const juce::Array getInstrMIDIInputFromSource(int index) { + if (auto graph = AudioCore::getInstance()->getGraph()) { + return graph->getInstrMidiInputFromSrcConnections(index); + } + return {}; + } + + const juce::Array getInstrAudioOutputToMixer(int index) { + if (auto graph = AudioCore::getInstance()->getGraph()) { + return graph->getInstrOutputToTrackConnections(index); + } + return {}; + } + EditorPointer getInstrEditor(int index) { if (auto graph = AudioCore::getInstance()->getGraph()) { if (auto instr = graph->getInstrumentProcessor(index)) { @@ -475,7 +497,7 @@ namespace quickAPI { return getPluginMIDIOutput(pointer); } - juce::Array getInstrParamCCLink(PluginHolder pointer) { + const juce::Array getInstrParamCCLink(PluginHolder pointer) { return getPluginParamCCLink(pointer); } @@ -507,7 +529,7 @@ namespace quickAPI { return getPluginMIDIOutput(pointer); } - juce::Array getEffectParamCCLink(PluginHolder pointer) { + const juce::Array getEffectParamCCLink(PluginHolder pointer) { return getPluginParamCCLink(pointer); } @@ -519,6 +541,60 @@ namespace quickAPI { return getPluginParamList(pointer); } + int getSeqTrackNum() { + if (auto graph = AudioCore::getInstance()->getGraph()) { + return graph->getSourceNum(); + } + return 0; + } + + const juce::String getSeqTrackName(int index) { + if (auto graph = AudioCore::getInstance()->getGraph()) { + if (auto seqTrack = graph->getSourceProcessor(index)) { + return seqTrack->getTrackName(); + } + } + return ""; + } + + const juce::StringArray getSeqTrackNameList() { + int size = getSeqTrackNum(); + + juce::StringArray result; + for (int i = 0; i < size; i++) { + result.add(getSeqTrackName(i)); + } + + return result; + } + + int getMixerTrackNum() { + if (auto graph = AudioCore::getInstance()->getGraph()) { + return graph->getTrackNum(); + } + return 0; + } + + const juce::String getMixerTrackName(int index) { + if (auto graph = AudioCore::getInstance()->getGraph()) { + if (auto track = graph->getTrackProcessor(index)) { + return track->getTrackName(); + } + } + return ""; + } + + const juce::StringArray getMixerTrackNameList() { + int size = getMixerTrackNum(); + + juce::StringArray result; + for (int i = 0; i < size; i++) { + result.add(getMixerTrackName(i)); + } + + return result; + } + const juce::String getMIDICCChannelName(int channel) { return utils::getMIDICCChannelName(channel); } diff --git a/src/audioCore/quickAPI/QuickGet.h b/src/audioCore/quickAPI/QuickGet.h index 62f83331..2367df40 100644 --- a/src/audioCore/quickAPI/QuickGet.h +++ b/src/audioCore/quickAPI/QuickGet.h @@ -81,10 +81,20 @@ namespace quickAPI { /** Param Index,CC Channel */ using PluginParamLink = std::tuple; + + /** Src Index, Dst Index */ + using MIDILink = std::tuple; + + /** Src Index, Src Channel, Dst Index, Dst Channel */ + using AudioLink = std::tuple; + int getInstrNum(); PluginHolder getInstrPointer(int index); const juce::String getInstrName(int index); bool getInstrBypass(int index); + bool getInstrMIDIInputFromDevice(int index); + const juce::Array getInstrMIDIInputFromSource(int index); + const juce::Array getInstrAudioOutputToMixer(int index); EditorPointer getInstrEditor(int index); const juce::String getInstrName(PluginHolder pointer); bool getInstrBypass(PluginHolder pointer); @@ -92,7 +102,7 @@ namespace quickAPI { int getInstrMIDIChannel(PluginHolder pointer); bool getInstrMIDICCIntercept(PluginHolder pointer); bool getInstrMIDIOutput(PluginHolder pointer); - juce::Array getInstrParamCCLink(PluginHolder pointer); + const juce::Array getInstrParamCCLink(PluginHolder pointer); const juce::String getInstrParamName(PluginHolder pointer, int paramIndex); const juce::StringArray getInstrParamList(PluginHolder pointer); @@ -101,10 +111,18 @@ namespace quickAPI { int getEffectMIDIChannel(PluginHolder pointer); bool getEffectMIDICCIntercept(PluginHolder pointer); bool getEffectMIDIOutput(PluginHolder pointer); - juce::Array getEffectParamCCLink(PluginHolder pointer); + const juce::Array getEffectParamCCLink(PluginHolder pointer); const juce::String getEffectParamName(PluginHolder pointer, int paramIndex); const juce::StringArray getEffectParamList(PluginHolder pointer); + int getSeqTrackNum(); + const juce::String getSeqTrackName(int index); + const juce::StringArray getSeqTrackNameList(); + + int getMixerTrackNum(); + const juce::String getMixerTrackName(int index); + const juce::StringArray getMixerTrackNameList(); + const juce::String getMIDICCChannelName(int channel); const juce::StringArray getMIDICCChannelNameList(); } \ No newline at end of file diff --git a/src/ui/component/InstrIOComponent.cpp b/src/ui/component/InstrIOComponent.cpp index 2b913529..30de933e 100644 --- a/src/ui/component/InstrIOComponent.cpp +++ b/src/ui/component/InstrIOComponent.cpp @@ -1,5 +1,7 @@ #include "InstrIOComponent.h" +#include "../misc/DragSourceType.h" #include "../Utils.h" +#include "../../audioCore/AC_API.h" InstrIOComponent::InstrIOComponent(bool isInput) : isInput(isInput) { @@ -13,7 +15,7 @@ void InstrIOComponent::paint(juce::Graphics& g) { /** Color */ juce::Colour colorOn = this->isInput - ? juce::Colours::blue + ? juce::Colours::skyblue :juce::Colours::lightgreen; juce::Colour colorOff = this->isInput ? juce::Colours::indianred @@ -47,7 +49,135 @@ void InstrIOComponent::paint(juce::Graphics& g) { void InstrIOComponent::update(int index) { this->index = index; if (index > -1) { - /** TODO */ + if (this->isInput) { + /** Get Input Connections */ + this->midiInputFromDevice = quickAPI::getInstrMIDIInputFromDevice(index); + this->midiInputFromSource = quickAPI::getInstrMIDIInputFromSource(index); + + /** Create Temp */ + this->inputSourceTemp.clear(); + for (auto& [src, dst] : this->midiInputFromSource) { + this->inputSourceTemp.insert(src); + } + + /** Set Flag */ + this->linked = this->midiInputFromDevice || (!(this->inputSourceTemp.empty())); + } + else { + /** Get Output Connections */ + this->audioOutputToMixer = quickAPI::getInstrAudioOutputToMixer(index); + + /** Create Temp */ + this->outputMixerTemp.clear(); + for (auto& [src, srcc, dst, dstc] : this->audioOutputToMixer) { + this->outputMixerTemp.insert(dst); + } + + /** Set Flag */ + this->linked = !(this->outputMixerTemp.empty()); + } this->repaint(); } } + +void InstrIOComponent::mouseDrag(const juce::MouseEvent& event) { + if (event.mods.isLeftButtonDown()) { + if (auto dragSource = juce::DragAndDropContainer::findParentDragContainerFor(this)) { + dragSource->startDragging(this->getDragSourceDescription(), + this, juce::ScaledImage{}, true); + } + } +} + +void InstrIOComponent::mouseUp(const juce::MouseEvent& event) { + if (event.mods.isLeftButtonDown()) { + if (!event.mouseWasDraggedSinceMouseDown()) { + this->showLinkMenu(); + } + } + else if (event.mods.isRightButtonDown()) { + this->showUnlinkMenu(); + } +} + +enum InstrIOMenuType { + Device = 1, NumBase +}; + +void InstrIOComponent::showLinkMenu() { + auto menu = this->createLinkMenu(); + int result = menu.showAt(this); + + /** TODO */ +} + +void InstrIOComponent::showUnlinkMenu() { + auto menu = this->createUnlinkMenu(); + int result = menu.showAt(this); + + /** TODO */ +} + +juce::var InstrIOComponent::getDragSourceDescription() const { + auto object = std::make_unique(); + + if (this->isInput) { + object->setProperty("type", (int)DragSourceType::InstrInput); + } + else { + object->setProperty("type", (int)DragSourceType::InstrOutput); + } + object->setProperty("instr", this->index); + object->setProperty("name", this->name); + + return juce::var{ object.release() }; +} + +juce::PopupMenu InstrIOComponent::createLinkMenu() { + juce::PopupMenu menu; + + if (this->isInput) { + menu.addItem(InstrIOMenuType::Device, TRANS("MIDI Input"), true, this->midiInputFromDevice); + menu.addSeparator(); + + auto seqTracks = quickAPI::getSeqTrackNameList(); + for (int i = 0; i < seqTracks.size(); i++) { + juce::String name = TRANS("Sequencer Track") + " #" + juce::String{i} + " " + seqTracks[i]; + menu.addItem(InstrIOMenuType::NumBase + i, name, true, this->inputSourceTemp.contains(i)); + } + } + else { + auto mixerTracks = quickAPI::getMixerTrackNameList(); + for (int i = 0; i < mixerTracks.size(); i++) { + juce::String name = TRANS("Mixer Track") + " #" + juce::String{ i } + " " + mixerTracks[i]; + menu.addItem(InstrIOMenuType::NumBase + i, name, true, this->outputMixerTemp.contains(i)); + } + } + + return menu; +} + +juce::PopupMenu InstrIOComponent::createUnlinkMenu() { + juce::PopupMenu menu; + + if (this->isInput) { + menu.addItem(InstrIOMenuType::Device, TRANS("MIDI Input"), + this->midiInputFromDevice, this->midiInputFromDevice); + menu.addSeparator(); + + for (auto i : this->inputSourceTemp) { + auto trackName = quickAPI::getSeqTrackName(i); + juce::String name = TRANS("Sequencer Track") + " #" + juce::String{ i } + " " + trackName; + menu.addItem(InstrIOMenuType::NumBase + i, name, true, true); + } + } + else { + for (auto i : this->outputMixerTemp) { + auto trackName = quickAPI::getMixerTrackName(i); + juce::String name = TRANS("Mixer Track") + " #" + juce::String{ i } + " " + trackName; + menu.addItem(InstrIOMenuType::NumBase + i, name, true, true); + } + } + + return menu; +} diff --git a/src/ui/component/InstrIOComponent.h b/src/ui/component/InstrIOComponent.h index ac2609c4..8c7f74ac 100644 --- a/src/ui/component/InstrIOComponent.h +++ b/src/ui/component/InstrIOComponent.h @@ -11,10 +11,29 @@ class InstrIOComponent final : public juce::Component { void update(int index); + void mouseDrag(const juce::MouseEvent& event) override; + void mouseUp(const juce::MouseEvent& event) override; + private: const bool isInput; int index = -1; bool linked = false; + juce::String name; + + using MIDILink = std::tuple; + using AudioLink = std::tuple; + bool midiInputFromDevice = false; + juce::Array midiInputFromSource; + juce::Array audioOutputToMixer; + std::set inputSourceTemp; + std::set outputMixerTemp; + + void showLinkMenu(); + void showUnlinkMenu(); + + juce::var getDragSourceDescription() const; + juce::PopupMenu createLinkMenu(); + juce::PopupMenu createUnlinkMenu(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(InstrIOComponent) }; diff --git a/src/ui/misc/DragSourceType.h b/src/ui/misc/DragSourceType.h index 68be95ba..12d55728 100644 --- a/src/ui/misc/DragSourceType.h +++ b/src/ui/misc/DragSourceType.h @@ -3,5 +3,7 @@ enum class DragSourceType { Plugin = 0x1000, Source, - SourceSynth + SourceSynth, + InstrInput, + InstrOutput };