Files
SonicForgeStudio/native_host/vst3_host_bridge.cpp
T

328 lines
11 KiB
C++

// vst3_host_bridge.cpp — VST3 GUI bridge (T9, spec vsti_gui)
//
// DLL export C API để attach VST3 editor vào HWND cha (cửa sổ nổi `vst_gui_*`
// từ T8). Flow chuẩn hosting: Module::create(path) → PlugProvider →
// getController → createView(kEditor) → getSize → attached(hwnd,
// kPlatformTypeHWND). Đóng: view->removed() + release (IPtr).
//
// QUYẾT ĐỊNH (T9): VST3 SDK THUẦN (không JUCE) — nhẹ, license BSD-3 sạch;
// T10 VST2 dùng vestige.h clean-room riêng; T12 native audio loop cũng SDK
// thuần. Nâng cấp nếu cần: gộp engine bằng JUCE khi có yêu cầu rõ ràng.
//
// T11 sẽ mở rộng ComponentHandler::performEdit → đẩy lên JS (vst_param_changed);
// hiện tại no-op để plugin không crash khi user chỉnh param trong editor.
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <cstdint>
#include <cstdio>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "public.sdk/source/common/commonstringconvert.h"
#include "pluginterfaces/base/funknown.h"
#include "pluginterfaces/gui/iplugview.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstcomponent.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
using namespace Steinberg;
using namespace Steinberg::Vst;
namespace {
// Callback param: plugin đổi param trong editor → đẩy lên host (T11).
// handle = instance handle; paramId = ParamID; valueNormalized 0..1.
typedef void (__cdecl* SF_ParamChangedCallback)(int32 handle, int32 paramId,
double valueNormalized, void* userdata);
// ComponentHandler tối thiểu — T11: performEdit → callback param sync.
class ComponentHandler : public IComponentHandler
{
public:
ComponentHandler () = default;
tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) override
{
if (FUnknownPrivate::iidEqual (_iid, IComponentHandler::iid) ||
FUnknownPrivate::iidEqual (_iid, FUnknown::iid))
{
*obj = this;
addRef ();
return kResultOk;
}
*obj = nullptr;
return kNoInterface;
}
uint32 PLUGIN_API addRef () override { return 1; }
uint32 PLUGIN_API release () override { return 1; }
tresult PLUGIN_API beginEdit (ParamID /*id*/) override { return kResultOk; }
tresult PLUGIN_API performEdit (ParamID id, ParamValue valueNormalized) override
{
if (cb)
cb (handle, static_cast<int32> (id), static_cast<double> (valueNormalized), userdata);
return kResultOk;
}
tresult PLUGIN_API endEdit (ParamID /*id*/) override { return kResultOk; }
tresult PLUGIN_API restartComponent (int32 /*flags*/) override { return kResultOk; }
void setCallback (int32 h, SF_ParamChangedCallback c, void* u)
{
handle = h;
cb = c;
userdata = u;
}
private:
int32 handle = 0;
SF_ParamChangedCallback cb = nullptr;
void* userdata = nullptr;
};
struct Instance
{
VST3::Hosting::Module::Ptr module;
IPtr<PlugProvider> plugProvider;
IPtr<IEditController> controller;
IPtr<IPlugView> view;
ComponentHandler handler;
};
std::mutex g_mutex;
std::map<int32, std::unique_ptr<Instance>> g_instances;
int32 g_next_handle = 1;
void set_err (char* err, int32 err_cap, const char* msg)
{
if (err && err_cap > 0)
{
std::snprintf (err, static_cast<size_t> (err_cap), "%s", msg);
}
}
} // namespace
extern "C" {
// Attach VST3 editor vào parent_hwnd. module_path_utf8: đường dẫn tới .vst3
// (UTF-8). plugin_name: tên class VST3 (ClassInfo::name — khớp plugin_id JS).
// Trả handle (>= 1) hoặc 0 + err. out_w/out_h: kích thước ưa thích của editor.
__declspec (dllexport) int32 SF_VST3_Attach (const char* module_path_utf8,
const char* plugin_name,
HWND parent_hwnd,
int32* out_w,
int32* out_h,
char* err,
int32 err_cap)
{
std::lock_guard<std::mutex> lock (g_mutex);
if (!module_path_utf8 || !plugin_name || !parent_hwnd)
{
set_err (err, err_cap, "null arg");
return 0;
}
auto inst = std::make_unique<Instance> ();
std::string loadError;
inst->module = VST3::Hosting::Module::create (module_path_utf8, loadError);
if (!inst->module)
{
set_err (err, err_cap, "cannot load VST3 module");
return 0;
}
VST3::Hosting::ClassInfo classInfo;
bool found = false;
auto factory = inst->module->getFactory ();
for (auto& ci : factory.classInfos ())
{
if (ci.category () == kVstAudioEffectClass && ci.name () == plugin_name)
{
classInfo = ci;
found = true;
break;
}
}
if (!found)
{
set_err (err, err_cap, "no VST3 audio effect class with that name");
return 0;
}
inst->plugProvider = IPtr<PlugProvider> (new PlugProvider (factory, classInfo, true));
if (!inst->plugProvider->initialize ())
{
set_err (err, err_cap, "plugin initialize failed");
return 0;
}
IEditController* rawController = inst->plugProvider->getController (); // +1 ref
if (!rawController)
{
set_err (err, err_cap, "plugin has no edit controller");
return 0;
}
inst->controller = IPtr<IEditController> (rawController); // nhận +1
inst->controller->setComponentHandler (&inst->handler);
inst->view = inst->controller->createView (ViewType::kEditor);
if (!inst->view)
{
set_err (err, err_cap, "plugin has no editor view");
return 0;
}
ViewRect r {};
if (inst->view->getSize (&r) == kResultTrue)
{
if (out_w)
*out_w = r.getWidth ();
if (out_h)
*out_h = r.getHeight ();
}
if (inst->view->attached ((void*) parent_hwnd, kPlatformTypeHWND) != kResultTrue)
{
set_err (err, err_cap, "view attach failed");
return 0;
}
int32 handle = g_next_handle++;
g_instances[handle] = std::move (inst);
return handle;
}
// Đóng editor: removed() + release (IPtr tự release khi xóa Instance).
__declspec (dllexport) int32 SF_VST3_Close (int32 handle, char* err, int32 err_cap)
{
std::lock_guard<std::mutex> lock (g_mutex);
auto it = g_instances.find (handle);
if (it == g_instances.end ())
{
set_err (err, err_cap, "bad handle");
return -1;
}
if (it->second->view)
{
it->second->view->removed ();
it->second->view = nullptr;
}
g_instances.erase (it);
return 0;
}
// Kích thước ưa thích hiện tại của editor.
__declspec (dllexport) int32 SF_VST3_GetSize (int32 handle, int32* out_w, int32* out_h)
{
std::lock_guard<std::mutex> lock (g_mutex);
auto it = g_instances.find (handle);
if (it == g_instances.end ())
return -1;
ViewRect r {};
if (it->second->view && it->second->view->getSize (&r) == kResultTrue)
{
if (out_w)
*out_w = r.getWidth ();
if (out_h)
*out_h = r.getHeight ();
return 0;
}
return -2;
}
// Báo view: parent HWND đã được resize (w,h) — plugin cập nhật nội dung.
__declspec (dllexport) int32 SF_VST3_Resize (int32 handle, int32 w, int32 h)
{
std::lock_guard<std::mutex> lock (g_mutex);
auto it = g_instances.find (handle);
if (it == g_instances.end ())
return -1;
if (it->second->view)
{
ViewRect r = {};
r.right = w;
r.bottom = h;
if (it->second->view->onSize (&r) == kResultTrue)
return 0;
}
return -2;
}
// Đăng ký callback param (T11): plugin đổi param trong editor → cb(handle,
// paramId, valueNormalized, userdata). userdata là con trỏ do host giữ.
__declspec (dllexport) int32 SF_VST3_SetParamCallback (int32 handle,
SF_ParamChangedCallback cb,
void* userdata)
{
std::lock_guard<std::mutex> lock (g_mutex);
auto it = g_instances.find (handle);
if (it == g_instances.end ())
return -1;
it->second->handler.setCallback (handle, cb, userdata);
return 0;
}
// JS automation → setParamNormalized (T11).
__declspec (dllexport) int32 SF_VST3_SetParam (int32 handle, int32 paramId, double valueNormalized)
{
std::lock_guard<std::mutex> lock (g_mutex);
auto it = g_instances.find (handle);
if (it == g_instances.end ())
return -1;
if (!it->second->controller)
return -2;
it->second->controller->setParamNormalized (static_cast<ParamID> (paramId),
static_cast<ParamValue> (valueNormalized));
return 0;
}
// Số tham số plugin (để JS dựng UI param list).
__declspec (dllexport) int32 SF_VST3_GetParamCount (int32 handle)
{
std::lock_guard<std::mutex> lock (g_mutex);
auto it = g_instances.find (handle);
if (it == g_instances.end ())
return -1;
if (!it->second->controller)
return -2;
return static_cast<int32> (it->second->controller->getParameterCount ());
}
// Thông tin param thứ index (0-based): id + tên (title) + giá trị normalized.
// Trả 0 nếu OK; -1 bad handle; -2 no controller; -3 index ngoài phạm vi.
__declspec (dllexport) int32 SF_VST3_GetParamInfo (int32 handle, int32 index,
int32* out_id, char* out_title,
int32 title_cap, double* out_value)
{
std::lock_guard<std::mutex> lock (g_mutex);
auto it = g_instances.find (handle);
if (it == g_instances.end ())
return -1;
IEditController* ctrl = it->second->controller;
if (!ctrl)
return -2;
if (index < 0 || static_cast<uint32> (index) >= ctrl->getParameterCount ())
return -3;
ParameterInfo info = {};
if (ctrl->getParameterInfo (static_cast<int32> (index), info) != kResultTrue)
return -3;
if (out_id)
*out_id = static_cast<int32> (info.id);
if (out_title && title_cap > 0)
{
std::string titleUtf8 = StringConvert::convert (std::u16string (info.title));
std::strncpy (out_title, titleUtf8.c_str (), static_cast<size_t> (title_cap - 1));
out_title[title_cap - 1] = '\0';
}
if (out_value)
*out_value = static_cast<double> (ctrl->getParamNormalized (info.id));
return 0;
}
} // extern "C"