| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE library.
 -    Copyright (c) 2020 - Raw Material Software Limited
 - 
 -    JUCE is an open source library subject to commercial or open-source
 -    licensing.
 - 
 -    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 -    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 - 
 -    End User License Agreement: www.juce.com/juce-6-licence
 -    Privacy Policy: www.juce.com/juce-privacy-policy
 - 
 -    Or: You may also use this code under the terms of the GPL v3 (see
 -    www.gnu.org/licenses).
 - 
 -    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 -    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 -    DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - #include <JuceHeader.h>
 - #include "../UI/MainHostWindow.h"
 - #include "PluginGraph.h"
 - #include "InternalPlugins.h"
 - #include "../UI/GraphEditorPanel.h"
 - 
 - static std::unique_ptr<ScopedDPIAwarenessDisabler> makeDPIAwarenessDisablerForPlugin (const PluginDescription& desc)
 - {
 -     return shouldAutoScalePlugin (desc) ? std::make_unique<ScopedDPIAwarenessDisabler>()
 -                                         : nullptr;
 - }
 - 
 - //==============================================================================
 - PluginGraph::PluginGraph (AudioPluginFormatManager& fm, KnownPluginList& kpl)
 -     : FileBasedDocument (getFilenameSuffix(),
 -                          getFilenameWildcard(),
 -                          "Load a graph",
 -                          "Save a graph"),
 -       formatManager (fm),
 -       knownPlugins (kpl)
 - {
 -     newDocument();
 -     graph.addListener (this);
 - }
 - 
 - PluginGraph::~PluginGraph()
 - {
 -     graph.removeListener (this);
 -     graph.removeChangeListener (this);
 -     graph.clear();
 - }
 - 
 - PluginGraph::NodeID PluginGraph::getNextUID() noexcept
 - {
 -     return PluginGraph::NodeID (++(lastUID.uid));
 - }
 - 
 - //==============================================================================
 - void PluginGraph::changeListenerCallback (ChangeBroadcaster*)
 - {
 -     changed();
 - 
 -     for (int i = activePluginWindows.size(); --i >= 0;)
 -         if (! graph.getNodes().contains (activePluginWindows.getUnchecked(i)->node))
 -             activePluginWindows.remove (i);
 - }
 - 
 - AudioProcessorGraph::Node::Ptr PluginGraph::getNodeForName (const String& name) const
 - {
 -     for (auto* node : graph.getNodes())
 -         if (auto p = node->getProcessor())
 -             if (p->getName().equalsIgnoreCase (name))
 -                 return node;
 - 
 -     return nullptr;
 - }
 - 
 - void PluginGraph::addPlugin (const PluginDescription& desc, Point<double> pos)
 - {
 -     std::shared_ptr<ScopedDPIAwarenessDisabler> dpiDisabler = makeDPIAwarenessDisablerForPlugin (desc);
 - 
 -     formatManager.createPluginInstanceAsync (desc,
 -                                              graph.getSampleRate(),
 -                                              graph.getBlockSize(),
 -                                              [this, pos, dpiDisabler] (std::unique_ptr<AudioPluginInstance> instance, const String& error)
 -                                              {
 -                                                  addPluginCallback (std::move (instance), error, pos);
 -                                              });
 - }
 - 
 - void PluginGraph::addPluginCallback (std::unique_ptr<AudioPluginInstance> instance,
 -                                      const String& error, Point<double> pos)
 - {
 -     if (instance == nullptr)
 -     {
 -         AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 -                                           TRANS("Couldn't create plugin"),
 -                                           error);
 -     }
 -     else
 -     {
 -         instance->enableAllBuses();
 - 
 -         if (auto node = graph.addNode (std::move (instance)))
 -         {
 -             node->properties.set ("x", pos.x);
 -             node->properties.set ("y", pos.y);
 -             changed();
 -         }
 -     }
 - }
 - 
 - void PluginGraph::setNodePosition (NodeID nodeID, Point<double> pos)
 - {
 -     if (auto* n = graph.getNodeForId (nodeID))
 -     {
 -         n->properties.set ("x", jlimit (0.0, 1.0, pos.x));
 -         n->properties.set ("y", jlimit (0.0, 1.0, pos.y));
 -     }
 - }
 - 
 - Point<double> PluginGraph::getNodePosition (NodeID nodeID) const
 - {
 -     if (auto* n = graph.getNodeForId (nodeID))
 -         return { static_cast<double> (n->properties ["x"]),
 -                  static_cast<double> (n->properties ["y"]) };
 - 
 -     return {};
 - }
 - 
 - //==============================================================================
 - void PluginGraph::clear()
 - {
 -     closeAnyOpenPluginWindows();
 -     graph.clear();
 -     changed();
 - }
 - 
 - PluginWindow* PluginGraph::getOrCreateWindowFor (AudioProcessorGraph::Node* node, PluginWindow::Type type)
 - {
 -     jassert (node != nullptr);
 - 
 -    #if JUCE_IOS || JUCE_ANDROID
 -     closeAnyOpenPluginWindows();
 -    #else
 -     for (auto* w : activePluginWindows)
 -         if (w->node.get() == node && w->type == type)
 -             return w;
 -    #endif
 - 
 -     if (auto* processor = node->getProcessor())
 -     {
 -         if (auto* plugin = dynamic_cast<AudioPluginInstance*> (processor))
 -         {
 -             auto description = plugin->getPluginDescription();
 - 
 -             if (! plugin->hasEditor() && description.pluginFormatName == "Internal")
 -             {
 -                 getCommandManager().invokeDirectly (CommandIDs::showAudioSettings, false);
 -                 return nullptr;
 -             }
 - 
 -             auto localDpiDisabler = makeDPIAwarenessDisablerForPlugin (description);
 -             return activePluginWindows.add (new PluginWindow (node, type, activePluginWindows));
 -         }
 -     }
 - 
 -     return nullptr;
 - }
 - 
 - bool PluginGraph::closeAnyOpenPluginWindows()
 - {
 -     bool wasEmpty = activePluginWindows.isEmpty();
 -     activePluginWindows.clear();
 -     return ! wasEmpty;
 - }
 - 
 - //==============================================================================
 - String PluginGraph::getDocumentTitle()
 - {
 -     if (! getFile().exists())
 -         return "Unnamed";
 - 
 -     return getFile().getFileNameWithoutExtension();
 - }
 - 
 - void PluginGraph::newDocument()
 - {
 -     clear();
 -     setFile ({});
 - 
 -     graph.removeChangeListener (this);
 - 
 -     InternalPluginFormat internalFormat;
 - 
 -     jassert (internalFormat.getAllTypes().size() > 3);
 - 
 -     addPlugin (internalFormat.getAllTypes()[0], { 0.5,  0.1 });
 -     addPlugin (internalFormat.getAllTypes()[1], { 0.25, 0.1 });
 -     addPlugin (internalFormat.getAllTypes()[2], { 0.5,  0.9 });
 -     addPlugin (internalFormat.getAllTypes()[3], { 0.25, 0.9 });
 - 
 -     MessageManager::callAsync ([this]
 -     {
 -         setChangedFlag (false);
 -         graph.addChangeListener (this);
 -     });
 - }
 - 
 - Result PluginGraph::loadDocument (const File& file)
 - {
 -     if (auto xml = parseXMLIfTagMatches (file, "FILTERGRAPH"))
 -     {
 -         graph.removeChangeListener (this);
 -         restoreFromXml (*xml);
 - 
 -         MessageManager::callAsync ([this]
 -         {
 -             setChangedFlag (false);
 -             graph.addChangeListener (this);
 -         });
 - 
 -         return Result::ok();
 -     }
 - 
 -     return Result::fail ("Not a valid graph file");
 - }
 - 
 - Result PluginGraph::saveDocument (const File& file)
 - {
 -     auto xml = createXml();
 - 
 -     if (! xml->writeTo (file, {}))
 -         return Result::fail ("Couldn't write to the file");
 - 
 -     return Result::ok();
 - }
 - 
 - File PluginGraph::getLastDocumentOpened()
 - {
 -     RecentlyOpenedFilesList recentFiles;
 -     recentFiles.restoreFromString (getAppProperties().getUserSettings()
 -                                         ->getValue ("recentFilterGraphFiles"));
 - 
 -     return recentFiles.getFile (0);
 - }
 - 
 - void PluginGraph::setLastDocumentOpened (const File& file)
 - {
 -     RecentlyOpenedFilesList recentFiles;
 -     recentFiles.restoreFromString (getAppProperties().getUserSettings()
 -                                         ->getValue ("recentFilterGraphFiles"));
 - 
 -     recentFiles.addFile (file);
 - 
 -     getAppProperties().getUserSettings()
 -         ->setValue ("recentFilterGraphFiles", recentFiles.toString());
 - }
 - 
 - //==============================================================================
 - static void readBusLayoutFromXml (AudioProcessor::BusesLayout& busesLayout, AudioProcessor& plugin,
 -                                   const XmlElement& xml, bool isInput)
 - {
 -     auto& targetBuses = (isInput ? busesLayout.inputBuses
 -                                  : busesLayout.outputBuses);
 -     int maxNumBuses = 0;
 - 
 -     if (auto* buses = xml.getChildByName (isInput ? "INPUTS" : "OUTPUTS"))
 -     {
 -         for (auto* e : buses->getChildWithTagNameIterator ("BUS"))
 -         {
 -             const int busIdx = e->getIntAttribute ("index");
 -             maxNumBuses = jmax (maxNumBuses, busIdx + 1);
 - 
 -             // the number of buses on busesLayout may not be in sync with the plugin after adding buses
 -             // because adding an input bus could also add an output bus
 -             for (int actualIdx = plugin.getBusCount (isInput) - 1; actualIdx < busIdx; ++actualIdx)
 -                 if (! plugin.addBus (isInput))
 -                     return;
 - 
 -             for (int actualIdx = targetBuses.size() - 1; actualIdx < busIdx; ++actualIdx)
 -                 targetBuses.add (plugin.getChannelLayoutOfBus (isInput, busIdx));
 - 
 -             auto layout = e->getStringAttribute ("layout");
 - 
 -             if (layout.isNotEmpty())
 -                 targetBuses.getReference (busIdx) = AudioChannelSet::fromAbbreviatedString (layout);
 -         }
 -     }
 - 
 -     // if the plugin has more buses than specified in the xml, then try to remove them!
 -     while (maxNumBuses < targetBuses.size())
 -     {
 -         if (! plugin.removeBus (isInput))
 -             return;
 - 
 -         targetBuses.removeLast();
 -     }
 - }
 - 
 - //==============================================================================
 - static XmlElement* createBusLayoutXml (const AudioProcessor::BusesLayout& layout, const bool isInput)
 - {
 -     auto& buses = isInput ? layout.inputBuses
 -                           : layout.outputBuses;
 - 
 -     auto* xml = new XmlElement (isInput ? "INPUTS" : "OUTPUTS");
 - 
 -     for (int busIdx = 0; busIdx < buses.size(); ++busIdx)
 -     {
 -         auto& set = buses.getReference (busIdx);
 - 
 -         auto* bus = xml->createNewChildElement ("BUS");
 -         bus->setAttribute ("index", busIdx);
 -         bus->setAttribute ("layout", set.isDisabled() ? "disabled" : set.getSpeakerArrangementAsString());
 -     }
 - 
 -     return xml;
 - }
 - 
 - static XmlElement* createNodeXml (AudioProcessorGraph::Node* const node) noexcept
 - {
 -     if (auto* plugin = dynamic_cast<AudioPluginInstance*> (node->getProcessor()))
 -     {
 -         auto e = new XmlElement ("FILTER");
 - 
 -         e->setAttribute ("uid",      (int) node->nodeID.uid);
 -         e->setAttribute ("x",        node->properties ["x"].toString());
 -         e->setAttribute ("y",        node->properties ["y"].toString());
 - 
 -         for (int i = 0; i < (int) PluginWindow::Type::numTypes; ++i)
 -         {
 -             auto type = (PluginWindow::Type) i;
 - 
 -             if (node->properties.contains (PluginWindow::getOpenProp (type)))
 -             {
 -                 e->setAttribute (PluginWindow::getLastXProp (type), node->properties[PluginWindow::getLastXProp (type)].toString());
 -                 e->setAttribute (PluginWindow::getLastYProp (type), node->properties[PluginWindow::getLastYProp (type)].toString());
 -                 e->setAttribute (PluginWindow::getOpenProp (type),  node->properties[PluginWindow::getOpenProp (type)].toString());
 -             }
 -         }
 - 
 -         {
 -             PluginDescription pd;
 -             plugin->fillInPluginDescription (pd);
 -             e->addChildElement (pd.createXml().release());
 -         }
 - 
 -         {
 -             MemoryBlock m;
 -             node->getProcessor()->getStateInformation (m);
 -             e->createNewChildElement ("STATE")->addTextElement (m.toBase64Encoding());
 -         }
 - 
 -         auto layout = plugin->getBusesLayout();
 - 
 -         auto layouts = e->createNewChildElement ("LAYOUT");
 -         layouts->addChildElement (createBusLayoutXml (layout, true));
 -         layouts->addChildElement (createBusLayoutXml (layout, false));
 - 
 -         return e;
 -     }
 - 
 -     jassertfalse;
 -     return nullptr;
 - }
 - 
 - void PluginGraph::createNodeFromXml (const XmlElement& xml)
 - {
 -     PluginDescription pd;
 - 
 -     for (auto* e : xml.getChildIterator())
 -     {
 -         if (pd.loadFromXml (*e))
 -             break;
 -     }
 - 
 -     auto createInstanceWithFallback = [&]() -> std::unique_ptr<AudioPluginInstance>
 -     {
 -         auto createInstance = [this] (const PluginDescription& description)
 -         {
 -             String errorMessage;
 - 
 -             auto localDpiDisabler = makeDPIAwarenessDisablerForPlugin (description);
 - 
 -             return formatManager.createPluginInstance (description,
 -                                                        graph.getSampleRate(),
 -                                                        graph.getBlockSize(),
 -                                                        errorMessage);
 -         };
 - 
 -         if (auto instance = createInstance (pd))
 -             return instance;
 - 
 -         const auto allFormats = formatManager.getFormats();
 -         const auto matchingFormat = std::find_if (allFormats.begin(), allFormats.end(),
 -                                                   [&] (const AudioPluginFormat* f) { return f->getName() == pd.pluginFormatName; });
 - 
 -         if (matchingFormat == allFormats.end())
 -             return nullptr;
 - 
 -         const auto plugins = knownPlugins.getTypesForFormat (**matchingFormat);
 -         const auto matchingPlugin = std::find_if (plugins.begin(), plugins.end(),
 -                                                   [&] (const PluginDescription& desc) { return pd.uniqueId == desc.uniqueId; });
 - 
 -         if (matchingPlugin == plugins.end())
 -             return nullptr;
 - 
 -         return createInstance (*matchingPlugin);
 -     };
 - 
 -     if (auto instance = createInstanceWithFallback())
 -     {
 -         if (auto* layoutEntity = xml.getChildByName ("LAYOUT"))
 -         {
 -             auto layout = instance->getBusesLayout();
 - 
 -             readBusLayoutFromXml (layout, *instance, *layoutEntity, true);
 -             readBusLayoutFromXml (layout, *instance, *layoutEntity, false);
 - 
 -             instance->setBusesLayout (layout);
 -         }
 - 
 -         if (auto node = graph.addNode (std::move (instance), NodeID ((uint32) xml.getIntAttribute ("uid"))))
 -         {
 -             if (auto* state = xml.getChildByName ("STATE"))
 -             {
 -                 MemoryBlock m;
 -                 m.fromBase64Encoding (state->getAllSubText());
 - 
 -                 node->getProcessor()->setStateInformation (m.getData(), (int) m.getSize());
 -             }
 - 
 -             node->properties.set ("x", xml.getDoubleAttribute ("x"));
 -             node->properties.set ("y", xml.getDoubleAttribute ("y"));
 - 
 -             for (int i = 0; i < (int) PluginWindow::Type::numTypes; ++i)
 -             {
 -                 auto type = (PluginWindow::Type) i;
 - 
 -                 if (xml.hasAttribute (PluginWindow::getOpenProp (type)))
 -                 {
 -                     node->properties.set (PluginWindow::getLastXProp (type), xml.getIntAttribute (PluginWindow::getLastXProp (type)));
 -                     node->properties.set (PluginWindow::getLastYProp (type), xml.getIntAttribute (PluginWindow::getLastYProp (type)));
 -                     node->properties.set (PluginWindow::getOpenProp  (type), xml.getIntAttribute (PluginWindow::getOpenProp (type)));
 - 
 -                     if (node->properties[PluginWindow::getOpenProp (type)])
 -                     {
 -                         jassert (node->getProcessor() != nullptr);
 - 
 -                         if (auto w = getOrCreateWindowFor (node, type))
 -                             w->toFront (true);
 -                     }
 -                 }
 -             }
 -         }
 -     }
 - }
 - 
 - std::unique_ptr<XmlElement> PluginGraph::createXml() const
 - {
 -     auto xml = std::make_unique<XmlElement> ("FILTERGRAPH");
 - 
 -     for (auto* node : graph.getNodes())
 -         xml->addChildElement (createNodeXml (node));
 - 
 -     for (auto& connection : graph.getConnections())
 -     {
 -         auto e = xml->createNewChildElement ("CONNECTION");
 - 
 -         e->setAttribute ("srcFilter", (int) connection.source.nodeID.uid);
 -         e->setAttribute ("srcChannel", connection.source.channelIndex);
 -         e->setAttribute ("dstFilter", (int) connection.destination.nodeID.uid);
 -         e->setAttribute ("dstChannel", connection.destination.channelIndex);
 -     }
 - 
 -     return xml;
 - }
 - 
 - void PluginGraph::restoreFromXml (const XmlElement& xml)
 - {
 -     clear();
 - 
 -     for (auto* e : xml.getChildWithTagNameIterator ("FILTER"))
 -     {
 -         createNodeFromXml (*e);
 -         changed();
 -     }
 - 
 -     for (auto* e : xml.getChildWithTagNameIterator ("CONNECTION"))
 -     {
 -         graph.addConnection ({ { NodeID ((uint32) e->getIntAttribute ("srcFilter")), e->getIntAttribute ("srcChannel") },
 -                                { NodeID ((uint32) e->getIntAttribute ("dstFilter")), e->getIntAttribute ("dstChannel") } });
 -     }
 - 
 -     graph.removeIllegalConnections();
 - }
 - 
 - File PluginGraph::getDefaultGraphDocumentOnMobile()
 - {
 -     auto persistantStorageLocation = File::getSpecialLocation (File::userApplicationDataDirectory);
 -     return persistantStorageLocation.getChildFile ("state.filtergraph");
 - }
 
 
  |