1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

VST3 Host: Avoid resusing IMessage and IAttributeList instances

This fixes an issue where the editor of VocAlign Project 5 would fail to
open in the AudioPluginHost.
This commit is contained in:
reuk 2021-10-27 13:01:31 +01:00
parent 7098b13b44
commit 555408be40
No known key found for this signature in database
GPG key ID: 9ADCD339CFC98A11

View file

@ -329,7 +329,6 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
VST3HostContext()
{
appName = File::getSpecialLocation (File::currentApplicationFile).getFileNameWithoutExtension();
attributeList = new AttributeList (this);
}
~VST3HostContext() override = default;
@ -513,18 +512,13 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
if (doUIDsMatch (cid, Vst::IMessage::iid) && doUIDsMatch (iid, Vst::IMessage::iid))
{
VSTComSmartPtr<Message> m (new Message (attributeList));
messageMap.add (m);
m->addRef();
*obj = m;
*obj = new Message;
return kResultOk;
}
if (doUIDsMatch (cid, Vst::IAttributeList::iid) && doUIDsMatch (iid, Vst::IAttributeList::iid))
{
VSTComSmartPtr<AttributeList> l (new AttributeList (this));
l->addRef();
*obj = l;
*obj = new AttributeList;
return kResultOk;
}
@ -544,12 +538,6 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
//==============================================================================
tresult PLUGIN_API queryInterface (const TUID iid, void** obj) override
{
if (doUIDsMatch (iid, Vst::IAttributeList::iid))
{
*obj = attributeList.get();
return kResultOk;
}
return testForMultiple (*this,
iid,
UniqueBase<Vst::IComponentHandler>{},
@ -572,23 +560,214 @@ private:
void restartComponentOnMessageThread (int32 flags) override;
//==============================================================================
class Attribute
{
public:
using Int = Steinberg::int64;
using Float = double;
using String = std::vector<Vst::TChar>;
using Binary = std::vector<char>;
explicit Attribute (Int x) noexcept { constructFrom (std::move (x)); }
explicit Attribute (Float x) noexcept { constructFrom (std::move (x)); }
explicit Attribute (String x) noexcept { constructFrom (std::move (x)); }
explicit Attribute (Binary x) noexcept { constructFrom (std::move (x)); }
Attribute (Attribute&& other) noexcept
{
moveFrom (std::move (other));
}
Attribute& operator= (Attribute&& other) noexcept
{
reset();
moveFrom (std::move (other));
return *this;
}
~Attribute() noexcept
{
reset();
}
tresult getInt (Steinberg::int64& result) const
{
if (kind != Kind::Int)
return kResultFalse;
result = storage.storedInt;
return kResultTrue;
}
tresult getFloat (double& result) const
{
if (kind != Kind::Float)
return kResultFalse;
result = storage.storedFloat;
return kResultTrue;
}
tresult getString (Vst::TChar* data, Steinberg::uint32 numBytes) const
{
if (kind != Kind::String)
return kResultFalse;
std::memcpy (data,
storage.storedString.data(),
jmin (sizeof (Vst::TChar) * storage.storedString.size(), (size_t) numBytes));
return kResultTrue;
}
tresult getBinary (const void*& data, Steinberg::uint32& numBytes) const
{
if (kind != Kind::Binary)
return kResultFalse;
data = storage.storedBinary.data();
numBytes = (Steinberg::uint32) storage.storedBinary.size();
return kResultTrue;
}
private:
void constructFrom (Int x) noexcept { kind = Kind::Int; new (&storage.storedInt) Int (std::move (x)); }
void constructFrom (Float x) noexcept { kind = Kind::Float; new (&storage.storedFloat) Float (std::move (x)); }
void constructFrom (String x) noexcept { kind = Kind::String; new (&storage.storedString) String (std::move (x)); }
void constructFrom (Binary x) noexcept { kind = Kind::Binary; new (&storage.storedBinary) Binary (std::move (x)); }
void reset() noexcept
{
switch (kind)
{
case Kind::Int: break;
case Kind::Float: break;
case Kind::String: storage.storedString.~vector(); break;
case Kind::Binary: storage.storedBinary.~vector(); break;
}
}
void moveFrom (Attribute&& other) noexcept
{
switch (other.kind)
{
case Kind::Int: constructFrom (std::move (other.storage.storedInt)); break;
case Kind::Float: constructFrom (std::move (other.storage.storedFloat)); break;
case Kind::String: constructFrom (std::move (other.storage.storedString)); break;
case Kind::Binary: constructFrom (std::move (other.storage.storedBinary)); break;
}
}
enum class Kind { Int, Float, String, Binary };
union Storage
{
Storage() {}
~Storage() {}
Steinberg::int64 storedInt;
double storedFloat;
std::vector<Vst::TChar> storedString;
std::vector<char> storedBinary;
};
Storage storage;
Kind kind;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Attribute)
};
//==============================================================================
class AttributeList : public Vst::IAttributeList
{
public:
AttributeList() = default;
virtual ~AttributeList() = default;
JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_METHODS
//==============================================================================
tresult PLUGIN_API setInt (AttrID attr, Steinberg::int64 value) override
{
return set (attr, value);
}
tresult PLUGIN_API setFloat (AttrID attr, double value) override
{
return set (attr, value);
}
tresult PLUGIN_API setString (AttrID attr, const Vst::TChar* string) override
{
return set (attr, std::vector<Vst::TChar> (string, string + 1 + tstrlen (string)));
}
tresult PLUGIN_API setBinary (AttrID attr, const void* data, Steinberg::uint32 size) override
{
const auto* ptr = static_cast<const char*> (data);
return set (attr, std::vector<char> (ptr, ptr + size));
}
tresult PLUGIN_API getInt (AttrID attr, Steinberg::int64& result) override
{
return get (attr, [&] (const auto& x) { return x.getInt (result); });
}
tresult PLUGIN_API getFloat (AttrID attr, double& result) override
{
return get (attr, [&] (const auto& x) { return x.getFloat (result); });
}
tresult PLUGIN_API getString (AttrID attr, Vst::TChar* result, Steinberg::uint32 length) override
{
return get (attr, [&] (const auto& x) { return x.getString (result, length); });
}
tresult PLUGIN_API getBinary (AttrID attr, const void*& data, Steinberg::uint32& size) override
{
return get (attr, [&] (const auto& x) { return x.getBinary (data, size); });
}
private:
template <typename Value>
tresult set (AttrID attr, Value&& value)
{
if (attr == nullptr)
return kInvalidArgument;
const auto iter = attributes.find (attr);
if (iter != attributes.end())
iter->second = Attribute (std::move (value));
else
attributes.emplace (attr, Attribute (std::move (value)));
return kResultTrue;
}
template <typename Visitor>
tresult get (AttrID attr, Visitor&& visitor)
{
if (attr == nullptr)
return kInvalidArgument;
const auto iter = attributes.find (attr);
if (iter == attributes.cend())
return kResultFalse;
return visitor (iter->second);
}
std::map<std::string, Attribute> attributes;
Atomic<int> refCount { 1 };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AttributeList)
};
struct Message : public Vst::IMessage
{
explicit Message (Vst::IAttributeList* list)
: attributeList (list)
{
}
Message (Vst::IAttributeList* list, FIDString id)
: attributeList (list), messageId (toString (id))
{
}
Message (Vst::IAttributeList* list, FIDString id, const var& v)
: value (v), attributeList (list), messageId (toString (id))
{
}
Message() = default;
virtual ~Message() = default;
JUCE_DECLARE_VST3_COM_REF_METHODS
@ -596,206 +775,16 @@ private:
FIDString PLUGIN_API getMessageID() override { return messageId.toRawUTF8(); }
void PLUGIN_API setMessageID (FIDString id) override { messageId = toString (id); }
Vst::IAttributeList* PLUGIN_API getAttributes() override { return attributeList; }
var value;
Vst::IAttributeList* PLUGIN_API getAttributes() override { return &attributeList; }
private:
VSTComSmartPtr<Vst::IAttributeList> attributeList;
AttributeList attributeList;
String messageId;
Atomic<int> refCount;
Atomic<int> refCount { 1 };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Message)
};
class MessageMap
{
public:
tresult getBinary (const char* id, const void*& data, Steinberg::uint32& size)
{
jassert (id != nullptr);
const ScopedLock lock (mutex);
const auto it = storage.find (id);
if (it != storage.cend())
{
if (auto* binaryData = it->second->value.getBinaryData())
{
data = binaryData->getData();
size = (Steinberg::uint32) binaryData->getSize();
return kResultTrue;
}
}
return kResultFalse;
}
template <typename Type>
void addMessageToQueue (const char* id, Vst::IAttributeList* list, const Type& value)
{
jassert (id != nullptr);
const ScopedLock lock (mutex);
const auto it = storage.find (id);
if (it != storage.cend())
it->second->value = value;
else
storage.emplace (id, new Message (list, id, value));
}
template <typename Type>
bool findMessageOnQueueWithID (const char* id, Type& value)
{
jassert (id != nullptr);
const ScopedLock lock (mutex);
const auto it = storage.find (id);
if (it == storage.cend())
return false;
value = it->second->value;
return true;
}
void add (VSTComSmartPtr<Message> message)
{
const ScopedLock lock (mutex);
const auto* id = message->getMessageID();
storage.erase (id);
storage.emplace (id, std::move (message));
}
private:
struct Comparator
{
bool operator() (const char* a, const char* b) const noexcept
{
return std::strcmp (a, b) < 0;
}
};
// Steinberg's docs say:
// > Please note that messages from the processor to the controller must not be sent during
// the process call, as this would not be fast enough and would break the real time
// processing. Such tasks should be handled in a separate timer thread.
// Using a lock here is fine (plugins should be aware that sending messages is not
// realtime-safe), and protects the data structure in case the processor sends messages from
// a background thread rather than from the message thread.
std::map<const char*, VSTComSmartPtr<Message>, Comparator> storage;
CriticalSection mutex;
};
MessageMap messageMap;
//==============================================================================
struct AttributeList : public Vst::IAttributeList
{
AttributeList (VST3HostContext* o) : owner (o) {}
virtual ~AttributeList() = default;
JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_METHODS
//==============================================================================
tresult PLUGIN_API setInt (AttrID id, Steinberg::int64 value) override
{
addMessageToQueue (id, value);
return kResultTrue;
}
tresult PLUGIN_API setFloat (AttrID id, double value) override
{
addMessageToQueue (id, value);
return kResultTrue;
}
tresult PLUGIN_API setString (AttrID id, const Vst::TChar* string) override
{
addMessageToQueue (id, toString (string));
return kResultTrue;
}
tresult PLUGIN_API setBinary (AttrID id, const void* data, Steinberg::uint32 size) override
{
jassert (data != nullptr || size == 0);
addMessageToQueue (id, MemoryBlock (data, (size_t) size));
return kResultTrue;
}
//==============================================================================
tresult PLUGIN_API getInt (AttrID id, Steinberg::int64& result) override
{
jassert (id != nullptr);
if (findMessageOnQueueWithID (id, result))
return kResultTrue;
jassertfalse;
return kResultFalse;
}
tresult PLUGIN_API getFloat (AttrID id, double& result) override
{
jassert (id != nullptr);
if (findMessageOnQueueWithID (id, result))
return kResultTrue;
jassertfalse;
return kResultFalse;
}
tresult PLUGIN_API getString (AttrID id, Vst::TChar* result, Steinberg::uint32 length) override
{
jassert (id != nullptr);
String stringToFetch;
if (findMessageOnQueueWithID (id, stringToFetch))
{
Steinberg::String str (stringToFetch.toRawUTF8());
str.copyTo (result, 0, (Steinberg::int32) jmin (length, (Steinberg::uint32) std::numeric_limits<Steinberg::int32>::max()));
return kResultTrue;
}
jassertfalse;
return kResultFalse;
}
tresult PLUGIN_API getBinary (AttrID id, const void*& data, Steinberg::uint32& size) override
{
return owner->messageMap.getBinary (id, data, size);
}
private:
VST3HostContext* owner;
Atomic<int> refCount;
//==============================================================================
template <typename Type>
void addMessageToQueue (AttrID id, const Type& value)
{
owner->messageMap.addMessageToQueue (id, this, value);
}
template <typename Type>
bool findMessageOnQueueWithID (AttrID id, Type& value)
{
return owner->messageMap.findMessageOnQueueWithID (id, value);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AttributeList)
};
VSTComSmartPtr<AttributeList> attributeList;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3HostContext)