6a3b430da0
- Vst2Instrument: audioMaster callback thu VstEvents, push SHM midiOutQueue
- Vst3Instrument: IEventList output bus (kAudioMasterWantMidi) — check
- SharedMemoryIPC.h: midiOutQueue[64] + midiOutCount (offset 27556/28324, sizeof 28328)
- shm.rs: MidiEventIPC Serialize, drain_midi_out() emit 'bridge-midi-out'
- lib.rs: pump emit midi-out events
- nativeBridgeService.js: onMidiOut API
- app.jsx: route MIDI-out về selected track qua SonicMidiRouter (feedback guard)
- TASKS.md: A14 ✅
77 lines
3.4 KiB
C++
77 lines
3.4 KiB
C++
// 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.
|
|
// GUI editor: supported for plugins with kEffectFlagsHasEditor (effEditOpen/
|
|
// effEditClose/effEditIdle); 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;
|
|
bool attachView(void* parentWindowHandle) override;
|
|
void closeGUI() override;
|
|
bool hasAttachedView() const override;
|
|
void guiIdle() 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_; }
|
|
const std::string& pluginDir() const { return pluginDir_; }
|
|
uint32_t maxBlockSize() const { return maxBlockSize_; }
|
|
const void* effect() const; // AEffect*; null until loaded
|
|
// Host window the editor is attached to (attachView sets, closeGUI
|
|
// clears). The audioMaster SizeWindow handler moves it via the hook.
|
|
void* editorParent() const { return editorParent_; }
|
|
// Register the SizeWindow hook (main.cpp posts the resize to the UI
|
|
// worker). Probe builds never set it -> no-op.
|
|
static void setResizeHook(void (*fn)(void* hwnd, int w, int h));
|
|
// A14: register the plugin-MIDI-out sink (main.cpp writes SHM
|
|
// midiOutQueue). Probe builds never set it -> no-op.
|
|
static void setMidiOutHook(void (*fn)(uint8_t cmd, uint8_t ch, uint8_t d1,
|
|
uint8_t d2, uint32_t deltaFrames));
|
|
|
|
private:
|
|
void* state_; // Vst2HostState* (pimpl — aeffect.h stays out of the header)
|
|
std::string path_;
|
|
std::string pluginDir_;
|
|
double sampleRate_;
|
|
uint32_t maxBlockSize_;
|
|
bool loaded_;
|
|
bool guiAttached_ = false; // effEditOpen done, not yet effEditClose'd
|
|
void* editorParent_ = nullptr; // host window the editor is a child of
|
|
uint32_t channel_ = 0;
|
|
// scratch buffers for silent plugin inputs and extra output channels
|
|
// (plugins with numOutputs > 2 need full pointer arrays)
|
|
std::vector<float> inL_;
|
|
std::vector<float> inR_;
|
|
std::vector<float> extraOut_;
|
|
};
|
|
|
|
#endif // VST2_INSTRUMENT_H
|