Fix crash: Processor owns scan thread + results, Editor just reads them
Browse files
Source/PluginBridgeProcessor.cpp
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
#include "PluginBridgeProcessor.h"
|
| 2 |
#include "PluginBridgeEditor.h"
|
|
|
|
| 3 |
|
| 4 |
PluginBridgeProcessor::PluginBridgeProcessor()
|
| 5 |
: AudioProcessor(BusesProperties()
|
|
@@ -30,13 +31,11 @@ void PluginBridgeProcessor::processBlock(juce::AudioBuffer<float>& buffer,
|
|
| 30 |
{
|
| 31 |
juce::ScopedNoDenormals noDenormals;
|
| 32 |
|
| 33 |
-
// Clear extra output channels
|
| 34 |
auto totalNumInputChannels = getTotalNumInputChannels();
|
| 35 |
auto totalNumOutputChannels = getTotalNumOutputChannels();
|
| 36 |
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
|
| 37 |
buffer.clear(i, 0, buffer.getNumSamples());
|
| 38 |
|
| 39 |
-
// Route audio through hosted plugin
|
| 40 |
hostManager.processBlock(buffer, midiMessages);
|
| 41 |
}
|
| 42 |
|
|
@@ -47,7 +46,6 @@ juce::AudioProcessorEditor* PluginBridgeProcessor::createEditor()
|
|
| 47 |
|
| 48 |
void PluginBridgeProcessor::getStateInformation(juce::MemoryBlock& destData)
|
| 49 |
{
|
| 50 |
-
// Save the path of the hosted plugin so it reloads on session restore
|
| 51 |
juce::ValueTree state("PluginBridgeState");
|
| 52 |
state.setProperty("hostedPluginPath", hostManager.getLoadedPluginPath(), nullptr);
|
| 53 |
|
|
@@ -85,6 +83,52 @@ void PluginBridgeProcessor::loadPluginFromFile(const juce::File& file)
|
|
| 85 |
hostManager.loadPlugin(file, getSampleRate(), getBlockSize());
|
| 86 |
}
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
// JUCE plugin entry point
|
| 89 |
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
| 90 |
{
|
|
|
|
| 1 |
#include "PluginBridgeProcessor.h"
|
| 2 |
#include "PluginBridgeEditor.h"
|
| 3 |
+
#include <thread>
|
| 4 |
|
| 5 |
PluginBridgeProcessor::PluginBridgeProcessor()
|
| 6 |
: AudioProcessor(BusesProperties()
|
|
|
|
| 31 |
{
|
| 32 |
juce::ScopedNoDenormals noDenormals;
|
| 33 |
|
|
|
|
| 34 |
auto totalNumInputChannels = getTotalNumInputChannels();
|
| 35 |
auto totalNumOutputChannels = getTotalNumOutputChannels();
|
| 36 |
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
|
| 37 |
buffer.clear(i, 0, buffer.getNumSamples());
|
| 38 |
|
|
|
|
| 39 |
hostManager.processBlock(buffer, midiMessages);
|
| 40 |
}
|
| 41 |
|
|
|
|
| 46 |
|
| 47 |
void PluginBridgeProcessor::getStateInformation(juce::MemoryBlock& destData)
|
| 48 |
{
|
|
|
|
| 49 |
juce::ValueTree state("PluginBridgeState");
|
| 50 |
state.setProperty("hostedPluginPath", hostManager.getLoadedPluginPath(), nullptr);
|
| 51 |
|
|
|
|
| 83 |
hostManager.loadPlugin(file, getSampleRate(), getBlockSize());
|
| 84 |
}
|
| 85 |
|
| 86 |
+
void PluginBridgeProcessor::startPluginScan()
|
| 87 |
+
{
|
| 88 |
+
if (scanning.load() || scanComplete.load())
|
| 89 |
+
return;
|
| 90 |
+
|
| 91 |
+
scanning.store(true);
|
| 92 |
+
|
| 93 |
+
// Detached thread is safe here because Processor outlives Editor
|
| 94 |
+
// and scannedPlugins is only written by this thread, read by message thread after scanComplete=true
|
| 95 |
+
std::thread([this]()
|
| 96 |
+
{
|
| 97 |
+
juce::AudioPluginFormatManager formatManager;
|
| 98 |
+
#if JUCE_PLUGINHOST_VST3
|
| 99 |
+
formatManager.addFormat(new juce::VST3PluginFormat());
|
| 100 |
+
#endif
|
| 101 |
+
#if JUCE_PLUGINHOST_AU
|
| 102 |
+
formatManager.addFormat(new juce::AudioUnitPluginFormat());
|
| 103 |
+
#endif
|
| 104 |
+
|
| 105 |
+
juce::KnownPluginList pluginList;
|
| 106 |
+
|
| 107 |
+
for (int i = 0; i < formatManager.getNumFormats(); ++i)
|
| 108 |
+
{
|
| 109 |
+
auto* format = formatManager.getFormat(i);
|
| 110 |
+
auto searchPaths = format->getDefaultLocationsToSearch();
|
| 111 |
+
|
| 112 |
+
juce::PluginDirectoryScanner scanner(
|
| 113 |
+
pluginList,
|
| 114 |
+
*format,
|
| 115 |
+
searchPaths,
|
| 116 |
+
true,
|
| 117 |
+
juce::File()
|
| 118 |
+
);
|
| 119 |
+
|
| 120 |
+
juce::String pluginName;
|
| 121 |
+
while (scanner.scanNextFile(false, pluginName)) {}
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
// Copy results (safe — Editor only reads after scanComplete is true)
|
| 125 |
+
scannedPlugins = pluginList.getTypes();
|
| 126 |
+
scanning.store(false);
|
| 127 |
+
scanComplete.store(true);
|
| 128 |
+
|
| 129 |
+
}).detach();
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
// JUCE plugin entry point
|
| 133 |
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
| 134 |
{
|