From 84a86fa21a0a9a429412ad1044769f7aee638a6d Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 4 Nov 2019 21:46:24 +0100 Subject: [PATCH] VST3: Implement host classes --- libs/ardour/ardour/vst3_host.h | 414 +++++++++++++++++ libs/ardour/vst3_host.cc | 784 +++++++++++++++++++++++++++++++++ libs/ardour/wscript | 2 +- 3 files changed, 1199 insertions(+), 1 deletion(-) create mode 100644 libs/ardour/ardour/vst3_host.h create mode 100644 libs/ardour/vst3_host.cc diff --git a/libs/ardour/ardour/vst3_host.h b/libs/ardour/ardour/vst3_host.h new file mode 100644 index 0000000000..65d60447a1 --- /dev/null +++ b/libs/ardour/ardour/vst3_host.h @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2019-2020 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _ardour_vst3_host_h_ +#define _ardour_vst3_host_h_ + +#include +#include +#include + +#include + +#include + +#include "ardour/libardour_visibility.h" +#include "vst3/vst3.h" + +#define QUERY_INTERFACE_IMPL(Interface) \ +tresult queryInterface (const TUID _iid, void** obj) \ +{ \ + QUERY_INTERFACE (_iid, obj, FUnknown::iid, Interface) \ + QUERY_INTERFACE (_iid, obj, Interface::iid, Interface) \ + *obj = nullptr; \ + return kNoInterface; \ +} + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + +namespace Steinberg { + +LIBARDOUR_API extern std::string tchar_to_utf8 (Vst::TChar const* s); +LIBARDOUR_API extern bool utf8_to_tchar (Vst::TChar* rv, const char* s, size_t l = 0); +LIBARDOUR_API extern bool utf8_to_tchar (Vst::TChar* rv, std::string const& s, size_t l = 0); + +namespace Vst { + /* see public.sdk/source/vst/vstpresetfile.cpp */ + typedef char ChunkID[4]; // using ChunkID = char[4]; + static const int32 kClassIDSize = 32; // ASCII-encoded FUID + static const int32 kHeaderSize = sizeof (ChunkID) + sizeof (int32) + kClassIDSize + sizeof (TSize); + static const int32 kListOffsetPos = kHeaderSize - sizeof (TSize); +} + +class LIBARDOUR_API HostAttribute +{ +public: + enum Type { + kInteger, + kFloat, + kString, + kBinary + }; + + HostAttribute (int64 value) + : _size (0) + , _type (kInteger) + { + v.intValue = value; + } + + HostAttribute (double value) + : _size (0) + , _type (kFloat) + { + v.floatValue = value; + } + + HostAttribute (const Vst::TChar* value, uint32 size) + : _size (size) + , _type (kString) + { + v.stringValue = new Vst::TChar[_size]; + memcpy (v.stringValue, value, _size * sizeof (Vst::TChar)); + } + + HostAttribute (const void* value, uint32 size) + : _size (size) + , _type (kBinary) + { + v.binaryValue = new char[_size]; + memcpy (v.binaryValue, value, _size); + } + + ~HostAttribute () + { + if (_size) { + delete[] v.binaryValue; + } + } + + Type getType () const { return _type; } + int64 intValue () const { return v.intValue; } + double floatValue () const { return v.floatValue; } + + const Vst::TChar* stringValue (uint32& stringSize) + { + stringSize = _size; + return v.stringValue; + } + + const void* binaryValue (uint32& binarySize) + { + binarySize = _size; + return v.binaryValue; + } + +protected: + union v { + int64 intValue; + double floatValue; + Vst::TChar* stringValue; + char* binaryValue; + } v; + + uint32 _size; + Type _type; + +private: + /* prevent copy construction */ + HostAttribute (HostAttribute const& other); +}; + +class LIBARDOUR_API RefObject : public FUnknown +{ +public: + RefObject (); + virtual ~RefObject () {} + uint32 addRef (); + uint32 release (); + +private: + gint _cnt; // atomic +}; + +class LIBARDOUR_API HostAttributeList : public Vst::IAttributeList, public RefObject +{ +public: + HostAttributeList (); + virtual ~HostAttributeList (); + + QUERY_INTERFACE_IMPL (Vst::IAttributeList); + uint32 addRef () SMTG_OVERRIDE { return RefObject::addRef (); } + uint32 release () SMTG_OVERRIDE { return RefObject::release (); } + + tresult setInt (AttrID aid, int64 value) SMTG_OVERRIDE; + tresult getInt (AttrID aid, int64& value) SMTG_OVERRIDE; + tresult setFloat (AttrID aid, double value) SMTG_OVERRIDE; + tresult getFloat (AttrID aid, double& value) SMTG_OVERRIDE; + tresult setString (AttrID aid, const Vst::TChar* string) SMTG_OVERRIDE; + tresult getString (AttrID aid, Vst::TChar* string, uint32 size) SMTG_OVERRIDE; + tresult setBinary (AttrID aid, const void* data, uint32 size) SMTG_OVERRIDE; + tresult getBinary (AttrID aid, const void*& data, uint32& size) SMTG_OVERRIDE; + +protected: + void removeAttrID (AttrID aid); + + std::map list; +}; + +class LIBARDOUR_API HostMessage : public Vst::IMessage, public RefObject +{ +public: + HostMessage (); + virtual ~HostMessage (); + + QUERY_INTERFACE_IMPL (Vst::IMessage); + uint32 addRef () SMTG_OVERRIDE { return RefObject::addRef (); } + uint32 release () SMTG_OVERRIDE { return RefObject::release (); } + + const char* getMessageID () SMTG_OVERRIDE; + void setMessageID (const char* messageID) SMTG_OVERRIDE; + Vst::IAttributeList* getAttributes () SMTG_OVERRIDE; + +protected: + char* _messageId; + boost::shared_ptr _attribute_list; +}; + +class LIBARDOUR_API PlugInterfaceSupport : public Vst::IPlugInterfaceSupport +{ +public: + PlugInterfaceSupport (); + QUERY_INTERFACE_IMPL (Vst::IPlugInterfaceSupport); + uint32 addRef () SMTG_OVERRIDE { return 1; } + uint32 release () SMTG_OVERRIDE { return 1; } + + tresult isPlugInterfaceSupported (const TUID) SMTG_OVERRIDE; + void addPlugInterfaceSupported (const TUID); + +private: + std::vector _interfaces; +}; + +class LIBARDOUR_API HostApplication : public Vst::IHostApplication +{ +public: + static Vst::IHostApplication* getHostContext () + { + static HostApplication* app = new HostApplication; + return app; + } + + HostApplication (); + virtual ~HostApplication () {} + tresult queryInterface (const TUID _iid, void** obj) SMTG_OVERRIDE; + + uint32 addRef () SMTG_OVERRIDE { return 1; } + uint32 release () SMTG_OVERRIDE { return 1; } + + tresult getName (Vst::String128 name) SMTG_OVERRIDE; + tresult createInstance (TUID cid, TUID _iid, void** obj) SMTG_OVERRIDE; + +protected: + boost::shared_ptr _plug_interface_support; +}; + +class LIBARDOUR_LOCAL Vst3ParamValueQueue : public Vst::IParamValueQueue +{ +public: + QUERY_INTERFACE_IMPL (Vst::IParamValueQueue); + uint32 addRef () SMTG_OVERRIDE { return 1; } + uint32 release () SMTG_OVERRIDE { return 1; } + + static const int maxNumPoints = 64; + + Vst3ParamValueQueue() { + _values.reserve (maxNumPoints); + _id = Vst::kNoParamId; + } + + Vst::ParamID getParameterId() SMTG_OVERRIDE { return _id; } + + void setParameterId (Vst::ParamID id) { + _values.clear(); + _id = id; + } + + int32 getPointCount() SMTG_OVERRIDE { return _values.size(); } + tresult getPoint (int32 index, int32&, Vst::ParamValue&) SMTG_OVERRIDE; + tresult addPoint (int32, Vst::ParamValue, int32&) SMTG_OVERRIDE; + +protected: + struct Value { + Value (Vst::ParamValue v, int32 offset) + : value(v) + , sampleOffset(offset) + {} + Vst::ParamValue value; + int32 sampleOffset; + }; + + std::vector _values; + Vst::ParamID _id; +}; + +class LIBARDOUR_LOCAL Vst3ParameterChanges : public Vst::IParameterChanges +{ +public: + QUERY_INTERFACE_IMPL (Vst::IParameterChanges); + uint32 addRef () SMTG_OVERRIDE { return 1; } + uint32 release () SMTG_OVERRIDE { return 1; } + + Vst3ParameterChanges () { + clear (); + } + + void set_n_params (int n) { + _queue.resize(n); + } + + void clear () { + _used_queue_count = 0; + } + + int32 getParameterCount() SMTG_OVERRIDE { + return _used_queue_count; + } + + Vst::IParamValueQueue* getParameterData (int32 index) SMTG_OVERRIDE; + Vst::IParamValueQueue* addParameterData (Vst::ParamID const& id, int32& index) SMTG_OVERRIDE; + +protected: + std::vector _queue; + int _used_queue_count; +}; + +class LIBARDOUR_LOCAL Vst3EventList : public Vst::IEventList +{ +public: + Vst3EventList() { + _events.reserve (128); + } + + QUERY_INTERFACE_IMPL (Vst::IEventList) + uint32 addRef () SMTG_OVERRIDE { return 1; } + uint32 release () SMTG_OVERRIDE { return 1; } + + int32 PLUGIN_API getEventCount() { + return _events.size (); + } + + tresult PLUGIN_API getEvent (int32 index, Vst::Event& e) { + if (index >= 0 && index < (int32)_events.size ()) { + e = _events[index]; + return kResultTrue; + } else { + return kResultFalse; + } + } + + tresult PLUGIN_API addEvent (Vst::Event& e) { + _events.push_back (e); + return kResultTrue; + } + + void clear() { + _events.clear(); + } + +protected: + std::vector _events; +}; + +class LIBARDOUR_LOCAL RAMStream : public IBStream +{ +public: + RAMStream (); + RAMStream (uint8_t* data, size_t size); + RAMStream (std::string const& fn); + + virtual ~RAMStream (); + + QUERY_INTERFACE_IMPL (IBStream) + uint32 addRef () SMTG_OVERRIDE { return 1; } + uint32 release () SMTG_OVERRIDE { return 1; } + + /* IBStream API */ + tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead) SMTG_OVERRIDE; + tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten) SMTG_OVERRIDE; + tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result) SMTG_OVERRIDE; + tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE; + + /* convenience API for state I/O */ + void rewind () { _pos = 0; } + bool readonly () const { return _readonly; } + + bool write_int32 (int32 i); + bool write_int64 (int64 i); + bool write_ChunkID (const Vst::ChunkID& id); + bool write_TUID (const TUID& tuid); + + bool read_int32 (int32& i); + bool read_int64 (int64& i); + bool read_ChunkID (Vst::ChunkID& id); + bool read_TUID (TUID& tuid); + + /* direct access */ + uint8_t const* data () const { return _data; } + int64 size () const { return _size; } + +#ifndef NDEBUG + void hexdump (int64 max_len = 64) const; +#endif + +private: + bool reallocate_buffer (int64 size, bool exact); + + template bool read_pod (T& t) { + int32 n_read = 0; + read ((void *)&t, sizeof(T), &n_read); + return n_read == sizeof(T); + } + + template bool write_pod (const T& t) { + int32 written = 0; + write (const_cast((const void *)&t), sizeof(T), &written); + return written == sizeof(T); + } + + uint8_t* _data; + int64 _size; + int64 _alloc; + int64 _pos; + bool _readonly; +}; + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# pragma GCC diagnostic pop +#endif + +} // namespace Steinberg +#endif diff --git a/libs/ardour/vst3_host.cc b/libs/ardour/vst3_host.cc new file mode 100644 index 0000000000..12251d99e1 --- /dev/null +++ b/libs/ardour/vst3_host.cc @@ -0,0 +1,784 @@ +/* + * Copyright (C) 2019-2020 Robin Gareus + * Copyright (C) 2019 Steinberg Media Technologies GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include + +#if (__cplusplus >= 201103L) +#include +#endif + +#include "ardour/vst3_host.h" + +using namespace Steinberg; + +DEF_CLASS_IID (FUnknown) +DEF_CLASS_IID (IBStream) +DEF_CLASS_IID (IPluginBase) +DEF_CLASS_IID (IPluginFactory) +DEF_CLASS_IID (IPluginFactory2) +DEF_CLASS_IID (IPlugFrame) +DEF_CLASS_IID (IPlugView) +DEF_CLASS_IID (Vst::IAttributeList) +DEF_CLASS_IID (Vst::IAudioProcessor) +DEF_CLASS_IID (Vst::IAutomationState) +DEF_CLASS_IID (Vst::IComponent) +DEF_CLASS_IID (Vst::IComponentHandler) +DEF_CLASS_IID (Vst::IConnectionPoint) +DEF_CLASS_IID (Vst::IEditController) +DEF_CLASS_IID (Vst::IEventList) +DEF_CLASS_IID (Vst::IHostApplication) +DEF_CLASS_IID (Vst::IMessage) +DEF_CLASS_IID (Vst::IMidiMapping) +DEF_CLASS_IID (Vst::IParameterChanges) +DEF_CLASS_IID (Vst::IParamValueQueue) +DEF_CLASS_IID (Vst::IPlugInterfaceSupport) +DEF_CLASS_IID (Vst::IProgramListData) +DEF_CLASS_IID (Vst::IUnitData) +DEF_CLASS_IID (Vst::IUnitInfo) + +#if SMTG_OS_LINUX +DEF_CLASS_IID (Linux::IRunLoop); +#endif + +std::string +Steinberg::tchar_to_utf8 (Vst::TChar const* s) +{ + glong len; + gchar* utf8 = g_utf16_to_utf8 ((const gunichar2*)s, -1, NULL, &len, NULL); + if (!utf8 || len == 0) { + return ""; + } + std::string rv (utf8, len); + g_free (utf8); + return rv; +} + +bool +Steinberg::utf8_to_tchar (Vst::TChar* rv, const char* s, size_t l) +{ + glong len; + gunichar2* s16 = g_utf8_to_utf16 (s, -1, NULL, &len, NULL); + if (!s16 || len == 0) { + memset (rv, 0, sizeof (Vst::TChar)); + return false; + } + if (l > 0 && l < len) { + len = l; + } + memcpy (rv, s16, len * sizeof (Vst::TChar)); + g_free (s16); + return true; +} + +bool +Steinberg::utf8_to_tchar (Vst::TChar* rv, std::string const& s, size_t l) +{ + return utf8_to_tchar (rv, s.c_str(), l); +} + +/* ****************************************************************************/ + +RefObject::RefObject () +{ + g_atomic_int_set (&_cnt, 1); +} + +uint32 +RefObject::addRef () +{ + g_atomic_int_inc (&_cnt); + return g_atomic_int_get (&_cnt); +} + +uint32 +RefObject::release () +{ + if (g_atomic_int_dec_and_test (&_cnt)) { + delete this; + return 0; + } + return g_atomic_int_get (&_cnt); +} + +/* ****************************************************************************/ +/* copy/edit from public.sdk/source/vst/hosting/hostclasses.cpp */ +/* ****************************************************************************/ + +#include "vst3/pluginterfaces/base/funknown.cpp" + +HostAttributeList::HostAttributeList () +{ +} + +HostAttributeList::~HostAttributeList () +{ + std::map::reverse_iterator it = list.rbegin (); + while (it != list.rend ()) { + delete it->second; + it++; + } +} + +void +HostAttributeList::removeAttrID (AttrID aid) +{ + std::map::iterator it = list.find (aid); + if (it != list.end ()) { + delete it->second; + list.erase (it); + } +} + +tresult +HostAttributeList::setInt (AttrID aid, int64 value) +{ + removeAttrID (aid); + list[aid] = new HostAttribute (value); + return kResultTrue; +} + +tresult +HostAttributeList::getInt (AttrID aid, int64& value) +{ + std::map::iterator it = list.find (aid); + if (it != list.end () && it->second) { + value = it->second->intValue (); + return kResultTrue; + } + return kResultFalse; +} + +tresult +HostAttributeList::setFloat (AttrID aid, double value) +{ + removeAttrID (aid); + list[aid] = new HostAttribute (value); + return kResultTrue; +} + +tresult +HostAttributeList::getFloat (AttrID aid, double& value) +{ + std::map::iterator it = list.find (aid); + if (it != list.end () && it->second) { + value = it->second->floatValue (); + return kResultTrue; + } + return kResultFalse; +} + +tresult +HostAttributeList::setString (AttrID aid, const Vst::TChar* string) +{ + removeAttrID (aid); + list[aid] = new HostAttribute (string, wcslen ((const wchar_t*)string)); + return kResultTrue; +} + +tresult +HostAttributeList::getString (AttrID aid, Vst::TChar* string, uint32 size) +{ + std::map::iterator it = list.find (aid); + if (it != list.end () && it->second) { + uint32 stringSize = 0; + const Vst::TChar* _string = it->second->stringValue (stringSize); + memcpy (string, _string, std::min (stringSize, size) * sizeof (Vst::TChar)); + return kResultTrue; + } + return kResultFalse; +} + +tresult +HostAttributeList::setBinary (AttrID aid, const void* data, uint32 size) +{ + removeAttrID (aid); + list[aid] = new HostAttribute (data, size); + return kResultTrue; +} + +tresult +HostAttributeList::getBinary (AttrID aid, const void*& data, uint32& size) +{ + std::map::iterator it = list.find (aid); + if (it != list.end () && it->second) { + data = it->second->binaryValue (size); + return kResultTrue; + } + size = 0; + return kResultFalse; +} + +/* ****************************************************************************/ + +HostMessage::HostMessage () + : _messageId (0) +{ +} + +HostMessage::~HostMessage () +{ + setMessageID (0); + if (_attribute_list) { + //_attribute_list->release (); + } +} + +const char* +HostMessage::getMessageID () +{ + return _messageId; +} + +void +HostMessage::setMessageID (const char* mid) +{ + if (_messageId) { + delete[] _messageId; + } + if (mid) { + size_t len = strlen (mid) + 1; + _messageId = new char[len]; + strcpy (_messageId, mid); + } else { + _messageId = 0; + } +} + +Vst::IAttributeList* +HostMessage::getAttributes () +{ + if (!_attribute_list) { + _attribute_list.reset (new HostAttributeList); + } + return _attribute_list.get (); +} + +/* ****************************************************************************/ + +PlugInterfaceSupport::PlugInterfaceSupport () +{ + using namespace Vst; + + //---VST 3.0.0-------------------------------- + addPlugInterfaceSupported (IComponent::iid); + addPlugInterfaceSupported (IAudioProcessor::iid); + addPlugInterfaceSupported (IEditController::iid); + addPlugInterfaceSupported (IConnectionPoint::iid); + + addPlugInterfaceSupported (IUnitInfo::iid); + addPlugInterfaceSupported (IUnitData::iid); + addPlugInterfaceSupported (IProgramListData::iid); + + //---VST 3.0.1-------------------------------- + addPlugInterfaceSupported (IMidiMapping::iid); + +#if 0 + //---VST 3.1---------------------------------- + addPlugInterfaceSupported (IEditController2::iid); + + //---VST 3.0.2-------------------------------- + addPlugInterfaceSupported (IParameterFinder::iid); + + //---VST 3.1---------------------------------- + addPlugInterfaceSupported (IAudioPresentationLatency::iid); + + //---VST 3.5---------------------------------- + addPlugInterfaceSupported (IKeyswitchController::iid); + addPlugInterfaceSupported (IContextMenuTarget::iid); + addPlugInterfaceSupported (IEditControllerHostEditing::iid); + addPlugInterfaceSupported (IXmlRepresentationController::iid); + addPlugInterfaceSupported (INoteExpressionController::iid); + + //---VST 3.6.5-------------------------------- + addPlugInterfaceSupported (ChannelContext::IInfoListener::iid); + addPlugInterfaceSupported (IPrefetchableSupport::iid); + addPlugInterfaceSupported (IAutomationState::iid); + + //---VST 3.6.11-------------------------------- + addPlugInterfaceSupported (INoteExpressionPhysicalUIMapping::iid); + + //---VST 3.6.12-------------------------------- + addPlugInterfaceSupported (IMidiLearn::iid); +#endif +} + +tresult +PlugInterfaceSupport::isPlugInterfaceSupported (const TUID _iid) +{ + const FUID uid = FUID::fromTUID (_iid); + if (std::find (_interfaces.begin (), _interfaces.end (), uid) != _interfaces.end ()) { + return kResultTrue; + } + return kResultFalse; +} + +void +PlugInterfaceSupport::addPlugInterfaceSupported (const TUID id) +{ + _interfaces.push_back (FUID::fromTUID (id)); +} + +/* ****************************************************************************/ + +HostApplication::HostApplication () +{ +#if (__cplusplus >= 201103L) + _plug_interface_support = boost::make_unique (); +#else + _plug_interface_support.reset (new PlugInterfaceSupport); +#endif +} + +tresult +HostApplication::queryInterface (const char* _iid, void** obj) +{ + QUERY_INTERFACE (_iid, obj, FUnknown::iid, IHostApplication) + QUERY_INTERFACE (_iid, obj, IHostApplication::iid, IHostApplication) + + if (_plug_interface_support && _plug_interface_support->queryInterface (Vst::IHostApplication::iid, obj) == kResultTrue) { + return kResultOk; + } + + *obj = nullptr; + return kResultFalse; +} + +tresult +HostApplication::getName (Vst::String128 name) +{ + utf8_to_tchar (name, PROGRAM_NAME, 128); + return kResultTrue; +} + +tresult +HostApplication::createInstance (TUID cid, TUID _iid, void** obj) +{ + FUID classID (FUID::fromTUID (cid)); + FUID interfaceID (FUID::fromTUID (_iid)); + if (classID == Vst::IMessage::iid && interfaceID == Vst::IMessage::iid) { + *obj = (Vst::IMessage*)new HostMessage; + return kResultTrue; + } else if (classID == Vst::IAttributeList::iid && interfaceID == Vst::IAttributeList::iid) { + *obj = (Vst::IAttributeList*)new HostAttributeList; + return kResultTrue; + } + *obj = nullptr; + return kResultFalse; +} + +/* ****************************************************************************/ + +tresult +Vst3ParamValueQueue::getPoint (int32 index, int32& sampleOffset, Vst::ParamValue& value) +{ + if (index >=0 && index < (int32)_values.size ()) { + const Value& v = _values[index]; + sampleOffset = v.sampleOffset; + value = v.value; + return kResultTrue; + } + return kResultFalse; + +} + +tresult +Vst3ParamValueQueue::addPoint (int32 sampleOffset, Vst::ParamValue value, int32& index) +{ + int32 dest_index = (int32)_values.size (); + for (uint32 i = 0; i < _values.size (); ++i) { + if (_values[i].sampleOffset == sampleOffset) { + _values[i].value = value; + index = i; + return kResultTrue; + } else if (_values[i].sampleOffset > sampleOffset) { + dest_index = i; + break; + } + } + + Value v (value, sampleOffset); + if (dest_index == (int32)_values.size ()) { + _values.push_back (v); + } else { + _values.insert (_values.begin () + dest_index, v); + } + + index = dest_index; + return kResultTrue; +} + +/* ****************************************************************************/ + +Vst::IParamValueQueue* +Vst3ParameterChanges::getParameterData (int32 index) +{ + if (index < _used_queue_count) { + return &_queue[index]; + } + return 0; +} + +Vst::IParamValueQueue* +Vst3ParameterChanges::addParameterData (Vst::ParamID const& pid, int32& index) +{ + for (int32 i = 0; i < _used_queue_count; ++i) { + if (_queue[i].getParameterId () == pid) { + index = i; + return &_queue[i]; + } + } + + if (_used_queue_count < (int32)_queue.size ()) { + index = _used_queue_count++; + _queue[index].setParameterId (pid); + return &_queue[index]; + } + index = 0; + return 0; +} + +/* ****************************************************************************/ + +RAMStream::RAMStream () + : _data (0) + , _size (0) + , _alloc (0) + , _pos (0) + , _readonly (false) +{ +} + +RAMStream::RAMStream (uint8_t* data, size_t size) + : _data (0) + , _size (size) + , _alloc (0) + , _pos (0) + , _readonly (true) +{ + if (reallocate_buffer (_size, true)) { + memcpy (_data, data, _size); + } else { + _size = 0; + } +} + +RAMStream::RAMStream (std::string const& fn) + : _data (0) + , _size (0) + , _alloc (0) + , _pos (0) + , _readonly (true) +{ + gchar* buf = NULL; + gsize length = 0; + + if (!g_file_get_contents (fn.c_str (), &buf, &length, NULL)) { + return; + } + if (reallocate_buffer (length, true)) { + _size = length; + memcpy (_data, buf, _size); + } + g_free (buf); +} + +RAMStream::~RAMStream () +{ + free (_data); +} + +bool +RAMStream::reallocate_buffer (int64 size, bool exact) +{ + if (size <= 0) { + free (_data); + _data = 0; + _alloc = 0; + return true; + } + + if (size == _alloc) { + assert (_data); + return true; + } + + if (!exact) { + if (size <= _alloc) { + /* don't shrink */ + assert (_data); + return true; + } + if (size > _alloc) { + size = (((size - 1) / 8192) + 1) * 8192; + } + } + + _data = (uint8_t*) realloc (_data, size); + + if (_data) { + _alloc = size; + return true; + } else { + _alloc = 0; + return false; + } +} + +tresult +RAMStream::read (void* buffer, int32 n_bytes, int32* n_read) +{ + assert (_pos >= 0 && _pos <= _size); + int64 available = _size - _pos; + + if (n_bytes < 0 || available < 0) { + n_bytes = 0; + } else if (n_bytes > available) { + n_bytes = available; + } + + if (n_bytes > 0) { + memcpy(buffer, &_data[_pos], n_bytes); + _pos += n_bytes; + } + if (n_read) { + *n_read = n_bytes; + } + return kResultTrue; +} + +tresult +RAMStream::write (void* buffer, int32 n_bytes, int32* n_written) +{ + if (_readonly) { + return kResultFalse; + } + if (n_bytes < 0) { + return kInvalidArgument; + } + + if (!reallocate_buffer (_pos + n_bytes, false)) { + return kOutOfMemory; + } + + if (buffer && _data && _pos >= 0 && n_bytes > 0) { + memcpy (&_data[_pos], buffer, n_bytes); + _pos += n_bytes; + _size = _pos; + } else { + n_bytes = 0; + } + + if (n_written) { + *n_written = n_bytes; + } + return kResultTrue; +} + +tresult +RAMStream::seek (int64 pos, int32 mode, int64* result) +{ + switch (mode) { + case kIBSeekSet: + _pos = pos; + break; + case kIBSeekCur: + _pos += pos; + break; + case kIBSeekEnd: + _pos = _size + pos; + break; + default: + return kInvalidArgument; + } + if (_pos < 0) { + _pos = 0; + } + if (result) { + *result = _pos; + } + return kResultTrue; +} + +tresult +RAMStream::tell (int64* pos) +{ + if (!pos) { + return kInvalidArgument; + } + *pos = _pos; + return kResultTrue; +} + +bool +RAMStream::write_int32 (int32 i) { + /* pluginterfaces/base/ftypes.h */ +#if BYTEORDER == kBigEndian + SWAP_32 (i) +#endif + return write_pod (i); +} + +bool +RAMStream::write_int64 (int64 i) +{ + /* pluginterfaces/base/ftypes.h */ +#if BYTEORDER == kBigEndian + SWAP_64 (i) +#endif + return write_pod (i); +} + +bool +RAMStream::write_ChunkID (const Vst::ChunkID& id) +{ + return write_pod (id); +} + +#if COM_COMPATIBLE +/* pluginterfaces/base/funknown.cpp */ +struct GUIDStruct { + uint32_t data1; + uint16_t data2; + uint16_t data3; + uint8_t data4[8]; +}; +#endif + +bool +RAMStream::write_TUID (const TUID& tuid) +{ + int i = 0; + int32 n_bytes = 0; + char buf[Vst::kClassIDSize + 1]; + +#if COM_COMPATIBLE + GUIDStruct guid; + memcpy (&guid, tuid, sizeof(GUIDStruct)); + sprintf(buf, "%08X%04X%04X", guid.data1, guid.data2, guid.data3); + i += 8; +#endif + + for (; i < (int)sizeof(TUID); ++i){ + sprintf(buf + 2 * i, "%02X", (uint8_t)tuid[i]); + } + write (buf, Vst::kClassIDSize, &n_bytes); + return n_bytes == Vst::kClassIDSize; +} + +bool +RAMStream::read_int32 (int32& i) { + if (!read_pod (i)) { + return false; + } +#if BYTEORDER == kBigEndian + SWAP_32 (i) +#endif + return true; +} + +bool +RAMStream::read_int64 (int64& i) { + if (!read_pod (i)) { + return false; + } +#if BYTEORDER == kBigEndian + SWAP_64 (i) +#endif + return true; +} + +bool +RAMStream::read_ChunkID (Vst::ChunkID& id) +{ + return read_pod (id); +} + +bool +RAMStream::read_TUID (TUID& tuid) +{ + int i = 0; + int32 n_bytes = 0; + char buf[Vst::kClassIDSize+1]; + + read((void *)buf, Vst::kClassIDSize, &n_bytes); + if (n_bytes != Vst::kClassIDSize) { + return false; + } + + buf[Vst::kClassIDSize] = '\0'; + +#if COM_COMPATIBLE + GUIDStruct guid; + sscanf (buf, "%08x", &guid.data1); + sscanf (buf+8, "%04hx", &guid.data2); + sscanf (buf+12, "%04hx", &guid.data3); + memcpy (tuid, &guid, sizeof(TUID) >> 1); + i += 16; +#endif + + for (; i < Vst::kClassIDSize; i += 2){ + uint32_t temp; + sscanf (buf + i, "%02X", &temp); + tuid[i >> 1] = temp; + } + + return true; +} + +#ifndef NDEBUG + +#include +#include +#include + +void +RAMStream::hexdump (int64 max_len) const +{ + std::ostringstream out; + + size_t row_size = 16; + size_t length = max_len > 0 ? std::min (max_len, _size) : _size; + + out << std::setfill('0'); + for (size_t i = 0; i < length; i += row_size) { + out << "0x" << std::setw(6) << std::hex << i << ": "; + for (size_t j = 0; j < row_size; ++j) { + if (i + j < length) { + out << std::hex << std::setw(2) << static_cast(_data[i + j]) << " "; + } else { + out << " "; + } + } + out << " "; + if (true) { + for (size_t j = 0; j < row_size; ++j) { + if (i + j < length) { + if (std::isprint(_data[i + j])) { + out << static_cast(_data[i + j]); + } else { + out << "."; + } + } + } + } + out << std::endl; + } + std::cout << out.str (); +} +#endif diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 3e403a8578..65764b9d69 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -460,7 +460,7 @@ def build(bld): obj.defines += [ 'MACVST_SUPPORT' ] if bld.is_defined('VST3_SUPPORT'): - obj.source += [ 'vst3_plugin.cc', 'vst3_module.cc' ] + obj.source += [ 'vst3_plugin.cc', 'vst3_module.cc', 'vst3_host.cc' ] obj.defines += [ 'VST3_SUPPORT' ] if bld.is_defined('HAVE_COREAUDIO'):