Browse Source

Cleanup and refactoring work on the AudioProcessorGraph and the audio plugin host demo

tags/2021-05-28
jules 8 years ago
parent
commit
d0514c7924
24 changed files with 1699 additions and 2115 deletions
  1. +26
    -0
      BREAKING-CHANGES.txt
  2. +2
    -0
      examples/audio plugin host/Builds/LinuxMakefile/Makefile
  3. +3
    -1
      examples/audio plugin host/Builds/MacOSX/Plugin Host.xcodeproj/project.pbxproj
  4. +1
    -0
      examples/audio plugin host/Builds/VisualStudio2013/Plugin Host_App.vcxproj
  5. +3
    -0
      examples/audio plugin host/Builds/VisualStudio2013/Plugin Host_App.vcxproj.filters
  6. +1
    -0
      examples/audio plugin host/Builds/VisualStudio2015/Plugin Host_App.vcxproj
  7. +3
    -0
      examples/audio plugin host/Builds/VisualStudio2015/Plugin Host_App.vcxproj.filters
  8. +1
    -0
      examples/audio plugin host/Builds/VisualStudio2017/Plugin Host_App.vcxproj
  9. +3
    -0
      examples/audio plugin host/Builds/VisualStudio2017/Plugin Host_App.vcxproj.filters
  10. +17
    -8
      examples/audio plugin host/Plugin Host.jucer
  11. +150
    -209
      examples/audio plugin host/Source/FilterGraph.cpp
  12. +21
    -46
      examples/audio plugin host/Source/FilterGraph.h
  13. +52
    -66
      examples/audio plugin host/Source/FilterIOConfiguration.cpp
  14. +15
    -18
      examples/audio plugin host/Source/FilterIOConfiguration.h
  15. +211
    -456
      examples/audio plugin host/Source/GraphEditorPanel.cpp
  16. +27
    -82
      examples/audio plugin host/Source/GraphEditorPanel.h
  17. +2
    -2
      examples/audio plugin host/Source/HostStartup.cpp
  18. +10
    -5
      examples/audio plugin host/Source/InternalFilters.cpp
  19. +1
    -0
      examples/audio plugin host/Source/InternalFilters.h
  20. +57
    -56
      examples/audio plugin host/Source/MainHostWindow.cpp
  21. +17
    -19
      examples/audio plugin host/Source/MainHostWindow.h
  22. +201
    -0
      examples/audio plugin host/Source/PluginWindow.h
  23. +785
    -1046
      modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
  24. +90
    -101
      modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h

+ 26
- 0
BREAKING-CHANGES.txt View File

@@ -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
=============


+ 2
- 0
examples/audio plugin host/Builds/LinuxMakefile/Makefile View File

@@ -23,6 +23,8 @@ ifndef CONFIG
CONFIG=Debug
endif
JUCE_ARCH_LABEL := $(shell uname -m)
ifeq ($(CONFIG),Debug)
JUCE_BINDIR := build
JUCE_LIBDIR := build


+ 3
- 1
examples/audio plugin host/Builds/MacOSX/Plugin Host.xcodeproj/project.pbxproj View File

@@ -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,


+ 1
- 0
examples/audio plugin host/Builds/VisualStudio2013/Plugin Host_App.vcxproj View File

@@ -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"/>


+ 3
- 0
examples/audio plugin host/Builds/VisualStudio2013/Plugin Host_App.vcxproj.filters View File

@@ -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>


+ 1
- 0
examples/audio plugin host/Builds/VisualStudio2015/Plugin Host_App.vcxproj View File

@@ -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"/>


+ 3
- 0
examples/audio plugin host/Builds/VisualStudio2015/Plugin Host_App.vcxproj.filters View File

@@ -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>


+ 1
- 0
examples/audio plugin host/Builds/VisualStudio2017/Plugin Host_App.vcxproj View File

@@ -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"/>


+ 3
- 0
examples/audio plugin host/Builds/VisualStudio2017/Plugin Host_App.vcxproj.filters View File

@@ -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>


+ 17
- 8
examples/audio plugin host/Plugin Host.jucer View File

@@ -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"


+ 150
- 209
examples/audio plugin host/Source/FilterGraph.cpp View File

@@ -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();


+ 21
- 46
examples/audio plugin host/Source/FilterGraph.h View File

@@ -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)
};

+ 52
- 66
examples/audio plugin host/Source/FilterIOConfiguration.cpp View File

@@ -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;
}

+ 15
- 18
examples/audio plugin host/Source/FilterIOConfiguration.h View File

@@ -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)
};

+ 211
- 456
examples/audio plugin host/Source/GraphEditorPanel.cpp
File diff suppressed because it is too large
View File


+ 27
- 82
examples/audio plugin host/Source/GraphEditorPanel.h View File

@@ -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); }

+ 2
- 2
examples/audio plugin host/Source/HostStartup.cpp View File

@@ -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);
}


+ 10
- 5
examples/audio plugin host/Source/InternalFilters.cpp View File

@@ -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());
}


+ 1
- 0
examples/audio plugin host/Source/InternalFilters.h View File

@@ -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;
};

+ 57
- 56
examples/audio plugin host/Source/MainHostWindow.cpp View File

@@ -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())


+ 17
- 19
examples/audio plugin host/Source/MainHostWindow.h View File

@@ -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;


+ 201
- 0
examples/audio plugin host/Source/PluginWindow.h View File

@@ -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)
};

+ 785
- 1046
modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
File diff suppressed because it is too large
View File


+ 90
- 101
modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h View File

@@ -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)
};


Loading…
Cancel
Save