T9: VST3 GUI bridge DLL (native_host/vst3_host_bridge.cpp, CMakeLists.txt)
This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
project(sonicforge_native_host LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
if(NOT DEFINED VST3_SDK_ROOT)
|
||||||
|
set(VST3_SDK_ROOT "" CACHE PATH "Path to VST3 SDK root")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(vst3_host_bridge SHARED
|
||||||
|
vst3_host_bridge.cpp
|
||||||
|
${VST3_SDK_ROOT}/public.sdk/source/common/commonstringconvert.cpp
|
||||||
|
${VST3_SDK_ROOT}/public.sdk/source/common/threadchecker_win32.cpp
|
||||||
|
${VST3_SDK_ROOT}/public.sdk/source/vst/hosting/module.cpp
|
||||||
|
${VST3_SDK_ROOT}/public.sdk/source/vst/hosting/module_win32.cpp
|
||||||
|
${VST3_SDK_ROOT}/public.sdk/source/vst/hosting/plugprovider.cpp
|
||||||
|
${VST3_SDK_ROOT}/public.sdk/source/vst/hosting/hostclasses.cpp
|
||||||
|
${VST3_SDK_ROOT}/public.sdk/source/vst/hosting/pluginterfacesupport.cpp
|
||||||
|
${VST3_SDK_ROOT}/public.sdk/source/vst/hosting/connectionproxy.cpp
|
||||||
|
${VST3_SDK_ROOT}/public.sdk/source/vst/utility/stringconvert.cpp
|
||||||
|
${VST3_SDK_ROOT}/public.sdk/source/vst/vstinitiids.cpp
|
||||||
|
${VST3_SDK_ROOT}/pluginterfaces/base/funknown.cpp
|
||||||
|
${VST3_SDK_ROOT}/pluginterfaces/base/ustring.cpp
|
||||||
|
${VST3_SDK_ROOT}/pluginterfaces/base/coreiids.cpp
|
||||||
|
${VST3_SDK_ROOT}/base/source/baseiids.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(vst3_host_bridge PRIVATE ${VST3_SDK_ROOT})
|
||||||
|
target_compile_definitions(vst3_host_bridge PRIVATE
|
||||||
|
_CRT_SECURE_NO_WARNINGS
|
||||||
|
NOMINMAX
|
||||||
|
WIN32_LEAN_AND_MEAN
|
||||||
|
)
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
// 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 "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 {
|
||||||
|
|
||||||
|
// ComponentHandler tối thiểu — T11: performEdit → JS 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
|
||||||
|
{
|
||||||
|
return kResultOk;
|
||||||
|
}
|
||||||
|
tresult PLUGIN_API endEdit (ParamID /*id*/) override { return kResultOk; }
|
||||||
|
tresult PLUGIN_API restartComponent (int32 /*flags*/) override { return kResultOk; }
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
Reference in New Issue
Block a user