Fix crash: Scan plugins on message thread via Timer (one per tick), not background thread"
Browse files- Source/PluginBridgeProcessor.cpp +57 -31
Source/PluginBridgeProcessor.cpp
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
#include "PluginBridgeProcessor.h"
|
| 2 |
#include "PluginBridgeEditor.h"
|
| 3 |
-
#include <thread>
|
| 4 |
|
| 5 |
PluginBridgeProcessor::PluginBridgeProcessor()
|
| 6 |
: AudioProcessor(BusesProperties()
|
|
@@ -14,6 +13,7 @@ PluginBridgeProcessor::PluginBridgeProcessor()
|
|
| 14 |
PluginBridgeProcessor::~PluginBridgeProcessor()
|
| 15 |
{
|
| 16 |
mcpServer.stop();
|
|
|
|
| 17 |
}
|
| 18 |
|
| 19 |
void PluginBridgeProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
|
|
@@ -90,43 +90,69 @@ void PluginBridgeProcessor::startPluginScan()
|
|
| 90 |
|
| 91 |
scanning.store(true);
|
| 92 |
|
| 93 |
-
//
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
{
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
-
|
| 106 |
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 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 |
-
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
scanning.store(false);
|
| 127 |
scanComplete.store(true);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
}
|
| 131 |
|
| 132 |
// JUCE plugin entry point
|
|
|
|
| 1 |
#include "PluginBridgeProcessor.h"
|
| 2 |
#include "PluginBridgeEditor.h"
|
|
|
|
| 3 |
|
| 4 |
PluginBridgeProcessor::PluginBridgeProcessor()
|
| 5 |
: AudioProcessor(BusesProperties()
|
|
|
|
| 13 |
PluginBridgeProcessor::~PluginBridgeProcessor()
|
| 14 |
{
|
| 15 |
mcpServer.stop();
|
| 16 |
+
stopTimer();
|
| 17 |
}
|
| 18 |
|
| 19 |
void PluginBridgeProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
|
|
|
|
| 90 |
|
| 91 |
scanning.store(true);
|
| 92 |
|
| 93 |
+
// Initialize scanner on message thread
|
| 94 |
+
scanFormatManager = std::make_unique<juce::AudioPluginFormatManager>();
|
| 95 |
+
#if JUCE_PLUGINHOST_VST3
|
| 96 |
+
scanFormatManager->addFormat(new juce::VST3PluginFormat());
|
| 97 |
+
#endif
|
| 98 |
+
#if JUCE_PLUGINHOST_AU
|
| 99 |
+
scanFormatManager->addFormat(new juce::AudioUnitPluginFormat());
|
| 100 |
+
#endif
|
| 101 |
+
|
| 102 |
+
scanList = std::make_unique<juce::KnownPluginList>();
|
| 103 |
+
|
| 104 |
+
// Build file list to scan
|
| 105 |
+
for (int i = 0; i < scanFormatManager->getNumFormats(); ++i)
|
| 106 |
{
|
| 107 |
+
auto* format = scanFormatManager->getFormat(i);
|
| 108 |
+
auto paths = format->getDefaultLocationsToSearch();
|
| 109 |
+
|
| 110 |
+
// Use a temporary scanner just to get the file list
|
| 111 |
+
juce::PluginDirectoryScanner tempScanner(
|
| 112 |
+
*scanList, *format, paths, true, juce::File());
|
| 113 |
+
|
| 114 |
+
// We'll use the scanner directly — start it
|
| 115 |
+
scanners.add(new ScannerState{
|
| 116 |
+
std::make_unique<juce::PluginDirectoryScanner>(
|
| 117 |
+
*scanList, *format, paths, true, juce::File()),
|
| 118 |
+
false
|
| 119 |
+
});
|
| 120 |
+
}
|
| 121 |
|
| 122 |
+
currentScannerIndex = 0;
|
| 123 |
|
| 124 |
+
// Use Timer to scan one file per tick on the message thread
|
| 125 |
+
startTimerHz(30); // 30 files per second — responsive UI
|
| 126 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
+
void PluginBridgeProcessor::timerCallback()
|
| 129 |
+
{
|
| 130 |
+
if (currentScannerIndex >= scanners.size())
|
| 131 |
+
{
|
| 132 |
+
// All scanners done
|
| 133 |
+
stopTimer();
|
| 134 |
+
scannedPlugins = scanList->getTypes();
|
| 135 |
scanning.store(false);
|
| 136 |
scanComplete.store(true);
|
| 137 |
+
scanners.clear();
|
| 138 |
+
scanFormatManager.reset();
|
| 139 |
+
scanList.reset();
|
| 140 |
+
return;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
auto* state = scanners[currentScannerIndex];
|
| 144 |
+
if (state->done)
|
| 145 |
+
{
|
| 146 |
+
currentScannerIndex++;
|
| 147 |
+
return;
|
| 148 |
+
}
|
| 149 |
|
| 150 |
+
juce::String pluginName;
|
| 151 |
+
if (!state->scanner->scanNextFile(true, pluginName))
|
| 152 |
+
{
|
| 153 |
+
state->done = true;
|
| 154 |
+
currentScannerIndex++;
|
| 155 |
+
}
|
| 156 |
}
|
| 157 |
|
| 158 |
// JUCE plugin entry point
|