T11: IPC param sync 2 chieu native<->JS (setParamNormalized/setParameter, vst_param_changed event, guard chong loop)
This commit is contained in:
@@ -26,6 +26,10 @@
|
||||
|
||||
namespace {
|
||||
|
||||
// Callback param (T11): plugin đổi param (audioMasterAutomate) → host.
|
||||
typedef void (VSTCALLBACK* SF_ParamChangedCallback)(int32 handle, int32 paramIndex,
|
||||
double valueNormalized, void* userdata);
|
||||
|
||||
struct Instance
|
||||
{
|
||||
HMODULE module = nullptr;
|
||||
@@ -38,9 +42,14 @@ struct Instance
|
||||
ERect* editorRect = nullptr;
|
||||
bool editorOpen = false;
|
||||
bool active = false;
|
||||
int32 handle = 0;
|
||||
SF_ParamChangedCallback paramCb = nullptr;
|
||||
void* paramCbUserdata = nullptr;
|
||||
};
|
||||
|
||||
std::mutex g_mutex;
|
||||
std::mutex g_effect_map_mutex; // riêng: audioMasterAutomate gọi từ audio thread
|
||||
std::map<AEffect*, Instance*> g_effect_map;
|
||||
std::map<int32, std::unique_ptr<Instance>> g_instances;
|
||||
int32 g_next_handle = 1;
|
||||
|
||||
@@ -59,10 +68,9 @@ thread_local Instance* tls_loading = nullptr;
|
||||
VstIntPtr VSTCALLBACK hostAudioMasterImpl (AEffect* effect, int32 opcode, int32 index,
|
||||
VstIntPtr value, void* ptr, float opt)
|
||||
{
|
||||
(void) effect;
|
||||
(void) index;
|
||||
(void) value;
|
||||
(void) opt;
|
||||
(void) ptr;
|
||||
Instance* inst = tls_loading;
|
||||
switch (opcode)
|
||||
{
|
||||
@@ -72,6 +80,22 @@ VstIntPtr VSTCALLBACK hostAudioMasterImpl (AEffect* effect, int32 opcode, int32
|
||||
return inst ? inst->sampleRate : 44100;
|
||||
case audioMasterGetBlockSize:
|
||||
return inst ? inst->blockSize : 512;
|
||||
case audioMasterAutomate:
|
||||
{
|
||||
// Plugin đổi param (user kéo knob trong editor) → callback lên host.
|
||||
// Effect → instance qua map riêng (audio thread, không giữ g_mutex).
|
||||
Instance* owner = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (g_effect_map_mutex);
|
||||
auto it = g_effect_map.find (effect);
|
||||
if (it != g_effect_map.end ())
|
||||
owner = it->second;
|
||||
}
|
||||
if (owner && owner->paramCb)
|
||||
owner->paramCb (owner->handle, index, static_cast<double> (opt),
|
||||
owner->paramCbUserdata);
|
||||
return 1;
|
||||
}
|
||||
case audioMasterCurrentId:
|
||||
case audioMasterGetLanguage:
|
||||
return 0;
|
||||
@@ -200,6 +224,11 @@ __declspec (dllexport) int32 SF_VST2_Load (const char* module_path_utf8,
|
||||
}
|
||||
|
||||
int32 handle = g_next_handle++;
|
||||
inst->handle = handle;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (g_effect_map_mutex);
|
||||
g_effect_map[inst->effect] = inst.get ();
|
||||
}
|
||||
g_instances[handle] = std::move (inst);
|
||||
return handle;
|
||||
}
|
||||
@@ -217,6 +246,10 @@ __declspec (dllexport) int32 SF_VST2_Close (int32 handle, char* err, int32 err_c
|
||||
Instance* inst = it->second.get ();
|
||||
if (inst->effect)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (g_effect_map_mutex);
|
||||
g_effect_map.erase (inst->effect);
|
||||
}
|
||||
if (inst->active)
|
||||
{
|
||||
inst->effect->dispatcher (inst->effect, effStopProcess, 0, 0, nullptr, 0.0f);
|
||||
@@ -372,4 +405,63 @@ __declspec (dllexport) int32 SF_VST2_Process (int32 handle, float* buffers, int3
|
||||
return -4;
|
||||
}
|
||||
|
||||
// Đăng ký callback param (T11): plugin đổi param (audioMasterAutomate) →
|
||||
// cb(handle, paramIndex, valueNormalized, userdata).
|
||||
__declspec (dllexport) int32 SF_VST2_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->paramCb = cb;
|
||||
it->second->paramCbUserdata = userdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// JS automation → setParameter (T11).
|
||||
__declspec (dllexport) int32 SF_VST2_SetParam (int32 handle, int32 paramIndex,
|
||||
double valueNormalized)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (g_mutex);
|
||||
auto it = g_instances.find (handle);
|
||||
if (it == g_instances.end ())
|
||||
return -1;
|
||||
Instance* inst = it->second.get ();
|
||||
if (!inst->effect || !inst->effect->setParameter)
|
||||
return -2;
|
||||
inst->effect->setParameter (inst->effect, paramIndex,
|
||||
static_cast<float> (valueNormalized));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Số tham số plugin.
|
||||
__declspec (dllexport) int32 SF_VST2_GetParamCount (int32 handle)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (g_mutex);
|
||||
auto it = g_instances.find (handle);
|
||||
if (it == g_instances.end ())
|
||||
return -1;
|
||||
Instance* inst = it->second.get ();
|
||||
if (!inst->effect || !inst->effect->numParams)
|
||||
return -2;
|
||||
return inst->effect->numParams (inst->effect);
|
||||
}
|
||||
|
||||
// Giá trị param (0..1) — JS đọc để dựng UI.
|
||||
__declspec (dllexport) int32 SF_VST2_GetParam (int32 handle, int32 paramIndex, double* out_value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (g_mutex);
|
||||
auto it = g_instances.find (handle);
|
||||
if (it == g_instances.end ())
|
||||
return -1;
|
||||
Instance* inst = it->second.get ();
|
||||
if (!inst->effect || !inst->effect->getParameter)
|
||||
return -2;
|
||||
if (out_value)
|
||||
*out_value = static_cast<double> (inst->effect->getParameter (inst->effect, paramIndex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -13,9 +13,33 @@ struct FakeState
|
||||
{
|
||||
float phase = 0.0f;
|
||||
int32 sampleRate = 44100;
|
||||
float gain = 0.5f; // param 0 — "Gain" (0..1)
|
||||
};
|
||||
|
||||
FakeState g_state;
|
||||
void* g_audioMaster = nullptr;
|
||||
|
||||
void VSTCALLBACK fakeSetParameter (AEffect* effect, int32 index, float value)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
g_state.gain = value;
|
||||
// Báo host: param đổi từ editor/plugin (T11) — chống loop host-side.
|
||||
if (g_audioMaster)
|
||||
{
|
||||
typedef VstIntPtr (VSTCALLBACK* AMFn) (AEffect*, int32, int32, VstIntPtr, void*, float);
|
||||
reinterpret_cast<AMFn> (g_audioMaster) (effect, audioMasterAutomate, index, 0, nullptr,
|
||||
value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float VSTCALLBACK fakeGetParameter (AEffect*, int32 index)
|
||||
{
|
||||
if (index == 0)
|
||||
return g_state.gain;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
int32 VSTCALLBACK fakeDispatcher (AEffect* effect, int32 opcode, int32 index,
|
||||
VstIntPtr value, void* ptr, float opt)
|
||||
@@ -91,15 +115,18 @@ void VSTCALLBACK fakeProcessReplacing (AEffect*, float** inputs, float** outputs
|
||||
|
||||
AEffect g_effect;
|
||||
|
||||
AEffect* VSTCALLBACK createInstance (void*)
|
||||
AEffect* VSTCALLBACK createInstance (void* audioMaster)
|
||||
{
|
||||
g_audioMaster = audioMaster;
|
||||
std::memset (&g_effect, 0, sizeof (g_effect));
|
||||
g_effect.magic = CCONST ('V', 's', 't', 'P');
|
||||
g_effect.dispatcher = &fakeDispatcher;
|
||||
g_effect.processReplacing = &fakeProcessReplacing;
|
||||
g_effect.setParameter = &fakeSetParameter;
|
||||
g_effect.getParameter = &fakeGetParameter;
|
||||
g_effect.numInputs = [] (AEffect*) -> int32 { return 0; };
|
||||
g_effect.numOutputs = [] (AEffect*) -> int32 { return 2; };
|
||||
g_effect.numParams = [] (AEffect*) -> int32 { return 0; };
|
||||
g_effect.numParams = [] (AEffect*) -> int32 { return 1; };
|
||||
g_effect.numPrograms = [] (AEffect*) -> int32 { return 0; };
|
||||
g_effect.flags = [] (AEffect*) -> int32 { return effFlagsCanReplacing | effFlagsIsSynth; };
|
||||
g_effect.uniqueID = CCONST ('F', 'k', '2', 'V');
|
||||
@@ -111,6 +138,5 @@ AEffect* VSTCALLBACK createInstance (void*)
|
||||
|
||||
extern "C" __declspec (dllexport) AEffect* VSTPluginMain (void* audioMaster)
|
||||
{
|
||||
(void) audioMaster;
|
||||
return createInstance (audioMaster);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#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"
|
||||
@@ -35,7 +36,12 @@ using namespace Steinberg::Vst;
|
||||
|
||||
namespace {
|
||||
|
||||
// ComponentHandler tối thiểu — T11: performEdit → JS param sync.
|
||||
// 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:
|
||||
@@ -55,12 +61,26 @@ public:
|
||||
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
|
||||
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
|
||||
@@ -233,4 +253,75 @@ __declspec (dllexport) int32 SF_VST3_Resize (int32 handle, int32 w, int32 h)
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user