Browse Source

VST3: Implement UI->DSP state messaging

Signed-off-by: falkTX <falktx@falktx.com>
pull/330/head
falkTX 3 years ago
parent
commit
4ef519266d
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
4 changed files with 235 additions and 27 deletions
  1. +34
    -6
      distrho/src/DistrhoPluginVST3.cpp
  2. +179
    -11
      distrho/src/DistrhoUIVST3.cpp
  3. +20
    -1
      distrho/src/travesty/base.h
  4. +2
    -9
      examples/States/Makefile

+ 34
- 6
distrho/src/DistrhoPluginVST3.cpp View File

@@ -182,11 +182,11 @@ const char* tuid2str(const v3_tuid iid)

// --------------------------------------------------------------------------------------------------------------------

static void strncpy(char* const dst, const char* const src, const size_t size)
static void strncpy(char* const dst, const char* const src, const size_t length)
{
DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
DISTRHO_SAFE_ASSERT_RETURN(length > 0,);

if (const size_t len = std::min(std::strlen(src), size-1U))
if (const size_t len = std::min(std::strlen(src), length-1U))
{
std::memcpy(dst, src, len);
dst[len] = '\0';
@@ -197,11 +197,11 @@ static void strncpy(char* const dst, const char* const src, const size_t size)
}
}

static void strncpy_utf16(int16_t* const dst, const char* const src, const size_t size)
void strncpy_utf16(int16_t* const dst, const char* const src, const size_t length)
{
DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
DISTRHO_SAFE_ASSERT_RETURN(length > 0,);

if (const size_t len = std::min(std::strlen(src), size-1U))
if (const size_t len = std::min(std::strlen(src), length-1U))
{
for (size_t i=0; i<len; ++i)
{
@@ -1322,6 +1322,34 @@ public:
return requestParameterValueChange(rindex, value) ? V3_OK : V3_INTERNAL_ERR;
}

#if DISTRHO_PLUGIN_WANT_STATE
if (std::strcmp(msgid, "state-set") == 0)
{
int16_t* key16;
int16_t* value16;
uint32_t keySize, valueSize;
v3_result res;

res = v3_cpp_obj(attrs)->get_binary(attrs, "key", (const void**)&key16, &keySize);
DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);

res = v3_cpp_obj(attrs)->get_binary(attrs, "value", (const void**)&value16, &valueSize);
DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);

// do cheap inline conversion
char* const key = (char*)key16;
char* const value = (char*)value16;

for (uint32_t i=0; i<keySize/sizeof(int16_t); ++i)
key[i] = key16[i];
for (uint32_t i=0; i<valueSize/sizeof(int16_t); ++i)
value[i] = value16[i];

fPlugin.setState(key, value);
return V3_OK;
}
#endif

return V3_NOT_IMPLEMENTED;
}



+ 179
- 11
distrho/src/DistrhoUIVST3.cpp View File

@@ -73,12 +73,27 @@ static constexpr const setStateFunc setStateCallback = nullptr;
// Utility functions (defined on plugin side)

const char* tuid2str(const v3_tuid iid);
void strncpy_utf16(int16_t* dst, const char* src, size_t length);

// --------------------------------------------------------------------------------------------------------------------
// create message object (needed by the UI class, implementation comes later)

static v3_message** dpf_message_create(const char* id);

// --------------------------------------------------------------------------------------------------------------------
// custom attribute list struct, used for sending utf8 strings

struct v3_attribute_list_utf8 {
V3_API v3_result (*set_string_utf8)(void* self, const char* id, const char* string);
V3_API v3_result (*get_string_utf8)(void* self, const char* id, char* string, uint32_t size);
};

static constexpr const v3_tuid v3_attribute_list_utf8_iid =
V3_ID(d_cconst('D','P','F',' '),
d_cconst('c','l','a','s'),
d_cconst('a','t','t','r'),
d_cconst('u','t','f','8'));

// --------------------------------------------------------------------------------------------------------------------

/**
@@ -317,12 +332,28 @@ private:
}
#endif

#if DISTRHO_PLUGIN_WANT_STATE
void setState(const char* const /*key*/, const char* const /*value*/)
void setState(const char* const key, const char* const value)
{
// fUiHelper->setStateFromUI(key, value);
DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);

v3_message** const message = dpf_message_create("state-set");
DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);

v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message);
DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr,);

v3_attribute_list_utf8** utf8attrs = nullptr;
DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj_query_interface(attrs, v3_attribute_list_utf8_iid, &utf8attrs) == V3_OK,);
DISTRHO_SAFE_ASSERT_RETURN(utf8attrs != nullptr,);

v3_cpp_obj(utf8attrs)->set_string_utf8(utf8attrs, "key", key);
v3_cpp_obj(utf8attrs)->set_string_utf8(utf8attrs, "value", value);
v3_cpp_obj(fConnection)->notify(fConnection, message);

v3_cpp_obj_unref(message);
}

#if DISTRHO_PLUGIN_WANT_STATE
static void setStateCallback(void* ptr, const char* key, const char* value)
{
((UIVst3*)ptr)->setState(key, value);
@@ -337,10 +368,9 @@ private:
*/

// --------------------------------------------------------------------------------------------------------------------
// dpf_attribute_list

struct dpf_attribute_value {
char type;
char type; // one of: i, f, s, b
union {
int64_t integer;
double v_float;
@@ -352,16 +382,113 @@ struct dpf_attribute_value {
};
};

static void dpf_attribute_list_free(std::map<std::string, dpf_attribute_value>& attrs)
{
for (auto& it : attrs)
{
dpf_attribute_value& v(it.second);

switch (v.type)
{
case 's':
case 'b':
std::free(v.binary.ptr);
break;
}
}
}

// --------------------------------------------------------------------------------------------------------------------
// dpf_attribute_list_dpf (the custom utf8 variant)

struct v3_attribute_list_utf8_cpp : v3_funknown {
v3_attribute_list_utf8 attr;
};

struct dpf_attribute_list_dpf : v3_attribute_list_utf8_cpp {
std::map<std::string, dpf_attribute_value>& attrs;

dpf_attribute_list_dpf(std::map<std::string, dpf_attribute_value>& a)
: attrs(a)
{
static const uint8_t* kSupportedInterfaces[] = {
v3_funknown_iid,
v3_attribute_list_utf8_iid
};

// ------------------------------------------------------------------------------------------------------------
// v3_funknown

query_interface = []V3_API(void* self, const v3_tuid iid, void** iface) -> v3_result
{
d_stdout("dpf_attribute_list_dpf::query_interface => %p %s %p", self, tuid2str(iid), iface);
*iface = NULL;
DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE);

for (const uint8_t* interface_iid : kSupportedInterfaces)
{
if (v3_tuid_match(interface_iid, iid))
{
*iface = self;
return V3_OK;
}
}

return V3_NO_INTERFACE;
};

// there is only a single instance of this, so we don't have to care here
ref = []V3_API(void*) -> uint32_t { return 1; };
unref = []V3_API(void*) -> uint32_t { return 0; };

// ------------------------------------------------------------------------------------------------------------
// v3_attribute_list_utf8

attr.set_string_utf8 = []V3_API(void* self, const char* id, const char* string) -> v3_result
{
dpf_attribute_list_dpf* const attr = *(dpf_attribute_list_dpf**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

const uint32_t size = std::strlen(string) + 1;
int16_t* const copy = (int16_t*)std::malloc(sizeof(int16_t) * size);
DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM);

DISTRHO_NAMESPACE::strncpy_utf16(copy, string, size);

dpf_attribute_value& attrval(attr->attrs[id]);
attrval.type = 's';
attrval.binary.ptr = copy;
attrval.binary.size = sizeof(int16_t) * size;
return V3_OK;
};

attr.get_string_utf8 = []V3_API(void* self, const char* id, char*, uint32_t) -> v3_result
{
dpf_attribute_list_dpf* const attr = *(dpf_attribute_list_dpf**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

if (attr->attrs.find(id) == attr->attrs.end())
return V3_INVALID_ARG;

return V3_NOT_IMPLEMENTED;
};
}
};

// --------------------------------------------------------------------------------------------------------------------
// dpf_attribute_list

struct v3_attribute_list_cpp : v3_funknown {
v3_attribute_list attr;
};

struct dpf_attribute_list : v3_attribute_list_cpp {
ScopedPointer<dpf_attribute_list_dpf> attrdpf;
std::map<std::string, dpf_attribute_value> attrs;

dpf_attribute_list()
{
static const uint8_t* kSupportedInterfaces[] = {
static const uint8_t* kSupportedInterfacesBase[] = {
v3_funknown_iid,
v3_attribute_list_iid
};
@@ -375,7 +502,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
*iface = NULL;
DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE);

for (const uint8_t* interface_iid : kSupportedInterfaces)
for (const uint8_t* interface_iid : kSupportedInterfacesBase)
{
if (v3_tuid_match(interface_iid, iid))
{
@@ -384,6 +511,17 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
}
}

dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NO_INTERFACE);

if (v3_tuid_match(v3_attribute_list_utf8_iid, iid))
{
if (attr->attrdpf == nullptr)
attr->attrdpf = new dpf_attribute_list_dpf(attr->attrs);
*iface = &attr->attrdpf;
return V3_OK;
}

return V3_NO_INTERFACE;
};

@@ -400,6 +538,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

dpf_attribute_value& attrval(attr->attrs[id]);
attrval.type = 'i';
attrval.integer = value;
return V3_OK;
};
@@ -413,6 +552,8 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
return V3_INVALID_ARG;

const dpf_attribute_value& attrval(attr->attrs[id]);
DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'i', V3_INVALID_ARG);

*value = attrval.integer;
return V3_OK;
};
@@ -423,6 +564,7 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

dpf_attribute_value& attrval(attr->attrs[id]);
attrval.type = 'f';
attrval.v_float = value;
return V3_OK;
};
@@ -436,15 +578,21 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
return V3_INVALID_ARG;

const dpf_attribute_value& attrval(attr->attrs[id]);
DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 'f', V3_INVALID_ARG);

*value = attrval.v_float;
return V3_OK;
};

attr.set_string = []V3_API(void* self, const char* /*id*/, const int16_t* /*string*/) -> v3_result
attr.set_string = []V3_API(void* self, const char* id, const int16_t* /*string*/) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

dpf_attribute_value& attrval(attr->attrs[id]);
attrval.type = 's';
attrval.binary.ptr = nullptr;
attrval.binary.size = 0;
return V3_NOT_IMPLEMENTED;
};

@@ -456,18 +604,30 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
if (attr->attrs.find(id) == attr->attrs.end())
return V3_INVALID_ARG;

const dpf_attribute_value& attrval(attr->attrs[id]);
DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's', V3_INVALID_ARG);

return V3_NOT_IMPLEMENTED;
};

attr.set_binary = []V3_API(void* self, const char* /*id*/, const void* /*data*/, uint32_t /*size*/) -> v3_result
attr.set_binary = []V3_API(void* self, const char* id, const void* data, uint32_t size) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);

void* const copy = std::malloc(size);
DISTRHO_SAFE_ASSERT_RETURN(copy != nullptr, V3_NOMEM);

std::memcpy(copy, data, size);

dpf_attribute_value& attrval(attr->attrs[id]);
attrval.type = 'b';
attrval.binary.ptr = copy;
attrval.binary.size = size;
return V3_NOT_IMPLEMENTED;
};

attr.get_binary = []V3_API(void* self, const char* id, const void** /*data*/, uint32_t* /*size*/) -> v3_result
attr.get_binary = []V3_API(void* self, const char* id, const void** data, uint32_t* size) -> v3_result
{
dpf_attribute_list* const attr = *(dpf_attribute_list**)self;
DISTRHO_SAFE_ASSERT_RETURN(attr != nullptr, V3_NOT_INITIALISED);
@@ -475,7 +635,12 @@ struct dpf_attribute_list : v3_attribute_list_cpp {
if (attr->attrs.find(id) == attr->attrs.end())
return V3_INVALID_ARG;

return V3_NOT_IMPLEMENTED;
const dpf_attribute_value& attrval(attr->attrs[id]);
DISTRHO_SAFE_ASSERT_RETURN(attrval.type == 's' || attrval.type == 'b', V3_INVALID_ARG);

*data = attrval.binary.ptr;
*size = attrval.binary.size;
return V3_OK;
};
}
};
@@ -545,6 +710,9 @@ struct dpf_message : v3_message_cpp {
if (const int refcounter = --message->refcounter)
return refcounter;

if (message->attrlist != nullptr)
dpf_attribute_list_free(message->attrlist->attrs);

*message->self = nullptr;
delete messageptr;
return 0;


+ 20
- 1
distrho/src/travesty/base.h View File

@@ -25,6 +25,7 @@
*/

#ifdef __cplusplus

/**
* cast object into its proper C++ type.
* this is needed because `struct v3_funknown;` on a C++ class does not inherit `v3_funknown`'s fields.
@@ -43,10 +44,13 @@ constexpr T* v3_cpp_obj(T** obj)
*/
return static_cast<T*>(static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(*obj)) + sizeof(void*)*3));
}

#else

# ifndef constexpr
# define constexpr
# endif

#endif

/**
@@ -179,12 +183,27 @@ static constexpr const v3_tuid v3_plugin_base_iid =
V3_ID(0x22888DDB, 0x156E45AE, 0x8358B348, 0x08190625);

#ifdef __cplusplus

/**
* helper C++ function to manually call unref on an object.
* helper C++ functions to manually call v3_funknown methods on an object.
*/

template<class T, class M> static inline
v3_result v3_cpp_obj_query_interface(T** obj, const v3_tuid iid, M*** obj2)
{
return static_cast<v3_funknown*>(static_cast<void*>(*obj))->query_interface(obj, iid, (void**)obj2);
}

template<class T> static inline
uint32_t v3_cpp_obj_ref(T** obj)
{
return static_cast<v3_funknown*>(static_cast<void*>(*obj))->ref(obj);
}

template<class T> static inline
uint32_t v3_cpp_obj_unref(T** obj)
{
return static_cast<v3_funknown*>(static_cast<void*>(*obj))->unref(obj);
}

#endif

+ 2
- 9
examples/States/Makefile View File

@@ -26,17 +26,10 @@ include ../../Makefile.plugins.mk
# --------------------------------------------------------------
# Enable all possible plugin types

ifeq ($(HAVE_OPENGL),true)
TARGETS += jack
endif # HAVE_OPENGL

ifeq ($(HAVE_OPENGL),true)
TARGETS += lv2_sep
else
TARGETS += lv2_dsp
endif

TARGETS += vst
TARGETS += vst2
TARGETS += vst3

all: $(TARGETS)



Loading…
Cancel
Save