Add VST2 hosting to native bridge; fix CC7 data2 routing verified via SHM test

VST2 (Steinberg 2.4 ABI) host via Vst2Instrument: aeffect.h, dispatcher,
process events/replacing, kEffectStartProcess, headless GUI skip.
- create_instrument returns Vst2Instrument for InstrumentType::VST2
- 64B-aligned scratch buffers for SIMD plugins; SEH crash capture module+stack
- plugins.py: VST2 resolves .dll/.so; vst_engine.py reports VST2 type
- app.jsx: derive instrumentType from scan (VST3 vs VST2), not hardcoded
- Install exes refreshed from Release build

SF2 FluidSynth audio verified end-to-end through SHM ring (energy 1.33):
silence was a test-harness bug (CC7 value written to velocity field, bridge
reads data2) — app sends data2 correctly. All temporary diagnostics removed.
This commit is contained in:
2026-08-17 07:55:46 +07:00
parent 9d9b4b3686
commit 4111995a5c
12 changed files with 768 additions and 24 deletions
+57
View File
@@ -0,0 +1,57 @@
// native_bridge/src/Vst2Instrument.h
// VST2 (VST 2.4, 64-bit) instrument host. Clean-room ABI header
// (native_bridge/vestige/aeffect.h) — no Steinberg SDK, no LMMS code.
// Headless: no GUI editor (openGUI returns false); preset state is kept via
// effGetChunk/effSetChunk (fxp-style chunks).
#ifndef VST2_INSTRUMENT_H
#define VST2_INSTRUMENT_H
#include "INativeInstrument.h"
#include <string>
#include <vector>
class Vst2Instrument : public INativeInstrument {
public:
Vst2Instrument();
~Vst2Instrument() override;
bool loadPlugin(const std::string& path, double sampleRate);
void setChannel(uint32_t channel) { channel_ = channel; }
bool init(double sampleRate, uint32_t maxBlockSize) override;
void selectProgram(uint32_t channel, uint32_t bank, uint32_t program) override;
void noteOn(uint32_t channel, uint32_t pitch, float velocity, uint32_t sampleOffset) override;
void noteOff(uint32_t channel, uint32_t pitch, uint32_t sampleOffset) override;
void controlChange(uint32_t channel, uint32_t cc, uint32_t value) override;
void programChange(uint32_t channel, uint32_t program) override;
void pitchBend(uint32_t channel, uint32_t bend14) override;
bool openGUI(void* parentWindowHandle) override;
bool canOpenGUI() const override { return false; }
void closeGUI() override;
bool hasAttachedView() const override;
void captureState() override;
void restoreState() override;
std::string serializeState() const override;
void loadSerializedState(const std::string& base64) override;
void processAudioBlock(float* outputL, float* outputR, uint32_t numSamples) override;
// Accessors used by the audioMaster callback (same translation unit's
// friend-ish helpers) without exposing AEffect* in the header.
double sampleRate() const { return sampleRate_; }
uint32_t maxBlockSize() const { return maxBlockSize_; }
const void* effect() const; // AEffect*; null until loaded
private:
void* state_; // Vst2HostState* (pimpl — aeffect.h stays out of the header)
std::string path_;
double sampleRate_;
uint32_t maxBlockSize_;
bool loaded_;
uint32_t channel_ = 0;
// scratch buffers for silent plugin inputs
std::vector<float> inL_;
std::vector<float> inR_;
};
#endif // VST2_INSTRUMENT_H