mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Cleanup and refactoring work on the AudioProcessorGraph and the audio plugin host demo
This commit is contained in:
parent
f1f2a34d04
commit
d0514c7924
24 changed files with 1850 additions and 2266 deletions
|
|
@ -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
|
||||
void FilterGraph::changeListenerCallback (ChangeBroadcaster*)
|
||||
{
|
||||
return graph.getNumNodes();
|
||||
}
|
||||
changed();
|
||||
|
||||
AudioProcessorGraph::Node::Ptr FilterGraph::getNode (int index) const noexcept
|
||||
{
|
||||
return graph.getNode (index);
|
||||
}
|
||||
|
||||
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,64 +141,47 @@ 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
|
||||
{
|
||||
return graph.getConnectionBetween (sourceFilterUID, sourceFilterChannel,
|
||||
destFilterUID, destFilterChannel);
|
||||
}
|
||||
|
||||
bool FilterGraph::canConnect (uint32 sourceFilterUID, int sourceFilterChannel,
|
||||
uint32 destFilterUID, int destFilterChannel) const noexcept
|
||||
{
|
||||
return graph.canConnect (sourceFilterUID, sourceFilterChannel,
|
||||
destFilterUID, destFilterChannel);
|
||||
}
|
||||
|
||||
bool FilterGraph::addConnection (uint32 sourceFilterUID, int sourceFilterChannel,
|
||||
uint32 destFilterUID, int destFilterChannel)
|
||||
{
|
||||
const bool result = graph.addConnection (sourceFilterUID, sourceFilterChannel,
|
||||
destFilterUID, destFilterChannel);
|
||||
|
||||
if (result)
|
||||
changed();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void FilterGraph::removeConnection (const int index)
|
||||
{
|
||||
graph.removeConnection (index);
|
||||
changed();
|
||||
}
|
||||
|
||||
void FilterGraph::removeConnection (uint32 sourceFilterUID, int sourceFilterChannel,
|
||||
uint32 destFilterUID, int destFilterChannel)
|
||||
{
|
||||
if (graph.removeConnection (sourceFilterUID, sourceFilterChannel,
|
||||
destFilterUID, destFilterChannel))
|
||||
changed();
|
||||
}
|
||||
|
||||
void FilterGraph::clear()
|
||||
{
|
||||
PluginWindow::closeAllCurrentlyOpenWindows();
|
||||
|
||||
closeAnyOpenPluginWindows();
|
||||
graph.clear();
|
||||
changed();
|
||||
}
|
||||
|
||||
PluginWindow* FilterGraph::getOrCreateWindowFor (AudioProcessorGraph::Node* node, PluginWindow::Type type)
|
||||
{
|
||||
jassert (node != nullptr);
|
||||
|
||||
for (auto* w : activePluginWindows)
|
||||
if (w->node == node && w->type == type)
|
||||
return w;
|
||||
|
||||
if (auto* processor = node->getProcessor())
|
||||
{
|
||||
if (auto* plugin = dynamic_cast<AudioPluginInstance*> (processor))
|
||||
{
|
||||
auto description = plugin->getPluginDescription();
|
||||
|
||||
if (description.pluginFormatName == "Internal")
|
||||
{
|
||||
getCommandManager().invokeDirectly (CommandIDs::showAudioSettings, false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return activePluginWindows.add (new PluginWindow (node, type, activePluginWindows));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FilterGraph::closeAnyOpenPluginWindows()
|
||||
{
|
||||
bool wasEmpty = activePluginWindows.isEmpty();
|
||||
activePluginWindows.clear();
|
||||
return ! wasEmpty;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String FilterGraph::getDocumentTitle()
|
||||
{
|
||||
|
|
@ -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");
|
||||
auto& set = buses.getReference (busIdx);
|
||||
|
||||
auto* bus = xml->createNewChildElement ("BUS");
|
||||
bus->setAttribute ("index", busIdx);
|
||||
|
||||
const AudioChannelSet& set = buses.getReference (busIdx);
|
||||
const String layoutName = set.isDisabled() ? "disabled" : set.getSpeakerArrangementAsString();
|
||||
|
||||
bus->setAttribute ("layout", layoutName);
|
||||
|
||||
xml->addChildElement (bus);
|
||||
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 < PluginWindow::NumTypes; ++i)
|
||||
{
|
||||
PluginWindow::WindowFormatType type = (PluginWindow::WindowFormatType) i;
|
||||
|
||||
if (node->properties.contains (getOpenProp (type)))
|
||||
for (int i = 0; i < (int) PluginWindow::Type::numTypes; ++i)
|
||||
{
|
||||
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());
|
||||
auto type = (PluginWindow::Type) i;
|
||||
|
||||
if (node->properties.contains (PluginWindow::getOpenProp (type)))
|
||||
{
|
||||
e->setAttribute (PluginWindow::getLastXProp (type), node->properties[PluginWindow::getLastXProp (type)].toString());
|
||||
e->setAttribute (PluginWindow::getLastYProp (type), node->properties[PluginWindow::getLastYProp (type)].toString());
|
||||
e->setAttribute (PluginWindow::getOpenProp (type), node->properties[PluginWindow::getOpenProp (type)].toString());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
PluginDescription pd;
|
||||
plugin->fillInPluginDescription (pd);
|
||||
e->addChildElement (pd.createXml());
|
||||
}
|
||||
|
||||
{
|
||||
MemoryBlock m;
|
||||
node->getProcessor()->getStateInformation (m);
|
||||
e->createNewChildElement ("STATE")->addTextElement (m.toBase64Encoding());
|
||||
}
|
||||
|
||||
auto layout = plugin->getBusesLayout();
|
||||
|
||||
auto layouts = e->createNewChildElement ("LAYOUT");
|
||||
layouts->addChildElement (createBusLayoutXml (layout, true));
|
||||
layouts->addChildElement (createBusLayoutXml (layout, false));
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
XmlElement* layouts = new XmlElement ("LAYOUT");
|
||||
const AudioProcessor::BusesLayout layout = plugin->getBusesLayout();
|
||||
|
||||
const bool isInputChoices[] = { true, false };
|
||||
for (bool isInput : isInputChoices)
|
||||
layouts->addChildElement (createBusLayoutXml (layout, isInput));
|
||||
|
||||
e->addChildElement (layouts);
|
||||
|
||||
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 (const XmlElement* const state = xml.getChildByName ("STATE"))
|
||||
{
|
||||
MemoryBlock m;
|
||||
m.fromBase64Encoding (state->getAllSubText());
|
||||
|
||||
node->getProcessor()->setStateInformation (m.getData(), (int) m.getSize());
|
||||
}
|
||||
|
||||
node->properties.set ("x", xml.getDoubleAttribute ("x"));
|
||||
node->properties.set ("y", xml.getDoubleAttribute ("y"));
|
||||
|
||||
for (int i = 0; i < PluginWindow::NumTypes; ++i)
|
||||
{
|
||||
PluginWindow::WindowFormatType type = (PluginWindow::WindowFormatType) i;
|
||||
|
||||
if (xml.hasAttribute (getOpenProp (type)))
|
||||
if (auto* layoutEntity = xml.getChildByName ("LAYOUT"))
|
||||
{
|
||||
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)));
|
||||
auto layout = instance->getBusesLayout();
|
||||
|
||||
if (node->properties[getOpenProp (type)])
|
||||
readBusLayoutFromXml (layout, instance, *layoutEntity, true);
|
||||
readBusLayoutFromXml (layout, instance, *layoutEntity, false);
|
||||
|
||||
instance->setBusesLayout (layout);
|
||||
}
|
||||
|
||||
if (auto node = graph.addNode (instance, (NodeID) xml.getIntAttribute ("uid")))
|
||||
{
|
||||
if (auto* state = xml.getChildByName ("STATE"))
|
||||
{
|
||||
jassert (node->getProcessor() != nullptr);
|
||||
MemoryBlock m;
|
||||
m.fromBase64Encoding (state->getAllSubText());
|
||||
|
||||
if (PluginWindow* const w = PluginWindow::getWindowFor (node, type))
|
||||
w->toFront (true);
|
||||
node->getProcessor()->setStateInformation (m.getData(), (int) m.getSize());
|
||||
}
|
||||
|
||||
node->properties.set ("x", xml.getDoubleAttribute ("x"));
|
||||
node->properties.set ("y", xml.getDoubleAttribute ("y"));
|
||||
|
||||
for (int i = 0; i < (int) PluginWindow::Type::numTypes; ++i)
|
||||
{
|
||||
auto type = (PluginWindow::Type) i;
|
||||
|
||||
if (xml.hasAttribute (PluginWindow::getOpenProp (type)))
|
||||
{
|
||||
node->properties.set (PluginWindow::getLastXProp (type), xml.getIntAttribute (PluginWindow::getLastXProp (type)));
|
||||
node->properties.set (PluginWindow::getLastYProp (type), xml.getIntAttribute (PluginWindow::getLastYProp (type)));
|
||||
node->properties.set (PluginWindow::getOpenProp (type), xml.getIntAttribute (PluginWindow::getOpenProp (type)));
|
||||
|
||||
if (node->properties[PluginWindow::getOpenProp (type)])
|
||||
{
|
||||
jassert (node->getProcessor() != nullptr);
|
||||
|
||||
if (auto w = getOrCreateWindowFor (node, type))
|
||||
w->toFront (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
auto e = xml->createNewChildElement ("CONNECTION");
|
||||
|
||||
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);
|
||||
|
||||
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 (auto* node : graph->getNodes())
|
||||
if (node->getProcessor() == getAudioProcessor())
|
||||
return node->nodeID;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
if (auto* node = graph->getNode (i))
|
||||
if (node->getProcessor() == getAudioProcessor())
|
||||
return static_cast<int32> (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,203 +28,20 @@
|
|||
#include "GraphEditorPanel.h"
|
||||
#include "InternalFilters.h"
|
||||
#include "MainHostWindow.h"
|
||||
#include "FilterIOConfiguration.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
static Array<PluginWindow*> activePluginWindows;
|
||||
|
||||
PluginWindow::PluginWindow (AudioProcessorEditor* pluginEditor, AudioProcessorGraph::Node* o, WindowFormatType t)
|
||||
: DocumentWindow (pluginEditor->getName(),
|
||||
LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId),
|
||||
DocumentWindow::minimiseButton | DocumentWindow::closeButton),
|
||||
owner (o),
|
||||
type (t)
|
||||
struct GraphEditorPanel::PinComponent : public Component,
|
||||
public SettableTooltipClient
|
||||
{
|
||||
setSize (400, 300);
|
||||
|
||||
setContentOwned (pluginEditor, true);
|
||||
|
||||
setTopLeftPosition (owner->properties.getWithDefault (getLastXProp (type), Random::getSystemRandom().nextInt (500)),
|
||||
owner->properties.getWithDefault (getLastYProp (type), Random::getSystemRandom().nextInt (500)));
|
||||
|
||||
owner->properties.set (getOpenProp (type), true);
|
||||
|
||||
setVisible (true);
|
||||
|
||||
activePluginWindows.add (this);
|
||||
}
|
||||
|
||||
void PluginWindow::closeCurrentlyOpenWindowsFor (const uint32 nodeId)
|
||||
{
|
||||
for (int i = activePluginWindows.size(); --i >= 0;)
|
||||
if (activePluginWindows.getUnchecked(i)->owner->nodeId == nodeId)
|
||||
delete activePluginWindows.getUnchecked (i);
|
||||
}
|
||||
|
||||
void PluginWindow::closeAllCurrentlyOpenWindows()
|
||||
{
|
||||
if (activePluginWindows.size() > 0)
|
||||
PinComponent (GraphEditorPanel& p, AudioProcessorGraph::NodeAndChannel pinToUse, bool isIn)
|
||||
: panel (p), graph (p.graph), pin (pinToUse), isInput (isIn)
|
||||
{
|
||||
for (int i = activePluginWindows.size(); --i >= 0;)
|
||||
delete activePluginWindows.getUnchecked (i);
|
||||
|
||||
Component dummyModalComp;
|
||||
dummyModalComp.enterModalState (false);
|
||||
MessageManager::getInstance()->runDispatchLoopUntil (50);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct ProcessorProgramPropertyComp : public PropertyComponent,
|
||||
private AudioProcessorListener
|
||||
{
|
||||
ProcessorProgramPropertyComp (const String& name, AudioProcessor& p)
|
||||
: PropertyComponent (name), owner (p)
|
||||
{
|
||||
owner.addListener (this);
|
||||
}
|
||||
|
||||
~ProcessorProgramPropertyComp()
|
||||
{
|
||||
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 (ProcessorProgramPropertyComp)
|
||||
};
|
||||
|
||||
struct ProgramAudioProcessorEditor : public AudioProcessorEditor
|
||||
{
|
||||
ProgramAudioProcessorEditor (AudioProcessor* p) : AudioProcessorEditor (p)
|
||||
{
|
||||
jassert (p != nullptr);
|
||||
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 ProcessorProgramPropertyComp (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());
|
||||
}
|
||||
|
||||
PropertyPanel panel;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProgramAudioProcessorEditor)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
PluginWindow* PluginWindow::getWindowFor (AudioProcessorGraph::Node* node, WindowFormatType type)
|
||||
{
|
||||
jassert (node != nullptr);
|
||||
|
||||
for (auto* w : activePluginWindows)
|
||||
if (w->owner == node && w->type == type)
|
||||
return w;
|
||||
|
||||
auto* processor = node->getProcessor();
|
||||
AudioProcessorEditor* ui = nullptr;
|
||||
|
||||
if (auto* pluginInstance = dynamic_cast<AudioPluginInstance*> (processor))
|
||||
{
|
||||
auto description = pluginInstance->getPluginDescription();
|
||||
|
||||
if (description.pluginFormatName == "Internal")
|
||||
{
|
||||
getCommandManager().invokeDirectly (CommandIDs::showAudioSettings, false);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == Normal)
|
||||
{
|
||||
ui = processor->createEditorIfNeeded();
|
||||
|
||||
if (ui == nullptr)
|
||||
type = Generic;
|
||||
}
|
||||
|
||||
if (ui == nullptr)
|
||||
{
|
||||
if (type == Generic || type == Parameters) ui = new GenericAudioProcessorEditor (processor);
|
||||
else if (type == Programs) ui = new ProgramAudioProcessorEditor (processor);
|
||||
else if (type == AudioIO) ui = new FilterIOConfigurationWindow (processor);
|
||||
}
|
||||
|
||||
if (ui != nullptr)
|
||||
{
|
||||
if (auto* plugin = dynamic_cast<AudioPluginInstance*> (processor))
|
||||
ui->setName (plugin->getName());
|
||||
|
||||
return new PluginWindow (ui, node, type);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PluginWindow::~PluginWindow()
|
||||
{
|
||||
activePluginWindows.removeFirstMatchingValue (this);
|
||||
clearContentComponent();
|
||||
}
|
||||
|
||||
void PluginWindow::moved()
|
||||
{
|
||||
owner->properties.set (getLastXProp (type), getX());
|
||||
owner->properties.set (getLastYProp (type), getY());
|
||||
}
|
||||
|
||||
void PluginWindow::closeButtonPressed()
|
||||
{
|
||||
owner->properties.set (getOpenProp (type), false);
|
||||
delete this;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct PinComponent : public Component,
|
||||
public SettableTooltipClient
|
||||
{
|
||||
PinComponent (FilterGraph& g, uint32 id, int i, bool isIn)
|
||||
: graph (g), pluginID (id), index (i), isInput (isIn)
|
||||
{
|
||||
if (auto node = graph.getNodeForId (pluginID))
|
||||
if (auto node = graph.graph.getNodeForId (pin.nodeID))
|
||||
{
|
||||
String tip;
|
||||
|
||||
if (index == FilterGraph::midiChannelNumber)
|
||||
if (pin.isMIDI())
|
||||
{
|
||||
tip = isInput ? "MIDI Input"
|
||||
: "MIDI Output";
|
||||
|
|
@ -232,13 +49,13 @@ struct PinComponent : public Component,
|
|||
else
|
||||
{
|
||||
auto& processor = *node->getProcessor();
|
||||
auto channel = processor.getOffsetInBusBufferForAbsoluteChannelIndex (isInput, index, busIdx);
|
||||
auto channel = processor.getOffsetInBusBufferForAbsoluteChannelIndex (isInput, pin.channelIndex, busIdx);
|
||||
|
||||
if (auto* bus = processor.getBus (isInput, busIdx))
|
||||
tip = bus->getName() + ": " + AudioChannelSet::getAbbreviatedChannelTypeName (bus->getCurrentLayout().getTypeOfChannel (channel));
|
||||
else
|
||||
tip = (isInput ? "Main Input: "
|
||||
: "Main Output: ") + String (index + 1);
|
||||
: "Main Output: ") + String (pin.channelIndex + 1);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -250,44 +67,41 @@ struct PinComponent : public Component,
|
|||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
const float w = (float) getWidth();
|
||||
const float h = (float) getHeight();
|
||||
auto w = (float) getWidth();
|
||||
auto h = (float) getHeight();
|
||||
|
||||
Path p;
|
||||
p.addEllipse (w * 0.25f, h * 0.25f, w * 0.5f, h * 0.5f);
|
||||
p.addRectangle (w * 0.4f, isInput ? (0.5f * h) : 0.0f, w * 0.2f, h * 0.5f);
|
||||
|
||||
auto colour = (index == FilterGraph::midiChannelNumber ? Colours::red : Colours::green);
|
||||
auto colour = (pin.isMIDI() ? Colours::red : Colours::green);
|
||||
|
||||
g.setColour (colour.withRotatedHue (static_cast<float> (busIdx) / 5.0f));
|
||||
g.setColour (colour.withRotatedHue (busIdx / 5.0f));
|
||||
g.fillPath (p);
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e) override
|
||||
{
|
||||
getGraphPanel()->beginConnectorDrag (isInput ? 0 : pluginID, index,
|
||||
isInput ? pluginID : 0, index,
|
||||
e);
|
||||
AudioProcessorGraph::NodeAndChannel dummy { 0, 0 };
|
||||
|
||||
panel.beginConnectorDrag (isInput ? dummy : pin,
|
||||
isInput ? pin : dummy,
|
||||
e);
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e) override
|
||||
{
|
||||
getGraphPanel()->dragConnector (e);
|
||||
panel.dragConnector (e);
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent& e) override
|
||||
{
|
||||
getGraphPanel()->endDraggingConnector (e);
|
||||
}
|
||||
|
||||
GraphEditorPanel* getGraphPanel() const noexcept
|
||||
{
|
||||
return findParentComponentOfClass<GraphEditorPanel>();
|
||||
panel.endDraggingConnector (e);
|
||||
}
|
||||
|
||||
GraphEditorPanel& panel;
|
||||
FilterGraph& graph;
|
||||
const uint32 pluginID;
|
||||
const int index;
|
||||
AudioProcessorGraph::NodeAndChannel pin;
|
||||
const bool isInput;
|
||||
int busIdx = 0;
|
||||
|
||||
|
|
@ -295,9 +109,9 @@ struct PinComponent : public Component,
|
|||
};
|
||||
|
||||
//==============================================================================
|
||||
struct FilterComponent : public Component
|
||||
struct GraphEditorPanel::FilterComponent : public Component
|
||||
{
|
||||
FilterComponent (FilterGraph& g, uint32 id) : graph (g), pluginID (id)
|
||||
FilterComponent (GraphEditorPanel& p, uint32 id) : panel (p), graph (p.graph), pluginID (id)
|
||||
{
|
||||
shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, { 0, 1 }));
|
||||
setComponentEffect (&shadow);
|
||||
|
|
@ -305,11 +119,6 @@ struct FilterComponent : public Component
|
|||
setSize (150, 60);
|
||||
}
|
||||
|
||||
~FilterComponent()
|
||||
{
|
||||
deleteAllChildren();
|
||||
}
|
||||
|
||||
FilterComponent (const FilterComponent&) = delete;
|
||||
FilterComponent& operator= (const FilterComponent&) = delete;
|
||||
|
||||
|
|
@ -320,62 +129,7 @@ struct FilterComponent : public Component
|
|||
toFront (true);
|
||||
|
||||
if (e.mods.isPopupMenu())
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, "Delete this filter");
|
||||
m.addItem (2, "Disconnect all pins");
|
||||
m.addSeparator();
|
||||
m.addItem (3, "Show plugin UI");
|
||||
m.addItem (4, "Show all programs");
|
||||
m.addItem (5, "Show all parameters");
|
||||
m.addSeparator();
|
||||
m.addItem (6, "Configure Audio I/O");
|
||||
m.addItem (7, "Test state save/load");
|
||||
|
||||
auto r = m.show();
|
||||
|
||||
if (r == 1)
|
||||
{
|
||||
graph.removeFilter (pluginID);
|
||||
return;
|
||||
}
|
||||
else if (r == 2)
|
||||
{
|
||||
graph.disconnectFilter (pluginID);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto f = graph.getNodeForId (pluginID))
|
||||
{
|
||||
auto* processor = f->getProcessor();
|
||||
jassert (processor != nullptr);
|
||||
|
||||
if (r == 7)
|
||||
{
|
||||
MemoryBlock state;
|
||||
processor->getStateInformation (state);
|
||||
processor->setStateInformation (state.getData(), (int) state.getSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginWindow::WindowFormatType type = processor->hasEditor() ? PluginWindow::Normal
|
||||
: PluginWindow::Generic;
|
||||
|
||||
switch (r)
|
||||
{
|
||||
case 4: type = PluginWindow::Programs; break;
|
||||
case 5: type = PluginWindow::Parameters; break;
|
||||
case 6: type = PluginWindow::AudioIO; break;
|
||||
|
||||
default: break;
|
||||
};
|
||||
|
||||
if (auto* w = PluginWindow::getWindowFor (f, type))
|
||||
w->toFront (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
showPopupMenu();
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e) override
|
||||
|
|
@ -387,11 +141,13 @@ struct FilterComponent : public Component
|
|||
if (getParentComponent() != nullptr)
|
||||
pos = getParentComponent()->getLocalPoint (nullptr, pos);
|
||||
|
||||
graph.setNodePosition (pluginID,
|
||||
(pos.getX() + getWidth() / 2) / (double) getParentWidth(),
|
||||
(pos.getY() + getHeight() / 2) / (double) getParentHeight());
|
||||
pos += getLocalBounds().getCentre();
|
||||
|
||||
getGraphPanel()->updateComponents();
|
||||
graph.setNodePosition (pluginID,
|
||||
{ pos.x / (double) getParentWidth(),
|
||||
pos.y / (double) getParentHeight() });
|
||||
|
||||
panel.updateComponents();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -403,8 +159,8 @@ struct FilterComponent : public Component
|
|||
}
|
||||
else if (e.getNumberOfClicks() == 2)
|
||||
{
|
||||
if (auto f = graph.getNodeForId (pluginID))
|
||||
if (auto* w = PluginWindow::getWindowFor (f, PluginWindow::Normal))
|
||||
if (auto f = graph.graph.getNodeForId (pluginID))
|
||||
if (auto* w = graph.getOrCreateWindowFor (f, PluginWindow::Type::normal))
|
||||
w->toFront (true);
|
||||
}
|
||||
}
|
||||
|
|
@ -420,44 +176,38 @@ struct FilterComponent : public Component
|
|||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
auto boxArea = getLocalBounds().reduced (4, pinSize);
|
||||
|
||||
g.setColour (findColour (TextEditor::backgroundColourId));
|
||||
|
||||
const int x = 4;
|
||||
const int y = pinSize;
|
||||
const int w = getWidth() - x * 2;
|
||||
const int h = getHeight() - pinSize * 2;
|
||||
|
||||
g.fillRect (x, y, w, h);
|
||||
g.fillRect (boxArea.toFloat());
|
||||
|
||||
g.setColour (findColour (TextEditor::textColourId));
|
||||
g.setFont (font);
|
||||
g.drawFittedText (getName(), getLocalBounds().reduced (4, 2), Justification::centred, 2);
|
||||
g.drawFittedText (getName(), boxArea, Justification::centred, 2);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
if (auto f = graph.getNodeForId (pluginID))
|
||||
if (auto f = graph.graph.getNodeForId (pluginID))
|
||||
{
|
||||
if (auto* processor = f->getProcessor())
|
||||
{
|
||||
for (auto* child : getChildren())
|
||||
for (auto* pin : pins)
|
||||
{
|
||||
if (auto* pin = dynamic_cast<PinComponent*> (child))
|
||||
{
|
||||
const bool isInput = pin->isInput;
|
||||
int busIdx = 0;
|
||||
processor->getOffsetInBusBufferForAbsoluteChannelIndex (isInput, pin->index, busIdx);
|
||||
const bool isInput = pin->isInput;
|
||||
auto channelIndex = pin->pin.channelIndex;
|
||||
int busIdx = 0;
|
||||
processor->getOffsetInBusBufferForAbsoluteChannelIndex (isInput, channelIndex, busIdx);
|
||||
|
||||
const int total = isInput ? numIns : numOuts;
|
||||
const int index = pin->index == FilterGraph::midiChannelNumber ? (total - 1) : pin->index;
|
||||
const int total = isInput ? numIns : numOuts;
|
||||
const int index = pin->pin.isMIDI() ? (total - 1) : channelIndex;
|
||||
|
||||
auto totalSpaces = static_cast<float> (total) + (static_cast<float> (jmax (0, processor->getBusCount (isInput) - 1)) * 0.5f);
|
||||
auto indexPos = static_cast<float> (index) + (static_cast<float> (busIdx) * 0.5f);
|
||||
auto totalSpaces = static_cast<float> (total) + (static_cast<float> (jmax (0, processor->getBusCount (isInput) - 1)) * 0.5f);
|
||||
auto indexPos = static_cast<float> (index) + (static_cast<float> (busIdx) * 0.5f);
|
||||
|
||||
pin->setBounds (proportionOfWidth ((1.0f + indexPos) / (totalSpaces + 1.0f)) - pinSize / 2,
|
||||
pin->isInput ? 0 : (getHeight() - pinSize),
|
||||
pinSize, pinSize);
|
||||
}
|
||||
pin->setBounds (proportionOfWidth ((1.0f + indexPos) / (totalSpaces + 1.0f)) - pinSize / 2,
|
||||
pin->isInput ? 0 : (getHeight() - pinSize),
|
||||
pinSize, pinSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -465,23 +215,17 @@ struct FilterComponent : public Component
|
|||
|
||||
Point<float> getPinPos (int index, bool isInput) const
|
||||
{
|
||||
for (auto* child : getChildren())
|
||||
if (auto* pin = dynamic_cast<PinComponent*> (child))
|
||||
if (pin->index == index && isInput == pin->isInput)
|
||||
return getPosition().toFloat() + pin->getBounds().getCentre().toFloat();
|
||||
for (auto* pin : pins)
|
||||
if (pin->pin.channelIndex == index && isInput == pin->isInput)
|
||||
return getPosition().toFloat() + pin->getBounds().getCentre().toFloat();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
const AudioProcessorGraph::Node::Ptr f (graph.getNodeForId (pluginID));
|
||||
|
||||
if (f == nullptr)
|
||||
{
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
const AudioProcessorGraph::Node::Ptr f (graph.graph.getNodeForId (pluginID));
|
||||
jassert (f != nullptr);
|
||||
|
||||
numIns = f->getProcessor()->getTotalNumInputChannels();
|
||||
if (f->getProcessor()->acceptsMidi())
|
||||
|
|
@ -506,7 +250,7 @@ struct FilterComponent : public Component
|
|||
setName (f->getProcessor()->getName());
|
||||
|
||||
{
|
||||
Point<double> p = graph.getNodePosition (pluginID);
|
||||
auto p = graph.getNodePosition (pluginID);
|
||||
setCentreRelative ((float) p.x, (float) p.y);
|
||||
}
|
||||
|
||||
|
|
@ -515,32 +259,79 @@ struct FilterComponent : public Component
|
|||
numInputs = numIns;
|
||||
numOutputs = numOuts;
|
||||
|
||||
deleteAllChildren();
|
||||
pins.clear();
|
||||
|
||||
int i;
|
||||
for (i = 0; i < f->getProcessor()->getTotalNumInputChannels(); ++i)
|
||||
addAndMakeVisible (new PinComponent (graph, pluginID, i, true));
|
||||
for (int i = 0; i < f->getProcessor()->getTotalNumInputChannels(); ++i)
|
||||
addAndMakeVisible (pins.add (new PinComponent (panel, { pluginID, i }, true)));
|
||||
|
||||
if (f->getProcessor()->acceptsMidi())
|
||||
addAndMakeVisible (new PinComponent (graph, pluginID, FilterGraph::midiChannelNumber, true));
|
||||
addAndMakeVisible (pins.add (new PinComponent (panel, { pluginID, AudioProcessorGraph::midiChannelIndex }, true)));
|
||||
|
||||
for (i = 0; i < f->getProcessor()->getTotalNumOutputChannels(); ++i)
|
||||
addAndMakeVisible (new PinComponent (graph, pluginID, i, false));
|
||||
for (int i = 0; i < f->getProcessor()->getTotalNumOutputChannels(); ++i)
|
||||
addAndMakeVisible (pins.add (new PinComponent (panel, { pluginID, i }, false)));
|
||||
|
||||
if (f->getProcessor()->producesMidi())
|
||||
addAndMakeVisible (new PinComponent (graph, pluginID, FilterGraph::midiChannelNumber, false));
|
||||
addAndMakeVisible (pins.add (new PinComponent (panel, { pluginID, AudioProcessorGraph::midiChannelIndex }, false)));
|
||||
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
GraphEditorPanel* getGraphPanel() const noexcept
|
||||
AudioProcessor* getProcessor() const
|
||||
{
|
||||
return findParentComponentOfClass<GraphEditorPanel>();
|
||||
if (auto node = graph.graph.getNodeForId (pluginID))
|
||||
return node->getProcessor();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void showPopupMenu()
|
||||
{
|
||||
PopupMenu m;
|
||||
m.addItem (1, "Delete this filter");
|
||||
m.addItem (2, "Disconnect all pins");
|
||||
m.addSeparator();
|
||||
m.addItem (10, "Show plugin GUI");
|
||||
m.addItem (11, "Show all programs");
|
||||
m.addItem (12, "Show all parameters");
|
||||
m.addSeparator();
|
||||
m.addItem (20, "Configure Audio I/O");
|
||||
m.addItem (21, "Test state save/load");
|
||||
|
||||
switch (m.show())
|
||||
{
|
||||
case 1: graph.graph.removeNode (pluginID); break;
|
||||
case 2: graph.graph.disconnectNode (pluginID); break;
|
||||
case 10: showWindow (PluginWindow::Type::normal); break;
|
||||
case 11: showWindow (PluginWindow::Type::programs); break;
|
||||
case 12: showWindow (PluginWindow::Type::generic); break;
|
||||
case 20: showWindow (PluginWindow::Type::audioIO); break;
|
||||
case 21: testStateSaveLoad(); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void testStateSaveLoad()
|
||||
{
|
||||
if (auto* processor = getProcessor())
|
||||
{
|
||||
MemoryBlock state;
|
||||
processor->getStateInformation (state);
|
||||
processor->setStateInformation (state.getData(), (int) state.getSize());
|
||||
}
|
||||
}
|
||||
|
||||
void showWindow (PluginWindow::Type type)
|
||||
{
|
||||
if (auto node = graph.graph.getNodeForId (pluginID))
|
||||
if (auto* w = graph.getOrCreateWindowFor (node, type))
|
||||
w->toFront (true);
|
||||
}
|
||||
|
||||
GraphEditorPanel& panel;
|
||||
FilterGraph& graph;
|
||||
const uint32 pluginID;
|
||||
const AudioProcessorGraph::NodeID pluginID;
|
||||
OwnedArray<PinComponent> pins;
|
||||
int numInputs = 0, numOutputs = 0;
|
||||
int pinSize = 16;
|
||||
Point<int> originalPos;
|
||||
|
|
@ -551,30 +342,28 @@ struct FilterComponent : public Component
|
|||
|
||||
|
||||
//==============================================================================
|
||||
struct ConnectorComponent : public Component,
|
||||
public SettableTooltipClient
|
||||
struct GraphEditorPanel::ConnectorComponent : public Component,
|
||||
public SettableTooltipClient
|
||||
{
|
||||
ConnectorComponent (FilterGraph& g) : graph (g)
|
||||
ConnectorComponent (GraphEditorPanel& p) : panel (p), graph (p.graph)
|
||||
{
|
||||
setAlwaysOnTop (true);
|
||||
}
|
||||
|
||||
void setInput (uint32 newSourceID, int newSourceChannel)
|
||||
void setInput (AudioProcessorGraph::NodeAndChannel newSource)
|
||||
{
|
||||
if (sourceFilterID != newSourceID || sourceFilterChannel != newSourceChannel)
|
||||
if (connection.source != newSource)
|
||||
{
|
||||
sourceFilterID = newSourceID;
|
||||
sourceFilterChannel = newSourceChannel;
|
||||
connection.source = newSource;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void setOutput (uint32 newDestID, int newDestChannel)
|
||||
void setOutput (AudioProcessorGraph::NodeAndChannel newDest)
|
||||
{
|
||||
if (destFilterID != newDestID || destFilterChannel != newDestChannel)
|
||||
if (connection.destination != newDest)
|
||||
{
|
||||
destFilterID = newDestID;
|
||||
destFilterChannel = newDestChannel;
|
||||
connection.destination = newDest;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
|
@ -620,27 +409,19 @@ struct ConnectorComponent : public Component,
|
|||
p1 = lastInputPos;
|
||||
p2 = lastOutputPos;
|
||||
|
||||
if (auto* hostPanel = getGraphPanel())
|
||||
{
|
||||
if (auto* src = hostPanel->getComponentForFilter (sourceFilterID))
|
||||
p1 = src->getPinPos (sourceFilterChannel, false);
|
||||
if (auto* src = panel.getComponentForFilter (connection.source.nodeID))
|
||||
p1 = src->getPinPos (connection.source.channelIndex, false);
|
||||
|
||||
if (auto* dest = hostPanel->getComponentForFilter (destFilterID))
|
||||
p2 = dest->getPinPos (destFilterChannel, true);
|
||||
}
|
||||
if (auto* dest = panel.getComponentForFilter (connection.destination.nodeID))
|
||||
p2 = dest->getPinPos (connection.destination.channelIndex, true);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
if (sourceFilterChannel == FilterGraph::midiChannelNumber
|
||||
|| destFilterChannel == FilterGraph::midiChannelNumber)
|
||||
{
|
||||
if (connection.source.isMIDI() || connection.destination.isMIDI())
|
||||
g.setColour (Colours::red);
|
||||
}
|
||||
else
|
||||
{
|
||||
g.setColour (Colours::green);
|
||||
}
|
||||
|
||||
g.fillPath (linePath);
|
||||
}
|
||||
|
|
@ -670,30 +451,30 @@ struct ConnectorComponent : public Component,
|
|||
{
|
||||
if (dragging)
|
||||
{
|
||||
getGraphPanel()->dragConnector (e);
|
||||
panel.dragConnector (e);
|
||||
}
|
||||
else if (e.mouseWasDraggedSinceMouseDown())
|
||||
{
|
||||
dragging = true;
|
||||
|
||||
graph.removeConnection (sourceFilterID, sourceFilterChannel, destFilterID, destFilterChannel);
|
||||
graph.graph.removeConnection (connection);
|
||||
|
||||
double distanceFromStart, distanceFromEnd;
|
||||
getDistancesFromEnds (e.position, distanceFromStart, distanceFromEnd);
|
||||
getDistancesFromEnds (getPosition().toFloat() + e.position, distanceFromStart, distanceFromEnd);
|
||||
const bool isNearerSource = (distanceFromStart < distanceFromEnd);
|
||||
|
||||
getGraphPanel()->beginConnectorDrag (isNearerSource ? 0 : sourceFilterID,
|
||||
sourceFilterChannel,
|
||||
isNearerSource ? destFilterID : 0,
|
||||
destFilterChannel,
|
||||
e);
|
||||
AudioProcessorGraph::NodeAndChannel dummy { 0, 0 };
|
||||
|
||||
panel.beginConnectorDrag (isNearerSource ? dummy : connection.source,
|
||||
isNearerSource ? connection.destination : dummy,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent& e) override
|
||||
{
|
||||
if (dragging)
|
||||
getGraphPanel()->endDraggingConnector (e);
|
||||
panel.endDraggingConnector (e);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
|
|
@ -735,11 +516,6 @@ struct ConnectorComponent : public Component,
|
|||
linePath.setUsingNonZeroWinding (true);
|
||||
}
|
||||
|
||||
GraphEditorPanel* getGraphPanel() const noexcept
|
||||
{
|
||||
return findParentComponentOfClass<GraphEditorPanel>();
|
||||
}
|
||||
|
||||
void getDistancesFromEnds (Point<float> p, double& distanceFromStart, double& distanceFromEnd) const
|
||||
{
|
||||
Point<float> p1, p2;
|
||||
|
|
@ -749,9 +525,9 @@ struct ConnectorComponent : public Component,
|
|||
distanceFromEnd = p2.getDistanceFrom (p);
|
||||
}
|
||||
|
||||
GraphEditorPanel& panel;
|
||||
FilterGraph& graph;
|
||||
uint32 sourceFilterID = 0, destFilterID = 0;
|
||||
int sourceFilterChannel = 0, destFilterChannel = 0;
|
||||
AudioProcessorGraph::Connection connection { { 0, 0 }, { 0, 0 } };
|
||||
Point<float> lastInputPos, lastOutputPos;
|
||||
Path linePath, hitPath;
|
||||
bool dragging = false;
|
||||
|
|
@ -771,7 +547,8 @@ GraphEditorPanel::~GraphEditorPanel()
|
|||
{
|
||||
graph.removeChangeListener (this);
|
||||
draggingConnector = nullptr;
|
||||
deleteAllChildren();
|
||||
nodes.clear();
|
||||
connectors.clear();
|
||||
}
|
||||
|
||||
void GraphEditorPanel::paint (Graphics& g)
|
||||
|
|
@ -799,40 +576,32 @@ void GraphEditorPanel::mouseDown (const MouseEvent& e)
|
|||
|
||||
void GraphEditorPanel::createNewPlugin (const PluginDescription& desc, Point<int> position)
|
||||
{
|
||||
graph.addFilter (desc, position.toDouble() / Point<double> ((double) getWidth(), (double) getHeight()));
|
||||
graph.addPlugin (desc, position.toDouble() / Point<double> ((double) getWidth(), (double) getHeight()));
|
||||
}
|
||||
|
||||
FilterComponent* GraphEditorPanel::getComponentForFilter (const uint32 filterID) const
|
||||
GraphEditorPanel::FilterComponent* GraphEditorPanel::getComponentForFilter (const uint32 filterID) const
|
||||
{
|
||||
for (auto* child : getChildren())
|
||||
if (auto* fc = dynamic_cast<FilterComponent*> (child))
|
||||
if (fc->pluginID == filterID)
|
||||
return fc;
|
||||
for (auto* fc : nodes)
|
||||
if (fc->pluginID == filterID)
|
||||
return fc;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ConnectorComponent* GraphEditorPanel::getComponentForConnection (const AudioProcessorGraph::Connection& conn) const
|
||||
GraphEditorPanel::ConnectorComponent* GraphEditorPanel::getComponentForConnection (const AudioProcessorGraph::Connection& conn) const
|
||||
{
|
||||
for (auto* child : getChildren())
|
||||
{
|
||||
if (auto* c = dynamic_cast<ConnectorComponent*> (child))
|
||||
if (c->sourceFilterID == conn.sourceNodeId
|
||||
&& c->destFilterID == conn.destNodeId
|
||||
&& c->sourceFilterChannel == conn.sourceChannelIndex
|
||||
&& c->destFilterChannel == conn.destChannelIndex)
|
||||
return c;
|
||||
}
|
||||
for (auto* cc : connectors)
|
||||
if (cc->connection == conn)
|
||||
return cc;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PinComponent* GraphEditorPanel::findPinAt (Point<float> pos) const
|
||||
GraphEditorPanel::PinComponent* GraphEditorPanel::findPinAt (Point<float> pos) const
|
||||
{
|
||||
for (auto* child : getChildren())
|
||||
if (auto* fc = dynamic_cast<FilterComponent*> (child))
|
||||
if (auto* pin = dynamic_cast<PinComponent*> (fc->getComponentAt (pos.toInt() - fc->getPosition())))
|
||||
return pin;
|
||||
for (auto* fc : nodes)
|
||||
if (auto* pin = dynamic_cast<PinComponent*> (fc->getComponentAt (pos.toInt() - fc->getPosition())))
|
||||
return pin;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -849,67 +618,56 @@ void GraphEditorPanel::changeListenerCallback (ChangeBroadcaster*)
|
|||
|
||||
void GraphEditorPanel::updateComponents()
|
||||
{
|
||||
auto children = getChildren();
|
||||
for (auto child : children)
|
||||
if (auto* fc = dynamic_cast<FilterComponent*> (static_cast<Component*> (child)))
|
||||
fc->update();
|
||||
for (int i = nodes.size(); --i >= 0;)
|
||||
if (graph.graph.getNodeForId (nodes.getUnchecked(i)->pluginID) == nullptr)
|
||||
nodes.remove (i);
|
||||
|
||||
for (int i = getNumChildComponents(); --i >= 0;)
|
||||
for (int i = connectors.size(); --i >= 0;)
|
||||
if (! graph.graph.isConnected (connectors.getUnchecked(i)->connection))
|
||||
connectors.remove (i);
|
||||
|
||||
for (auto* fc : nodes)
|
||||
fc->update();
|
||||
|
||||
for (auto* cc : connectors)
|
||||
cc->update();
|
||||
|
||||
for (auto* f : graph.graph.getNodes())
|
||||
{
|
||||
auto* cc = dynamic_cast<ConnectorComponent*> (getChildComponent (i));
|
||||
|
||||
if (cc != nullptr && cc != draggingConnector)
|
||||
if (getComponentForFilter (f->nodeID) == 0)
|
||||
{
|
||||
if (graph.getConnectionBetween (cc->sourceFilterID, cc->sourceFilterChannel,
|
||||
cc->destFilterID, cc->destFilterChannel) == nullptr)
|
||||
{
|
||||
delete cc;
|
||||
}
|
||||
else
|
||||
{
|
||||
cc->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = graph.getNumFilters(); --i >= 0;)
|
||||
{
|
||||
auto f = graph.getNode (i);
|
||||
|
||||
if (getComponentForFilter (f->nodeId) == 0)
|
||||
{
|
||||
auto* comp = new FilterComponent (graph, f->nodeId);
|
||||
auto* comp = nodes.add (new FilterComponent (*this, f->nodeID));
|
||||
addAndMakeVisible (comp);
|
||||
comp->update();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = graph.getNumConnections(); --i >= 0;)
|
||||
for (auto& c : graph.graph.getConnections())
|
||||
{
|
||||
auto* c = graph.getConnection (i);
|
||||
|
||||
if (getComponentForConnection (*c) == 0)
|
||||
if (getComponentForConnection (c) == 0)
|
||||
{
|
||||
auto* comp = new ConnectorComponent (graph);
|
||||
auto* comp = connectors.add (new ConnectorComponent (*this));
|
||||
addAndMakeVisible (comp);
|
||||
|
||||
comp->setInput (c->sourceNodeId, c->sourceChannelIndex);
|
||||
comp->setOutput (c->destNodeId, c->destChannelIndex);
|
||||
comp->setInput (c.source);
|
||||
comp->setOutput (c.destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEditorPanel::beginConnectorDrag (const uint32 sourceFilterID, const int sourceFilterChannel,
|
||||
const uint32 destFilterID, const int destFilterChannel,
|
||||
void GraphEditorPanel::beginConnectorDrag (AudioProcessorGraph::NodeAndChannel source,
|
||||
AudioProcessorGraph::NodeAndChannel dest,
|
||||
const MouseEvent& e)
|
||||
{
|
||||
draggingConnector = dynamic_cast<ConnectorComponent*> (e.originalComponent);
|
||||
auto* c = dynamic_cast<ConnectorComponent*> (e.originalComponent);
|
||||
connectors.removeObject (c, false);
|
||||
draggingConnector = c;
|
||||
|
||||
if (draggingConnector == nullptr)
|
||||
draggingConnector = new ConnectorComponent (graph);
|
||||
draggingConnector = new ConnectorComponent (*this);
|
||||
|
||||
draggingConnector->setInput (sourceFilterID, sourceFilterChannel);
|
||||
draggingConnector->setOutput (destFilterID, destFilterChannel);
|
||||
draggingConnector->setInput (source);
|
||||
draggingConnector->setOutput (dest);
|
||||
|
||||
addAndMakeVisible (draggingConnector);
|
||||
draggingConnector->toFront (false);
|
||||
|
|
@ -929,30 +687,25 @@ void GraphEditorPanel::dragConnector (const MouseEvent& e)
|
|||
|
||||
if (auto* pin = findPinAt (pos))
|
||||
{
|
||||
auto srcFilter = draggingConnector->sourceFilterID;
|
||||
auto srcChannel = draggingConnector->sourceFilterChannel;
|
||||
auto dstFilter = draggingConnector->destFilterID;
|
||||
auto dstChannel = draggingConnector->destFilterChannel;
|
||||
auto connection = draggingConnector->connection;
|
||||
|
||||
if (srcFilter == 0 && ! pin->isInput)
|
||||
if (connection.source.nodeID == 0 && ! pin->isInput)
|
||||
{
|
||||
srcFilter = pin->pluginID;
|
||||
srcChannel = pin->index;
|
||||
connection.source = pin->pin;
|
||||
}
|
||||
else if (dstFilter == 0 && pin->isInput)
|
||||
else if (connection.destination.nodeID == 0 && pin->isInput)
|
||||
{
|
||||
dstFilter = pin->pluginID;
|
||||
dstChannel = pin->index;
|
||||
connection.destination = pin->pin;
|
||||
}
|
||||
|
||||
if (graph.canConnect (srcFilter, srcChannel, dstFilter, dstChannel))
|
||||
if (graph.graph.canConnect (connection))
|
||||
{
|
||||
pos = (pin->getParentComponent()->getPosition() + pin->getBounds().getCentre()).toFloat();
|
||||
draggingConnector->setTooltip (pin->getTooltip());
|
||||
}
|
||||
}
|
||||
|
||||
if (draggingConnector->sourceFilterID == 0)
|
||||
if (draggingConnector->connection.source.nodeID == 0)
|
||||
draggingConnector->dragStart (pos);
|
||||
else
|
||||
draggingConnector->dragEnd (pos);
|
||||
|
|
@ -967,41 +720,34 @@ void GraphEditorPanel::endDraggingConnector (const MouseEvent& e)
|
|||
draggingConnector->setTooltip ({});
|
||||
|
||||
auto e2 = e.getEventRelativeTo (this);
|
||||
|
||||
auto srcFilter = draggingConnector->sourceFilterID;
|
||||
auto srcChannel = draggingConnector->sourceFilterChannel;
|
||||
auto dstFilter = draggingConnector->destFilterID;
|
||||
auto dstChannel = draggingConnector->destFilterChannel;
|
||||
auto connection = draggingConnector->connection;
|
||||
|
||||
draggingConnector = nullptr;
|
||||
|
||||
if (auto* pin = findPinAt (e2.position))
|
||||
{
|
||||
if (srcFilter == 0)
|
||||
if (connection.source.nodeID == 0)
|
||||
{
|
||||
if (pin->isInput)
|
||||
return;
|
||||
|
||||
srcFilter = pin->pluginID;
|
||||
srcChannel = pin->index;
|
||||
connection.source = pin->pin;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! pin->isInput)
|
||||
return;
|
||||
|
||||
dstFilter = pin->pluginID;
|
||||
dstChannel = pin->index;
|
||||
connection.destination = pin->pin;
|
||||
}
|
||||
|
||||
graph.addConnection (srcFilter, srcChannel, dstFilter, dstChannel);
|
||||
graph.graph.addConnection (connection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct TooltipBar : public Component,
|
||||
private Timer
|
||||
struct GraphDocumentComponent::TooltipBar : public Component,
|
||||
private Timer
|
||||
{
|
||||
TooltipBar()
|
||||
{
|
||||
|
|
@ -1045,13 +791,11 @@ GraphDocumentComponent::GraphDocumentComponent (AudioPluginFormatManager& fm, Au
|
|||
|
||||
deviceManager.addChangeListener (graphPanel);
|
||||
|
||||
graphPlayer.setProcessor (&graph->getGraph());
|
||||
graphPlayer.setProcessor (&graph->graph);
|
||||
|
||||
keyState.addListener (&graphPlayer.getMidiMessageCollector());
|
||||
|
||||
addAndMakeVisible (keyboardComp = new MidiKeyboardComponent (keyState,
|
||||
MidiKeyboardComponent::horizontalKeyboard));
|
||||
|
||||
addAndMakeVisible (keyboardComp = new MidiKeyboardComponent (keyState, MidiKeyboardComponent::horizontalKeyboard));
|
||||
addAndMakeVisible (statusBar = new TooltipBar());
|
||||
|
||||
deviceManager.addAudioCallback (&graphPlayer);
|
||||
|
|
@ -1091,9 +835,15 @@ void GraphDocumentComponent::releaseGraph()
|
|||
{
|
||||
deviceManager.removeAudioCallback (&graphPlayer);
|
||||
deviceManager.removeMidiInputCallback (String(), &graphPlayer.getMidiMessageCollector());
|
||||
deviceManager.removeChangeListener (graphPanel);
|
||||
|
||||
deleteAllChildren();
|
||||
if (graphPanel != nullptr)
|
||||
{
|
||||
deviceManager.removeChangeListener (graphPanel);
|
||||
graphPanel = nullptr;
|
||||
}
|
||||
|
||||
keyboardComp = nullptr;
|
||||
statusBar = nullptr;
|
||||
|
||||
graphPlayer.setProcessor (nullptr);
|
||||
graph = nullptr;
|
||||
|
|
@ -1103,3 +853,8 @@ void GraphDocumentComponent::setDoublePrecision (bool doublePrecision)
|
|||
{
|
||||
graphPlayer.setDoublePrecisionProcessing (doublePrecision);
|
||||
}
|
||||
|
||||
bool GraphDocumentComponent::closeAnyOpenPluginWindows()
|
||||
{
|
||||
return graphPanel->graph.closeAnyOpenPluginWindows();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
201
examples/audio plugin host/Source/PluginWindow.h
Normal file
201
examples/audio plugin host/Source/PluginWindow.h
Normal 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)
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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;
|
||||
|
||||
const ScopedPointer<AudioProcessor> processor;
|
||||
bool isPrepared;
|
||||
struct Connection
|
||||
{
|
||||
Node* otherNode;
|
||||
int otherChannel, thisChannel;
|
||||
|
||||
Node (uint32 nodeId, AudioProcessor*) noexcept;
|
||||
bool operator== (const Connection&) const noexcept;
|
||||
};
|
||||
|
||||
const ScopedPointer<AudioProcessor> processor;
|
||||
Array<Connection> inputs, outputs;
|
||||
bool isPrepared = false;
|
||||
|
||||
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;
|
||||
Connection (NodeAndChannel source, NodeAndChannel destination) noexcept;
|
||||
|
||||
Connection (const Connection&) = default;
|
||||
Connection& operator= (const Connection&) = default;
|
||||
|
||||
bool operator== (const Connection&) const noexcept;
|
||||
bool operator!= (const Connection&) const noexcept;
|
||||
bool operator< (const Connection&) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** The ID number of the node which is the input source for this connection.
|
||||
@see AudioProcessorGraph::getNodeForId
|
||||
*/
|
||||
uint32 sourceNodeId;
|
||||
/** The channel and node which is the input source for this connection. */
|
||||
NodeAndChannel source;
|
||||
|
||||
/** The index of the output channel of the source node from which this
|
||||
connection takes its data.
|
||||
|
||||
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;
|
||||
|
||||
/** The index of the input channel of the destination node to which this
|
||||
connection delivers its data.
|
||||
|
||||
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 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.
|
||||
*/
|
||||
const Connection* getConnectionBetween (uint32 sourceNodeId,
|
||||
int sourceChannelIndex,
|
||||
uint32 destNodeId,
|
||||
int destChannelIndex) const;
|
||||
|
||||
/** Returns true if there is a connection between any of the channels of
|
||||
/** Returns true if there is a direct connection between any of the channels of
|
||||
two specified nodes.
|
||||
*/
|
||||
bool isConnected (uint32 possibleSourceNodeId,
|
||||
uint32 possibleDestNodeId) const;
|
||||
bool isConnected (NodeID possibleSourceNodeID, NodeID possibleDestNodeID) const noexcept;
|
||||
|
||||
/** Does a recursive check to see if there's a direct or indirect series of connections
|
||||
between these two nodes.
|
||||
*/
|
||||
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);
|
||||
bool addConnection (const Connection&);
|
||||
|
||||
/** Deletes the connection with the specified index. */
|
||||
void removeConnection (int index);
|
||||
|
||||
/** 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 = {};
|
||||
|
||||
struct RenderSequenceFloat;
|
||||
struct RenderSequenceDouble;
|
||||
ScopedPointer<RenderSequenceFloat> renderSequenceFloat;
|
||||
ScopedPointer<RenderSequenceDouble> renderSequenceDouble;
|
||||
|
||||
friend class AudioGraphIOProcessor;
|
||||
struct AudioProcessorGraphBufferHelpers;
|
||||
ScopedPointer<AudioProcessorGraphBufferHelpers> audioBuffers;
|
||||
|
||||
MidiBuffer* currentMidiInputBuffer;
|
||||
MidiBuffer currentMidiOutputBuffer;
|
||||
|
||||
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…
Add table
Add a link
Reference in a new issue