d7756b08ab
- calibrate: probe sandbox (jbridge) theo real-time pacing — child render qua SHM consumer-sync, probe µs-speed chỉ đo được silence/attack → makeup=1.0 mọi channel. Thêm virtual probeNeedsRealtime() (SandboxVst2Host → true), calibrate() Sleep ~1 block/5.8ms để child kịp nhận MIDI + render steady-state → makeup đo peak thật (3 channel Qin ≈ 2.6, trước đây = 1.0 → âm lượng đồng đều giữa các track/VSTi). - OPEN_GUI: SetWindowPos(HWND_TOPMOST) + SetForegroundWindow + BringWindowToTop → GUI VSTi hiển thị ở top window, tương tác ngay. - /api/v1/plugins/available: filter vst_instruments + extra theo is_fx của fx_vst_bridge --scan (trước merge entry) — modal Select Instrument chỉ còn soundfont + VSTi, không hiện VST FX (90 → 33).
98 lines
4.9 KiB
C++
98 lines
4.9 KiB
C++
// native_bridge/include/INativeInstrument.h
|
|
#ifndef I_NATIVE_INSTRUMENT_H
|
|
#define I_NATIVE_INSTRUMENT_H
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
|
|
enum class InstrumentType {
|
|
VST3,
|
|
VST2,
|
|
SOUNDFONT_SF2_SF3,
|
|
SFZ
|
|
};
|
|
|
|
class INativeInstrument {
|
|
public:
|
|
virtual ~INativeInstrument() = default;
|
|
|
|
// Initialize Engine with Sample Rate and Buffer Size
|
|
virtual bool init(double sampleRate, uint32_t maxBlockSize) = 0;
|
|
|
|
// Select Bank and Program Change
|
|
virtual void selectProgram(uint32_t channel, uint32_t bank, uint32_t program) = 0;
|
|
|
|
// Dispatch MIDI Note On / Note Off events
|
|
virtual void noteOn(uint32_t channel, uint32_t pitch, float velocity, uint32_t sampleOffset) = 0;
|
|
virtual void noteOff(uint32_t channel, uint32_t pitch, uint32_t sampleOffset) = 0;
|
|
|
|
// MIDI continuous controllers (A12): CC value, program change, 14-bit pitch bend
|
|
virtual void controlChange(uint32_t channel, uint32_t cc, uint32_t value) = 0;
|
|
virtual void programChange(uint32_t channel, uint32_t program) = 0;
|
|
virtual void pitchBend(uint32_t channel, uint32_t bend14) = 0;
|
|
|
|
// Open/close Native GUI window
|
|
virtual bool openGUI(void* parentWindowHandle) = 0;
|
|
// Headless engines (VST2 without an editor, SF2/SFZ): the bridge must
|
|
// not create/reuse a window for them.
|
|
virtual bool canOpenGUI() const { return true; }
|
|
// Sandbox hosts whose child owns its own top-level window (sandbox VST2):
|
|
// the bridge must not create a parent native window nor close other
|
|
// editors — attachView(nullptr) just forwards the OPEN_GUI control.
|
|
virtual bool ownsGuiWindow() const { return false; }
|
|
virtual void closeGUI() = 0;
|
|
// VST3 reopen-after-close: the bridge checks before openGUI whether the
|
|
// instance must be rebuilt; while it is, the engine sets reloading=true
|
|
// and the real-time loop skips processAudioBlock (teardown must not race
|
|
// the audio thread — a reload terminates the VST while renderAll may be
|
|
// inside processAudioBlock on the same instance).
|
|
virtual bool needsReload() const { return false; }
|
|
virtual void setReloading(bool /*on*/) {}
|
|
// VST3: rebuild the plugin instance (terminate + loadPlugin) on the owning
|
|
// apartment thread. The bridge calls it after tearing an editor down so the
|
|
// audio loop never processes an instance whose editor was dropped without
|
|
// view->removed(). Non-VST engines: no-op.
|
|
virtual bool reload() { return true; }
|
|
// VST3: split openGUI so instance teardown/reload runs on the owning
|
|
// apartment thread while view->attached() may run on a temporary thread
|
|
// with the channel worker pumping messages (see bridge main.cpp).
|
|
virtual bool reloadForGUI() { return true; }
|
|
virtual bool attachView(void* /*parentWindowHandle*/) { return false; }
|
|
virtual void resizeView(int /*w*/, int /*h*/) {}
|
|
// Periodic idle tick for attached editors (VST2 effEditIdle repaints;
|
|
// others: no-op). Called on the UI worker's pump loop.
|
|
virtual void guiIdle() {}
|
|
// True while the editor view is attached (GUI window open). The bridge uses
|
|
// it to dedupe repeated OPEN_GUI (frontend retry spam) — re-attaching a
|
|
// view that is already attached corrupts plugins (Nexus createView→null).
|
|
virtual bool hasAttachedView() const { return false; }
|
|
// G0.4: serialize/restore the plugin preset across instance reloads
|
|
// (GUI close/reopen). Non-VST engines: no-op (nothing to save).
|
|
virtual void captureState() {}
|
|
virtual void restoreState() {}
|
|
// G2.1: serialize the current preset to a portable string for the state
|
|
// file (VST3: base64 of captured component+controller state). Caller must
|
|
// call captureState() first (VST3 buffers are refreshed there). Non-VST
|
|
// engines: empty.
|
|
virtual std::string serializeState() const { return std::string(); }
|
|
// G2.3: replace the preset with a previously serialized one (VST3:
|
|
// base64 of [4B BE compLen][component][4B BE ctrlLen][controller]).
|
|
// Must be called after the instrument is loaded. Non-VST: no-op.
|
|
virtual void loadSerializedState(const std::string& /*base64*/) {}
|
|
// G2.1: last bank/program for a MIDI channel (SF2/SF3). Others: 0.
|
|
virtual uint32_t bankOf(uint32_t channel) const { return 0; }
|
|
virtual uint32_t programOf(uint32_t channel) const { return 0; }
|
|
|
|
// Engines whose render is paced by an external process (sandbox VST2
|
|
// child): the auto-calibration probe must consume at ~real-time speed so
|
|
// the child can wake, pick up the MIDI and render each block. Direct
|
|
// engines (VST3/VST2-in-proc/SF2/SFZ) render synchronously in
|
|
// processAudioBlock and probe at µs speed.
|
|
virtual bool probeNeedsRealtime() const { return false; }
|
|
|
|
// Real-time Audio PCM Float32 rendering loop
|
|
virtual void processAudioBlock(float* outputL, float* outputR, uint32_t numSamples) = 0;
|
|
};
|
|
|
|
#endif // I_NATIVE_INSTRUMENT_H
|