File size: 4,796 Bytes
644b727
 
 
 
590be7d
 
42bb859
fdc4e41
abf507a
4a72dce
fdc4e41
 
 
644b727
fdc4e41
 
644b727
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42bb859
644b727
 
590be7d
644b727
fdc4e41
590be7d
644b727
590be7d
 
 
 
 
4a72dce
 
fdc4e41
 
 
42bb859
644b727
 
fdc4e41
 
 
 
42bb859
 
 
 
fdc4e41
 
 
 
 
abf507a
 
 
590be7d
fdc4e41
42bb859
 
 
fdc4e41
42bb859
644b727
fdc4e41
 
 
 
 
 
 
 
 
 
 
590be7d
42bb859
fdc4e41
 
abf507a
42bb859
fdc4e41
 
 
 
 
590be7d
 
42bb859
590be7d
 
644b727
 
 
fdc4e41
 
 
 
 
 
 
 
644b727
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#pragma once

#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_dsp/juce_dsp.h>
#include "HelperConnection.h"
#include "McpServer.h"
#include "Blocklist.h"
#include "PluginSafetyDB.h"
#include "AudioAnalyser.h"
#include "Constants.h"
#include <map>
#include <mutex>
#include <set>

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<float>&, 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<juce::File> 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<void()> onPluginWillLoad;   // fired BEFORE old plugin/editor torn down
    std::function<void()> onPluginLoaded;
    std::function<void()> onPluginUnloaded;
    std::function<void(const juce::String&)> onLoadFailed;
    std::function<void(const juce::String&, bool)> onScanProgress; // path, safe

private:
    void loadInProcessPlugin(const juce::String& path);
    void unloadInProcessPlugin();

    // Spawns Helper with --scan <path>, 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> mcpServer;
    Blocklist    blocklist;
    PluginSafetyDB safetyDB;
    AudioAnalyser analyser;

    // In-process plugin (GUI instance — nullptr for unsafe plugins)
    juce::AudioPluginFormatManager inProcessFormatManager;
    std::unique_ptr<juce::AudioPluginInstance> inProcessPlugin;
    std::atomic<bool> 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<bool> scanRunning{false};

    // Manufacturer name cache (path → manufacturer)
    mutable std::map<std::string, std::string> manufacturerCache;
    mutable std::mutex manufacturerCacheMutex;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginBridgeProcessor)
};