| @@ -1,6 +1,32 @@ | |||
| JUCE breaking changes | |||
| ===================== | |||
| Develop | |||
| ======= | |||
| Change | |||
| ------ | |||
| AudioProcessorGraph interface has changed in a number of ways - Node objects | |||
| are now reference counted, there are different accessor methods to iterate them, | |||
| and misc other small improvements to the API | |||
| Possible Issues | |||
| --------------- | |||
| The changes won't cause any silent errors in user code, but will require some | |||
| manual refactoring | |||
| Workaround | |||
| ---------- | |||
| Just find equivalent new methods to replace existing code. | |||
| Rationale | |||
| --------- | |||
| The graph class was extremely old and creaky, and these changes is the start of | |||
| an improvement process that should eventually result in it being broken down | |||
| into fundamental graph building block classes for use in other contexts. | |||
| Version 5.2.0 | |||
| ============= | |||
| @@ -23,6 +23,8 @@ ifndef CONFIG | |||
| CONFIG=Debug | |||
| endif | |||
| JUCE_ARCH_LABEL := $(shell uname -m) | |||
| ifeq ($(CONFIG),Debug) | |||
| JUCE_BINDIR := build | |||
| JUCE_LIBDIR := build | |||
| @@ -70,6 +70,7 @@ | |||
| 6692043E22BB181F01767845 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MainHostWindow.h; path = ../../Source/MainHostWindow.h; sourceTree = "SOURCE_ROOT"; }; | |||
| 683CEE986A2467C850FE99E6 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_core.mm"; path = "../../JuceLibraryCode/include_juce_core.mm"; sourceTree = "SOURCE_ROOT"; }; | |||
| 6A71B2BCAC4239072BC2BD7E = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_basics"; path = "../../../../modules/juce_audio_basics"; sourceTree = "SOURCE_ROOT"; }; | |||
| 714C53257417E615916687E5 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PluginWindow.h; path = ../../Source/PluginWindow.h; sourceTree = "SOURCE_ROOT"; }; | |||
| 7DA35787B5F6F7440D667CC8 = {isa = PBXFileReference; lastKnownFileType = file.nib; name = RecentFilesMenuTemplate.nib; path = RecentFilesMenuTemplate.nib; sourceTree = "SOURCE_ROOT"; }; | |||
| 81C1A7770E082F56FE5A90A7 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_opengl"; path = "../../../../modules/juce_opengl"; sourceTree = "SOURCE_ROOT"; }; | |||
| 82800DBA287EF4BAB13B42FB = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_graphics.mm"; path = "../../JuceLibraryCode/include_juce_graphics.mm"; sourceTree = "SOURCE_ROOT"; }; | |||
| @@ -115,7 +116,8 @@ | |||
| 362BB539489999164C3A3D5B, | |||
| EE1BEF4055936CD0C543687C, | |||
| 1EC0F33A3BABE58138317375, | |||
| 6692043E22BB181F01767845, ); name = "Plugin Host"; sourceTree = "<group>"; }; | |||
| 6692043E22BB181F01767845, | |||
| 714C53257417E615916687E5, ); name = "Plugin Host"; sourceTree = "<group>"; }; | |||
| 9D8FE1F65CAD416AA606C47A = {isa = PBXGroup; children = ( | |||
| 6A71B2BCAC4239072BC2BD7E, | |||
| 5313EB852E41EE58B199B9A2, | |||
| @@ -1779,6 +1779,7 @@ | |||
| <ClInclude Include="..\..\Source\GraphEditorPanel.h"/> | |||
| <ClInclude Include="..\..\Source\InternalFilters.h"/> | |||
| <ClInclude Include="..\..\Source\MainHostWindow.h"/> | |||
| <ClInclude Include="..\..\Source\PluginWindow.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\audio_play_head\juce_AudioPlayHead.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioChannelSet.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioDataConverters.h"/> | |||
| @@ -2169,6 +2169,9 @@ | |||
| <ClInclude Include="..\..\Source\MainHostWindow.h"> | |||
| <Filter>Plugin Host</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\Source\PluginWindow.h"> | |||
| <Filter>Plugin Host</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\audio_play_head\juce_AudioPlayHead.h"> | |||
| <Filter>Juce Modules\juce_audio_basics\audio_play_head</Filter> | |||
| </ClInclude> | |||
| @@ -1779,6 +1779,7 @@ | |||
| <ClInclude Include="..\..\Source\GraphEditorPanel.h"/> | |||
| <ClInclude Include="..\..\Source\InternalFilters.h"/> | |||
| <ClInclude Include="..\..\Source\MainHostWindow.h"/> | |||
| <ClInclude Include="..\..\Source\PluginWindow.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\audio_play_head\juce_AudioPlayHead.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioChannelSet.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioDataConverters.h"/> | |||
| @@ -2169,6 +2169,9 @@ | |||
| <ClInclude Include="..\..\Source\MainHostWindow.h"> | |||
| <Filter>Plugin Host</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\Source\PluginWindow.h"> | |||
| <Filter>Plugin Host</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\audio_play_head\juce_AudioPlayHead.h"> | |||
| <Filter>Juce Modules\juce_audio_basics\audio_play_head</Filter> | |||
| </ClInclude> | |||
| @@ -1779,6 +1779,7 @@ | |||
| <ClInclude Include="..\..\Source\GraphEditorPanel.h"/> | |||
| <ClInclude Include="..\..\Source\InternalFilters.h"/> | |||
| <ClInclude Include="..\..\Source\MainHostWindow.h"/> | |||
| <ClInclude Include="..\..\Source\PluginWindow.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\audio_play_head\juce_AudioPlayHead.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioChannelSet.h"/> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\buffers\juce_AudioDataConverters.h"/> | |||
| @@ -2169,6 +2169,9 @@ | |||
| <ClInclude Include="..\..\Source\MainHostWindow.h"> | |||
| <Filter>Plugin Host</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\Source\PluginWindow.h"> | |||
| <Filter>Plugin Host</Filter> | |||
| </ClInclude> | |||
| <ClInclude Include="..\..\..\..\modules\juce_audio_basics\audio_play_head\juce_AudioPlayHead.h"> | |||
| <Filter>Juce Modules\juce_audio_basics\audio_play_head</Filter> | |||
| </ClInclude> | |||
| @@ -10,9 +10,11 @@ | |||
| objCExtraSuffix="M73TRi" vst3Folder="" extraCompilerFlags="-Wall -Wshadow -Wstrict-aliasing -Wconversion -Wsign-compare -Woverloaded-virtual -Wextra-semi"> | |||
| <CONFIGURATIONS> | |||
| <CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="Plugin Host" | |||
| osxSDK="default" osxCompatibility="default" osxArchitecture="default"/> | |||
| osxSDK="default" osxCompatibility="default" osxArchitecture="default" | |||
| enablePluginBinaryCopyStep="1"/> | |||
| <CONFIGURATION name="Release" isDebug="0" optimisation="2" targetName="Plugin Host" | |||
| osxSDK="default" osxCompatibility="default" osxArchitecture="default"/> | |||
| osxSDK="default" osxCompatibility="default" osxArchitecture="default" | |||
| enablePluginBinaryCopyStep="1"/> | |||
| </CONFIGURATIONS> | |||
| <MODULEPATHS> | |||
| <MODULEPATH id="juce_video" path="../../modules"/> | |||
| @@ -58,9 +60,11 @@ | |||
| <VS2013 targetFolder="Builds/VisualStudio2013" vstFolder="" vst3Folder=""> | |||
| <CONFIGURATIONS> | |||
| <CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="32-bit" | |||
| isDebug="1" optimisation="1" targetName="Plugin Host"/> | |||
| isDebug="1" optimisation="1" targetName="Plugin Host" debugInformationFormat="ProgramDatabase" | |||
| enablePluginBinaryCopyStep="0"/> | |||
| <CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="32-bit" | |||
| isDebug="0" optimisation="3" targetName="Plugin Host"/> | |||
| isDebug="0" optimisation="3" targetName="Plugin Host" debugInformationFormat="ProgramDatabase" | |||
| enablePluginBinaryCopyStep="0" linkTimeOptimisation="1"/> | |||
| </CONFIGURATIONS> | |||
| <MODULEPATHS> | |||
| <MODULEPATH id="juce_video" path="../../modules"/> | |||
| @@ -82,9 +86,11 @@ | |||
| <VS2015 targetFolder="Builds/VisualStudio2015" vstFolder="" vst3Folder=""> | |||
| <CONFIGURATIONS> | |||
| <CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="x64" | |||
| isDebug="1" optimisation="1" targetName="Plugin Host"/> | |||
| isDebug="1" optimisation="1" targetName="Plugin Host" debugInformationFormat="ProgramDatabase" | |||
| enablePluginBinaryCopyStep="0"/> | |||
| <CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="x64" | |||
| isDebug="0" optimisation="3" targetName="Plugin Host"/> | |||
| isDebug="0" optimisation="3" targetName="Plugin Host" debugInformationFormat="ProgramDatabase" | |||
| enablePluginBinaryCopyStep="0" linkTimeOptimisation="1"/> | |||
| </CONFIGURATIONS> | |||
| <MODULEPATHS> | |||
| <MODULEPATH id="juce_video" path="../../modules"/> | |||
| @@ -106,9 +112,11 @@ | |||
| <VS2017 targetFolder="Builds/VisualStudio2017" vst3Folder=""> | |||
| <CONFIGURATIONS> | |||
| <CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="x64" | |||
| isDebug="1" optimisation="1" targetName="Plugin Host"/> | |||
| isDebug="1" optimisation="1" targetName="Plugin Host" debugInformationFormat="ProgramDatabase" | |||
| enablePluginBinaryCopyStep="0"/> | |||
| <CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="x64" | |||
| isDebug="0" optimisation="3" targetName="Plugin Host"/> | |||
| isDebug="0" optimisation="3" targetName="Plugin Host" debugInformationFormat="ProgramDatabase" | |||
| enablePluginBinaryCopyStep="0" linkTimeOptimisation="1"/> | |||
| </CONFIGURATIONS> | |||
| <MODULEPATHS> | |||
| <MODULEPATH id="juce_video" path="../../modules"/> | |||
| @@ -151,6 +159,7 @@ | |||
| file="Source/MainHostWindow.cpp"/> | |||
| <FILE id="h1kpxyzHi" name="MainHostWindow.h" compile="0" resource="0" | |||
| file="Source/MainHostWindow.h"/> | |||
| <FILE id="ZwQDmm" name="PluginWindow.h" compile="0" resource="0" file="Source/PluginWindow.h"/> | |||
| </MAINGROUP> | |||
| <JUCEOPTIONS JUCE_WASAPI="enabled" JUCE_DIRECTSOUND="enabled" JUCE_ALSA="enabled" | |||
| JUCE_QUICKTIME="disabled" JUCE_USE_FLAC="disabled" JUCE_USE_OGGVORBIS="disabled" | |||
| @@ -32,22 +32,17 @@ | |||
| //============================================================================== | |||
| const int FilterGraph::midiChannelNumber = 0x1000; | |||
| FilterGraph::FilterGraph (AudioPluginFormatManager& fm) | |||
| : FileBasedDocument (filenameSuffix, | |||
| filenameWildcard, | |||
| : FileBasedDocument (getFilenameSuffix(), | |||
| getFilenameWildcard(), | |||
| "Load a filter graph", | |||
| "Save a filter graph"), | |||
| formatManager (fm) | |||
| { | |||
| InternalPluginFormat internalFormat; | |||
| addFilter (internalFormat.audioInDesc, { 0.5, 0.1 }); | |||
| addFilter (internalFormat.midiInDesc, { 0.25, 0.1 }); | |||
| addFilter (internalFormat.audioOutDesc, { 0.5, 0.9 }); | |||
| newDocument(); | |||
| graph.addListener (this); | |||
| graph.addChangeListener (this); | |||
| setChangedFlag (false); | |||
| } | |||
| @@ -55,42 +50,36 @@ FilterGraph::FilterGraph (AudioPluginFormatManager& fm) | |||
| FilterGraph::~FilterGraph() | |||
| { | |||
| graph.addListener (this); | |||
| graph.removeChangeListener (this); | |||
| graph.clear(); | |||
| } | |||
| uint32 FilterGraph::getNextUID() noexcept | |||
| FilterGraph::NodeID FilterGraph::getNextUID() noexcept | |||
| { | |||
| return ++lastUID; | |||
| } | |||
| //============================================================================== | |||
| int FilterGraph::getNumFilters() const noexcept | |||
| { | |||
| return graph.getNumNodes(); | |||
| } | |||
| AudioProcessorGraph::Node::Ptr FilterGraph::getNode (int index) const noexcept | |||
| void FilterGraph::changeListenerCallback (ChangeBroadcaster*) | |||
| { | |||
| return graph.getNode (index); | |||
| } | |||
| changed(); | |||
| AudioProcessorGraph::Node::Ptr FilterGraph::getNodeForId (uint32 uid) const | |||
| { | |||
| return graph.getNodeForId (uid); | |||
| for (int i = activePluginWindows.size(); --i >= 0;) | |||
| if (! graph.getNodes().contains (activePluginWindows.getUnchecked(i)->node)) | |||
| activePluginWindows.remove (i); | |||
| } | |||
| AudioProcessorGraph::Node::Ptr FilterGraph::getNodeForName (const String& name) const | |||
| { | |||
| for (int i = 0; i < graph.getNumNodes(); i++) | |||
| if (auto node = graph.getNode (i)) | |||
| if (auto p = node->getProcessor()) | |||
| if (p->getName().equalsIgnoreCase (name)) | |||
| return node; | |||
| for (auto* node : graph.getNodes()) | |||
| if (auto p = node->getProcessor()) | |||
| if (p->getName().equalsIgnoreCase (name)) | |||
| return node; | |||
| return nullptr; | |||
| } | |||
| void FilterGraph::addFilter (const PluginDescription& desc, Point<double> p) | |||
| void FilterGraph::addPlugin (const PluginDescription& desc, Point<double> p) | |||
| { | |||
| struct AsyncCallback : public AudioPluginFormat::InstantiationCompletionCallback | |||
| { | |||
| @@ -106,7 +95,9 @@ void FilterGraph::addFilter (const PluginDescription& desc, Point<double> p) | |||
| Point<double> position; | |||
| }; | |||
| formatManager.createPluginInstanceAsync (desc, graph.getSampleRate(), graph.getBlockSize(), | |||
| formatManager.createPluginInstanceAsync (desc, | |||
| graph.getSampleRate(), | |||
| graph.getBlockSize(), | |||
| new AsyncCallback (*this, p)); | |||
| } | |||
| @@ -122,7 +113,7 @@ void FilterGraph::addFilterCallback (AudioPluginInstance* instance, const String | |||
| { | |||
| instance->enableAllBuses(); | |||
| if (auto* node = graph.addNode (instance)) | |||
| if (auto node = graph.addNode (instance)) | |||
| { | |||
| node->properties.set ("x", pos.x); | |||
| node->properties.set ("y", pos.y); | |||
| @@ -131,38 +122,18 @@ void FilterGraph::addFilterCallback (AudioPluginInstance* instance, const String | |||
| } | |||
| } | |||
| void FilterGraph::removeFilter (const uint32 id) | |||
| void FilterGraph::setNodePosition (NodeID nodeID, Point<double> pos) | |||
| { | |||
| PluginWindow::closeCurrentlyOpenWindowsFor (id); | |||
| if (graph.removeNode (id)) | |||
| changed(); | |||
| } | |||
| void FilterGraph::disconnectFilter (const uint32 id) | |||
| { | |||
| if (graph.disconnectNode (id)) | |||
| changed(); | |||
| } | |||
| void FilterGraph::removeIllegalConnections() | |||
| { | |||
| if (graph.removeIllegalConnections()) | |||
| changed(); | |||
| } | |||
| void FilterGraph::setNodePosition (const uint32 nodeId, double x, double y) | |||
| { | |||
| if (AudioProcessorGraph::Node::Ptr n = graph.getNodeForId (nodeId)) | |||
| if (auto* n = graph.getNodeForId (nodeID)) | |||
| { | |||
| n->properties.set ("x", jlimit (0.0, 1.0, x)); | |||
| n->properties.set ("y", jlimit (0.0, 1.0, y)); | |||
| n->properties.set ("x", jlimit (0.0, 1.0, pos.x)); | |||
| n->properties.set ("y", jlimit (0.0, 1.0, pos.y)); | |||
| } | |||
| } | |||
| Point<double> FilterGraph::getNodePosition (const uint32 nodeId) const | |||
| Point<double> FilterGraph::getNodePosition (NodeID nodeID) const | |||
| { | |||
| if (auto n = graph.getNodeForId (nodeId)) | |||
| if (auto* n = graph.getNodeForId (nodeID)) | |||
| return { static_cast<double> (n->properties ["x"]), | |||
| static_cast<double> (n->properties ["y"]) }; | |||
| @@ -170,62 +141,45 @@ Point<double> FilterGraph::getNodePosition (const uint32 nodeId) const | |||
| } | |||
| //============================================================================== | |||
| int FilterGraph::getNumConnections() const noexcept | |||
| { | |||
| return graph.getNumConnections(); | |||
| } | |||
| const AudioProcessorGraph::Connection* FilterGraph::getConnection (const int index) const noexcept | |||
| { | |||
| return graph.getConnection (index); | |||
| } | |||
| const AudioProcessorGraph::Connection* FilterGraph::getConnectionBetween (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| uint32 destFilterUID, int destFilterChannel) const noexcept | |||
| void FilterGraph::clear() | |||
| { | |||
| return graph.getConnectionBetween (sourceFilterUID, sourceFilterChannel, | |||
| destFilterUID, destFilterChannel); | |||
| closeAnyOpenPluginWindows(); | |||
| graph.clear(); | |||
| changed(); | |||
| } | |||
| bool FilterGraph::canConnect (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| uint32 destFilterUID, int destFilterChannel) const noexcept | |||
| PluginWindow* FilterGraph::getOrCreateWindowFor (AudioProcessorGraph::Node* node, PluginWindow::Type type) | |||
| { | |||
| return graph.canConnect (sourceFilterUID, sourceFilterChannel, | |||
| destFilterUID, destFilterChannel); | |||
| } | |||
| jassert (node != nullptr); | |||
| bool FilterGraph::addConnection (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| uint32 destFilterUID, int destFilterChannel) | |||
| { | |||
| const bool result = graph.addConnection (sourceFilterUID, sourceFilterChannel, | |||
| destFilterUID, destFilterChannel); | |||
| for (auto* w : activePluginWindows) | |||
| if (w->node == node && w->type == type) | |||
| return w; | |||
| if (result) | |||
| changed(); | |||
| if (auto* processor = node->getProcessor()) | |||
| { | |||
| if (auto* plugin = dynamic_cast<AudioPluginInstance*> (processor)) | |||
| { | |||
| auto description = plugin->getPluginDescription(); | |||
| return result; | |||
| } | |||
| if (description.pluginFormatName == "Internal") | |||
| { | |||
| getCommandManager().invokeDirectly (CommandIDs::showAudioSettings, false); | |||
| return nullptr; | |||
| } | |||
| } | |||
| void FilterGraph::removeConnection (const int index) | |||
| { | |||
| graph.removeConnection (index); | |||
| changed(); | |||
| } | |||
| return activePluginWindows.add (new PluginWindow (node, type, activePluginWindows)); | |||
| } | |||
| void FilterGraph::removeConnection (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| uint32 destFilterUID, int destFilterChannel) | |||
| { | |||
| if (graph.removeConnection (sourceFilterUID, sourceFilterChannel, | |||
| destFilterUID, destFilterChannel)) | |||
| changed(); | |||
| return nullptr; | |||
| } | |||
| void FilterGraph::clear() | |||
| bool FilterGraph::closeAnyOpenPluginWindows() | |||
| { | |||
| PluginWindow::closeAllCurrentlyOpenWindows(); | |||
| graph.clear(); | |||
| changed(); | |||
| bool wasEmpty = activePluginWindows.isEmpty(); | |||
| activePluginWindows.clear(); | |||
| return ! wasEmpty; | |||
| } | |||
| //============================================================================== | |||
| @@ -244,9 +198,9 @@ void FilterGraph::newDocument() | |||
| InternalPluginFormat internalFormat; | |||
| addFilter (internalFormat.audioInDesc, { 0.5, 0.1 }); | |||
| addFilter (internalFormat.midiInDesc, { 0.25, 0.1 }); | |||
| addFilter (internalFormat.audioOutDesc, { 0.5, 0.9 }); | |||
| addPlugin (internalFormat.audioInDesc, { 0.5, 0.1 }); | |||
| addPlugin (internalFormat.midiInDesc, { 0.25, 0.1 }); | |||
| addPlugin (internalFormat.audioOutDesc, { 0.5, 0.9 }); | |||
| setChangedFlag (false); | |||
| } | |||
| @@ -267,7 +221,7 @@ Result FilterGraph::saveDocument (const File& file) | |||
| { | |||
| ScopedPointer<XmlElement> xml (createXml()); | |||
| if (! xml->writeToFile (file, String())) | |||
| if (! xml->writeToFile (file, {})) | |||
| return Result::fail ("Couldn't write to the file"); | |||
| return Result::ok(); | |||
| @@ -295,9 +249,11 @@ void FilterGraph::setLastDocumentOpened (const File& file) | |||
| } | |||
| //============================================================================== | |||
| static void readBusLayoutFromXml (AudioProcessor::BusesLayout& busesLayout, AudioProcessor* plugin, const XmlElement& xml, const bool isInput) | |||
| static void readBusLayoutFromXml (AudioProcessor::BusesLayout& busesLayout, AudioProcessor* plugin, | |||
| const XmlElement& xml, const bool isInput) | |||
| { | |||
| Array<AudioChannelSet>& targetBuses = (isInput ? busesLayout.inputBuses : busesLayout.outputBuses); | |||
| auto& targetBuses = (isInput ? busesLayout.inputBuses | |||
| : busesLayout.outputBuses); | |||
| int maxNumBuses = 0; | |||
| if (auto* buses = xml.getChildByName (isInput ? "INPUTS" : "OUTPUTS")) | |||
| @@ -310,12 +266,13 @@ static void readBusLayoutFromXml (AudioProcessor::BusesLayout& busesLayout, Audi | |||
| // 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; | |||
| if (! plugin->addBus (isInput)) | |||
| return; | |||
| for (int actualIdx = targetBuses.size() - 1; actualIdx < busIdx; ++actualIdx) | |||
| targetBuses.add (plugin->getChannelLayoutOfBus (isInput, busIdx)); | |||
| const String& layout = e->getStringAttribute("layout"); | |||
| auto layout = e->getStringAttribute ("layout"); | |||
| if (layout.isNotEmpty()) | |||
| targetBuses.getReference (busIdx) = AudioChannelSet::fromAbbreviatedString (layout); | |||
| @@ -335,22 +292,18 @@ static void readBusLayoutFromXml (AudioProcessor::BusesLayout& busesLayout, Audi | |||
| //============================================================================== | |||
| static XmlElement* createBusLayoutXml (const AudioProcessor::BusesLayout& layout, const bool isInput) | |||
| { | |||
| const Array<AudioChannelSet>& buses = (isInput ? layout.inputBuses : layout.outputBuses); | |||
| auto& buses = isInput ? layout.inputBuses | |||
| : layout.outputBuses; | |||
| XmlElement* xml = new XmlElement (isInput ? "INPUTS" : "OUTPUTS"); | |||
| auto* xml = new XmlElement (isInput ? "INPUTS" : "OUTPUTS"); | |||
| const int n = buses.size(); | |||
| for (int busIdx = 0; busIdx < n; ++busIdx) | |||
| for (int busIdx = 0; busIdx < buses.size(); ++busIdx) | |||
| { | |||
| XmlElement* bus = new XmlElement ("BUS"); | |||
| bus->setAttribute ("index", busIdx); | |||
| const AudioChannelSet& set = buses.getReference (busIdx); | |||
| const String layoutName = set.isDisabled() ? "disabled" : set.getSpeakerArrangementAsString(); | |||
| bus->setAttribute ("layout", layoutName); | |||
| auto& set = buses.getReference (busIdx); | |||
| xml->addChildElement (bus); | |||
| auto* bus = xml->createNewChildElement ("BUS"); | |||
| bus->setAttribute ("index", busIdx); | |||
| bus->setAttribute ("layout", set.isDisabled() ? "disabled" : set.getSpeakerArrangementAsString()); | |||
| } | |||
| return xml; | |||
| @@ -358,53 +311,48 @@ static XmlElement* createBusLayoutXml (const AudioProcessor::BusesLayout& layout | |||
| static XmlElement* createNodeXml (AudioProcessorGraph::Node* const node) noexcept | |||
| { | |||
| AudioPluginInstance* plugin = dynamic_cast<AudioPluginInstance*> (node->getProcessor()); | |||
| if (plugin == nullptr) | |||
| if (auto* plugin = dynamic_cast<AudioPluginInstance*> (node->getProcessor())) | |||
| { | |||
| jassertfalse; | |||
| return nullptr; | |||
| } | |||
| auto e = new XmlElement ("FILTER"); | |||
| e->setAttribute ("uid", (int) node->nodeID); | |||
| e->setAttribute ("x", node->properties ["x"].toString()); | |||
| e->setAttribute ("y", node->properties ["y"].toString()); | |||
| XmlElement* e = new XmlElement ("FILTER"); | |||
| e->setAttribute ("uid", (int) node->nodeId); | |||
| 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; | |||
| for (int i = 0; i < PluginWindow::NumTypes; ++i) | |||
| { | |||
| PluginWindow::WindowFormatType type = (PluginWindow::WindowFormatType) 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()); | |||
| } | |||
| } | |||
| if (node->properties.contains (getOpenProp (type))) | |||
| { | |||
| e->setAttribute (getLastXProp (type), node->properties[getLastXProp (type)].toString()); | |||
| e->setAttribute (getLastYProp (type), node->properties[getLastYProp (type)].toString()); | |||
| e->setAttribute (getOpenProp (type), node->properties[getOpenProp (type)].toString()); | |||
| PluginDescription pd; | |||
| plugin->fillInPluginDescription (pd); | |||
| e->addChildElement (pd.createXml()); | |||
| } | |||
| } | |||
| PluginDescription pd; | |||
| plugin->fillInPluginDescription (pd); | |||
| e->addChildElement (pd.createXml()); | |||
| XmlElement* state = new XmlElement ("STATE"); | |||
| MemoryBlock m; | |||
| node->getProcessor()->getStateInformation (m); | |||
| state->addTextElement (m.toBase64Encoding()); | |||
| e->addChildElement (state); | |||
| { | |||
| MemoryBlock m; | |||
| node->getProcessor()->getStateInformation (m); | |||
| e->createNewChildElement ("STATE")->addTextElement (m.toBase64Encoding()); | |||
| } | |||
| XmlElement* layouts = new XmlElement ("LAYOUT"); | |||
| const AudioProcessor::BusesLayout layout = plugin->getBusesLayout(); | |||
| auto layout = plugin->getBusesLayout(); | |||
| const bool isInputChoices[] = { true, false }; | |||
| for (bool isInput : isInputChoices) | |||
| layouts->addChildElement (createBusLayoutXml (layout, isInput)); | |||
| auto layouts = e->createNewChildElement ("LAYOUT"); | |||
| layouts->addChildElement (createBusLayoutXml (layout, true)); | |||
| layouts->addChildElement (createBusLayoutXml (layout, false)); | |||
| e->addChildElement (layouts); | |||
| return e; | |||
| } | |||
| return e; | |||
| jassertfalse; | |||
| return nullptr; | |||
| } | |||
| void FilterGraph::createNodeFromXml (const XmlElement& xml) | |||
| @@ -419,51 +367,50 @@ void FilterGraph::createNodeFromXml (const XmlElement& xml) | |||
| String errorMessage; | |||
| AudioPluginInstance* instance = formatManager.createPluginInstance (pd, graph.getSampleRate(), graph.getBlockSize(), errorMessage); | |||
| if (instance == nullptr) | |||
| return; | |||
| if (const XmlElement* const layoutEntity = xml.getChildByName ("LAYOUT")) | |||
| if (auto* instance = formatManager.createPluginInstance (pd, graph.getSampleRate(), | |||
| graph.getBlockSize(), errorMessage)) | |||
| { | |||
| AudioProcessor::BusesLayout layout = instance->getBusesLayout(); | |||
| const bool isInputChoices[] = { true, false }; | |||
| for (bool isInput : isInputChoices) | |||
| readBusLayoutFromXml (layout, instance, *layoutEntity, isInput); | |||
| instance->setBusesLayout (layout); | |||
| } | |||
| AudioProcessorGraph::Node::Ptr node (graph.addNode (instance, (uint32) xml.getIntAttribute ("uid"))); | |||
| if (auto* layoutEntity = xml.getChildByName ("LAYOUT")) | |||
| { | |||
| auto layout = instance->getBusesLayout(); | |||
| if (const XmlElement* const state = xml.getChildByName ("STATE")) | |||
| { | |||
| MemoryBlock m; | |||
| m.fromBase64Encoding (state->getAllSubText()); | |||
| readBusLayoutFromXml (layout, instance, *layoutEntity, true); | |||
| readBusLayoutFromXml (layout, instance, *layoutEntity, false); | |||
| node->getProcessor()->setStateInformation (m.getData(), (int) m.getSize()); | |||
| } | |||
| instance->setBusesLayout (layout); | |||
| } | |||
| node->properties.set ("x", xml.getDoubleAttribute ("x")); | |||
| node->properties.set ("y", xml.getDoubleAttribute ("y")); | |||
| if (auto node = graph.addNode (instance, (NodeID) xml.getIntAttribute ("uid"))) | |||
| { | |||
| if (auto* state = xml.getChildByName ("STATE")) | |||
| { | |||
| MemoryBlock m; | |||
| m.fromBase64Encoding (state->getAllSubText()); | |||
| for (int i = 0; i < PluginWindow::NumTypes; ++i) | |||
| { | |||
| PluginWindow::WindowFormatType type = (PluginWindow::WindowFormatType) i; | |||
| node->getProcessor()->setStateInformation (m.getData(), (int) m.getSize()); | |||
| } | |||
| if (xml.hasAttribute (getOpenProp (type))) | |||
| { | |||
| node->properties.set (getLastXProp (type), xml.getIntAttribute (getLastXProp (type))); | |||
| node->properties.set (getLastYProp (type), xml.getIntAttribute (getLastYProp (type))); | |||
| node->properties.set (getOpenProp (type), xml.getIntAttribute (getOpenProp (type))); | |||
| node->properties.set ("x", xml.getDoubleAttribute ("x")); | |||
| node->properties.set ("y", xml.getDoubleAttribute ("y")); | |||
| if (node->properties[getOpenProp (type)]) | |||
| for (int i = 0; i < (int) PluginWindow::Type::numTypes; ++i) | |||
| { | |||
| jassert (node->getProcessor() != nullptr); | |||
| if (PluginWindow* const w = PluginWindow::getWindowFor (node, type)) | |||
| w->toFront (true); | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -471,23 +418,19 @@ void FilterGraph::createNodeFromXml (const XmlElement& xml) | |||
| XmlElement* FilterGraph::createXml() const | |||
| { | |||
| XmlElement* xml = new XmlElement ("FILTERGRAPH"); | |||
| auto* xml = new XmlElement ("FILTERGRAPH"); | |||
| for (int i = 0; i < graph.getNumNodes(); ++i) | |||
| xml->addChildElement (createNodeXml (graph.getNode (i))); | |||
| for (auto* node : graph.getNodes()) | |||
| xml->addChildElement (createNodeXml (node)); | |||
| for (int i = 0; i < graph.getNumConnections(); ++i) | |||
| for (auto& connection : graph.getConnections()) | |||
| { | |||
| const AudioProcessorGraph::Connection* const fc = graph.getConnection(i); | |||
| XmlElement* e = new XmlElement ("CONNECTION"); | |||
| e->setAttribute ("srcFilter", (int) fc->sourceNodeId); | |||
| e->setAttribute ("srcChannel", fc->sourceChannelIndex); | |||
| e->setAttribute ("dstFilter", (int) fc->destNodeId); | |||
| e->setAttribute ("dstChannel", fc->destChannelIndex); | |||
| auto e = xml->createNewChildElement ("CONNECTION"); | |||
| xml->addChildElement (e); | |||
| e->setAttribute ("srcFilter", (int) connection.source.nodeID); | |||
| e->setAttribute ("srcChannel", connection.source.channelIndex); | |||
| e->setAttribute ("dstFilter", (int) connection.destination.nodeID); | |||
| e->setAttribute ("dstChannel", connection.destination.channelIndex); | |||
| } | |||
| return xml; | |||
| @@ -505,10 +448,8 @@ void FilterGraph::restoreFromXml (const XmlElement& xml) | |||
| forEachXmlChildElementWithTagName (xml, e, "CONNECTION") | |||
| { | |||
| addConnection ((uint32) e->getIntAttribute ("srcFilter"), | |||
| e->getIntAttribute ("srcChannel"), | |||
| (uint32) e->getIntAttribute ("dstFilter"), | |||
| e->getIntAttribute ("dstChannel")); | |||
| graph.addConnection ({ { (NodeID) e->getIntAttribute ("srcFilter"), e->getIntAttribute ("srcChannel") }, | |||
| { (NodeID) e->getIntAttribute ("dstFilter"), e->getIntAttribute ("dstChannel") } }); | |||
| } | |||
| graph.removeIllegalConnections(); | |||
| @@ -26,64 +26,38 @@ | |||
| #pragma once | |||
| class FilterInGraph; | |||
| class FilterGraph; | |||
| #include "PluginWindow.h" | |||
| const char* const filenameSuffix = ".filtergraph"; | |||
| const char* const filenameWildcard = "*.filtergraph"; | |||
| //============================================================================== | |||
| /** | |||
| A collection of filters and some connections between them. | |||
| */ | |||
| class FilterGraph : public FileBasedDocument, public AudioProcessorListener | |||
| class FilterGraph : public FileBasedDocument, | |||
| public AudioProcessorListener, | |||
| private ChangeListener | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| FilterGraph (AudioPluginFormatManager& formatManager); | |||
| FilterGraph (AudioPluginFormatManager&); | |||
| ~FilterGraph(); | |||
| //============================================================================== | |||
| AudioProcessorGraph& getGraph() noexcept { return graph; } | |||
| typedef AudioProcessorGraph::NodeID NodeID; | |||
| int getNumFilters() const noexcept; | |||
| AudioProcessorGraph::Node::Ptr getNode (int index) const noexcept; | |||
| void addPlugin (const PluginDescription&, Point<double>); | |||
| AudioProcessorGraph::Node::Ptr getNodeForId (uint32 uid) const; | |||
| AudioProcessorGraph::Node::Ptr getNodeForName (const String& name) const; | |||
| void addFilter (const PluginDescription&, Point<double>); | |||
| void addFilterCallback (AudioPluginInstance*, const String& error, Point<double> pos); | |||
| void removeFilter (const uint32 filterUID); | |||
| void disconnectFilter (const uint32 filterUID); | |||
| void removeIllegalConnections(); | |||
| void setNodePosition (uint32 nodeId, double x, double y); | |||
| Point<double> getNodePosition (uint32 nodeId) const; | |||
| void setNodePosition (NodeID, Point<double>); | |||
| Point<double> getNodePosition (NodeID) const; | |||
| //============================================================================== | |||
| int getNumConnections() const noexcept; | |||
| const AudioProcessorGraph::Connection* getConnection (const int index) const noexcept; | |||
| const AudioProcessorGraph::Connection* getConnectionBetween (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| uint32 destFilterUID, int destFilterChannel) const noexcept; | |||
| bool canConnect (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| uint32 destFilterUID, int destFilterChannel) const noexcept; | |||
| bool addConnection (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| uint32 destFilterUID, int destFilterChannel); | |||
| void removeConnection (const int index); | |||
| void removeConnection (uint32 sourceFilterUID, int sourceFilterChannel, | |||
| uint32 destFilterUID, int destFilterChannel); | |||
| void clear(); | |||
| PluginWindow* getOrCreateWindowFor (AudioProcessorGraph::Node*, PluginWindow::Type); | |||
| void closeCurrentlyOpenWindowsFor (AudioProcessorGraph::NodeID); | |||
| bool closeAnyOpenPluginWindows(); | |||
| //============================================================================== | |||
| void audioProcessorParameterChanged (AudioProcessor*, int, float) override {} | |||
| @@ -93,6 +67,9 @@ public: | |||
| XmlElement* createXml() const; | |||
| void restoreFromXml (const XmlElement& xml); | |||
| static const char* getFilenameSuffix() { return ".filtergraph"; } | |||
| static const char* getFilenameWildcard() { return "*.filtergraph"; } | |||
| //============================================================================== | |||
| void newDocument(); | |||
| String getDocumentTitle() override; | |||
| @@ -102,21 +79,19 @@ public: | |||
| void setLastDocumentOpened (const File& file) override; | |||
| //============================================================================== | |||
| /** The special channel index used to refer to a filter's midi channel. | |||
| */ | |||
| static const int midiChannelNumber; | |||
| AudioProcessorGraph graph; | |||
| private: | |||
| //============================================================================== | |||
| AudioPluginFormatManager& formatManager; | |||
| AudioProcessorGraph graph; | |||
| OwnedArray<PluginWindow> activePluginWindows; | |||
| uint32 lastUID = 0; | |||
| uint32 getNextUID() noexcept; | |||
| NodeID lastUID = 0; | |||
| NodeID getNextUID() noexcept; | |||
| void createNodeFromXml (const XmlElement& xml); | |||
| void addFilterCallback (AudioPluginInstance*, const String& error, Point<double>); | |||
| void changeListenerCallback (ChangeBroadcaster*) override; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilterGraph) | |||
| }; | |||
| @@ -32,11 +32,10 @@ | |||
| //============================================================================== | |||
| class NumberedBoxes : public TableListBox, | |||
| struct NumberedBoxes : public TableListBox, | |||
| private TableListBoxModel, | |||
| private Button::Listener | |||
| { | |||
| public: | |||
| struct Listener | |||
| { | |||
| virtual ~Listener() {} | |||
| @@ -98,6 +97,10 @@ public: | |||
| } | |||
| private: | |||
| //============================================================================== | |||
| Listener& listener; | |||
| bool canAddColumn, canRemoveColumn; | |||
| //============================================================================== | |||
| int getNumRows() override { return 1; } | |||
| void paintCell (Graphics&, int, int, int, int, bool) override {} | |||
| @@ -164,9 +167,7 @@ private: | |||
| listener.columnSelected (text.getIntValue()); | |||
| } | |||
| //============================================================================== | |||
| Listener& listener; | |||
| bool canAddColumn, canRemoveColumn; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NumberedBoxes) | |||
| }; | |||
| //============================================================================== | |||
| @@ -179,12 +180,8 @@ public: | |||
| InputOutputConfig (FilterIOConfigurationWindow& parent, bool direction) | |||
| : owner (parent), | |||
| ioTitle ("ioLabel", direction ? "Input Configuration" : "Output Configuration"), | |||
| nameLabel ("nameLabel", "Bus Name:"), | |||
| layoutLabel ("layoutLabel", "Channel Layout:"), | |||
| enabledToggle ("Enabled"), | |||
| ioBuses (*this, false, false), | |||
| isInput (direction), | |||
| currentBus (0) | |||
| isInput (direction) | |||
| { | |||
| ioTitle.setFont (ioTitle.getFont().withStyle (Font::bold)); | |||
| nameLabel.setFont (nameLabel.getFont().withStyle (Font::bold)); | |||
| @@ -223,7 +220,6 @@ public: | |||
| { | |||
| auto label = r.removeFromTop (24); | |||
| nameLabel.setBounds (label.removeFromLeft (100)); | |||
| enabledToggle.setBounds (label.removeFromRight (80)); | |||
| name.setBounds (label); | |||
| @@ -231,7 +227,6 @@ public: | |||
| { | |||
| auto label = r.removeFromTop (24); | |||
| layoutLabel.setBounds (label.removeFromLeft (100)); | |||
| layouts.setBounds (label); | |||
| } | |||
| @@ -304,9 +299,9 @@ private: | |||
| { | |||
| if (combo == &layouts) | |||
| { | |||
| if (auto* audioProcessor = owner.getAudioProcessor()) | |||
| if (auto* p = owner.getAudioProcessor()) | |||
| { | |||
| if (auto* bus = audioProcessor->getBus (isInput, currentBus)) | |||
| if (auto* bus = p->getBus (isInput, currentBus)) | |||
| { | |||
| auto selectedNumChannels = layouts.getSelectedId(); | |||
| @@ -332,24 +327,20 @@ private: | |||
| { | |||
| if (btn == &enabledToggle && enabledToggle.isEnabled()) | |||
| { | |||
| if (auto* audioProcessor = owner.getAudioProcessor()) | |||
| if (auto* p = owner.getAudioProcessor()) | |||
| { | |||
| if (auto* bus = audioProcessor->getBus (isInput, currentBus)) | |||
| if (auto* bus = p->getBus (isInput, currentBus)) | |||
| { | |||
| if (bus->isEnabled() != enabledToggle.getToggleState()) | |||
| { | |||
| bool success; | |||
| if (enabledToggle.getToggleState()) | |||
| success = bus->enable(); | |||
| else | |||
| success = bus->setCurrentLayout (AudioChannelSet::disabled()); | |||
| bool success = enabledToggle.getToggleState() ? bus->enable() | |||
| : bus->setCurrentLayout (AudioChannelSet::disabled()); | |||
| if (success) | |||
| { | |||
| updateBusLayout(); | |||
| if (InputOutputConfig* config = owner.getConfig (! isInput)) | |||
| if (auto* config = owner.getConfig (! isInput)) | |||
| config->updateBusLayout(); | |||
| owner.update(); | |||
| @@ -368,11 +359,11 @@ private: | |||
| //============================================================================== | |||
| void addColumn() override | |||
| { | |||
| if (auto* audioProcessor = owner.getAudioProcessor()) | |||
| if (auto* p = owner.getAudioProcessor()) | |||
| { | |||
| if (audioProcessor->canAddBus (isInput)) | |||
| if (p->canAddBus (isInput)) | |||
| { | |||
| if (audioProcessor->addBus (isInput)) | |||
| if (p->addBus (isInput)) | |||
| { | |||
| updateBusButtons(); | |||
| updateBusLayout(); | |||
| @@ -382,22 +373,22 @@ private: | |||
| config->updateBusButtons(); | |||
| config->updateBusLayout(); | |||
| } | |||
| owner.update(); | |||
| } | |||
| owner.update(); | |||
| } | |||
| } | |||
| } | |||
| void removeColumn() override | |||
| { | |||
| if (auto* audioProcessor = owner.getAudioProcessor()) | |||
| if (auto* p = owner.getAudioProcessor()) | |||
| { | |||
| if (audioProcessor->getBusCount (isInput) > 1 && audioProcessor->canRemoveBus (isInput)) | |||
| if (p->getBusCount (isInput) > 1 && p->canRemoveBus (isInput)) | |||
| { | |||
| if (audioProcessor->removeBus (isInput)) | |||
| if (p->removeBus (isInput)) | |||
| { | |||
| currentBus = jmin (audioProcessor->getBusCount (isInput) - 1, currentBus); | |||
| currentBus = jmin (p->getBusCount (isInput) - 1, currentBus); | |||
| updateBusButtons(); | |||
| updateBusLayout(); | |||
| @@ -428,40 +419,41 @@ private: | |||
| //============================================================================== | |||
| FilterIOConfigurationWindow& owner; | |||
| Label ioTitle, nameLabel, name, layoutLabel; | |||
| ToggleButton enabledToggle; | |||
| Label ioTitle, name; | |||
| Label nameLabel { "nameLabel", "Bus Name:" }; | |||
| Label layoutLabel { "layoutLabel", "Channel Layout:" }; | |||
| ToggleButton enabledToggle { "Enabled" }; | |||
| ComboBox layouts; | |||
| NumberedBoxes ioBuses; | |||
| bool isInput; | |||
| int currentBus; | |||
| int currentBus = 0; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InputOutputConfig) | |||
| }; | |||
| FilterIOConfigurationWindow::FilterIOConfigurationWindow (AudioProcessor* const p) | |||
| : AudioProcessorEditor (p), | |||
| title ("title", p->getName()) | |||
| FilterIOConfigurationWindow::FilterIOConfigurationWindow (AudioProcessor& p) | |||
| : AudioProcessorEditor (&p), | |||
| title ("title", p.getName()) | |||
| { | |||
| jassert (p != nullptr); | |||
| setOpaque (true); | |||
| title.setFont (title.getFont().withStyle (Font::bold)); | |||
| addAndMakeVisible (title); | |||
| { | |||
| ScopedLock renderLock (p->getCallbackLock()); | |||
| p->suspendProcessing (true); | |||
| p->releaseResources(); | |||
| ScopedLock renderLock (p.getCallbackLock()); | |||
| p.suspendProcessing (true); | |||
| p.releaseResources(); | |||
| } | |||
| if (p->getBusCount (true) > 0 || p->canAddBus (true)) | |||
| if (p.getBusCount (true) > 0 || p.canAddBus (true)) | |||
| addAndMakeVisible (inConfig = new InputOutputConfig (*this, true)); | |||
| if (p->getBusCount (false) > 0 || p->canAddBus (false)) | |||
| if (p.getBusCount (false) > 0 || p.canAddBus (false)) | |||
| addAndMakeVisible (outConfig = new InputOutputConfig (*this, false)); | |||
| currentLayout = p->getBusesLayout(); | |||
| currentLayout = p.getBusesLayout(); | |||
| setSize (400, (inConfig != nullptr && outConfig != nullptr ? 160 : 0) + 200); | |||
| } | |||
| @@ -506,38 +498,33 @@ void FilterIOConfigurationWindow::resized() | |||
| void FilterIOConfigurationWindow::update() | |||
| { | |||
| auto nodeId = getNodeId(); | |||
| auto nodeID = getNodeID(); | |||
| if (auto* graph = getGraph()) | |||
| if (nodeId != -1) | |||
| graph->disconnectNode (static_cast<uint32> (nodeId)); | |||
| if (nodeID != 0) | |||
| graph->disconnectNode (nodeID); | |||
| if (auto* graphEditor = getGraphEditor()) | |||
| if (auto* panel = graphEditor->graphPanel) | |||
| if (auto* panel = graphEditor->graphPanel.get()) | |||
| panel->updateComponents(); | |||
| } | |||
| int32 FilterIOConfigurationWindow::getNodeId() const | |||
| AudioProcessorGraph::NodeID FilterIOConfigurationWindow::getNodeID() const | |||
| { | |||
| if (auto* graph = getGraph()) | |||
| { | |||
| const int n = graph->getNumNodes(); | |||
| for (int i = 0; i < n; ++i) | |||
| if (auto* node = graph->getNode (i)) | |||
| if (node->getProcessor() == getAudioProcessor()) | |||
| return static_cast<int32> (node->nodeId); | |||
| } | |||
| for (auto* node : graph->getNodes()) | |||
| if (node->getProcessor() == getAudioProcessor()) | |||
| return node->nodeID; | |||
| return -1; | |||
| return 0; | |||
| } | |||
| MainHostWindow* FilterIOConfigurationWindow::getMainWindow() const | |||
| { | |||
| Component* comp; | |||
| auto& desktop = Desktop::getInstance(); | |||
| for (int idx = 0; (comp = Desktop::getInstance().getComponent(idx)) != nullptr; ++idx) | |||
| if (auto* mainWindow = dynamic_cast<MainHostWindow*> (comp)) | |||
| for (int i = desktop.getNumComponents(); --i >= 0;) | |||
| if (auto* mainWindow = dynamic_cast<MainHostWindow*> (desktop.getComponent(i))) | |||
| return mainWindow; | |||
| return nullptr; | |||
| @@ -546,8 +533,7 @@ MainHostWindow* FilterIOConfigurationWindow::getMainWindow() const | |||
| GraphDocumentComponent* FilterIOConfigurationWindow::getGraphEditor() const | |||
| { | |||
| if (auto* mainWindow = getMainWindow()) | |||
| if (auto* graphEditor = mainWindow->getGraphEditor()) | |||
| return graphEditor; | |||
| return mainWindow->graphHolder.get(); | |||
| return nullptr; | |||
| } | |||
| @@ -555,8 +541,8 @@ GraphDocumentComponent* FilterIOConfigurationWindow::getGraphEditor() const | |||
| AudioProcessorGraph* FilterIOConfigurationWindow::getGraph() const | |||
| { | |||
| if (auto* graphEditor = getGraphEditor()) | |||
| if (auto* graph = graphEditor->graph.get()) | |||
| return &graph->getGraph(); | |||
| if (auto* panel = graphEditor->graph.get()) | |||
| return &panel->graph; | |||
| return nullptr; | |||
| } | |||
| @@ -26,38 +26,35 @@ | |||
| #pragma once | |||
| #include "FilterGraph.h" | |||
| class MainHostWindow; | |||
| class GraphDocumentComponent; | |||
| class FilterIOConfigurationWindow : public AudioProcessorEditor | |||
| //============================================================================== | |||
| class FilterIOConfigurationWindow : public AudioProcessorEditor | |||
| { | |||
| public: | |||
| class InputOutputConfig; | |||
| //============================================================================== | |||
| FilterIOConfigurationWindow (AudioProcessor* const p); | |||
| FilterIOConfigurationWindow (AudioProcessor&); | |||
| ~FilterIOConfigurationWindow(); | |||
| //============================================================================== | |||
| void paint (Graphics& g) override; | |||
| void resized() override; | |||
| //============================================================================== | |||
| InputOutputConfig* getConfig (bool isInput) noexcept { return isInput ? inConfig : outConfig; } | |||
| void update(); | |||
| private: | |||
| //============================================================================== | |||
| MainHostWindow* getMainWindow() const; | |||
| GraphDocumentComponent* getGraphEditor() const; | |||
| AudioProcessorGraph* getGraph() const; | |||
| int32 getNodeId() const; | |||
| //============================================================================== | |||
| friend class InputOutputConfig; | |||
| class InputOutputConfig; | |||
| AudioProcessor::BusesLayout currentLayout; | |||
| Label title; | |||
| ScopedPointer<InputOutputConfig> inConfig, outConfig; | |||
| InputOutputConfig* getConfig (bool isInput) noexcept { return isInput ? inConfig : outConfig; } | |||
| void update(); | |||
| MainHostWindow* getMainWindow() const; | |||
| GraphDocumentComponent* getGraphEditor() const; | |||
| AudioProcessorGraph* getGraph() const; | |||
| AudioProcessorGraph::NodeID getNodeID() const; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilterIOConfigurationWindow) | |||
| }; | |||
| @@ -28,10 +28,6 @@ | |||
| #include "FilterGraph.h" | |||
| struct FilterComponent; | |||
| struct ConnectorComponent; | |||
| struct PinComponent; | |||
| //============================================================================== | |||
| /** | |||
| @@ -44,31 +40,37 @@ public: | |||
| GraphEditorPanel (FilterGraph& graph); | |||
| ~GraphEditorPanel(); | |||
| void paint (Graphics& g); | |||
| void mouseDown (const MouseEvent& e); | |||
| void createNewPlugin (const PluginDescription&, Point<int> position); | |||
| FilterComponent* getComponentForFilter (uint32 filterID) const; | |||
| ConnectorComponent* getComponentForConnection (const AudioProcessorGraph::Connection& conn) const; | |||
| PinComponent* findPinAt (Point<float>) const; | |||
| void resized(); | |||
| void changeListenerCallback (ChangeBroadcaster*); | |||
| void paint (Graphics&) override; | |||
| void mouseDown (const MouseEvent&) override; | |||
| void resized() override; | |||
| void changeListenerCallback (ChangeBroadcaster*) override; | |||
| void updateComponents(); | |||
| //============================================================================== | |||
| void beginConnectorDrag (uint32 sourceFilterID, int sourceFilterChannel, | |||
| uint32 destFilterID, int destFilterChannel, | |||
| const MouseEvent& e); | |||
| void dragConnector (const MouseEvent& e); | |||
| void endDraggingConnector (const MouseEvent& e); | |||
| void beginConnectorDrag (AudioProcessorGraph::NodeAndChannel source, | |||
| AudioProcessorGraph::NodeAndChannel dest, | |||
| const MouseEvent&); | |||
| void dragConnector (const MouseEvent&); | |||
| void endDraggingConnector (const MouseEvent&); | |||
| //============================================================================== | |||
| private: | |||
| FilterGraph& graph; | |||
| private: | |||
| struct FilterComponent; | |||
| struct ConnectorComponent; | |||
| struct PinComponent; | |||
| OwnedArray<FilterComponent> nodes; | |||
| OwnedArray<ConnectorComponent> connectors; | |||
| ScopedPointer<ConnectorComponent> draggingConnector; | |||
| FilterComponent* getComponentForFilter (AudioProcessorGraph::NodeID) const; | |||
| ConnectorComponent* getComponentForConnection (const AudioProcessorGraph::Connection&) const; | |||
| PinComponent* findPinAt (Point<float>) const; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GraphEditorPanel) | |||
| }; | |||
| @@ -82,7 +84,6 @@ private: | |||
| class GraphDocumentComponent : public Component | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| GraphDocumentComponent (AudioPluginFormatManager& formatManager, | |||
| AudioDeviceManager& deviceManager); | |||
| ~GraphDocumentComponent(); | |||
| @@ -90,82 +91,26 @@ public: | |||
| //============================================================================== | |||
| void createNewPlugin (const PluginDescription&, Point<int> position); | |||
| void setDoublePrecision (bool doublePrecision); | |||
| bool closeAnyOpenPluginWindows(); | |||
| //============================================================================== | |||
| ScopedPointer<FilterGraph> graph; | |||
| //============================================================================== | |||
| void resized(); | |||
| //============================================================================== | |||
| void unfocusKeyboardComponent(); | |||
| //============================================================================== | |||
| void releaseGraph(); | |||
| ScopedPointer<GraphEditorPanel> graphPanel; | |||
| ScopedPointer<MidiKeyboardComponent> keyboardComp; | |||
| private: | |||
| //============================================================================== | |||
| AudioDeviceManager& deviceManager; | |||
| AudioProcessorPlayer graphPlayer; | |||
| MidiKeyboardState keyState; | |||
| public: | |||
| GraphEditorPanel* graphPanel; | |||
| private: | |||
| Component* keyboardComp; | |||
| Component* statusBar; | |||
| struct TooltipBar; | |||
| ScopedPointer<TooltipBar> statusBar; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GraphDocumentComponent) | |||
| }; | |||
| //============================================================================== | |||
| /** A desktop window containing a plugin's UI. */ | |||
| class PluginWindow : public DocumentWindow | |||
| { | |||
| public: | |||
| enum WindowFormatType | |||
| { | |||
| Normal = 0, | |||
| Generic, | |||
| Programs, | |||
| Parameters, | |||
| AudioIO, | |||
| NumTypes | |||
| }; | |||
| PluginWindow (AudioProcessorEditor*, AudioProcessorGraph::Node*, WindowFormatType); | |||
| ~PluginWindow(); | |||
| static PluginWindow* getWindowFor (AudioProcessorGraph::Node*, WindowFormatType); | |||
| static void closeCurrentlyOpenWindowsFor (const uint32 nodeId); | |||
| static void closeAllCurrentlyOpenWindows(); | |||
| void moved() override; | |||
| void closeButtonPressed() override; | |||
| private: | |||
| AudioProcessorGraph::Node* owner; | |||
| WindowFormatType type; | |||
| float getDesktopScaleFactor() const override { return 1.0f; } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginWindow) | |||
| }; | |||
| inline String toString (PluginWindow::WindowFormatType type) | |||
| { | |||
| switch (type) | |||
| { | |||
| case PluginWindow::Normal: return "Normal"; | |||
| case PluginWindow::Generic: return "Generic"; | |||
| case PluginWindow::Programs: return "Programs"; | |||
| case PluginWindow::Parameters: return "Parameters"; | |||
| default: return String(); | |||
| } | |||
| } | |||
| inline String getLastXProp (PluginWindow::WindowFormatType type) { return "uiLastX_" + toString (type); } | |||
| inline String getLastYProp (PluginWindow::WindowFormatType type) { return "uiLastY_" + toString (type); } | |||
| inline String getOpenProp (PluginWindow::WindowFormatType type) { return "uiopen_" + toString (type); } | |||
| @@ -93,8 +93,8 @@ public: | |||
| } | |||
| if (fileToOpen.existsAsFile()) | |||
| if (GraphDocumentComponent* graph = mainWindow->getGraphEditor()) | |||
| if (FilterGraph* ioGraph = graph->graph.get()) | |||
| if (auto* graph = mainWindow->graphHolder.get()) | |||
| if (auto* ioGraph = graph->graph.get()) | |||
| ioGraph->loadFrom (fileToOpen, true); | |||
| } | |||
| @@ -48,17 +48,22 @@ InternalPluginFormat::InternalPluginFormat() | |||
| } | |||
| } | |||
| AudioPluginInstance* InternalPluginFormat::createInstance (const String& name) | |||
| { | |||
| if (name == audioOutDesc.name) return new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode); | |||
| if (name == audioInDesc.name) return new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode); | |||
| if (name == midiInDesc.name) return new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode); | |||
| return nullptr; | |||
| } | |||
| void InternalPluginFormat::createPluginInstance (const PluginDescription& desc, | |||
| double /*initialSampleRate*/, | |||
| int /*initialBufferSize*/, | |||
| void* userData, | |||
| void (*callback) (void*, AudioPluginInstance*, const String&)) | |||
| { | |||
| AudioPluginInstance* p = nullptr; | |||
| if (desc.name == audioOutDesc.name) p = new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode); | |||
| if (desc.name == audioInDesc.name) p = new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode); | |||
| if (desc.name == midiInDesc.name) p = new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode); | |||
| auto* p = createInstance (desc.name); | |||
| callback (userData, p, p == nullptr ? NEEDS_TRANS ("Invalid internal filter name") : String()); | |||
| } | |||
| @@ -60,6 +60,7 @@ private: | |||
| //============================================================================== | |||
| void createPluginInstance (const PluginDescription&, double initialSampleRate, int initialBufferSize, | |||
| void* userData, void (*callback) (void*, AudioPluginInstance*, const String&)) override; | |||
| AudioPluginInstance* createInstance (const String& name); | |||
| bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept override; | |||
| }; | |||
| @@ -33,14 +33,14 @@ | |||
| class MainHostWindow::PluginListWindow : public DocumentWindow | |||
| { | |||
| public: | |||
| PluginListWindow (MainHostWindow& owner_, AudioPluginFormatManager& pluginFormatManager) | |||
| PluginListWindow (MainHostWindow& mw, AudioPluginFormatManager& pluginFormatManager) | |||
| : DocumentWindow ("Available Plugins", | |||
| LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId), | |||
| DocumentWindow::minimiseButton | DocumentWindow::closeButton), | |||
| owner (owner_) | |||
| owner (mw) | |||
| { | |||
| const File deadMansPedalFile (getAppProperties().getUserSettings() | |||
| ->getFile().getSiblingFile ("RecentlyCrashedPluginsList")); | |||
| auto deadMansPedalFile = getAppProperties().getUserSettings() | |||
| ->getFile().getSiblingFile ("RecentlyCrashedPluginsList"); | |||
| setContentOwned (new PluginListComponent (pluginFormatManager, | |||
| owner.knownPluginList, | |||
| @@ -58,11 +58,10 @@ public: | |||
| ~PluginListWindow() | |||
| { | |||
| getAppProperties().getUserSettings()->setValue ("listWindowPos", getWindowStateAsString()); | |||
| clearContentComponent(); | |||
| } | |||
| void closeButtonPressed() | |||
| void closeButtonPressed() override | |||
| { | |||
| owner.pluginListWindow = nullptr; | |||
| } | |||
| @@ -91,7 +90,9 @@ MainHostWindow::MainHostWindow() | |||
| setResizeLimits (500, 400, 10000, 10000); | |||
| centreWithSize (800, 600); | |||
| setContentOwned (new GraphDocumentComponent (formatManager, deviceManager), false); | |||
| graphHolder = new GraphDocumentComponent (formatManager, deviceManager); | |||
| setContentNonOwned (graphHolder, false); | |||
| restoreWindowStateFromString (getAppProperties().getUserSettings()->getValue ("mainWindowPos")); | |||
| @@ -110,7 +111,7 @@ MainHostWindow::MainHostWindow() | |||
| knownPluginList.addChangeListener (this); | |||
| if (auto* filterGraph = getGraphEditor()->graph.get()) | |||
| if (auto* filterGraph = graphHolder->graph.get()) | |||
| filterGraph->addChangeListener (this); | |||
| addKeyListener (getCommandManager().getKeyMappings()); | |||
| @@ -131,7 +132,7 @@ MainHostWindow::~MainHostWindow() | |||
| pluginListWindow = nullptr; | |||
| knownPluginList.removeChangeListener (this); | |||
| if (auto* filterGraph = getGraphEditor()->graph.get()) | |||
| if (auto* filterGraph = graphHolder->graph.get()) | |||
| filterGraph->removeChangeListener (this); | |||
| getAppProperties().getUserSettings()->setValue ("mainWindowPos", getWindowStateAsString()); | |||
| @@ -142,6 +143,8 @@ MainHostWindow::~MainHostWindow() | |||
| #else | |||
| setMenuBar (nullptr); | |||
| #endif | |||
| graphHolder = nullptr; | |||
| } | |||
| void MainHostWindow::closeButtonPressed() | |||
| @@ -165,18 +168,24 @@ struct AsyncQuitRetrier : private Timer | |||
| void MainHostWindow::tryToQuitApplication() | |||
| { | |||
| PluginWindow::closeAllCurrentlyOpenWindows(); | |||
| if (ModalComponentManager::getInstance()->cancelAllModalComponents()) | |||
| if (graphHolder->closeAnyOpenPluginWindows()) | |||
| { | |||
| // Really important thing to note here: if the last call just deleted any plugin windows, | |||
| // we won't exit immediately - instead we'll use our AsyncQuitRetrier to let the message | |||
| // loop run for another brief moment, then try again. This will give any plugins a chance | |||
| // to flush any GUI events that may have been in transit before the app forces them to | |||
| // be unloaded | |||
| new AsyncQuitRetrier(); | |||
| } | |||
| else if (ModalComponentManager::getInstance()->cancelAllModalComponents()) | |||
| { | |||
| new AsyncQuitRetrier(); | |||
| } | |||
| else if (getGraphEditor() == nullptr | |||
| || getGraphEditor()->graph->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk) | |||
| else if (graphHolder == nullptr || graphHolder->graph->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk) | |||
| { | |||
| // Some plug-ins do not want [NSApp stop] to be called | |||
| // before the plug-ins are not deallocated. | |||
| getGraphEditor()->releaseGraph(); | |||
| graphHolder->releaseGraph(); | |||
| JUCEApplication::quit(); | |||
| } | |||
| @@ -198,11 +207,10 @@ void MainHostWindow::changeListenerCallback (ChangeBroadcaster* changed) | |||
| getAppProperties().saveIfNeeded(); | |||
| } | |||
| } | |||
| else if (changed == getGraphEditor()->graph) | |||
| else if (graphHolder != nullptr && changed == graphHolder->graph) | |||
| { | |||
| String title = JUCEApplication::getInstance()->getApplicationName(); | |||
| File f = getGraphEditor()->graph->getFile(); | |||
| auto title = JUCEApplication::getInstance()->getApplicationName(); | |||
| auto f = graphHolder->graph->getFile(); | |||
| if (f.existsAsFile()) | |||
| title = f.getFileName() + " - " + title; | |||
| @@ -286,9 +294,9 @@ void MainHostWindow::menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/ | |||
| { | |||
| if (menuItemID == 250) | |||
| { | |||
| if (auto* graphEditor = getGraphEditor()) | |||
| if (auto* filterGraph = graphEditor->graph.get()) | |||
| filterGraph->clear(); | |||
| if (graphHolder != nullptr) | |||
| if (auto* graph = graphHolder->graph.get()) | |||
| graph->clear(); | |||
| } | |||
| else if (menuItemID >= 100 && menuItemID < 200) | |||
| { | |||
| @@ -296,9 +304,10 @@ void MainHostWindow::menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/ | |||
| recentFiles.restoreFromString (getAppProperties().getUserSettings() | |||
| ->getValue ("recentFilterGraphFiles")); | |||
| if (auto* graphEditor = getGraphEditor()) | |||
| if (graphEditor->graph != nullptr && graphEditor->graph->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk) | |||
| graphEditor->graph->loadFrom (recentFiles.getFile (menuItemID - 100), true); | |||
| if (graphHolder != nullptr) | |||
| if (auto* graph = graphHolder->graph.get()) | |||
| if (graph != nullptr && graph->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk) | |||
| graph->loadFrom (recentFiles.getFile (menuItemID - 100), true); | |||
| } | |||
| else if (menuItemID >= 200 && menuItemID < 210) | |||
| { | |||
| @@ -323,26 +332,25 @@ void MainHostWindow::menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/ | |||
| void MainHostWindow::menuBarActivated (bool isActivated) | |||
| { | |||
| if (auto* graphEditor = getGraphEditor()) | |||
| if (isActivated) | |||
| graphEditor->unfocusKeyboardComponent(); | |||
| if (isActivated && graphHolder != nullptr) | |||
| graphHolder->unfocusKeyboardComponent(); | |||
| } | |||
| void MainHostWindow::createPlugin (const PluginDescription& desc, Point<int> pos) | |||
| { | |||
| if (auto* graphEditor = getGraphEditor()) | |||
| graphEditor->createNewPlugin (desc, pos); | |||
| if (graphHolder != nullptr) | |||
| graphHolder->createNewPlugin (desc, pos); | |||
| } | |||
| void MainHostWindow::addPluginsToMenu (PopupMenu& m) const | |||
| { | |||
| if (auto* graphEditor = getGraphEditor()) | |||
| if (graphHolder != nullptr) | |||
| { | |||
| int i = 0; | |||
| for (auto* t : internalTypes) | |||
| m.addItem (++i, t->name + " (" + t->pluginFormatName + ")", | |||
| graphEditor->graph->getNodeForName (t->name) == nullptr); | |||
| graphHolder->graph->getNodeForName (t->name) == nullptr); | |||
| } | |||
| m.addSeparator(); | |||
| @@ -439,28 +447,26 @@ void MainHostWindow::getCommandInfo (const CommandID commandID, ApplicationComma | |||
| bool MainHostWindow::perform (const InvocationInfo& info) | |||
| { | |||
| auto* graphEditor = getGraphEditor(); | |||
| switch (info.commandID) | |||
| { | |||
| case CommandIDs::newFile: | |||
| if (graphEditor != nullptr && graphEditor->graph != nullptr && graphEditor->graph->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk) | |||
| graphEditor->graph->newDocument(); | |||
| if (graphHolder != nullptr && graphHolder->graph != nullptr && graphHolder->graph->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk) | |||
| graphHolder->graph->newDocument(); | |||
| break; | |||
| case CommandIDs::open: | |||
| if (graphEditor != nullptr && graphEditor->graph != nullptr && graphEditor->graph->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk) | |||
| graphEditor->graph->loadFromUserSpecifiedFile (true); | |||
| if (graphHolder != nullptr && graphHolder->graph != nullptr && graphHolder->graph->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk) | |||
| graphHolder->graph->loadFromUserSpecifiedFile (true); | |||
| break; | |||
| case CommandIDs::save: | |||
| if (graphEditor != nullptr && graphEditor->graph != nullptr) | |||
| graphEditor->graph->save (true, true); | |||
| if (graphHolder != nullptr && graphHolder->graph != nullptr) | |||
| graphHolder->graph->save (true, true); | |||
| break; | |||
| case CommandIDs::saveAs: | |||
| if (graphEditor != nullptr && graphEditor->graph != nullptr) | |||
| graphEditor->graph->saveAs (File(), true, true, true); | |||
| if (graphHolder != nullptr && graphHolder->graph != nullptr) | |||
| graphHolder->graph->saveAs (File(), true, true, true); | |||
| break; | |||
| case CommandIDs::showPluginListEditor: | |||
| @@ -486,8 +492,8 @@ bool MainHostWindow::perform (const InvocationInfo& info) | |||
| menuItemsChanged(); | |||
| } | |||
| if (graphEditor != nullptr) | |||
| graphEditor->setDoublePrecision (newIsDoublePrecision); | |||
| if (graphHolder != nullptr) | |||
| graphHolder->setDoublePrecision (newIsDoublePrecision); | |||
| } | |||
| break; | |||
| @@ -537,9 +543,9 @@ void MainHostWindow::showAudioSettings() | |||
| getAppProperties().getUserSettings()->setValue ("audioDeviceState", audioState); | |||
| getAppProperties().getUserSettings()->saveIfNeeded(); | |||
| if (auto* graphEditor = getGraphEditor()) | |||
| if (graphEditor->graph != nullptr) | |||
| graphEditor->graph->removeIllegalConnections(); | |||
| if (graphHolder != nullptr) | |||
| if (graphHolder->graph != nullptr) | |||
| graphHolder->graph->graph.removeIllegalConnections(); | |||
| } | |||
| bool MainHostWindow::isInterestedInFileDrag (const StringArray&) | |||
| @@ -561,11 +567,11 @@ void MainHostWindow::fileDragExit (const StringArray&) | |||
| void MainHostWindow::filesDropped (const StringArray& files, int x, int y) | |||
| { | |||
| if (auto* graphEditor = getGraphEditor()) | |||
| if (graphHolder != nullptr) | |||
| { | |||
| if (files.size() == 1 && File (files[0]).hasFileExtension (filenameSuffix)) | |||
| if (files.size() == 1 && File (files[0]).hasFileExtension (FilterGraph::getFilenameSuffix())) | |||
| { | |||
| if (auto* filterGraph = graphEditor->graph.get()) | |||
| if (auto* filterGraph = graphHolder->graph.get()) | |||
| if (filterGraph->saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk) | |||
| filterGraph->loadFrom (File (files[0]), true); | |||
| } | |||
| @@ -574,7 +580,7 @@ void MainHostWindow::filesDropped (const StringArray& files, int x, int y) | |||
| OwnedArray<PluginDescription> typesFound; | |||
| knownPluginList.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound); | |||
| auto pos = graphEditor->getLocalPoint (this, Point<int> (x, y)); | |||
| auto pos = graphHolder->getLocalPoint (this, Point<int> (x, y)); | |||
| for (int i = 0; i < jmin (5, typesFound.size()); ++i) | |||
| if (auto* desc = typesFound.getUnchecked(i)) | |||
| @@ -583,11 +589,6 @@ void MainHostWindow::filesDropped (const StringArray& files, int x, int y) | |||
| } | |||
| } | |||
| GraphDocumentComponent* MainHostWindow::getGraphEditor() const | |||
| { | |||
| return dynamic_cast<GraphDocumentComponent*> (getContentComponent()); | |||
| } | |||
| bool MainHostWindow::isDoublePrecisionProcessing() | |||
| { | |||
| if (auto* props = getAppProperties().getUserSettings()) | |||
| @@ -48,8 +48,6 @@ ApplicationCommandManager& getCommandManager(); | |||
| ApplicationProperties& getAppProperties(); | |||
| //============================================================================== | |||
| /** | |||
| */ | |||
| class MainHostWindow : public DocumentWindow, | |||
| public MenuBarModel, | |||
| public ApplicationCommandTarget, | |||
| @@ -62,24 +60,24 @@ public: | |||
| ~MainHostWindow(); | |||
| //============================================================================== | |||
| void closeButtonPressed(); | |||
| void changeListenerCallback (ChangeBroadcaster*); | |||
| void closeButtonPressed() override; | |||
| void changeListenerCallback (ChangeBroadcaster*) override; | |||
| bool isInterestedInFileDrag (const StringArray& files); | |||
| void fileDragEnter (const StringArray& files, int, int); | |||
| void fileDragMove (const StringArray& files, int, int); | |||
| void fileDragExit (const StringArray& files); | |||
| void filesDropped (const StringArray& files, int, int); | |||
| bool isInterestedInFileDrag (const StringArray& files) override; | |||
| void fileDragEnter (const StringArray& files, int, int) override; | |||
| void fileDragMove (const StringArray& files, int, int) override; | |||
| void fileDragExit (const StringArray& files) override; | |||
| void filesDropped (const StringArray& files, int, int) override; | |||
| void menuBarActivated (bool isActive); | |||
| void menuBarActivated (bool isActive) override; | |||
| StringArray getMenuBarNames(); | |||
| PopupMenu getMenuForIndex (int topLevelMenuIndex, const String& menuName); | |||
| void menuItemSelected (int menuItemID, int topLevelMenuIndex); | |||
| ApplicationCommandTarget* getNextCommandTarget(); | |||
| void getAllCommands (Array<CommandID>& commands); | |||
| void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result); | |||
| bool perform (const InvocationInfo& info); | |||
| StringArray getMenuBarNames() override; | |||
| PopupMenu getMenuForIndex (int topLevelMenuIndex, const String& menuName) override; | |||
| void menuItemSelected (int menuItemID, int topLevelMenuIndex) override; | |||
| ApplicationCommandTarget* getNextCommandTarget() override; | |||
| void getAllCommands (Array<CommandID>&) override; | |||
| void getCommandInfo (CommandID, ApplicationCommandInfo&) override; | |||
| bool perform (const InvocationInfo&) override; | |||
| void tryToQuitApplication(); | |||
| @@ -88,11 +86,11 @@ public: | |||
| void addPluginsToMenu (PopupMenu&) const; | |||
| const PluginDescription* getChosenType (int menuID) const; | |||
| GraphDocumentComponent* getGraphEditor() const; | |||
| bool isDoublePrecisionProcessing(); | |||
| void updatePrecisionMenuItem (ApplicationCommandInfo& info); | |||
| ScopedPointer<GraphDocumentComponent> graphHolder; | |||
| private: | |||
| //============================================================================== | |||
| AudioDeviceManager deviceManager; | |||
| @@ -0,0 +1,201 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2017 - ROLI Ltd. | |||
| 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 5 End-User License | |||
| Agreement and JUCE 5 Privacy Policy (both updated and effective as of the | |||
| 27th April 2017). | |||
| End User License Agreement: www.juce.com/juce-5-licence | |||
| Privacy Policy: www.juce.com/juce-5-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. | |||
| ============================================================================== | |||
| */ | |||
| #pragma once | |||
| #include "FilterIOConfiguration.h" | |||
| class FilterGraph; | |||
| //============================================================================== | |||
| /** | |||
| A desktop window containing a plugin's GUI. | |||
| */ | |||
| class PluginWindow : public DocumentWindow | |||
| { | |||
| public: | |||
| enum class Type | |||
| { | |||
| normal = 0, | |||
| generic, | |||
| programs, | |||
| audioIO, | |||
| numTypes | |||
| }; | |||
| PluginWindow (AudioProcessorGraph::Node* n, Type t, OwnedArray<PluginWindow>& windowList) | |||
| : DocumentWindow (n->getProcessor()->getName(), | |||
| LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId), | |||
| DocumentWindow::minimiseButton | DocumentWindow::closeButton), | |||
| activeWindowList (windowList), | |||
| node (n), type (t) | |||
| { | |||
| setSize (400, 300); | |||
| if (auto* ui = createProcessorEditor (*node->getProcessor(), type)) | |||
| setContentOwned (ui, true); | |||
| setTopLeftPosition (node->properties.getWithDefault (getLastXProp (type), Random::getSystemRandom().nextInt (500)), | |||
| node->properties.getWithDefault (getLastYProp (type), Random::getSystemRandom().nextInt (500))); | |||
| node->properties.set (getOpenProp (type), true); | |||
| setVisible (true); | |||
| } | |||
| ~PluginWindow() | |||
| { | |||
| clearContentComponent(); | |||
| } | |||
| void moved() override | |||
| { | |||
| node->properties.set (getLastXProp (type), getX()); | |||
| node->properties.set (getLastYProp (type), getY()); | |||
| } | |||
| void closeButtonPressed() override | |||
| { | |||
| node->properties.set (getOpenProp (type), false); | |||
| activeWindowList.removeObject (this); | |||
| } | |||
| static String getLastXProp (Type type) { return "uiLastX_" + getTypeName (type); } | |||
| static String getLastYProp (Type type) { return "uiLastY_" + getTypeName (type); } | |||
| static String getOpenProp (Type type) { return "uiopen_" + getTypeName (type); } | |||
| OwnedArray<PluginWindow>& activeWindowList; | |||
| const AudioProcessorGraph::Node::Ptr node; | |||
| const Type type; | |||
| private: | |||
| float getDesktopScaleFactor() const override { return 1.0f; } | |||
| static AudioProcessorEditor* createProcessorEditor (AudioProcessor& processor, PluginWindow::Type type) | |||
| { | |||
| if (type == PluginWindow::Type::normal) | |||
| { | |||
| if (auto* ui = processor.createEditorIfNeeded()) | |||
| return ui; | |||
| type = PluginWindow::Type::generic; | |||
| } | |||
| if (type == PluginWindow::Type::generic) | |||
| return new GenericAudioProcessorEditor (&processor); | |||
| if (type == PluginWindow::Type::programs) | |||
| return new ProgramAudioProcessorEditor (processor); | |||
| if (type == PluginWindow::Type::audioIO) | |||
| return new FilterIOConfigurationWindow (processor); | |||
| jassertfalse; | |||
| return {}; | |||
| } | |||
| static String getTypeName (Type type) | |||
| { | |||
| switch (type) | |||
| { | |||
| case Type::normal: return "Normal"; | |||
| case Type::generic: return "Generic"; | |||
| case Type::programs: return "Programs"; | |||
| case Type::audioIO: return "IO"; | |||
| default: return {}; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| struct ProgramAudioProcessorEditor : public AudioProcessorEditor | |||
| { | |||
| ProgramAudioProcessorEditor (AudioProcessor& p) : AudioProcessorEditor (p) | |||
| { | |||
| setOpaque (true); | |||
| addAndMakeVisible (panel); | |||
| Array<PropertyComponent*> programs; | |||
| auto numPrograms = p.getNumPrograms(); | |||
| int totalHeight = 0; | |||
| for (int i = 0; i < numPrograms; ++i) | |||
| { | |||
| auto name = p.getProgramName (i).trim(); | |||
| if (name.isEmpty()) | |||
| name = "Unnamed"; | |||
| auto pc = new PropertyComp (name, p); | |||
| programs.add (pc); | |||
| totalHeight += pc->getPreferredHeight(); | |||
| } | |||
| panel.addProperties (programs); | |||
| setSize (400, jlimit (25, 400, totalHeight)); | |||
| } | |||
| void paint (Graphics& g) override | |||
| { | |||
| g.fillAll (Colours::grey); | |||
| } | |||
| void resized() override | |||
| { | |||
| panel.setBounds (getLocalBounds()); | |||
| } | |||
| private: | |||
| struct PropertyComp : public PropertyComponent, | |||
| private AudioProcessorListener | |||
| { | |||
| PropertyComp (const String& name, AudioProcessor& p) : PropertyComponent (name), owner (p) | |||
| { | |||
| owner.addListener (this); | |||
| } | |||
| ~PropertyComp() | |||
| { | |||
| owner.removeListener (this); | |||
| } | |||
| void refresh() override {} | |||
| void audioProcessorChanged (AudioProcessor*) override {} | |||
| void audioProcessorParameterChanged (AudioProcessor*, int, float) override {} | |||
| AudioProcessor& owner; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyComp) | |||
| }; | |||
| PropertyPanel panel; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProgramAudioProcessorEditor) | |||
| }; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginWindow) | |||
| }; | |||
| @@ -42,6 +42,7 @@ namespace juce | |||
| AudioProcessorPlayer object. | |||
| */ | |||
| class JUCE_API AudioProcessorGraph : public AudioProcessor, | |||
| public ChangeBroadcaster, | |||
| private AsyncUpdater | |||
| { | |||
| public: | |||
| @@ -54,6 +55,32 @@ public: | |||
| */ | |||
| ~AudioProcessorGraph(); | |||
| /** Each node in the graph has a UID of this type. */ | |||
| typedef uint32 NodeID; | |||
| //============================================================================== | |||
| /** A special index that represents the midi channel of a node. | |||
| This is used as a channel index value if you want to refer to the midi input | |||
| or output instead of an audio channel. | |||
| */ | |||
| enum { midiChannelIndex = 0x1000 }; | |||
| //============================================================================== | |||
| /** | |||
| Represents an input or output channel of a node in an AudioProcessorGraph. | |||
| */ | |||
| struct NodeAndChannel | |||
| { | |||
| NodeID nodeID; | |||
| int channelIndex; | |||
| bool isMIDI() const noexcept { return channelIndex == midiChannelIndex; } | |||
| bool operator== (const NodeAndChannel& other) const noexcept { return nodeID == other.nodeID && channelIndex == other.channelIndex; } | |||
| bool operator!= (const NodeAndChannel& other) const noexcept { return ! operator== (other); } | |||
| }; | |||
| //============================================================================== | |||
| /** Represents one of the nodes, or processors, in an AudioProcessorGraph. | |||
| @@ -66,7 +93,7 @@ public: | |||
| /** The ID number assigned to this node. | |||
| This is assigned by the graph that owns it, and can't be changed. | |||
| */ | |||
| const uint32 nodeId; | |||
| const NodeID nodeID; | |||
| /** The actual processor object that this node represents. */ | |||
| AudioProcessor* getProcessor() const noexcept { return processor; } | |||
| @@ -87,10 +114,19 @@ public: | |||
| //============================================================================== | |||
| friend class AudioProcessorGraph; | |||
| struct Connection | |||
| { | |||
| Node* otherNode; | |||
| int otherChannel, thisChannel; | |||
| bool operator== (const Connection&) const noexcept; | |||
| }; | |||
| const ScopedPointer<AudioProcessor> processor; | |||
| bool isPrepared; | |||
| Array<Connection> inputs, outputs; | |||
| bool isPrepared = false; | |||
| Node (uint32 nodeId, AudioProcessor*) noexcept; | |||
| Node (NodeID, AudioProcessor*) noexcept; | |||
| void setParentGraph (AudioProcessorGraph*) const; | |||
| void prepare (double newSampleRate, int newBlockSize, AudioProcessorGraph*, ProcessingPrecision); | |||
| @@ -107,41 +143,21 @@ public: | |||
| struct JUCE_API Connection | |||
| { | |||
| //============================================================================== | |||
| Connection (uint32 sourceNodeId, int sourceChannelIndex, | |||
| uint32 destNodeId, int destChannelIndex) noexcept; | |||
| //============================================================================== | |||
| /** The ID number of the node which is the input source for this connection. | |||
| @see AudioProcessorGraph::getNodeForId | |||
| */ | |||
| uint32 sourceNodeId; | |||
| /** The index of the output channel of the source node from which this | |||
| connection takes its data. | |||
| Connection (NodeAndChannel source, NodeAndChannel destination) noexcept; | |||
| If this value is the special number AudioProcessorGraph::midiChannelIndex, then | |||
| it is referring to the source node's midi output. Otherwise, it is the zero-based | |||
| index of an audio output channel in the source node. | |||
| */ | |||
| int sourceChannelIndex; | |||
| /** The ID number of the node which is the destination for this connection. | |||
| @see AudioProcessorGraph::getNodeForId | |||
| */ | |||
| uint32 destNodeId; | |||
| Connection (const Connection&) = default; | |||
| Connection& operator= (const Connection&) = default; | |||
| /** The index of the input channel of the destination node to which this | |||
| connection delivers its data. | |||
| bool operator== (const Connection&) const noexcept; | |||
| bool operator!= (const Connection&) const noexcept; | |||
| bool operator< (const Connection&) const noexcept; | |||
| If this value is the special number AudioProcessorGraph::midiChannelIndex, then | |||
| it is referring to the destination node's midi input. Otherwise, it is the zero-based | |||
| index of an audio input channel in the destination node. | |||
| */ | |||
| int destChannelIndex; | |||
| private: | |||
| //============================================================================== | |||
| JUCE_LEAK_DETECTOR (Connection) | |||
| /** The channel and node which is the input source for this connection. */ | |||
| NodeAndChannel source; | |||
| /** The channel and node which is the input source for this connection. */ | |||
| NodeAndChannel destination; | |||
| }; | |||
| //============================================================================== | |||
| @@ -150,6 +166,9 @@ public: | |||
| */ | |||
| void clear(); | |||
| /** Returns the array of nodes in the graph. */ | |||
| const ReferenceCountedArray<Node>& getNodes() const noexcept { return nodes; } | |||
| /** Returns the number of nodes in the graph. */ | |||
| int getNumNodes() const noexcept { return nodes.size(); } | |||
| @@ -157,13 +176,13 @@ public: | |||
| This will return nullptr if the index is out of range. | |||
| @see getNodeForId | |||
| */ | |||
| Node* getNode (const int index) const noexcept { return nodes [index]; } | |||
| Node* getNode (int index) const noexcept { return nodes [index]; } | |||
| /** Searches the graph for a node with the given ID number and returns it. | |||
| If no such node was found, this returns nullptr. | |||
| @see getNode | |||
| */ | |||
| Node* getNodeForId (const uint32 nodeId) const; | |||
| Node* getNodeForId (NodeID) const; | |||
| /** Adds a node to the graph. | |||
| @@ -176,69 +195,56 @@ public: | |||
| If this succeeds, it returns a pointer to the newly-created node. | |||
| */ | |||
| Node* addNode (AudioProcessor* newProcessor, uint32 nodeId = 0); | |||
| Node::Ptr addNode (AudioProcessor* newProcessor, NodeID nodeId = {}); | |||
| /** Deletes a node within the graph which has the specified ID. | |||
| This will also delete any connections that are attached to this node. | |||
| */ | |||
| bool removeNode (uint32 nodeId); | |||
| bool removeNode (NodeID); | |||
| /** Deletes a node within the graph. | |||
| This will also delete any connections that are attached to this node. | |||
| */ | |||
| bool removeNode (Node* node); | |||
| bool removeNode (Node*); | |||
| //============================================================================== | |||
| /** Returns the number of connections in the graph. */ | |||
| int getNumConnections() const { return connections.size(); } | |||
| /** Returns the list of connections in the graph. */ | |||
| std::vector<Connection> getConnections() const; | |||
| /** Returns a pointer to one of the connections in the graph. */ | |||
| const Connection* getConnection (int index) const { return connections [index]; } | |||
| /** Returns true if the given connection exists. */ | |||
| bool isConnected (const Connection&) const noexcept; | |||
| /** Searches for a connection between some specified channels. | |||
| If no such connection is found, this returns nullptr. | |||
| /** Returns true if there is a direct connection between any of the channels of | |||
| two specified nodes. | |||
| */ | |||
| const Connection* getConnectionBetween (uint32 sourceNodeId, | |||
| int sourceChannelIndex, | |||
| uint32 destNodeId, | |||
| int destChannelIndex) const; | |||
| bool isConnected (NodeID possibleSourceNodeID, NodeID possibleDestNodeID) const noexcept; | |||
| /** Returns true if there is a connection between any of the channels of | |||
| two specified nodes. | |||
| /** Does a recursive check to see if there's a direct or indirect series of connections | |||
| between these two nodes. | |||
| */ | |||
| bool isConnected (uint32 possibleSourceNodeId, | |||
| uint32 possibleDestNodeId) const; | |||
| bool isAnInputTo (Node& source, Node& destination) const noexcept; | |||
| /** Returns true if it would be legal to connect the specified points. */ | |||
| bool canConnect (uint32 sourceNodeId, int sourceChannelIndex, | |||
| uint32 destNodeId, int destChannelIndex) const; | |||
| bool canConnect (const Connection&) const; | |||
| /** Attempts to connect two specified channels of two nodes. | |||
| If this isn't allowed (e.g. because you're trying to connect a midi channel | |||
| to an audio one or other such nonsense), then it'll return false. | |||
| */ | |||
| bool addConnection (uint32 sourceNodeId, int sourceChannelIndex, | |||
| uint32 destNodeId, int destChannelIndex); | |||
| /** Deletes the connection with the specified index. */ | |||
| void removeConnection (int index); | |||
| bool addConnection (const Connection&); | |||
| /** Deletes any connection between two specified points. | |||
| Returns true if a connection was actually deleted. | |||
| */ | |||
| bool removeConnection (uint32 sourceNodeId, int sourceChannelIndex, | |||
| uint32 destNodeId, int destChannelIndex); | |||
| /** Deletes the given connection. */ | |||
| bool removeConnection (const Connection&); | |||
| /** Removes all connections from the specified node. */ | |||
| bool disconnectNode (uint32 nodeId); | |||
| bool disconnectNode (NodeID); | |||
| /** Returns true if the given connection's channel numbers map on to valid | |||
| channels at each end. | |||
| Even if a connection is valid when created, its status could change if | |||
| a node changes its channel config. | |||
| */ | |||
| bool isConnectionLegal (const Connection* connection) const; | |||
| bool isConnectionLegal (const Connection&) const; | |||
| /** Performs a sanity checks of all the connections. | |||
| @@ -247,15 +253,6 @@ public: | |||
| */ | |||
| bool removeIllegalConnections(); | |||
| //============================================================================== | |||
| /** A special number that represents the midi channel of a node. | |||
| This is used as a channel index value if you want to refer to the midi input | |||
| or output instead of an audio channel. | |||
| */ | |||
| static const int midiChannelIndex; | |||
| //============================================================================== | |||
| /** A special type of AudioProcessor that can live inside an AudioProcessorGraph | |||
| in order to use the audio that comes into and out of the graph itself. | |||
| @@ -305,7 +302,7 @@ public: | |||
| bool isOutput() const noexcept; | |||
| //============================================================================== | |||
| AudioGraphIOProcessor (const IODeviceType type); | |||
| AudioGraphIOProcessor (IODeviceType); | |||
| ~AudioGraphIOProcessor(); | |||
| const String getName() const override; | |||
| @@ -337,11 +334,7 @@ public: | |||
| private: | |||
| const IODeviceType type; | |||
| AudioProcessorGraph* graph; | |||
| //============================================================================== | |||
| template <typename floatType> | |||
| void processAudio (AudioBuffer<floatType>& buffer, MidiBuffer& midiMessages); | |||
| AudioProcessorGraph* graph = nullptr; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioGraphIOProcessor) | |||
| }; | |||
| @@ -373,33 +366,29 @@ public: | |||
| void setStateInformation (const void* data, int sizeInBytes) override; | |||
| private: | |||
| //============================================================================== | |||
| template <typename floatType> | |||
| void processAudio (AudioBuffer<floatType>& buffer, MidiBuffer& midiMessages); | |||
| template <typename floatType> | |||
| void sliceAndProcess (AudioBuffer<floatType>& buffer, MidiBuffer& midiMessages); | |||
| //============================================================================== | |||
| ReferenceCountedArray<Node> nodes; | |||
| OwnedArray<Connection> connections; | |||
| uint32 lastNodeId; | |||
| OwnedArray<MidiBuffer> midiBuffers; | |||
| Array<void*> renderingOps; | |||
| NodeID lastNodeID = {}; | |||
| friend class AudioGraphIOProcessor; | |||
| struct AudioProcessorGraphBufferHelpers; | |||
| ScopedPointer<AudioProcessorGraphBufferHelpers> audioBuffers; | |||
| struct RenderSequenceFloat; | |||
| struct RenderSequenceDouble; | |||
| ScopedPointer<RenderSequenceFloat> renderSequenceFloat; | |||
| ScopedPointer<RenderSequenceDouble> renderSequenceDouble; | |||
| MidiBuffer* currentMidiInputBuffer; | |||
| MidiBuffer currentMidiOutputBuffer; | |||
| friend class AudioGraphIOProcessor; | |||
| bool isPrepared; | |||
| bool isPrepared = false; | |||
| void topologyChanged(); | |||
| void handleAsyncUpdate() override; | |||
| void clearRenderingSequence(); | |||
| void buildRenderingSequence(); | |||
| bool isAnInputTo (uint32 possibleInputId, uint32 possibleDestinationId, int recursionCheck) const; | |||
| bool anyNodesNeedPreparing() const noexcept; | |||
| bool isConnected (Node* src, int sourceChannel, Node* dest, int destChannel) const noexcept; | |||
| bool isAnInputTo (Node& src, Node& dst, int recursionCheck) const noexcept; | |||
| bool canConnect (Node* src, int sourceChannel, Node* dest, int destChannel) const noexcept; | |||
| bool isLegal (Node* src, int sourceChannel, Node* dest, int destChannel) const noexcept; | |||
| static void getNodeConnections (Node&, std::vector<Connection>&); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorGraph) | |||
| }; | |||