#pragma once #include #include #include "HelperConnection.h" #include "McpServer.h" #include "Blocklist.h" #include "PluginSafetyDB.h" #include "AudioAnalyser.h" #include "Constants.h" #include #include #include class PluginBridgeProcessor : public juce::AudioProcessor, private juce::AudioProcessorListener { public: PluginBridgeProcessor(); ~PluginBridgeProcessor() override; void prepareToPlay(double sampleRate, int samplesPerBlock) override; void releaseResources() override; void processBlock(juce::AudioBuffer&, juce::MidiBuffer&) override; juce::AudioProcessorEditor* createEditor() override; bool hasEditor() const override { return true; } const juce::String getName() const override { return "PluginBridge"; } bool acceptsMidi() const override { return true; } bool producesMidi() const override { return true; } double getTailLengthSeconds() const override { return 0.0; } int getNumPrograms() override { return 1; } int getCurrentProgram() override { return 0; } void setCurrentProgram(int) override {} const juce::String getProgramName(int) override { return {}; } void changeProgramName(int, const juce::String&) override {} void getStateInformation(juce::MemoryBlock& destData) override; void setStateInformation(const void* data, int sizeInBytes) override; // MCP bool isMcpServerRunning() const; int getMcpServerPort() const; HelperConnection& getHelperConnection() { return helper; } // Plugin lifecycle void loadPlugin(const juce::String& pluginPath); void unloadPlugin(); bool isPluginLoaded() const { return pluginLoaded; } juce::String getLoadedPluginName() const { return pluginName; } int getLoadedPluginParamCount() const { return pluginParamCount; } HelperConnection::State getConnectionState() const { return helper.getState(); } juce::String getLastPluginPath() const { return pluginPath; } // In-process plugin instance (GUI only — null when plugin is unsafe) juce::AudioPluginInstance* getInProcessPlugin() const { return inProcessPlugin.get(); } // Plugin list juce::Array getInstalledPluginFiles() const; // Manufacturer name for a plugin file (reads moduleinfo.json for VST3, heuristic for AU) // Fast — no plugin loading. Results cached in memory. juce::String getPluginManufacturer(const juce::File& pluginFile) const; // Blocklist bool isBlocked(const juce::String& path) const; Blocklist& getBlocklist() { return blocklist; } // Safety DB PluginSafetyDB& getSafetyDB() { return safetyDB; } void startBackgroundScan(); void stopBackgroundScan(); // Audio analysis (for MCP get_analysis) std::string getAnalysis() const { return analyser.getCompactAnalysis(); } // Callbacks for editor std::function onPluginWillLoad; // fired BEFORE old plugin/editor torn down std::function onPluginLoaded; std::function onPluginUnloaded; std::function onLoadFailed; std::function onScanProgress; // path, safe private: void loadInProcessPlugin(const juce::String& path); void unloadInProcessPlugin(); // Spawns Helper with --scan , waits up to 10s, returns true if safe. // Safe to call from any thread. Never crashes Ableton on plugin crash. bool runPluginScan(const juce::String& path); // AudioProcessorListener — forwards in-process param changes to Helper void audioProcessorParameterChanged(juce::AudioProcessor*, int paramIdx, float value) override; void audioProcessorChanged(juce::AudioProcessor*, const ChangeDetails&) override {} HelperConnection helper; std::unique_ptr mcpServer; Blocklist blocklist; PluginSafetyDB safetyDB; AudioAnalyser analyser; // In-process plugin (GUI instance — nullptr for unsafe plugins) juce::AudioPluginFormatManager inProcessFormatManager; std::unique_ptr inProcessPlugin; std::atomic suppressParamSync{false}; bool pluginLoaded = false; juce::String pluginName; juce::String pluginPath; int pluginParamCount = 0; double currentSampleRate = 44100.0; int currentBlockSize = 512; // Background scanner std::thread scanThread; std::atomic scanRunning{false}; // Manufacturer name cache (path → manufacturer) mutable std::map manufacturerCache; mutable std::mutex manufacturerCacheMutex; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginBridgeProcessor) };