pluginbridge / Source /Plugin /PluginBridgeProcessor.cpp
RAM2118's picture
Fix: Add hidePluginGui() implementation
1dd0405 verified
Raw History Blame
7.5 kB
#include "PluginBridgeProcessor.h"
#include "PluginBridgeEditor.h"
#include "IPCProtocol.h"
#include "json.hpp"
using json = nlohmann::json;
PluginBridgeProcessor::PluginBridgeProcessor()
: AudioProcessor(BusesProperties()
.withInput("Input", juce::AudioChannelSet::stereo(), true)
.withOutput("Output", juce::AudioChannelSet::stereo(), true)),
mcpServer(PluginBridgeConstants::kDefaultMcpPort, helper)
{
helper.onHelperCrashed = [this]()
{
pluginLoaded = false;
pluginName.clear();
pluginParamCount = 0;
DBG("PluginBridgeProcessor: Helper crashed — plugin unloaded");
};
mcpServer.start();
}
PluginBridgeProcessor::~PluginBridgeProcessor()
{
mcpServer.stop();
helper.killHelper();
}
void PluginBridgeProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
{
currentSampleRate = sampleRate;
currentBlockSize = samplesPerBlock;
if (helper.isHelperRunning())
{
json cmd = {{"cmd", "configure"},
{"sampleRate", sampleRate},
{"blockSize", samplesPerBlock},
{"channels", 2}};
helper.sendCommand(cmd.dump());
auto* header = helper.getSharedAudio().getHeader();
if (header)
{
header->sampleRate.store((uint32_t)sampleRate);
header->blockSize.store((uint32_t)samplesPerBlock);
header->numChannels.store(2);
}
}
}
void PluginBridgeProcessor::releaseResources()
{
}
void PluginBridgeProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer&)
{
juce::ScopedNoDenormals noDenormals;
auto totalNumInputChannels = getTotalNumInputChannels();
auto totalNumOutputChannels = getTotalNumOutputChannels();
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear(i, 0, buffer.getNumSamples());
if (!pluginLoaded || !helper.isHelperRunning())
return;
auto& shm = helper.getSharedAudio();
if (!shm.isValid()) return;
int numChannels = buffer.getNumChannels();
int numSamples = buffer.getNumSamples();
const float* inputPtrs[2] = {
buffer.getReadPointer(0),
numChannels > 1 ? buffer.getReadPointer(1) : buffer.getReadPointer(0)
};
shm.writeInput(inputPtrs, numChannels, numSamples);
auto* header = shm.getHeader();
header->blockSize.store((uint32_t)numSamples);
header->numChannels.store((uint32_t)numChannels);
header->state.store(kStateInputReady);
helper.getInputSem().signal();
if (helper.getOutputSem().wait(IPC::kProcessBlockTimeoutMs))
{
float* outputPtrs[2] = {
buffer.getWritePointer(0),
numChannels > 1 ? buffer.getWritePointer(1) : buffer.getWritePointer(0)
};
shm.readOutput(outputPtrs, numChannels, numSamples);
header->state.store(kStateIdle);
}
}
juce::AudioProcessorEditor* PluginBridgeProcessor::createEditor()
{
return new PluginBridgeEditor(*this);
}
void PluginBridgeProcessor::getStateInformation(juce::MemoryBlock& destData)
{
juce::ValueTree state("PluginBridgeState");
state.setProperty("pluginPath", pluginPath, nullptr);
if (pluginLoaded && helper.isHelperRunning())
{
auto response = helper.sendCommand("{\"cmd\":\"get_state\"}");
try {
auto j = json::parse(response);
if (j.value("ok", false))
state.setProperty("pluginState", juce::String(j["state"].get<std::string>()), nullptr);
} catch (...) {}
}
juce::MemoryOutputStream stream(destData, false);
state.writeToStream(stream);
}
void PluginBridgeProcessor::setStateInformation(const void* data, int sizeInBytes)
{
auto state = juce::ValueTree::readFromData(data, (size_t)sizeInBytes);
if (state.isValid())
{
auto path = state.getProperty("pluginPath").toString();
if (path.isNotEmpty())
{
loadPlugin(path);
auto pluginState = state.getProperty("pluginState").toString();
if (pluginState.isNotEmpty() && helper.isHelperRunning())
{
json cmd = {{"cmd", "set_state"}, {"state", pluginState.toStdString()}};
helper.sendCommand(cmd.dump());
}
}
}
}
bool PluginBridgeProcessor::isMcpServerRunning() const
{
return mcpServer.isRunning();
}
int PluginBridgeProcessor::getMcpServerPort() const
{
return mcpServer.getPort();
}
void PluginBridgeProcessor::loadPlugin(const juce::String& path)
{
if (!helper.isHelperRunning())
{
if (!helper.spawnHelper())
{
DBG("PluginBridgeProcessor: Failed to spawn helper");
return;
}
json configCmd = {{"cmd", "configure"},
{"sampleRate", currentSampleRate},
{"blockSize", currentBlockSize},
{"channels", 2}};
helper.sendCommand(configCmd.dump());
}
json loadCmd = {{"cmd", "load"},
{"path", path.toStdString()},
{"sampleRate", currentSampleRate},
{"blockSize", currentBlockSize}};
auto response = helper.sendCommand(loadCmd.dump());
try
{
auto j = json::parse(response);
if (j.value("ok", false))
{
pluginLoaded = true;
pluginName = juce::String(j["name"].get<std::string>());
pluginParamCount = j.value("params", 0);
pluginPath = path;
DBG("PluginBridgeProcessor: Loaded " + pluginName + " (" + juce::String(pluginParamCount) + " params)");
}
else
{
pluginLoaded = false;
DBG("PluginBridgeProcessor: Failed to load: " + juce::String(j.value("error", "unknown")));
}
}
catch (...)
{
pluginLoaded = false;
}
}
void PluginBridgeProcessor::unloadPlugin()
{
if (helper.isHelperRunning())
helper.sendCommand("{\"cmd\":\"unload\"}");
pluginLoaded = false;
pluginName.clear();
pluginPath.clear();
pluginParamCount = 0;
}
void PluginBridgeProcessor::showPluginGui()
{
if (helper.isHelperRunning())
helper.sendCommand("{\"cmd\":\"show_gui\"}");
}
void PluginBridgeProcessor::hidePluginGui()
{
if (helper.isHelperRunning())
helper.sendCommand("{\"cmd\":\"hide_gui\"}");
}
juce::Array<juce::File> PluginBridgeProcessor::getInstalledPluginFiles() const
{
juce::Array<juce::File> results;
juce::Array<juce::File> dirs = {
juce::File("/Library/Audio/Plug-Ins/VST3"),
juce::File::getSpecialLocation(juce::File::userHomeDirectory)
.getChildFile("Library/Audio/Plug-Ins/VST3"),
juce::File("/Library/Audio/Plug-Ins/Components"),
juce::File::getSpecialLocation(juce::File::userHomeDirectory)
.getChildFile("Library/Audio/Plug-Ins/Components")
};
for (auto& dir : dirs)
{
if (dir.isDirectory())
{
auto vst3s = dir.findChildFiles(juce::File::findDirectories, false, "*.vst3");
auto aus = dir.findChildFiles(juce::File::findDirectories, false, "*.component");
results.addArray(vst3s);
results.addArray(aus);
}
}
results.sort();
return results;
}
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new PluginBridgeProcessor();
}