Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,37 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
// Version : 1.0
//
// Category : Common Base Classes
// Filename : public.sdk/source/common/commoniids.cpp
// Created by : Steinberg, 01/2019
// Description : Define some IIDs
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "pluginterfaces/gui/iplugview.h"
#include "pluginterfaces/gui/iplugviewcontentscalesupport.h"
namespace Steinberg
{
//----VST 3.0--------------------------------
DEF_CLASS_IID (IPlugView)
DEF_CLASS_IID (IPlugFrame)
//----VST 3.6.0--------------------------------
DEF_CLASS_IID (IPlugViewContentScaleSupport)
#if SMTG_OS_LINUX
DEF_CLASS_IID (Linux::IEventHandler)
DEF_CLASS_IID (Linux::ITimerHandler)
DEF_CLASS_IID (Linux::IRunLoop)
#endif
//------------------------------------------------------------------------
} // namespace Steinberg
@@ -0,0 +1,101 @@
//-----------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/common/commonstringconvert.cpp
// Created by : Steinberg, 07/2024
// Description : c++11 unicode string convert functions
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "commonstringconvert.h"
#include <codecvt>
#include <istream>
#include <locale>
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
//------------------------------------------------------------------------
namespace Steinberg {
namespace StringConvert {
//------------------------------------------------------------------------
namespace {
#if defined(_MSC_VER) && _MSC_VER >= 1900
#define USE_WCHAR_AS_UTF16TYPE
using UTF16Type = wchar_t;
#else
using UTF16Type = char16_t;
#endif
using Converter = std::wstring_convert<std::codecvt_utf8_utf16<UTF16Type>, UTF16Type>;
//------------------------------------------------------------------------
Converter& converter ()
{
static Converter conv;
return conv;
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
std::u16string convert (const std::string& utf8Str)
{
#if defined(USE_WCHAR_AS_UTF16TYPE)
auto wstr = converter ().from_bytes (utf8Str);
return {wstr.data (), wstr.data () + wstr.size ()};
#else
return converter ().from_bytes (utf8Str);
#endif
}
//------------------------------------------------------------------------
std::string convert (const std::u16string& str)
{
return converter ().to_bytes (reinterpret_cast<const UTF16Type*> (str.data ()),
reinterpret_cast<const UTF16Type*> (str.data () + str.size ()));
}
//------------------------------------------------------------------------
std::string convert (const char* str, uint32_t max)
{
std::string result;
if (str)
{
result.reserve (max);
for (uint32_t i = 0; i < max; ++i, ++str)
{
if (*str == 0)
break;
result += *str;
}
}
return result;
}
//------------------------------------------------------------------------
} // StringConvert
} // Steinberg
#ifdef __clang__
#pragma clang diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
@@ -0,0 +1,74 @@
//-----------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : VST SDK
//
// Category : stringconvert
// Filename : public.sdk/source/common/commonstringconvert.h
// Created by : Steinberg, 07/2024
// Description : read file routine
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include <cstdint>
#include <string>
namespace Steinberg {
namespace StringConvert {
//------------------------------------------------------------------------
/**
* convert an UTF-8 string to an UTF-16 string
*
* @param utf8Str UTF-8 string
*
* @return UTF-16 string
*/
std::u16string convert (const std::string& utf8Str);
//------------------------------------------------------------------------
/**
* convert an UTF-16 string to an UTF-8 string
*
* @param str UTF-16 string
*
* @return UTF-8 string
*/
std::string convert (const std::u16string& str);
//------------------------------------------------------------------------
/**
* convert a ASCII string buffer to an UTF-8 string
*
* @param str ASCII string buffer
* @param max maximum characters in str
*
* @return UTF-8 string
*/
std::string convert (const char* str, uint32_t max);
//------------------------------------------------------------------------
/**
* convert a number to an UTF-16 string
*
* @param value number
*
* @return UTF-16 string
*/
template <typename NumberT>
std::u16string toString (NumberT value)
{
auto u8str = std::to_string (value);
return StringConvert::convert (u8str);
}
//------------------------------------------------------------------------
} // namespace StringConvert
} // namespace Steinberg
@@ -0,0 +1,299 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
//
// Category : Common Classes
// Filename : public.sdk/source/common/memorystream.cpp
// Created by : Steinberg, 03/2008
// Description : IBStream Implementation for memory blocks
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "memorystream.h"
#include "pluginterfaces/base/futils.h"
#include <cstdlib>
namespace Steinberg {
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (MemoryStream, IBStream, IBStream::iid)
static const TSize kMemGrowAmount = 4096;
//-----------------------------------------------------------------------------
MemoryStream::MemoryStream (void* data, TSize length)
: memory ((char*)data)
, memorySize (length)
, size (length)
, cursor (0)
, ownMemory (false)
, allocationError (false)
{
FUNKNOWN_CTOR
}
//-----------------------------------------------------------------------------
MemoryStream::MemoryStream ()
: memory (nullptr)
, memorySize (0)
, size (0)
, cursor (0)
, ownMemory (true)
, allocationError (false)
{
FUNKNOWN_CTOR
}
//-----------------------------------------------------------------------------
MemoryStream::~MemoryStream ()
{
if (ownMemory && memory)
::free (memory);
FUNKNOWN_DTOR
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API MemoryStream::read (void* data, int32 numBytes, int32* numBytesRead)
{
if (memory == nullptr)
{
if (allocationError)
return kOutOfMemory;
numBytes = 0;
}
else
{
// Does read exceed size ?
if (cursor + numBytes > size)
{
int32 maxBytes = int32 (size - cursor);
// Has length become zero or negative ?
if (maxBytes <= 0)
{
cursor = size;
numBytes = 0;
}
else
numBytes = maxBytes;
}
if (numBytes)
{
memcpy (data, &memory[cursor], static_cast<size_t> (numBytes));
cursor += numBytes;
}
}
if (numBytesRead)
*numBytesRead = numBytes;
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API MemoryStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
{
if (allocationError)
return kOutOfMemory;
if (buffer == nullptr)
return kInvalidArgument;
// Does write exceed size ?
TSize requiredSize = cursor + numBytes;
if (requiredSize > size)
{
if (requiredSize > memorySize)
setSize (requiredSize);
else
size = requiredSize;
}
// Copy data
if (memory && cursor >= 0 && numBytes > 0)
{
memcpy (&memory[cursor], buffer, static_cast<size_t> (numBytes));
// Update cursor
cursor += numBytes;
}
else
numBytes = 0;
if (numBytesWritten)
*numBytesWritten = numBytes;
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API MemoryStream::seek (int64 pos, int32 mode, int64* result)
{
switch (mode)
{
case kIBSeekSet:
cursor = pos;
break;
case kIBSeekCur:
cursor = cursor + pos;
break;
case kIBSeekEnd:
cursor = size + pos;
break;
}
if (ownMemory == false)
if (cursor > memorySize)
cursor = memorySize;
if (result)
*result = cursor;
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API MemoryStream::tell (int64* pos)
{
if (!pos)
return kInvalidArgument;
*pos = cursor;
return kResultTrue;
}
//------------------------------------------------------------------------
TSize MemoryStream::getSize () const
{
return size;
}
//------------------------------------------------------------------------
void MemoryStream::setSize (TSize s)
{
if (s <= 0)
{
if (ownMemory && memory)
free (memory);
memory = nullptr;
memorySize = 0;
size = 0;
cursor = 0;
return;
}
TSize newMemorySize = (((Max (memorySize, s) - 1) / kMemGrowAmount) + 1) * kMemGrowAmount;
if (newMemorySize == memorySize)
{
size = s;
return;
}
if (memory && ownMemory == false)
{
allocationError = true;
return;
}
ownMemory = true;
char* newMemory = nullptr;
if (memory)
{
newMemory = (char*)realloc (memory, (size_t)newMemorySize);
if (newMemory == nullptr && newMemorySize > 0)
{
newMemory = (char*)malloc ((size_t)newMemorySize);
if (newMemory)
{
memcpy (newMemory, memory, (size_t)Min (newMemorySize, memorySize));
free (memory);
}
}
}
else
newMemory = (char*)malloc ((size_t)newMemorySize);
if (newMemory == nullptr)
{
if (newMemorySize > 0)
allocationError = true;
memory = nullptr;
memorySize = 0;
size = 0;
cursor = 0;
}
else
{
memory = newMemory;
memorySize = newMemorySize;
size = s;
}
}
//------------------------------------------------------------------------
char* MemoryStream::getData () const
{
return memory;
}
//------------------------------------------------------------------------
char* MemoryStream::detachData ()
{
if (ownMemory)
{
char* result = memory;
memory = nullptr;
memorySize = 0;
size = 0;
cursor = 0;
return result;
}
return nullptr;
}
//------------------------------------------------------------------------
bool MemoryStream::truncate ()
{
if (ownMemory == false)
return false;
if (memorySize == size)
return true;
memorySize = size;
if (memorySize == 0)
{
if (memory)
{
free (memory);
memory = nullptr;
}
}
else
{
if (memory)
{
char* newMemory = (char*)realloc (memory, (size_t)memorySize);
if (newMemory)
memory = newMemory;
}
}
return true;
}
//------------------------------------------------------------------------
bool MemoryStream::truncateToCursor ()
{
size = cursor;
return truncate ();
}
} // namespace
@@ -0,0 +1,59 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
//
// Category : Common Classes
// Filename : public.sdk/source/common/memorystream.h
// Created by : Steinberg, 03/2008
// Description : IBStream Implementation for memory blocks
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/base/ibstream.h"
namespace Steinberg {
//------------------------------------------------------------------------
/** Memory based Stream for IBStream implementation (using malloc).
\ingroup sdkBase
*/
class MemoryStream : public IBStream
{
public:
//------------------------------------------------------------------------
MemoryStream ();
MemoryStream (void* memory, TSize memorySize); ///< reuse a given memory without getting ownership
virtual ~MemoryStream ();
//---IBStream---------------------------------------
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;
TSize getSize () const; ///< returns the current memory size
void setSize (TSize size); ///< set the memory size, a realloc will occur if memory already used
char* getData () const; ///< returns the memory pointer
char* detachData (); ///< returns the memory pointer and give up ownership
bool truncate (); ///< realloc to the current use memory size if needed
bool truncateToCursor (); ///< truncate memory at current cursor position
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
char* memory; // memory block
TSize memorySize; // size of the memory block
TSize size; // size of the stream
int64 cursor; // stream pointer
bool ownMemory; // stream has allocated memory itself
bool allocationError; // stream invalid
};
} // namespace
@@ -0,0 +1,58 @@
//------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
//
// Category : Helpers
// Project : Steinberg Plug-In SDK
// Filename : public.sdk/source/common/openurl.cpp
// Created by : Steinberg 04.2020
// Description : Simple helper allowing to open a URL in the default associated application
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "openurl.h"
#include "pluginterfaces/base/ftypes.h"
#if SMTG_OS_WINDOWS
// keep this order
#include <windows.h>
#include <shellapi.h>
#else
#include <cstdlib>
#endif
//-----------------------------------------------------------------------------
namespace Steinberg {
//-----------------------------------------------------------------------------
bool openURLInDefaultApplication (const String& address)
{
bool res = false;
#if SMTG_OS_WINDOWS
auto r = ShellExecuteA (nullptr, "open", address.text8 (), nullptr, nullptr, SW_SHOWNORMAL);
res = (r != nullptr);
#elif SMTG_OS_OSX
String cmd;
cmd += "open \"";
cmd += address.text8 ();
cmd += "\"";
res = (system (cmd) == 0);
#elif SMTG_OS_LINUX
String cmd;
cmd += "xdg-open \"";
cmd += address.text8 ();
cmd += "\"";
res = (system (cmd) == 0);
#endif
return res;
}
//------------------------------------------------------------------------
} // namespace Steinberg
@@ -0,0 +1,39 @@
//------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
//
// Category : Helpers
// Project : Steinberg Plug-In SDK
// Filename : public.sdk/source/common/openurl.h
// Created by : Steinberg 04.2020
// Description : Simple helper allowing to open a URL in the default associated application
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "base/source/fstring.h"
namespace Steinberg {
/** Open the given URL into the default web browser.
\ingroup sdkBase
It returns true if a default application is found and opened else false.
Example:
\code{.cpp}
if (openURLInDefaultApplication ("https://www.steinberg.net/"))
{
// everything seems to be ok
}
\endcode
*/
bool openURLInDefaultApplication (const String& address);
//------------------------------------------------------------------------
} // namespace Steinberg
@@ -0,0 +1,80 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
//
// Category : Common Base Classes
// Filename : public.sdk/source/common/pluginview.cpp
// Created by : Steinberg, 01/2004
// Description : Plug-In View Implementation
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "pluginview.h"
namespace Steinberg {
//------------------------------------------------------------------------
// CPluginView implementation
//------------------------------------------------------------------------
CPluginView::CPluginView (const ViewRect* _rect)
: rect (0, 0, 0, 0)
{
if (_rect)
rect = *_rect;
}
//------------------------------------------------------------------------
CPluginView::~CPluginView ()
{
}
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginView::isPlatformTypeSupported (FIDString /*type*/)
{
return kNotImplemented;
}
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginView::attached (void* parent, FIDString /*type*/)
{
systemWindow = parent;
attachedToParent ();
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginView::removed ()
{
systemWindow = nullptr;
removedFromParent ();
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginView::onSize (ViewRect* newSize)
{
if (newSize)
rect = *newSize;
return kResultTrue;
}
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginView::getSize (ViewRect* size)
{
if (size)
{
*size = rect;
return kResultTrue;
}
return kInvalidArgument;
}
//------------------------------------------------------------------------
} // namespace Steinberg
@@ -0,0 +1,96 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
//
// Category : Common Base Classes
// Filename : public.sdk/source/common/pluginview.h
// Created by : Steinberg, 01/2004
// Description : Plug-In View Implementation
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/gui/iplugview.h"
#include "base/source/fobject.h"
namespace Steinberg {
//------------------------------------------------------------------------
/** Plug-In view default implementation.
\ingroup sdkBase
Can be used as base class for an IPlugView implementation.
*/
class CPluginView : public FObject, public IPlugView
{
public:
//------------------------------------------------------------------------
CPluginView (const ViewRect* rect = nullptr);
~CPluginView () SMTG_OVERRIDE;
/** Returns its current frame rectangle. */
const ViewRect& getRect () const { return rect; }
/** Sets a new frame rectangle. */
void setRect (const ViewRect& r) { rect = r; }
/** Checks if this view is attached to its parent view. */
bool isAttached () const { return systemWindow != nullptr; }
/** Calls when this view will be attached to its parent view. */
virtual void attachedToParent () {}
/** Calls when this view will be removed from its parent view. */
virtual void removedFromParent () {}
//---from IPlugView-------
tresult PLUGIN_API isPlatformTypeSupported (FIDString type) SMTG_OVERRIDE;
tresult PLUGIN_API attached (void* parent, FIDString type) SMTG_OVERRIDE;
tresult PLUGIN_API removed () SMTG_OVERRIDE;
tresult PLUGIN_API onWheel (float /*distance*/) SMTG_OVERRIDE { return kResultFalse; }
tresult PLUGIN_API onKeyDown (char16 /*key*/, int16 /*keyMsg*/,
int16 /*modifiers*/) SMTG_OVERRIDE
{
return kResultFalse;
}
tresult PLUGIN_API onKeyUp (char16 /*key*/, int16 /*keyMsg*/, int16 /*modifiers*/) SMTG_OVERRIDE
{
return kResultFalse;
}
tresult PLUGIN_API getSize (ViewRect* size) SMTG_OVERRIDE;
tresult PLUGIN_API onSize (ViewRect* newSize) SMTG_OVERRIDE;
tresult PLUGIN_API onFocus (TBool /*state*/) SMTG_OVERRIDE { return kResultFalse; }
tresult PLUGIN_API setFrame (IPlugFrame* frame) SMTG_OVERRIDE
{
plugFrame = frame;
return kResultTrue;
}
tresult PLUGIN_API canResize () SMTG_OVERRIDE { return kResultFalse; }
tresult PLUGIN_API checkSizeConstraint (ViewRect* /*rect*/) SMTG_OVERRIDE
{
return kResultFalse;
}
//---Interface------
OBJ_METHODS (CPluginView, FObject)
DEFINE_INTERFACES
DEF_INTERFACE (IPlugView)
END_DEFINE_INTERFACES (FObject)
REFCOUNT_METHODS (FObject)
//------------------------------------------------------------------------
protected:
ViewRect rect;
void* systemWindow {nullptr};
IPtr<IPlugFrame> plugFrame;
};
//------------------------------------------------------------------------
} // namespace Steinberg
@@ -0,0 +1,64 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category : readfile
// Filename : public.sdk/source/common/readfile.cpp
// Created by : Steinberg, 3/2023
// Description : read file routine
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "readfile.h"
#include "pluginterfaces/base/fplatform.h"
#if SMTG_OS_WINDOWS
#include "commonstringconvert.h"
#endif
#include <fstream>
#if !SMTG_CPP17
#include <sstream>
#endif
namespace Steinberg {
//------------------------------------------------------------------------
std::string readFile (const std::string& path)
{
#if SMTG_OS_WINDOWS
auto u16Path = StringConvert::convert (path);
std::ifstream file (reinterpret_cast<const wchar_t*> (u16Path.data ()),
std::ios_base::in | std::ios_base::binary);
#else
std::ifstream file (path, std::ios_base::in | std::ios_base::binary);
#endif
if (!file.is_open ())
return {};
#if SMTG_CPP17
auto size = file.seekg (0, std::ios_base::end).tellg ();
file.seekg (0, std::ios_base::beg);
std::string data;
data.resize (size);
file.read (data.data (), data.size ());
if (file.bad ())
return {};
return data;
#else
std::stringstream buffer;
buffer << file.rdbuf ();
return buffer.str ();
#endif // SMTG_CPP17
}
//------------------------------------------------------------------------
} // namespace Steinberg
@@ -0,0 +1,34 @@
//-----------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : VST SDK
//
// Category : readfile
// Filename : public.sdk/source/common/readfile.h
// Created by : Steinberg, 3/2023
// Description : read file routine
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include <string>
namespace Steinberg {
//------------------------------------------------------------------------
/** Reads entire file content
\ingroup sdkBase
Returns entire file content at the given path
\endcode
*/
std::string readFile (const std::string& path);
//------------------------------------------------------------------------
} // namespace Steinberg
@@ -0,0 +1,45 @@
//------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
//
// Project : Steinberg Plug-In SDK
// Filename : public.sdk/source/common/systemclipboard.h
// Created by : Steinberg 04.2020
// Description : Simple helper allowing to copy/retrieve text to/from the system clipboard
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include <string>
//------------------------------------------------------------------------
namespace Steinberg {
namespace SystemClipboard {
//-----------------------------------------------------------------------------
/** Copies the given text into the system clipboard
\ingroup sdkBase
\param text UTF-8 encoded text
\return true on success
*/
bool copyTextToClipboard (const std::string& text);
//-----------------------------------------------------------------------------
/** Retrieves the current text from the system clipboard
\ingroup sdkBase
\param text UTF-8 encoded text
\return true on success
*/
bool getTextFromClipboard (std::string& text);
//-----------------------------------------------------------------------------
} // namespace SystemClipboard
} // namespace Steinberg
@@ -0,0 +1,44 @@
//------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
//
// Project : Steinberg Plug-In SDK
// Filename : public.sdk/source/common/systemclipboard_linux.cpp
// Created by : Steinberg 04.2023
// Description : Simple helper allowing to copy/retrieve text to/from the system clipboard
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "systemclipboard.h"
#include "pluginterfaces/base/fplatform.h"
#if SMTG_OS_LINUX
//------------------------------------------------------------------------
namespace Steinberg {
namespace SystemClipboard {
//-----------------------------------------------------------------------------
bool copyTextToClipboard (const std::string& text)
{
// TODO
return false;
}
//-----------------------------------------------------------------------------
bool getTextFromClipboard (std::string& text)
{
// TODO
return false;
}
//------------------------------------------------------------------------
} // namespace SystemClipboard
} // namespace Steinberg
#endif // SMTG_OS_LINUX
@@ -0,0 +1,71 @@
//------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
//
// Project : Steinberg Plug-In SDK
// Filename : public.sdk/source/common/systemclipboard_mac.mm
// Created by : Steinberg 04.2020
// Description : Simple helper allowing to copy/retrieve text to/from the system clipboard
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "systemclipboard.h"
#include "pluginterfaces/base/fplatform.h"
#if SMTG_OS_OSX
#import <Cocoa/Cocoa.h>
//------------------------------------------------------------------------
namespace Steinberg {
namespace SystemClipboard {
//-----------------------------------------------------------------------------
bool copyTextToClipboard (const std::string& text)
{
auto pb = [NSPasteboard generalPasteboard];
[pb clearContents];
auto nsString = [NSString stringWithUTF8String:text.data ()];
return [pb setString:nsString forType:NSPasteboardTypeString];
}
//-----------------------------------------------------------------------------
bool getTextFromClipboard (std::string& text)
{
auto pb = [NSPasteboard generalPasteboard];
if ([pb canReadItemWithDataConformingToTypes:@[NSPasteboardTypeString]])
{
if (auto items = [pb readObjectsForClasses:@[[NSString class]] options:nil])
{
if (items.count > 0)
{
text = [items[0] UTF8String];
return true;
}
}
}
return false;
}
//------------------------------------------------------------------------
} // namespace SystemClipboard
} // namespace Steinberg
#elif SMTG_OS_IOS
//------------------------------------------------------------------------
bool copyTextToClipboard (const std::string& text)
{
return false;
}
//------------------------------------------------------------------------
bool getTextFromClipboard (std::string& text)
{
return false;
}
#endif // SMTG_OS_MACOS
@@ -0,0 +1,148 @@
//------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
//
// Project : Steinberg Plug-In SDK
// Filename : public.sdk/source/common/systemclipboard_win32.cpp
// Created by : Steinberg 04.2020
// Description : Simple helper allowing to copy/retrieve text to/from the system clipboard
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "systemclipboard.h"
#include "pluginterfaces/base/fplatform.h"
#if SMTG_OS_WINDOWS
#include <vector>
#include <windows.h>
//------------------------------------------------------------------------
namespace Steinberg {
namespace SystemClipboard {
namespace {
//------------------------------------------------------------------------
struct Clipboard
{
Clipboard () { open = OpenClipboard (nullptr); }
~Clipboard ()
{
if (open)
CloseClipboard ();
}
bool open {false};
};
//------------------------------------------------------------------------
std::vector<WCHAR> convertToWide (const std::string& text)
{
std::vector<WCHAR> wideStr;
auto numChars =
MultiByteToWideChar (CP_UTF8, 0, text.data (), static_cast<int> (text.size ()), nullptr, 0);
if (numChars)
{
wideStr.resize (static_cast<size_t> (numChars) + 1);
numChars = MultiByteToWideChar (CP_UTF8, 0, text.data (), static_cast<int> (text.size ()),
wideStr.data (), static_cast<int> (wideStr.size ()));
}
wideStr[numChars] = 0;
wideStr.resize (static_cast<size_t> (numChars) + 1);
return wideStr;
}
//------------------------------------------------------------------------
std::string convertToUTF8 (const WCHAR* data, const SIZE_T& dataSize)
{
std::string text;
auto numChars =
WideCharToMultiByte (CP_UTF8, 0, data, static_cast<int> (dataSize / sizeof (WCHAR)),
nullptr, 0, nullptr, nullptr);
text.resize (static_cast<size_t> (numChars) + 1);
numChars = WideCharToMultiByte (CP_UTF8, 0, data, static_cast<int> (dataSize / sizeof (WCHAR)),
const_cast<char*> (text.data ()),
static_cast<int> (text.size ()), nullptr, nullptr);
text.resize (numChars);
return text;
}
//------------------------------------------------------------------------
} // anonymous
//-----------------------------------------------------------------------------
bool copyTextToClipboard (const std::string& text)
{
Clipboard cb;
if (text.empty () || !cb.open)
return false;
if (!EmptyClipboard ())
return false;
bool result = false;
auto wideStr = convertToWide (text);
auto byteSize = wideStr.size () * sizeof (WCHAR);
if (auto memory = GlobalAlloc (GMEM_MOVEABLE | GMEM_ZEROINIT, byteSize))
{
if (auto* data = static_cast<WCHAR*> (GlobalLock (memory)))
{
#if defined(__MINGW32__)
memcpy (data, wideStr.data (), byteSize);
#else
memcpy_s (data, byteSize, wideStr.data (), byteSize);
#endif
GlobalUnlock (memory);
auto handle = SetClipboardData (CF_UNICODETEXT, memory);
result = handle != nullptr;
}
}
return result;
}
//-----------------------------------------------------------------------------
bool getTextFromClipboard (std::string& text)
{
Clipboard cb;
if (!cb.open)
return false;
if (!IsClipboardFormatAvailable (CF_UNICODETEXT))
return false;
bool result = false;
// Get handle of clipboard object for unicode text
if (auto hData = GetClipboardData (CF_UNICODETEXT))
{
// Lock the handle to get the actual text pointer
if (auto* data = (const WCHAR*)GlobalLock (hData))
{
auto dataSize = GlobalSize (hData);
text = convertToUTF8 (data, dataSize);
// Release the lock
GlobalUnlock (hData);
result = true;
}
}
return result;
}
//------------------------------------------------------------------------
} // namespace SystemClipboard
} // namespace Steinberg
#endif // SMTG_OS_WINDOWS
@@ -0,0 +1,39 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/source/common/threadchecker.h
// Created by : Steinberg, 01/2019
// Description : thread checker
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/base/ftypes.h"
#include <memory>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
class ThreadChecker
{
public:
static std::unique_ptr<ThreadChecker> create ();
virtual bool test (const char* failmessage = nullptr, bool exit = false) = 0;
virtual ~ThreadChecker () noexcept = default;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,56 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/source/common/threadchecker_linux.cpp
// Created by : Steinberg, 01/2019
// Description : linux thread checker
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "threadchecker.h"
#if SMTG_OS_LINUX
#include <cstdio>
#include <pthread.h>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
class LinuxThreadChecker : public ThreadChecker
{
public:
bool test (const char* failmessage = nullptr, bool exit = false) override
{
if (threadID == pthread_self ())
return true;
if (failmessage)
fprintf (stderr, "%s", failmessage);
if (exit)
std::terminate ();
return false;
}
pthread_t threadID {pthread_self ()};
};
//------------------------------------------------------------------------
std::unique_ptr<ThreadChecker> ThreadChecker::create ()
{
return std::unique_ptr<ThreadChecker> (new LinuxThreadChecker);
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
#endif // SMTG_OS_LINUX
@@ -0,0 +1,56 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/source/common/threadchecker_mac.mm
// Created by : Steinberg, 01/2019
// Description : macOS thread checker
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "threadchecker.h"
#if SMTG_OS_MACOS
#include <pthread.h>
#include <Foundation/Foundation.h>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
class MacThreadChecker : public ThreadChecker
{
public:
bool test (const char* failmessage = nullptr, bool exit = false) override
{
if (threadID == pthread_self ())
return true;
if (failmessage)
NSLog (@"%s", failmessage);
if (exit)
std::terminate ();
return false;
}
pthread_t threadID {pthread_self ()};
};
//------------------------------------------------------------------------
std::unique_ptr<ThreadChecker> ThreadChecker::create ()
{
return std::unique_ptr<ThreadChecker> (new MacThreadChecker);
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
#endif
@@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/source/common/threadchecker_win32.cpp
// Created by : Steinberg, 01/2019
// Description : win32 thread checker
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "threadchecker.h"
#if SMTG_OS_WINDOWS
#include <windows.h>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
class Win32ThreadChecker : public ThreadChecker
{
public:
bool test (const char* failmessage = nullptr, bool exit = false) override
{
if (threadID == GetCurrentThreadId ())
return true;
if (failmessage)
OutputDebugStringA (failmessage);
if (exit)
std::terminate ();
return false;
}
DWORD threadID {GetCurrentThreadId ()};
};
//------------------------------------------------------------------------
std::unique_ptr<ThreadChecker> ThreadChecker::create ()
{
return std::unique_ptr<ThreadChecker> (new Win32ThreadChecker);
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
#endif // SMTG_OS_WINDOWS
@@ -0,0 +1,96 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
// Version : 1.0
//
// Category : Common Base Classes
// Filename : public.sdk/source/main/dllmain.cpp
// Created by : Steinberg, 01/2004
// Description : Windows DLL Entry
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "pluginterfaces/base/ftypes.h"
#include "pluginterfaces/base/fstrdefs.h"
#include <windows.h>
#if defined(_MSC_VER) && defined(DEVELOPMENT)
#include <crtdbg.h>
#endif
//------------------------------------------------------------------------
HINSTANCE ghInst = nullptr;
void* moduleHandle = nullptr;
#define VST_MAX_PATH 2048
Steinberg::tchar gPath[VST_MAX_PATH] = {0};
//------------------------------------------------------------------------
extern bool InitModule (); ///< must be provided by plug-in: called when the library is loaded
extern bool DeinitModule (); ///< must be provided by plug-in: called when the library is unloaded
//------------------------------------------------------------------------
#ifdef __cplusplus
extern "C" {
#endif
static int moduleCounter {0}; // counting for InitDll/ExitDll pairs
//------------------------------------------------------------------------
/** must be called from host right after loading dll,
must be provided by the plug-in!
Note: this could be called more than one time! */
SMTG_EXPORT_SYMBOL bool InitDll ()
{
if (++moduleCounter == 1)
return InitModule ();
return true;
}
//------------------------------------------------------------------------
/** must be called from host right before unloading dll
must be provided by the plug-in!
Note: this could be called more than one time! */
SMTG_EXPORT_SYMBOL bool ExitDll ()
{
if (--moduleCounter == 0)
return DeinitModule ();
if (moduleCounter < 0)
return false;
return true;
}
#ifdef __cplusplus
} // extern "C"
#endif
//------------------------------------------------------------------------
BOOL WINAPI DllMain (HINSTANCE hInst, DWORD dwReason, LPVOID /*lpvReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
#if defined(_MSC_VER) && defined(DEVELOPMENT)
_CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG);
_CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG);
_CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
int flag = _CrtSetDbgFlag (_CRTDBG_REPORT_FLAG);
_CrtSetDbgFlag (flag | _CRTDBG_LEAK_CHECK_DF);
#endif
moduleHandle = ghInst = hInst;
// gets the path of the component
if (GetModuleFileName (ghInst, Steinberg::wscast (gPath), MAX_PATH) > 0)
{
Steinberg::tchar* bkslash = Steinberg::wscast (wcsrchr (Steinberg::wscast (gPath), L'\\'));
if (bkslash)
gPath[bkslash - gPath + 1] = 0;
}
}
return TRUE;
}
@@ -0,0 +1,62 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
// Version : 1.0
//
// Category : Common Base Classes
// Filename : public.sdk/source/main/linuxmain.cpp
// Created by : Steinberg, 03/2017
// Description : Linux Component Entry
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "pluginterfaces/base/fplatform.h"
void* moduleHandle = nullptr;
//------------------------------------------------------------------------
bool InitModule (); ///< must be provided by plug-in: called when the library is loaded
bool DeinitModule (); ///< must be provided by plug-in: called when the library is unloaded
//------------------------------------------------------------------------
extern "C"
{
/** must be provided by the plug-in! */
SMTG_EXPORT_SYMBOL bool ModuleEntry (void*);
SMTG_EXPORT_SYMBOL bool ModuleExit (void);
}
static int moduleCounter {0}; // counting for ModuleEntry/ModuleExit pairs
//------------------------------------------------------------------------
/** must be called from host right after loading dll
Note: this could be called more than one time! */
bool ModuleEntry (void* sharedLibraryHandle)
{
if (++moduleCounter == 1)
{
moduleHandle = sharedLibraryHandle;
return InitModule ();
}
return true;
}
//------------------------------------------------------------------------
/** must be called from host right before unloading dll
Note: this could be called more than one time! */
bool ModuleExit (void)
{
if (--moduleCounter == 0)
{
moduleHandle = nullptr;
return DeinitModule ();
}
else if (moduleCounter < 0)
return false;
return true;
}
@@ -0,0 +1,96 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
// Version : 1.0
//
// Category : Common Base Classes
// Filename : public.sdk/source/main/macmain.cpp
// Created by : Steinberg, 01/2004
// Description : Mac OS X Bundle Entry
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "pluginterfaces/base/fplatform.h"
#ifndef __CF_USE_FRAMEWORK_INCLUDES__
#define __CF_USE_FRAMEWORK_INCLUDES__ 1
#endif
#include <CoreFoundation/CoreFoundation.h>
//------------------------------------------------------------------------
CFBundleRef ghInst = nullptr;
int bundleRefCounter = 0; // counting for bundleEntry/bundleExit pairs
void* moduleHandle = nullptr;
#define VST_MAX_PATH 2048
char gPath[VST_MAX_PATH] = {0};
//------------------------------------------------------------------------
bool InitModule (); ///< must be provided by plug-in: called when the library is loaded
bool DeinitModule (); ///< must be provided by plug-in: called when the library is unloaded
//------------------------------------------------------------------------
extern "C" {
/** bundleEntry and bundleExit must be provided by the plug-in! */
SMTG_EXPORT_SYMBOL bool bundleEntry (CFBundleRef);
SMTG_EXPORT_SYMBOL bool bundleExit (void);
}
#include <vector>
std::vector<CFBundleRef> gBundleRefs;
//------------------------------------------------------------------------
/** must be called from host right after loading bundle
Note: this could be called more than one time! */
bool bundleEntry (CFBundleRef ref)
{
if (ref)
{
bundleRefCounter++;
CFRetain (ref);
// hold all bundle refs until plug-in is fully uninitialized
gBundleRefs.push_back (ref);
if (!moduleHandle)
{
ghInst = ref;
moduleHandle = ref;
// obtain the bundle path
CFURLRef tempURL = CFBundleCopyBundleURL (ref);
CFURLGetFileSystemRepresentation (tempURL, true, reinterpret_cast<UInt8*> (gPath), VST_MAX_PATH);
CFRelease (tempURL);
}
if (bundleRefCounter == 1)
return InitModule ();
}
return true;
}
//------------------------------------------------------------------------
/** must be called from host right before unloading bundle
Note: this could be called more than one time! */
bool bundleExit (void)
{
if (--bundleRefCounter == 0)
{
DeinitModule ();
// release the CFBundleRef's once all bundleExit clients called in
// there is no way to identify the proper CFBundleRef of the bundleExit call
for (size_t i = 0; i < gBundleRefs.size (); i++)
CFRelease (gBundleRefs[i]);
gBundleRefs.clear ();
}
else if (bundleRefCounter < 0)
return false;
return true;
}
@@ -0,0 +1,103 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
//
// Category : Common Base Classes
// Filename : public.sdk/source/main/moduleinit.cpp
// Created by : Steinberg, 11/2020
// Description : Module Initializers/Terminators
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "moduleinit.h"
#include <atomic>
#include <vector>
extern void* moduleHandle; // from dllmain.cpp, linuxmain.cpp or macmain.cpp
//------------------------------------------------------------------------
namespace Steinberg {
namespace {
//------------------------------------------------------------------------
using FunctionVector = std::vector<std::pair<ModuleInitPriority, ModuleInitFunction>>;
//------------------------------------------------------------------------
FunctionVector& getInitFunctions ()
{
static FunctionVector gInitVector;
return gInitVector;
}
//------------------------------------------------------------------------
FunctionVector& getTermFunctions ()
{
static FunctionVector gTermVector;
return gTermVector;
}
//------------------------------------------------------------------------
void addInitFunction (ModuleInitFunction&& func, ModuleInitPriority prio)
{
getInitFunctions ().emplace_back (prio, std::move (func));
}
//------------------------------------------------------------------------
void addTerminateFunction (ModuleInitFunction&& func, ModuleInitPriority prio)
{
getTermFunctions ().emplace_back (prio, std::move (func));
}
//------------------------------------------------------------------------
void sortAndRunFunctions (FunctionVector& array)
{
std::sort (array.begin (), array.end (),
[] (const FunctionVector::value_type& v1, const FunctionVector::value_type& v2) {
return v1.first < v2.first;
});
for (auto& entry : array)
entry.second ();
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
ModuleInitializer::ModuleInitializer (ModuleInitFunction&& func, ModuleInitPriority prio)
{
addInitFunction (std::move (func), prio);
}
//------------------------------------------------------------------------
ModuleTerminator::ModuleTerminator (ModuleInitFunction&& func, ModuleInitPriority prio)
{
addTerminateFunction (std::move (func), prio);
}
//------------------------------------------------------------------------
PlatformModuleHandle getPlatformModuleHandle ()
{
return reinterpret_cast<PlatformModuleHandle> (moduleHandle);
}
//------------------------------------------------------------------------
} // Steinberg
//------------------------------------------------------------------------
bool InitModule ()
{
Steinberg::sortAndRunFunctions (Steinberg::getInitFunctions ());
return true;
}
//------------------------------------------------------------------------
bool DeinitModule ()
{
Steinberg::sortAndRunFunctions (Steinberg::getTermFunctions ());
return true;
}
@@ -0,0 +1,93 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
//
// Category : Common Base Classes
// Filename : public.sdk/source/main/moduleinit.h
// Created by : Steinberg, 11/2020
// Description : Module Initializers/Terminators
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/base/ftypes.h"
#include <algorithm>
#include <functional>
#include <limits>
#include <numeric>
//------------------------------------------------------------------------
/** A replacement for InitModule and DeinitModule
*
* If you link this file the InitModule and DeinitModule functions are
* implemented and you can use this to register functions that will be
* called when the module is loaded and before the module is unloaded.
*
* Use this for one time initializers or cleanup functions.
* For example: if you depend on a 3rd party library that needs
* initialization before you can use it you can write an initializer like this:
*
* static ModuleInitializer InitMyExternalLib ([] () { MyExternalLib::init (); });
*
* Or you have a lazy create wavetable you need to free the allocated memory later:
*
* static ModuleTerminator FreeWaveTableMemory ([] () { MyWaveTable::free (); });
*/
//------------------------------------------------------------------------
#if SMTG_OS_WINDOWS
using HINSTANCE = struct HINSTANCE__*;
namespace Steinberg { using PlatformModuleHandle = HINSTANCE; }
//------------------------------------------------------------------------
#elif SMTG_OS_OSX || SMTG_OS_IOS
typedef struct __CFBundle* CFBundleRef;
namespace Steinberg { using PlatformModuleHandle = CFBundleRef; }
//------------------------------------------------------------------------
#elif SMTG_OS_LINUX
namespace Steinberg { using PlatformModuleHandle = void*; }
#endif
//------------------------------------------------------------------------
namespace Steinberg {
using ModuleInitFunction = std::function<void ()>;
using ModuleInitPriority = uint32;
static constexpr ModuleInitPriority DefaultModulePriority =
std::numeric_limits<ModuleInitPriority>::max () / 2;
//------------------------------------------------------------------------
struct ModuleInitializer
{
/**
* Register a function which is called when the module is loaded
* @param func function to call
* @param prio priority
*/
ModuleInitializer (ModuleInitFunction&& func,
ModuleInitPriority prio = DefaultModulePriority);
};
//------------------------------------------------------------------------
struct ModuleTerminator
{
/**
* Register a function which is called when the module is unloaded
* @param func function to call
* @param prio priority
*/
ModuleTerminator (ModuleInitFunction&& func,
ModuleInitPriority prio = DefaultModulePriority);
};
//------------------------------------------------------------------------
PlatformModuleHandle getPlatformModuleHandle ();
//------------------------------------------------------------------------
} // Steinberg
@@ -0,0 +1,340 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
//
// Category : Common Base Classes
// Filename : public.sdk/source/main/pluginfactory.cpp
// Created by : Steinberg, 01/2004
// Description : Standard Plug-In Factory
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "pluginfactory.h"
#if SMTG_OS_LINUX
#include "pluginterfaces/base/funknownimpl.h"
#include "pluginterfaces/gui/iplugview.h"
#include "base/source/timer.h"
#endif
#include <algorithm>
#include <cstdlib>
namespace Steinberg {
DEF_CLASS_IID (IPluginFactoryInternal);
CPluginFactory* gPluginFactory = nullptr;
//------------------------------------------------------------------------
// CPluginFactory implementation
//------------------------------------------------------------------------
CPluginFactory::CPluginFactory (const PFactoryInfo& info)
: classes (nullptr), classCount (0), maxClassCount (0)
{
FUNKNOWN_CTOR
factoryInfo = info;
}
//------------------------------------------------------------------------
CPluginFactory::~CPluginFactory ()
{
if (gPluginFactory == this)
gPluginFactory = nullptr;
if (classes)
free (classes);
FUNKNOWN_DTOR
}
//------------------------------------------------------------------------
IMPLEMENT_REFCOUNT (CPluginFactory)
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginFactory::queryInterface (FIDString _iid, void** obj)
{
QUERY_INTERFACE (_iid, obj, IPluginFactory::iid, IPluginFactory)
QUERY_INTERFACE (_iid, obj, IPluginFactory2::iid, IPluginFactory2)
QUERY_INTERFACE (_iid, obj, IPluginFactory3::iid, IPluginFactory3)
QUERY_INTERFACE (_iid, obj, IPluginFactoryInternal::iid, IPluginFactoryInternal)
QUERY_INTERFACE (_iid, obj, FUnknown::iid, IPluginFactory)
*obj = nullptr;
return kNoInterface;
}
//------------------------------------------------------------------------
bool CPluginFactory::registerClass (const PClassInfo* info, FUnknown* (*createFunc) (void*),
void* context)
{
if (!info || !createFunc)
return false;
PClassInfo2 info2;
memcpy (&info2, info, sizeof (PClassInfo));
return registerClass (&info2, createFunc, context);
}
//------------------------------------------------------------------------
bool CPluginFactory::registerClass (const PClassInfo2* info, FUnknown* (*createFunc) (void*),
void* context)
{
if (!info || !createFunc)
return false;
if (classCount >= maxClassCount)
{
if (!growClasses ())
return false;
}
PClassEntry& entry = classes[classCount];
entry.info8 = *info;
entry.info16.fromAscii (*info);
entry.createFunc = createFunc;
entry.context = context;
entry.isUnicode = false;
classCount++;
return true;
}
//------------------------------------------------------------------------
bool CPluginFactory::registerClass (const PClassInfoW* info, FUnknown* (*createFunc) (void*),
void* context)
{
if (!info || !createFunc)
return false;
if (classCount >= maxClassCount)
{
if (!growClasses ())
return false;
}
PClassEntry& entry = classes[classCount];
entry.info16 = *info;
entry.createFunc = createFunc;
entry.context = context;
entry.isUnicode = true;
classCount++;
return true;
}
//------------------------------------------------------------------------
bool CPluginFactory::growClasses ()
{
static const int32 delta = 10;
size_t size = (maxClassCount + delta) * sizeof (PClassEntry);
void* memory = classes;
if (!memory)
memory = malloc (size);
else
memory = realloc (memory, size);
if (!memory)
return false;
classes = static_cast<PClassEntry*> (memory);
maxClassCount += delta;
return true;
}
//------------------------------------------------------------------------
bool CPluginFactory::isClassRegistered (const FUID& cid)
{
for (int32 i = 0; i < classCount; i++)
{
if (FUnknownPrivate::iidEqual (cid, classes[i].info16.cid))
return true;
}
return false;
}
//------------------------------------------------------------------------
void CPluginFactory::removeAllClasses ()
{
classCount = 0;
}
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginFactory::getFactoryInfo (PFactoryInfo* info)
{
if (info)
memcpy (info, &factoryInfo, sizeof (PFactoryInfo));
return kResultOk;
}
//------------------------------------------------------------------------
int32 PLUGIN_API CPluginFactory::countClasses ()
{
return classCount;
}
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginFactory::getClassInfo (int32 index, PClassInfo* info)
{
if (info && (index >= 0 && index < classCount))
{
if (classes[index].isUnicode)
{
memset (info, 0, sizeof (PClassInfo));
return kResultFalse;
}
memcpy (info, &classes[index].info8, sizeof (PClassInfo));
return kResultOk;
}
return kInvalidArgument;
}
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginFactory::getClassInfo2 (int32 index, PClassInfo2* info)
{
if (info && (index >= 0 && index < classCount))
{
if (classes[index].isUnicode)
{
memset (info, 0, sizeof (PClassInfo2));
return kResultFalse;
}
memcpy (info, &classes[index].info8, sizeof (PClassInfo2));
return kResultOk;
}
return kInvalidArgument;
}
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginFactory::getClassInfoUnicode (int32 index, PClassInfoW* info)
{
if (info && (index >= 0 && index < classCount))
{
memcpy (info, &classes[index].info16, sizeof (PClassInfoW));
return kResultOk;
}
return kInvalidArgument;
}
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginFactory::createInstance (FIDString cid, FIDString _iid, void** obj)
{
for (int32 i = 0; i < classCount; i++)
{
if (memcmp (classes[i].info16.cid, cid, sizeof (TUID)) == 0)
{
FUnknown* instance = classes[i].createFunc (classes[i].context);
if (instance)
{
if (instance->queryInterface (_iid, obj) == kResultOk)
{
instance->release ();
return kResultOk;
}
instance->release ();
}
break;
}
}
*obj = nullptr;
return kNoInterface;
}
#if SMTG_OS_LINUX
//------------------------------------------------------------------------
namespace /*anonymous*/ {
//------------------------------------------------------------------------
class LinuxPlatformTimer : public U::Extends<Timer, U::Directly<Linux::ITimerHandler>>
{
public:
~LinuxPlatformTimer () noexcept override { stop (); }
tresult init (ITimerCallback* cb, uint32 timeout)
{
if (!runLoop || cb == nullptr || timeout == 0)
return kResultFalse;
auto result = runLoop->registerTimer (this, timeout);
if (result == kResultTrue)
{
callback = cb;
timerRegistered = true;
}
return result;
}
void PLUGIN_API onTimer () override { callback->onTimer (this); }
void stop () override
{
if (timerRegistered)
{
if (runLoop)
runLoop->unregisterTimer (this);
timerRegistered = false;
}
}
bool timerRegistered {false};
ITimerCallback* callback;
static IPtr<Linux::IRunLoop> runLoop;
};
IPtr<Linux::IRunLoop> LinuxPlatformTimer::runLoop;
//------------------------------------------------------------------------
Timer* createLinuxTimer (ITimerCallback* cb, uint32 milliseconds)
{
if (!LinuxPlatformTimer::runLoop)
return nullptr;
auto timer = NEW LinuxPlatformTimer;
if (timer->init (cb, milliseconds) == kResultTrue)
return timer;
timer->release ();
return nullptr;
}
} // anonymous
#endif // SMTG_OS_LINUX
//------------------------------------------------------------------------
tresult PLUGIN_API CPluginFactory::setHostContext (FUnknown* context)
{
std::for_each (hostContextCallbacks.begin (), hostContextCallbacks.end (),
[context] (const auto& cb) { cb (context); });
#if SMTG_OS_LINUX
if (auto runLoop = U::cast<Linux::IRunLoop> (context))
{
LinuxPlatformTimer::runLoop = runLoop;
InjectCreateTimerFunction (createLinuxTimer);
}
else
{
LinuxPlatformTimer::runLoop.reset ();
InjectCreateTimerFunction (nullptr);
}
return kResultTrue;
#else
(void) context;
return kNotImplemented;
#endif
}
//------------------------------------------------------------------------
void PLUGIN_API CPluginFactory::addHostContextCallback (HostContextCallbackFunc func)
{
hostContextCallbacks.push_back (func);
}
//------------------------------------------------------------------------
} // namespace Steinberg
@@ -0,0 +1,191 @@
//------------------------------------------------------------------------
// Project : SDK Core
//
// Category : Common Base Classes
// Filename : public.sdk/source/main/pluginfactory.h
// Created by : Steinberg, 01/2004
// Description : Standard Plug-In Factory
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/base/ipluginbase.h"
#include <vector>
namespace Steinberg {
//------------------------------------------------------------------------
class IPluginFactoryInternal : public FUnknown
{
public:
using HostContextCallbackFunc = void (*) (FUnknown*);
virtual void PLUGIN_API addHostContextCallback (HostContextCallbackFunc func) = 0;
//------------------------------------------------------------------------
static const FUID iid;
};
DECLARE_CLASS_IID (IPluginFactoryInternal, 0x5A6AD11A, 0x22AF40F3, 0xBCA1C147, 0x506C88D9)
//------------------------------------------------------------------------
/** Default Class Factory implementation.
\ingroup sdkBase
\see classFactoryMacros
*/
class CPluginFactory : public IPluginFactory3, public IPluginFactoryInternal
{
public:
//------------------------------------------------------------------------
CPluginFactory (const PFactoryInfo& info);
virtual ~CPluginFactory ();
//--- ---------------------------------------------------------------------
/** Registers a plug-in class with classInfo version 1, returns true for success. */
bool registerClass (const PClassInfo* info, FUnknown* (*createFunc) (void*),
void* context = nullptr);
/** Registers a plug-in class with classInfo version 2, returns true for success. */
bool registerClass (const PClassInfo2* info, FUnknown* (*createFunc) (void*),
void* context = nullptr);
/** Registers a plug-in class with classInfo Unicode version, returns true for success. */
bool registerClass (const PClassInfoW* info, FUnknown* (*createFunc) (void*),
void* context = nullptr);
/** Check if a class for a given classId is already registered. */
bool isClassRegistered (const FUID& cid);
/** Remove all classes (no class exported) */
void removeAllClasses ();
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
//---from IPluginFactory------
tresult PLUGIN_API getFactoryInfo (PFactoryInfo* info) SMTG_OVERRIDE;
int32 PLUGIN_API countClasses () SMTG_OVERRIDE;
tresult PLUGIN_API getClassInfo (int32 index, PClassInfo* info) SMTG_OVERRIDE;
tresult PLUGIN_API createInstance (FIDString cid, FIDString _iid, void** obj) SMTG_OVERRIDE;
//---from IPluginFactory2-----
tresult PLUGIN_API getClassInfo2 (int32 index, PClassInfo2* info) SMTG_OVERRIDE;
//---from IPluginFactory3-----
tresult PLUGIN_API getClassInfoUnicode (int32 index, PClassInfoW* info) SMTG_OVERRIDE;
tresult PLUGIN_API setHostContext (FUnknown* context) SMTG_OVERRIDE;
//---from IPluginFactoryInternal
void PLUGIN_API addHostContextCallback (HostContextCallbackFunc func) SMTG_OVERRIDE;
//------------------------------------------------------------------------
protected:
/// @cond
struct PClassEntry
{
//-----------------------------------
PClassInfo2 info8;
PClassInfoW info16;
FUnknown* (*createFunc) (void*);
void* context;
bool isUnicode;
//-----------------------------------
};
/// @endcond
PFactoryInfo factoryInfo;
PClassEntry* classes;
int32 classCount;
int32 maxClassCount;
std::vector<HostContextCallbackFunc> hostContextCallbacks;
bool growClasses ();
};
extern CPluginFactory* gPluginFactory;
//------------------------------------------------------------------------
} // namespace Steinberg
//------------------------------------------------------------------------
/** \defgroup classFactoryMacros Macros for defining the class factory
\ingroup sdkBase
\b Example - How to use the class factory macros:
\code
BEGIN_FACTORY ("Steinberg Technologies",
"http://www.steinberg.de",
"mailto:info@steinberg.de",
PFactoryInfo::kNoFlags)
DEF_CLASS (INLINE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000),
PClassInfo::kManyInstances,
"Service",
"Test Service",
TestService::newInstance)
END_FACTORY
\endcode
@{*/
#define BEGIN_FACTORY_CLASS(FactoryClass,vendor,url,email,flags) using namespace Steinberg; \
SMTG_EXPORT_SYMBOL IPluginFactory* PLUGIN_API GetPluginFactory () { \
if (!gPluginFactory) \
{ static PFactoryInfo factoryInfo (vendor,url,email,flags); \
gPluginFactory = new FactoryClass (factoryInfo); \
#define BEGIN_FACTORY(vendor,url,email,flags) using namespace Steinberg; \
SMTG_EXPORT_SYMBOL IPluginFactory* PLUGIN_API GetPluginFactory () { \
if (!gPluginFactory) \
{ static PFactoryInfo factoryInfo (vendor,url,email,flags); \
gPluginFactory = new CPluginFactory (factoryInfo); \
#define DEF_CLASS(cid,cardinality,category,name,createMethod) \
{ TUID lcid = cid; static PClassInfo componentClass (lcid,cardinality,category,name); \
gPluginFactory->registerClass (&componentClass,createMethod); }
#define DEF_CLASS1(cid,cardinality,category,name,createMethod) \
{ static PClassInfo componentClass (cid,cardinality,category,name); \
gPluginFactory->registerClass (&componentClass,createMethod); }
#define DEF_CLASS2(cid,cardinality,category,name,classFlags,subCategories,version,sdkVersion,createMethod) \
{ TUID lcid = cid; static PClassInfo2 componentClass (lcid,cardinality,category,name,classFlags,subCategories,nullptr,version,sdkVersion);\
gPluginFactory->registerClass (&componentClass,createMethod); }
#define DEF_CLASS_W(cid,cardinality,category,name,classFlags,subCategories,version,sdkVersion,createMethod) \
{ TUID lcid = cid; static PClassInfoW componentClass (lcid,cardinality,category,name,classFlags,subCategories,nullptr,version,sdkVersion);\
gPluginFactory->registerClass (&componentClass,createMethod); }
#define DEF_CLASS_W2(cid,cardinality,category,name,classFlags,subCategories,vendor,version,sdkVersion,createMethod) \
{ TUID lcid = cid; static PClassInfoW componentClass (lcid,cardinality,category,name,classFlags,subCategories,vendor,version,sdkVersion);\
gPluginFactory->registerClass (&componentClass,createMethod); }
#define END_FACTORY } else gPluginFactory->addRef (); \
return gPluginFactory; }
#define DEF_VST3_CLASS(pluginName, pluginVst3Categories, classFlags, pluginVersion, processorCID, \
processorCreateFunc, controllerCID, controllerCreateFunc) \
{ \
{ \
const Steinberg::TUID lcid = processorCID; \
static Steinberg::PClassInfo2 processorClass ( \
lcid, Steinberg::PClassInfo::kManyInstances, kVstAudioEffectClass, pluginName, \
classFlags, pluginVst3Categories, 0, pluginVersion, kVstVersionString); \
gPluginFactory->registerClass (&processorClass, processorCreateFunc); \
} \
{ \
const Steinberg::TUID lcid = controllerCID; \
static Steinberg::PClassInfo2 controllerClass ( \
lcid, Steinberg::PClassInfo::kManyInstances, kVstComponentControllerClass, \
pluginName, 0, "", 0, pluginVersion, kVstVersionString); \
gPluginFactory->registerClass (&controllerClass, controllerCreateFunc); \
} \
}
/** @} */
@@ -0,0 +1,201 @@
//------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : SDK Core
//
// Category : Common Base Classes
// Filename : public.sdk/source/main/pluginfactory_constexpr.h
// Created by : Steinberg, 10/2021
// Description : Standard Plug-In Factory (constexpr variant)
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/base/funknownimpl.h"
#include "pluginterfaces/base/ipluginbase.h"
#if !SMTG_CPP17
#error "C++17 is required for this header"
#endif
#include <array>
namespace Steinberg {
//------------------------------------------------------------------------
/** IPluginFactory implementation with compile time provided factory and class infos.
\ingroup sdkBase
\see \ref constexprClassFactoryMacros
You can use this factory when your number of classes are known during compile time. The
advantage here is that during runtime no unnecessary setup is needed.
Please note that this only supports ASCII names and thus if you need to support Unicode names
you must use another implementation.
This only works when compiling with c++17 or newer.
*/
template <typename T>
class PluginFactory
: public U::ImplementsNonDestroyable<U::Directly<IPluginFactory2>, U::Indirectly<IPluginFactory>>
{
public:
//------------------------------------------------------------------------
tresult PLUGIN_API getFactoryInfo (PFactoryInfo* info) override
{
if (info == nullptr)
return kInvalidArgument;
*info = T::factoryInfo;
return kResultTrue;
}
int32 PLUGIN_API countClasses () override { return static_cast<int32> (T::classInfos.size ()); }
tresult PLUGIN_API getClassInfo (int32 index, PClassInfo* info) override
{
if (index < 0 || index >= static_cast<int32> (T::classInfos.size ()) || info == nullptr)
return kInvalidArgument;
*info = {};
const auto& ci = T::classInfos[index];
copyTUID (info->cid, ci.cid);
info->cardinality = ci.cardinality;
if (ci.category)
strncpy (info->category, ci.category, PClassInfo::kCategorySize);
if (ci.name)
strncpy (info->name, ci.name, PClassInfo::kNameSize);
return kResultTrue;
}
tresult PLUGIN_API createInstance (FIDString cid, FIDString iid, void** obj) override
{
for (const auto& e : T::classInfos)
{
if (FUnknownPrivate::iidEqual (e.cid, cid))
{
if (auto instance = e.create (e.context))
{
if (instance->queryInterface (iid, obj) == kResultOk)
{
instance->release ();
return kResultOk;
}
else
instance->release ();
}
}
}
*obj = nullptr;
return kNoInterface;
}
tresult PLUGIN_API getClassInfo2 (int32 index, PClassInfo2* info) override
{
if (index < 0 || index >= static_cast<int32> (T::classInfos.size ()) || info == nullptr)
return kInvalidArgument;
*info = T::classInfos[index];
return kResultTrue;
}
};
//------------------------------------------------------------------------
namespace PluginFactoryDetail {
//------------------------------------------------------------------------
struct ClassInfo2WithCreateFunc : PClassInfo2
{
using CreateInstanceFunc = FUnknown* (*)(void*);
CreateInstanceFunc create {nullptr};
void* context {nullptr};
};
//------------------------------------------------------------------------
inline constexpr ClassInfo2WithCreateFunc makeClassInfo2 (
const TUID cid, int32 cardinality, const char8* category, const char8* name, int32 classFlags,
const char8* subCategories, const char8* vendor, const char8* version, const char8* sdkVersion,
ClassInfo2WithCreateFunc::CreateInstanceFunc func, void* context = nullptr)
{
ClassInfo2WithCreateFunc classInfo {};
copyTUID (classInfo.cid, cid);
classInfo.cardinality = cardinality;
strncpy8 (classInfo.category, category, PClassInfo::kCategorySize);
strncpy8 (classInfo.name, name, PClassInfo::kNameSize);
classInfo.classFlags = classFlags;
if (subCategories)
strncpy8 (classInfo.subCategories, subCategories, PClassInfo2::kSubCategoriesSize);
if (vendor)
strncpy8 (classInfo.vendor, vendor, PClassInfo2::kVendorSize);
if (version)
strncpy8 (classInfo.version, version, PClassInfo2::kVersionSize);
if (sdkVersion)
strncpy8 (classInfo.sdkVersion, sdkVersion, PClassInfo2::kVersionSize);
classInfo.create = func;
classInfo.context = context;
return classInfo;
}
//------------------------------------------------------------------------
} // namespace PluginFactoryDetail
} // namespace Steinberg
#ifdef BEGIN_FACTORY_DEF
#undef BEGIN_FACTORY_DEF
#endif
/** \defgroup constexprClassFactoryMacros Macros for defining the compile time class factory
\ingroup sdkBase
\b Example:
\code
static constexpr size_t numberOfClasses = 1;
static DECLARE_UID (TestPluginUID, 0x00000001, 0x00000002, 0x00000003, 0x00000004);
BEGIN_FACTORY_DEF ("MyCompany", "mycompany.com", "info@mycompany.com", numberOfClasses)
DEF_CLASS (TestPluginUID,
PClassInfo::kManyInstances,
"PlugIn",
"Test PlugIn",
0,
"SubCategory",
"1.0.0",
stringSDKVersion,
TestPlugin::newInstance,
nullptr)
END_FACTORY
\endcode
@{*/
// clang-format off
#define BEGIN_FACTORY_DEF(company, url, email, noClasses) \
struct SMTG_HIDDEN_SYMBOL FactoryData \
{ \
static constexpr Steinberg::PFactoryInfo factoryInfo = { \
company, url, email, Steinberg::PFactoryInfo::kUnicode}; \
\
static constexpr std::array<Steinberg::PluginFactoryDetail::ClassInfo2WithCreateFunc, \
noClasses> \
classInfos = {
#define DEF_CLASS(cid, cardinality, category, name, classFlags, subCategories, version, \
sdkVersion, createMethod, createContext) \
Steinberg::PluginFactoryDetail::makeClassInfo2 (cid, cardinality, category, name, classFlags, \
subCategories, nullptr, version, sdkVersion, \
createMethod, createContext),
#define END_FACTORY \
};}; \
SMTG_EXPORT_SYMBOL Steinberg::IPluginFactory* PLUGIN_API GetPluginFactory () \
{ \
static Steinberg::PluginFactory<FactoryData> factory; \
return &factory; \
}
// clang-format on
/** @} */
@@ -0,0 +1,51 @@
if(NOT SMTG_LINUX)
set(target aax_wrapper)
set(${target}_sources
${SDK_ROOT}/public.sdk/source/vst/basewrapper/basewrapper.sdk.cpp
aaxentry.cpp
aaxlibrary.cpp
aaxwrapper.cpp
aaxwrapper.h
aaxwrapper_description.h
aaxwrapper_gui.cpp
aaxwrapper_gui.h
aaxwrapper_parameters.cpp
aaxwrapper_parameters.h
resource/PlugIn.ico
)
add_library(${target} STATIC ${${target}_sources})
target_include_directories(${target}
PRIVATE
"${SMTG_AAX_SDK_PATH}/Interfaces"
"${SMTG_AAX_SDK_PATH}/Interfaces/ACF"
"${SMTG_AAX_SDK_PATH}/Libs/AAXLibrary/Include"
)
target_link_libraries(${target}
PRIVATE
base
)
smtg_target_setup_universal_binary(${target})
target_compile_features(aax_wrapper
PUBLIC
cxx_std_17
)
if(XCODE)
add_compile_options(-Wno-incompatible-ms-struct)
elseif(SMTG_WIN)
# too much warnings in the AAX SDK!!
add_compile_options(/wd4996)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_compile_options(/GR)
if(MSVC)
target_compile_options(${target}
PRIVATE
/wd4127 # conditional expression is constant
/wd5033 # 'register' is no longer a supported storage class
)
endif(MSVC)
endif(XCODE)
endif()
@@ -0,0 +1,213 @@
//------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxentry.h
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
/**
* plugin entry from AAX_Exports.cpp
*/
//-----------------------------------------------------------------------------
#include "pluginterfaces/base/fplatform.h"
// change names to avoid different linkage
#define ACFRegisterPlugin ACFRegisterPlugin_
#define ACFRegisterComponent ACFRegisterComponent_
#define ACFGetClassFactory ACFGetClassFactory_
#define ACFCanUnloadNow ACFCanUnloadNow_
#define ACFStartup ACFStartup_
#define ACFShutdown ACFShutdown_
//#define INITACFIDS // Make sure all of the AVX2 uids are defined.
#include "AAX.h"
#include "AAX_Init.h"
#include "acfresult.h"
#include "acfunknown.h"
#undef ACFRegisterPlugin
#undef ACFRegisterComponent
#undef ACFGetClassFactory
#undef ACFCanUnloadNow
#undef ACFStartup
#undef ACFShutdown
// defined in basewrapper.cpp
extern bool _InitModule ();
extern bool _DeinitModule ();
// reference this in the plugin to force inclusion of the wrapper in the link
int AAXWrapper_linkAnchor;
//------------------------------------------------------------------------
#if defined(__GNUC__)
#define AAX_EXPORT extern "C" __attribute__ ((visibility ("default"))) ACFRESULT
#else
#define AAX_EXPORT extern "C" __declspec (dllexport) ACFRESULT __stdcall
#endif
AAX_EXPORT ACFRegisterPlugin (IACFUnknown* pUnkHost, IACFPluginDefinition** ppPluginDefinition);
AAX_EXPORT ACFRegisterComponent (IACFUnknown* pUnkHost, acfUInt32 index,
IACFComponentDefinition** ppComponentDefinition);
AAX_EXPORT ACFGetClassFactory (IACFUnknown* pUnkHost, const acfCLSID& clsid, const acfIID& iid,
void** ppOut);
AAX_EXPORT ACFCanUnloadNow (IACFUnknown* pUnkHost);
AAX_EXPORT ACFStartup (IACFUnknown* pUnkHost);
AAX_EXPORT ACFShutdown (IACFUnknown* pUnkHost);
AAX_EXPORT ACFGetSDKVersion (acfUInt64* oSDKVersion);
//------------------------------------------------------------------------
// \func ACFRegisterPlugin
// \brief Determines the number of components defined in the dll.
//
ACFAPI ACFRegisterPlugin (IACFUnknown* pUnkHostVoid, IACFPluginDefinition** ppPluginDefinitionVoid)
{
ACFRESULT result = ACF_OK;
try
{
result = AAXRegisterPlugin (pUnkHostVoid, ppPluginDefinitionVoid);
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
// \func ACFRegisterComponent
// \brief Registers a specific component in the DLL.
//
ACFAPI ACFRegisterComponent (IACFUnknown* pUnkHost, acfUInt32 index,
IACFComponentDefinition** ppComponentDefinition)
{
ACFRESULT result = ACF_OK;
try
{
result = AAXRegisterComponent (pUnkHost, index, ppComponentDefinition);
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
// \func ACFGetClassFactory
// \brief Gets the factory for a given class ID.
//
ACFAPI ACFGetClassFactory (IACFUnknown* pUnkHost, const acfCLSID& clsid, const acfIID& iid,
void** ppOut)
{
ACFRESULT result = ACF_OK;
try
{
result = AAXGetClassFactory (pUnkHost, clsid, iid, ppOut);
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
// \func ACFCanUnloadNow
// \brief Figures out if all objects are released so we can unload.
//
ACFAPI ACFCanUnloadNow (IACFUnknown* pUnkHost)
{
ACFRESULT result = ACF_OK;
try
{
result = AAXCanUnloadNow (pUnkHost);
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
// \func ACFStartup
// \brief Called once at init time.
//
ACFAPI ACFStartup (IACFUnknown* pUnkHost)
{
ACFRESULT result = ACF_OK;
try
{
result = AAXStartup (pUnkHost);
if (result == ACF_OK)
{
if (!_InitModule ())
{
AAXShutdown (pUnkHost);
result = ACF_E_UNEXPECTED;
}
}
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
// \func ACFShutdown
// \brief Called once at termination of dll.
//
ACFAPI ACFShutdown (IACFUnknown* pUnkHost)
{
ACFRESULT result = ACF_OK;
try
{
_DeinitModule ();
result = AAXShutdown (pUnkHost);
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
ACFAPI ACFGetSDKVersion (acfUInt64* oSDKVersion)
{
return AAXGetSDKVersion (oSDKVersion);
}
/// \endcond
@@ -0,0 +1,95 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxlibrary.cpp
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
// instead of linking to a library, we just include the sources here to have
// full control over compile settings
#define I18N_LIB 1
#define PLUGIN_SDK_BUILD 1
#define DPA_PLUGIN_BUILD 1
#define INITACFIDS // Make sure all of the AVX2 uids are defined.
#define UNICODE 1
#ifdef _WIN32
#ifndef WIN32
#define WIN32 // for CMutex.cpp
#endif
#define WINDOWS_VERSION 1 // for AAXWrapper_GUI.h
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreorder"
#pragma clang diagnostic ignored "-Wundef-prefix"
#endif
#include "AAX_Atomic.h"
#include "../Interfaces/ACF/CACFClassFactory.cpp"
#include "../Libs/AAXLibrary/source/AAX_CACFUnknown.cpp"
#include "../Libs/AAXLibrary/source/AAX_CChunkDataParser.cpp"
#include "../Libs/AAXLibrary/source/AAX_CEffectDirectData.cpp"
#include "../Libs/AAXLibrary/source/AAX_CEffectGUI.cpp"
#include "../Libs/AAXLibrary/source/AAX_CEffectParameters.cpp"
#include "../Libs/AAXLibrary/source/AAX_CHostProcessor.cpp"
#include "../Libs/AAXLibrary/source/AAX_CHostServices.cpp"
#include "../Libs/AAXLibrary/source/AAX_CMutex.cpp"
#include "../Libs/AAXLibrary/source/AAX_CPacketDispatcher.cpp"
#include "../Libs/AAXLibrary/source/AAX_CParameter.cpp"
#include "../Libs/AAXLibrary/source/AAX_CParameterManager.cpp"
#include "../Libs/AAXLibrary/source/AAX_CString.cpp"
#include "../Libs/AAXLibrary/source/AAX_CUIDs.cpp"
#include "../Libs/AAXLibrary/source/AAX_CommonConversions.cpp"
#include "../Libs/AAXLibrary/source/AAX_IEffectDirectData.cpp"
#include "../Libs/AAXLibrary/source/AAX_IEffectGUI.cpp"
#include "../Libs/AAXLibrary/source/AAX_IEffectParameters.cpp"
#include "../Libs/AAXLibrary/source/AAX_IHostProcessor.cpp"
#include "../Libs/AAXLibrary/source/AAX_Init.cpp"
#include "../Libs/AAXLibrary/source/AAX_Properties.cpp"
#include "../Libs/AAXLibrary/source/AAX_VAutomationDelegate.cpp"
#include "../Libs/AAXLibrary/source/AAX_VCollection.cpp"
#include "../Libs/AAXLibrary/source/AAX_VComponentDescriptor.cpp"
#include "../Libs/AAXLibrary/source/AAX_VController.cpp"
#include "../Libs/AAXLibrary/source/AAX_VDescriptionHost.cpp"
#include "../Libs/AAXLibrary/source/AAX_VEffectDescriptor.cpp"
#include "../Libs/AAXLibrary/source/AAX_VFeatureInfo.cpp"
#include "../Libs/AAXLibrary/source/AAX_VHostProcessorDelegate.cpp"
#include "../Libs/AAXLibrary/source/AAX_VHostServices.cpp"
#include "../Libs/AAXLibrary/source/AAX_VPageTable.cpp"
#include "../Libs/AAXLibrary/source/AAX_VPrivateDataAccess.cpp"
#include "../Libs/AAXLibrary/source/AAX_VPropertyMap.cpp"
#include "../Libs/AAXLibrary/source/AAX_VTransport.cpp"
#include "../Libs/AAXLibrary/source/AAX_VViewContainer.cpp"
#ifdef _WIN32
#include "../Libs/AAXLibrary/source/AAX_CAutoreleasePool.Win.cpp"
#else
//#include "../Libs/AAXLibrary/source/AAX_CAutoreleasePool.OSX.mm"
#endif
#undef min
#undef max
// put at the very end, uses "using namespace std"
#include "../Libs/AAXLibrary/source/AAX_SliderConversions.cpp"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
/// \endcond
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,178 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper.h
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#include "public.sdk/source/vst/basewrapper/basewrapper.h"
#include "base/thread/include/flock.h"
#include <bitset>
#include <list>
#include <memory>
struct AAX_Plugin_Desc;
struct AAX_Effect_Desc;
class AAX_IComponentDescriptor;
class AAXWrapper_Parameters;
class AAXWrapper_GUI;
namespace Steinberg {
namespace Vst {
class IAudioProcessor;
class IEditController;
}
}
struct AAXWrapper_Context
{
void* ptr[1]; // array of numDataPointers pointers
};
//------------------------------------------------------------------------
class AAXWrapper : public Steinberg::Vst::BaseWrapper,
public Steinberg::Vst::IComponentHandler2,
public Steinberg::Vst::IVst3ToAAXWrapper
{
public:
// static creation method (will owned factory)
static AAXWrapper* create (Steinberg::IPluginFactory* factory,
const Steinberg::TUID vst3ComponentID, AAX_Plugin_Desc* desc,
AAXWrapper_Parameters* p);
AAXWrapper (Steinberg::Vst::BaseWrapper::SVST3Config& config, AAXWrapper_Parameters* p, AAX_Plugin_Desc* desc);
~AAXWrapper ();
//--- VST 3 Interfaces ------------------------------------------------------
// IHostApplication
Steinberg::tresult PLUGIN_API getName (Steinberg::Vst::String128 name) SMTG_OVERRIDE;
// IComponentHandler
Steinberg::tresult PLUGIN_API beginEdit (Steinberg::Vst::ParamID tag) SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API performEdit (
Steinberg::Vst::ParamID tag, Steinberg::Vst::ParamValue valueNormalized) SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API endEdit (Steinberg::Vst::ParamID tag) SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API restartComponent (Steinberg::int32 flags) SMTG_OVERRIDE;
// IComponentHandler2
Steinberg::tresult PLUGIN_API setDirty (Steinberg::TBool state) SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API requestOpenEditor (
Steinberg::FIDString name = Steinberg::Vst::ViewType::kEditor) SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API startGroupEdit () SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API finishGroupEdit () SMTG_OVERRIDE;
// FUnknown
DEF_INTERFACES_2 (Steinberg::Vst::IComponentHandler2, Steinberg::Vst::IVst3ToAAXWrapper, BaseWrapper);
REFCOUNT_METHODS (BaseWrapper);
// AAXWrapper_Parameters callbacks
void setGUI (AAXWrapper_GUI* gui) { mAAXGUI = gui; }
Steinberg::int32 /*AAX_Result*/ getParameterInfo (const char* aaxId,
Steinberg::Vst::ParameterInfo& paramInfo);
Steinberg::int32 /*AAX_Result*/ ResetFieldData (Steinberg::int32 index, void* inData,
Steinberg::uint32 inDataSize);
Steinberg::int32 Process (AAXWrapper_Context* instance);
Steinberg::uint32 getNumMIDIports () const { return mCountMIDIports; }
void setSideChainEnable (bool enable);
bool generatePageTables (const char* outputFile);
void setRenderingOffline (bool val);
static void DescribeAlgorithmComponent (AAX_IComponentDescriptor* outDesc,
const AAX_Effect_Desc* desc,
const AAX_Plugin_Desc* pdesc);
//--- ---------------------------------------------------------------------
Steinberg::uint32 getNumAAXOutputs () const { return mAAXOutputs; }
//------------------------------------------------------------------------
// BaseWrapper overrides ---------------------------------
//------------------------------------------------------------------------
bool init () SMTG_OVERRIDE;
bool _sizeWindow (Steinberg::int32 width, Steinberg::int32 height) SMTG_OVERRIDE;
void onTimer (Steinberg::Timer* timer) SMTG_OVERRIDE;
Steinberg::int32 _getChunk (void** data, bool isPreset) SMTG_OVERRIDE;
Steinberg::int32 _setChunk (void* data, Steinberg::int32 byteSize, bool isPreset) SMTG_OVERRIDE;
void setupProcessTimeInfo () SMTG_OVERRIDE;
//------------------------------------------------------------------------
private:
void processOutputParametersChanges () SMTG_OVERRIDE;
Steinberg::tresult setupBusArrangements (AAX_Plugin_Desc* desc);
Steinberg::int32 countSidechainBusChannels (Steinberg::Vst::BusDirection dir,
Steinberg::uint64& scBusBitset);
void guessActiveOutputs (float** out, Steinberg::uint32 num);
void updateActiveOutputState ();
AAXWrapper_Parameters* mAAXParams = nullptr;
AAXWrapper_GUI* mAAXGUI = nullptr;
Steinberg::uint32 mAAXOutputs = 0;
Steinberg::Base::Thread::FLock mSyncCalls; // synchronize calls expected in the same thread in VST3
AAX_Plugin_Desc* mPluginDesc = nullptr;
Steinberg::uint32 mCountMIDIports = 0;
// as of ProTools 12 (?) the context struct does no longer allow unused slots,
// so we have to generate indices into the context struct dynamically
// context pointer to AAXWrapper always first
static const Steinberg::int32 idxContext = 0;
static const Steinberg::int32 idxBufferSize = 1;
Steinberg::int32 idxInputChannels = -1;
Steinberg::int32 idxOutputChannels = -1;
Steinberg::int32 idxSideChainInputChannels = -1;
Steinberg::int32 idxMidiPorts = -1;
Steinberg::int32 idxAuxOutputs = -1;
Steinberg::int32 idxMeters = -1;
Steinberg::int32 numDataPointers = 0;
static const Steinberg::int32 maxActiveChannels = 128;
std::bitset<maxActiveChannels> mActiveChannels;
std::bitset<maxActiveChannels> mPropagatedChannels;
Steinberg::uint32 mCntMeters = 0;
std::unique_ptr<Steinberg::Vst::ParamID[]> mMeterIds;
struct GetChunkMessage;
void* mainThread = nullptr;
Steinberg::Base::Thread::FLock msgQueueLock;
std::list<GetChunkMessage*> msgQueue;
float mBypassGain = 1.0;
float* mMetersTmp = nullptr;
Steinberg::Vst::TQuarterNotes mLastPpqPos = 0;
Steinberg::Vst::TQuarterNotes mNextPpqPos = 0;
bool mWantsSetChunk = false;
bool mSettingChunk = false;
bool mSimulateBypass = false;
bool mBypass = false;
bool mPresetChanged = false;
bool mBypassBeforePresetChanged = false;
bool mWantsSetChunkIsPreset = false;
friend class AAXWrapper_Parameters;
friend class AAXWrapper_GUI;
};
/// \endcond
@@ -0,0 +1,84 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_description.h
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#include "base/source/fstring.h"
using namespace Steinberg;
struct AAX_Aux_Desc
{
const char* mName;
int32 mChannels; // -1 for same as output channel
};
struct AAX_Meter_Desc
{
const char* mName;
uint32 mID;
uint32 mOrientation; // see AAX_EMeterOrientation
uint32 mType; // see AAX_EMeterType
};
struct AAX_MIDI_Desc
{
const char* mName;
uint32 mMask;
};
struct AAX_Plugin_Desc
{
const char* mEffectID; // unique for each channel layout as in "com.steinberg.aaxwrapper.mono"
const char* mName;
uint32 mPlugInIDNative; // unique for each channel layout
uint32 mPlugInIDAudioSuite; // unique for each channel layout
int32 mInputChannels;
int32 mOutputChannels;
int32 mSideChainInputChannels;
AAX_MIDI_Desc* mMIDIports;
AAX_Aux_Desc* mAuxOutputChannels; // zero terminated
AAX_Meter_Desc* mMeters;
uint32 mLatency;
};
struct AAX_Effect_Desc
{
const char* mManufacturer;
const char* mProduct;
uint32 mManufacturerID;
uint32 mProductID;
const char* mCategory;
TUID mVST3PluginID;
uint32 mVersion;
const char* mPageFile;
AAX_Plugin_Desc* mPluginDesc;
};
// reference this in the Plug-In to force inclusion of the wrapper in the link
extern int AAXWrapper_linkAnchor;
AAX_Effect_Desc* AAXWrapper_GetDescription (); // to be defined by the Plug-In
/// \endcond
@@ -0,0 +1,135 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_gui.cpp
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundef-prefix"
#endif
#include "aaxwrapper_gui.h"
#include "aaxwrapper.h"
#include "aaxwrapper_parameters.h"
#include "AAX_IViewContainer.h"
using namespace Steinberg;
using namespace Steinberg::Vst;
using namespace Steinberg::Base::Thread;
//------------------------------------------------------------------------
void AAXWrapper_GUI::CreateViewContainer ()
{
if (GetViewContainerType () == AAX_eViewContainer_Type_HWND ||
GetViewContainerType () == AAX_eViewContainer_Type_NSView)
{
mHWND = this->GetViewContainerPtr ();
AAXWrapper* wrapper =
static_cast<AAXWrapper_Parameters*> (GetEffectParameters ())->getWrapper ();
FGuard guard (wrapper->mSyncCalls);
wrapper->setGUI (this);
mInOpen = true;
if (auto* editor = wrapper->getEditor ())
editor->_open (mHWND);
mInOpen = false;
}
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_GUI::GetViewSize (AAX_Point* oEffectViewSize) const
{
oEffectViewSize->horz = 1024;
oEffectViewSize->vert = 768;
auto* that = const_cast<AAXWrapper_GUI*> (this);
auto* params = static_cast<AAXWrapper_Parameters*> (that->GetEffectParameters ());
int32 width, height;
if (params->getWrapper ()->getEditorSize (width, height))
{
oEffectViewSize->horz = static_cast<float> (width);
oEffectViewSize->vert = static_cast<float> (height);
}
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_GUI::SetControlHighlightInfo (AAX_CParamID iParameterID,
AAX_CBoolean /*iIsHighlighted*/,
AAX_EHighlightColor /*iColor*/)
{
AAXWrapper* wrapper =
static_cast<AAXWrapper_Parameters*> (GetEffectParameters ())->getWrapper ();
Vst::ParamID id = getVstParamID (iParameterID);
if (id == kNoParamId)
return AAX_ERROR_INVALID_PARAMETER_ID;
// TODO
wrapper;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
void AAXWrapper_GUI::DeleteViewContainer ()
{
AAXWrapper* wrapper =
static_cast<AAXWrapper_Parameters*> (GetEffectParameters ())->getWrapper ();
wrapper->setGUI (nullptr);
if (auto* editor = wrapper->getEditor ())
editor->_close ();
}
//------------------------------------------------------------------------
// METHOD: CreateViewContents
//------------------------------------------------------------------------
void AAXWrapper_GUI::CreateViewContents ()
{
}
//------------------------------------------------------------------------
bool AAXWrapper_GUI::setWindowSize (AAX_Point& size)
{
if (mInOpen)
mRefreshSize = true; // redo later, resizing might silently not work during opening the UI
if (AAX_IViewContainer* vc = GetViewContainer ())
if (vc->SetViewSize (size) == AAX_SUCCESS)
return true;
return false;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_GUI::TimerWakeup ()
{
if (mRefreshSize)
{
mRefreshSize = false;
AAX_Point size;
if (GetViewSize (&size) == AAX_SUCCESS)
if (!setWindowSize (size))
mRefreshSize = true;
}
return AAX_CEffectGUI::TimerWakeup ();
}
/// \endcond
#ifdef __clang__
#pragma clang diagnostic pop
#endif
@@ -0,0 +1,53 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_gui.h
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#include "AAX_CEffectGUI.h"
#include "pluginterfaces/base/fplatform.h"
//==============================================================================
class AAXWrapper_GUI : public AAX_CEffectGUI
{
public:
static AAX_IEffectGUI* AAX_CALLBACK Create ();
AAXWrapper_GUI () = default;
virtual ~AAXWrapper_GUI () = default;
void CreateViewContents () SMTG_OVERRIDE;
void CreateViewContainer () SMTG_OVERRIDE;
void DeleteViewContainer () SMTG_OVERRIDE;
AAX_Result GetViewSize (AAX_Point* oEffectViewSize) const SMTG_OVERRIDE;
AAX_Result SetControlHighlightInfo (AAX_CParamID /* iParameterID */,
AAX_CBoolean /* iIsHighlighted */,
AAX_EHighlightColor /* iColor */) SMTG_OVERRIDE;
AAX_Result TimerWakeup () SMTG_OVERRIDE;
bool setWindowSize (AAX_Point& size); // calback from AAXWrapper
private:
bool mInOpen = false;
bool mRefreshSize = false;
void* mHWND = nullptr;
};
/// \endcond
@@ -0,0 +1,858 @@
//------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_parameters.cpp
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#include "aaxwrapper_parameters.h"
#include "aaxwrapper.h"
#include "aaxwrapper_description.h"
#include "AAX_CBinaryDisplayDelegate.h"
#include "AAX_CBinaryTaperDelegate.h"
#include "AAX_CLinearTaperDelegate.h"
#include "AAX_CNumberDisplayDelegate.h"
#include "AAX_CUnitDisplayDelegateDecorator.h"
#include "../hosting/hostclasses.h"
#include "pluginterfaces/base/funknownimpl.h"
#include "pluginterfaces/base/futils.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstchannelcontextinfo.h"
using namespace Steinberg;
using namespace Steinberg::Vst;
using namespace Steinberg::Base::Thread;
#define USE_TRACE 1
#if USE_TRACE
#define HAPI AAX_eTracePriorityHost_Normal
#define HLOG AAX_TRACE
#else
#define HAPI 0
#if SMTG_OS_WINDOWS
#define HLOG __noop
#else
#define HLOG(...) \
do \
{ \
} while (false)
#endif
#endif
SMTG_EXPORT_SYMBOL IPluginFactory* PLUGIN_API GetPluginFactory ();
const char* kBypassId = "Byp";
ParameterInfo kParamInfoBypass = {CCONST ('B', 'y', 'p', 0),
STR ("Bypass"),
STR ("Bypass"),
STR (""),
1,
0,
-1,
Vst::ParameterInfo::kCanAutomate | Vst::ParameterInfo::kIsBypass};
//------------------------------------------------------------------------
// AAXWrapper_Parameters
//------------------------------------------------------------------------
AAXWrapper_Parameters::AAXWrapper_Parameters (int32_t plugIndex)
: AAX_CEffectParameters (), mSimulateBypass (false)
{
HLOG (HAPI, "%s", __FUNCTION__);
AAX_Effect_Desc* effDesc = AAXWrapper_GetDescription ();
mPluginDesc = effDesc->mPluginDesc + plugIndex;
mWrapper = AAXWrapper::create (GetPluginFactory (), effDesc->mVST3PluginID, mPluginDesc, this);
if (!mWrapper)
return;
#if DEVELOPMENT
static bool writePagetableFile;
if (writePagetableFile) // use debugger to set variable or jump into function
mWrapper->generatePageTables ("c:/tmp/pagetable.xml");
#endif
// if no VST3 Bypass found then simulate it
mSimulateBypass = (mWrapper->mBypassParameterID == Vst::kNoParamId);
mWrapper->mSimulateBypass = mSimulateBypass;
if (mParamNames.size () < (size_t)mWrapper->mNumParams)
{
mParamNames.resize (mWrapper->mNumParams);
for (size_t i = 0; i < mParamNames.size (); i++)
mParamNames[i].set (mWrapper->mParameterMap[i].vst3ID);
}
}
//------------------------------------------------------------------------
AAXWrapper_Parameters::~AAXWrapper_Parameters ()
{
delete mWrapper;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::EffectInit ()
{
HLOG (HAPI, "%s", __FUNCTION__);
if (AAX_IController* ctrl = Controller ())
{
AAX_CSampleRate sampleRate;
if (ctrl->GetSampleRate (&sampleRate) == AAX_SUCCESS)
mWrapper->_setSampleRate (sampleRate);
if (mWrapper->mProcessor)
ctrl->SetSignalLatency (
static_cast<int32> (mWrapper->mProcessor->getLatencySamples ()));
}
for (uint32 i = 0; i < static_cast<uint32> (mWrapper->mNumParams); i++)
{
AAX_CParamID iParameterID = mParamNames[i];
ParameterInfo paramInfo = {};
if (AAX_Result result = mWrapper->getParameterInfo (iParameterID, paramInfo))
return result;
String title = paramInfo.title;
AAX_IParameter* param = nullptr;
param = NEW AAX_CParameter<double> (
iParameterID, AAX_CString (title), paramInfo.defaultNormalizedValue,
AAX_CLinearTaperDelegate<double> (0, 1),
AAX_CUnitDisplayDelegateDecorator<double> (AAX_CNumberDisplayDelegate<double> (),
AAX_CString (title)),
true);
mParameterManager.AddParameter (param);
}
if (mSimulateBypass)
{
AAX_IParameter* param = NEW AAX_CParameter<bool> (
kBypassId, AAX_CString ("Bypass"), false, AAX_CBinaryTaperDelegate<bool> (),
AAX_CBinaryDisplayDelegate<bool> ("off", "on"), true);
mParameterManager.AddParameter (param);
}
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::ResetFieldData (AAX_CFieldIndex index, void* inData,
uint32_t inDataSize) const
{
HLOG (HAPI, "%s", __FUNCTION__);
return mWrapper->ResetFieldData (index, inData, inDataSize);
}
//------------------------------------------------------------------------
// METHOD: AAX_UpdateMIDINodes
// This will be called by the host if there are MIDI packets that need
// to be handled in the Data Model.
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::UpdateMIDINodes (AAX_CFieldIndex inFieldIndex,
AAX_CMidiPacket& inPacket)
{
HLOG (HAPI, "%s", __FUNCTION__);
AAX_Result result;
result = AAX_SUCCESS;
inFieldIndex;
inPacket;
// Do some MIDI work if necessary.
return result;
}
//------------------------------------------------------------------------
int32 AAXWrapper_Parameters::getParameterInfo (AAX_CParamID aaxId,
Vst::ParameterInfo& paramInfo) const
{
AAX_Result result = mWrapper->getParameterInfo (aaxId, paramInfo);
if (result != AAX_SUCCESS)
{
if (mSimulateBypass && strcmp (aaxId, kBypassId) == 0)
{
paramInfo = kParamInfoBypass;
result = AAX_SUCCESS;
}
}
return result;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetNumberOfParameters (int32_t* oNumControls) const
{
HLOG (HAPI, "%s", __FUNCTION__);
*oNumControls = mWrapper->mNumParams;
if (mSimulateBypass)
*oNumControls += 1;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetMasterBypassParameter (AAX_IString* oIDString) const
{
HLOG (HAPI, "%s", __FUNCTION__);
*oIDString = mSimulateBypass ? kBypassId : AAX_CID (mWrapper->mBypassParameterID);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterIsAutomatable (AAX_CParamID iParameterID,
AAX_CBoolean* oAutomatable) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
*oAutomatable = (paramInfo.flags & ParameterInfo::kCanAutomate) != 0;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterNumberOfSteps (AAX_CParamID iParameterID,
int32_t* oNumSteps) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
if (paramInfo.stepCount == 0)
*oNumSteps = 1024;
else
*oNumSteps = paramInfo.stepCount + 1;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterName (AAX_CParamID iParameterID,
AAX_IString* oName) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
*oName = String (paramInfo.title);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterNameOfLength (AAX_CParamID iParameterID,
AAX_IString* oName,
int32_t iNameLength) const
{
HLOG (HAPI, "%s(id=%s, len=%d)", __FUNCTION__, iParameterID, iNameLength);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
if (iNameLength >= tstrlen (paramInfo.title))
*oName = String (paramInfo.title);
else
{
if (iNameLength < tstrlen (paramInfo.shortTitle))
paramInfo.shortTitle[iNameLength] = 0;
*oName = String (paramInfo.shortTitle);
}
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterDefaultNormalizedValue (AAX_CParamID iParameterID,
double* oValue) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
*oValue = paramInfo.defaultNormalizedValue;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::SetParameterDefaultNormalizedValue (AAX_CParamID iParameterID,
double iValue)
{
iParameterID;
iValue;
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
return AAX_ERROR_UNIMPLEMENTED;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterType (AAX_CParamID iParameterID,
AAX_EParameterType* oParameterType) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
*oParameterType =
paramInfo.stepCount == 0 ? AAX_eParameterType_Continuous : AAX_eParameterType_Discrete;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterOrientation (
AAX_CParamID iParameterID, AAX_EParameterOrientation* oParameterOrientation) const
{
iParameterID;
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
*oParameterOrientation = AAX_eParameterOrientation_BottomMinTopMax; // we don't care
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameter (AAX_CParamID iParameterID,
AAX_IParameter** /*oParameter*/)
{
iParameterID;
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
SMTG_ASSERT (!"the host is not supposed to retrieve the AAX_IParameter interface");
return AAX_ERROR_UNIMPLEMENTED;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterIndex (AAX_CParamID iParameterID,
int32_t* oControlIndex) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
*oControlIndex = mWrapper->mNumParams;
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
*oControlIndex = -1;
int32_t idx = 0;
for (auto& item : mParamNames)
{
if (strcmp (item, iParameterID) == 0)
{
*oControlIndex = idx;
return AAX_SUCCESS;
}
idx++;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterIDFromIndex (int32_t iControlIndex,
AAX_IString* oParameterIDString) const
{
HLOG (HAPI, "%s(idx=%x)", __FUNCTION__, iControlIndex);
if ((size_t)iControlIndex >= mWrapper->mParameterMap.size ())
{
if (mSimulateBypass && iControlIndex == mWrapper->mNumParams)
{
oParameterIDString->Set (kBypassId);
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_INDEX;
}
*oParameterIDString = mParamNames[iControlIndex];
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterValueInfo (AAX_CParamID iParameterID,
int32_t /*iSelector*/,
int32_t* oValue) const
{
iParameterID;
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
*oValue = 0;
return AAX_ERROR_UNIMPLEMENTED;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterValueFromString (
AAX_CParamID iParameterID, double* oValue, const AAX_IString& iValueString) const
{
HLOG (HAPI, "%s(id=%s, string=%s)", __FUNCTION__, iParameterID, iValueString.Get ());
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
*oValue = strcmp (iValueString.Get (), "on") == 0;
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
String tmp (iValueString.Get ());
if (mWrapper->mController->getParamValueByString (id, (Vst::TChar*)tmp.text16 (), *oValue) !=
kResultTrue)
return AAX_ERROR_INVALID_PARAMETER_ID;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterStringFromValue (AAX_CParamID iParameterID,
double iValue,
AAX_IString* oValueString,
int32_t maxLength) const
{
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
oValueString->Set (iValue >= 0.5 ? "on" : "off");
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
String128 tmp = {0};
if (mWrapper->mController->getParamStringByValue (id, iValue, tmp) != kResultTrue)
return AAX_ERROR_INVALID_PARAMETER_ID;
if (maxLength < tstrlen (tmp))
tmp[maxLength] = 0;
*oValueString = String (tmp);
// String str (tmp);
// str.copyTo8 (text, 0, kVstMaxParamStrLen);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterValueString (AAX_CParamID iParameterID,
AAX_IString* oValueString,
int32_t iMaxLength) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
double value;
if (AAX_Result result = GetParameterNormalizedValue (iParameterID, &value))
return result;
return GetParameterStringFromValue (iParameterID, value, oValueString, iMaxLength);
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterNormalizedValue (AAX_CParamID iParameterID,
double* oValuePtr) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
*oValuePtr = (mWrapper->mBypass ? 1 : 0);
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
ParamValue value = 0;
if (!mWrapper->getLastParamChange (id, value))
value = mWrapper->mController->getParamNormalized (id);
*oValuePtr = value;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::SetParameterNormalizedValue (AAX_CParamID iParameterID,
double iValue)
{
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
return AAX_SUCCESS;
return AAX_ERROR_INVALID_PARAMETER_ID;
}
// mWrapper->addParameterChange (id, iValue, 0);
if (auto ad = AutomationDelegate ())
{
// Touch the control, Send that token, Release the control
ad->PostTouchRequest (iParameterID);
ad->PostSetValueRequest (iParameterID, iValue);
ad->PostReleaseRequest (iParameterID);
}
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::SetParameterNormalizedRelative (AAX_CParamID iParameterID,
double iValue)
{
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
mWrapper->mBypass = (mWrapper->mBypass + iValue >= 0.5);
mWrapper->_setBypass (mWrapper->mBypass);
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
ParamValue value = 0;
if (!mWrapper->getLastParamChange (id, value))
value = mWrapper->mController->getParamNormalized (id);
value = value + iValue;
if (value < 0)
value = 0;
else if (value > 1)
value = 1;
SetParameterNormalizedValue (iParameterID, value);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::TouchParameter (AAX_CParamID iParameterID)
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
if (auto ad = AutomationDelegate ())
return ad->PostTouchRequest (iParameterID);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::ReleaseParameter (AAX_CParamID iParameterID)
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
if (auto ad = AutomationDelegate ())
return ad->PostReleaseRequest (iParameterID);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::UpdateParameterTouch (AAX_CParamID iParameterID,
AAX_CBoolean /*iTouchState*/)
{
iParameterID;
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::UpdateParameterNormalizedValue (AAX_CParamID iParameterID,
double iValue,
AAX_EUpdateSource iSource)
{
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
mWrapper->mBypass = iValue >= 0.5;
mWrapper->_setBypass (mWrapper->mBypass);
}
else
return AAX_ERROR_INVALID_PARAMETER_ID;
}
else
mWrapper->addParameterChange (id, iValue, 0);
#if 1
return AAX_CEffectParameters::UpdateParameterNormalizedValue (iParameterID, iValue, iSource);
#else
if (AutomationDelegate ())
AutomationDelegate ()->PostCurrentValue (iParameterID, iValue);
// if (AAX_Result result = SetParameterNormalizedValue (iParameterID, iValue))
// return result;
// Now the control has changed
AAX_Result result = mPacketDispatcher.SetDirty (iParameterID);
++mNumPlugInChanges;
return result;
#endif
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::_UpdateParameterNormalizedRelative (AAX_CParamID iParameterID,
double iValue)
{
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
if (AAX_Result result = SetParameterNormalizedRelative (iParameterID, iValue))
return result;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::_GenerateCoefficients ()
{
HLOG (HAPI, "%s", __FUNCTION__);
AAX_Result result = mPacketDispatcher.Dispatch ();
return result;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetNumberOfChunks (int32_t* numChunks) const
{
HLOG (HAPI, "%s", __FUNCTION__);
*numChunks = 1;
return AAX_SUCCESS;
}
const AAX_CTypeID AAXWRAPPER_CONTROLS_CHUNK_ID = CCONST ('a', 'w', 'c', 'k');
const char AAXWRAPPER_CONTROLS_CHUNK_DESCRIPTION[] = "AAXWrapper State";
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetChunkIDFromIndex (int32_t index, AAX_CTypeID* chunkID) const
{
HLOG (HAPI, "%s", __FUNCTION__);
if (index != 0)
return AAX_ERROR_INVALID_CHUNK_INDEX;
*chunkID = AAXWRAPPER_CONTROLS_CHUNK_ID;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetChunkSize (AAX_CTypeID chunkID, uint32_t* oSize) const
{
HLOG (HAPI, "%s", __FUNCTION__);
if (chunkID != AAXWRAPPER_CONTROLS_CHUNK_ID)
return AAX_ERROR_INVALID_CHUNK_ID;
FGuard guard (mWrapper->mSyncCalls);
bool isPreset = false;
void* data;
*oSize = static_cast<uint32> (mWrapper->_getChunk (&data, isPreset));
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetChunk (AAX_CTypeID chunkID, AAX_SPlugInChunk* oChunk) const
{
HLOG (HAPI, "%s", __FUNCTION__);
if (chunkID != AAXWRAPPER_CONTROLS_CHUNK_ID)
return AAX_ERROR_INVALID_CHUNK_ID;
FGuard guard (mWrapper->mSyncCalls);
// assume GetChunkSize called before and size of oChunk correct
oChunk->fVersion = 1;
oChunk->fSize = static_cast<int32_t> (mWrapper->mChunk.getSize ());
memcpy (oChunk->fData, mWrapper->mChunk.getData (),
static_cast<size_t> (mWrapper->mChunk.getSize ()));
strncpy (reinterpret_cast<char*> (oChunk->fName), AAXWRAPPER_CONTROLS_CHUNK_DESCRIPTION, 31);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::SetChunk (AAX_CTypeID chunkID, const AAX_SPlugInChunk* iChunk)
{
HLOG (HAPI, "%s", __FUNCTION__);
if (chunkID != AAXWRAPPER_CONTROLS_CHUNK_ID)
return AAX_ERROR_INVALID_CHUNK_ID;
FGuard guard (mWrapper->mSyncCalls);
bool isPreset = mPresetOpened;
mWrapper->_setChunk (const_cast<char*> (iChunk->fData), iChunk->fSize, isPreset);
mPresetOpened = false;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::CompareActiveChunk (const AAX_SPlugInChunk* /*iChunk*/,
AAX_CBoolean* /*oIsEqual*/) const
{
HLOG (HAPI, "%s", __FUNCTION__);
return AAX_ERROR_UNIMPLEMENTED;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::_GetNumberOfChanges (int32_t* oValue) const
{
HLOG (HAPI, "%s", __FUNCTION__);
*oValue = mNumPlugInChanges;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
void AAXWrapper_Parameters::setDirty (bool state)
{
if (state)
mNumPlugInChanges++;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::NotificationReceived (AAX_CTypeID iNotificationType,
const void* iNotificationData,
uint32_t iNotificationDataSize)
{
switch (iNotificationType)
{
//--- Tell the plug-in about connection of the sidechain input
case AAX_eNotificationEvent_SideChainBeingConnected:
mWrapper->setSideChainEnable (true);
break;
//--- Tell the plug-in about disconnection of the sidechain
case AAX_eNotificationEvent_SideChainBeingDisconnected:
mWrapper->setSideChainEnable (false);
break;
//--- The host has changed its latency compensation for this plug-in instance.
case AAX_eNotificationEvent_SignalLatencyChanged:
{
int32_t outSample;
Controller ()->GetSignalLatency (&outSample);
if (mPluginDesc)
mPluginDesc->mLatency = static_cast<uint32> (outSample);
if (mWrapper->isActive ())
{
mWrapper->_suspend ();
mWrapper->_resume ();
}
break;
}
//--- Tell the plug-in that chunk data is coming from a TFX
case AAX_eNotificationEvent_PresetOpened:
{
// do not wanted to overwrite the bypass state when loading preset
double value;
if (GetParameterNormalizedValue (
mSimulateBypass ? kBypassId : AAX_CID (mWrapper->mBypassParameterID), &value) ==
AAX_SUCCESS)
mWrapper->mBypassBeforePresetChanged = (value >= 0.5);
mWrapper->mPresetChanged = true;
mPresetOpened = true;
break;
}
//--- Tell the plug-in that chunk data is coming from a PTX
case AAX_eNotificationEvent_SessionBeingOpened:
{
mPresetOpened = false;
break;
}
//--- Entering offline processing mode (i.e.offline bounce)
case AAX_eNotificationEvent_EnteringOfflineMode:
{
mWrapper->setRenderingOffline (true);
break;
}
//--- Exiting offline processing mode (i.e. offline bounce)
case AAX_eNotificationEvent_ExitingOfflineMode:
{
mWrapper->setRenderingOffline (false);
break;
}
//--- A string representing the path of the current session
case AAX_eNotificationEvent_SessionPathChanged:
{
AAX_CString str (*reinterpret_cast<const AAX_IString*> (iNotificationData));
mSessionPath = str.StdString ();
break;
}
//--- The current name of this plug-in instance's track
case AAX_eNotificationEvent_TrackNameChanged:
{
AAX_CString str (*reinterpret_cast<const AAX_IString*> (iNotificationData));
mChannelName = str.StdString ();
if (mWrapper->mController)
{
if (auto iChannelContextInfoListener =
U::cast<Vst::ChannelContext::IInfoListener> (mWrapper->mController))
{
auto list = Vst::HostAttributeList::make ();
String string;
string.fromUTF8 (mChannelName.data ());
list->setString (Vst::ChannelContext::kChannelNameKey, string);
list->setInt (Vst::ChannelContext::kChannelNameLengthKey, string.length ());
iChannelContextInfoListener->setChannelContextInfos (list);
}
}
break;
}
//--- The zero-indexed insert position of this plug-in instance within its track
case AAX_eNotificationEvent_InsertPositionChanged:
{
// auto tmp = *reinterpret_cast<const int32_t*> (iNotificationData);
break;
}
//--- Tell the plug-in the maximum allowed GUI dimensions
case AAX_eNotificationEvent_MaxViewSizeChanged:
{
// auto tmp = *reinterpret_cast<const AAX_Point*> (iNotificationData);
break;
}
}
return AAX_CEffectParameters::NotificationReceived (iNotificationType, iNotificationData,
iNotificationDataSize);
}
/// \endcond
@@ -0,0 +1,157 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_parameters.h
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#include "public.sdk/source/vst/basewrapper/basewrapper.h"
#include "AAX_CEffectParameters.h"
#include "AAX_Push8ByteStructAlignment.h"
class AAXWrapper;
struct AAX_Plugin_Desc;
//------------------------------------------------------------------------
// helper to convert to/from AAX/Vst IDs
struct AAX_CID
{
char str[10] {0};
AAX_CID () {}
AAX_CID (Steinberg::Vst::ParamID id) { set (id); }
void set (Steinberg::Vst::ParamID id) { snprintf (str, 10, "p%lX", static_cast<unsigned long> (id)); }
operator const char* () const { return str; }
};
Steinberg::Vst::ParamID getVstParamID (const char* aaxid);
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wignored-attributes"
#pragma clang diagnostic ignored "-Wincompatible-ms-struct"
#endif
//------------------------------------------------------------------------
class AAXWrapper_Parameters : public AAX_CEffectParameters
{
public:
// Constructor
AAXWrapper_Parameters (int32_t plugIndex);
~AAXWrapper_Parameters ();
static AAX_CEffectParameters* AAX_CALLBACK Create ();
// Overrides from AAX_CEffectParameters
AAX_Result EffectInit () SMTG_OVERRIDE;
AAX_Result ResetFieldData (AAX_CFieldIndex index, void* inData,
uint32_t inDataSize) const SMTG_OVERRIDE;
AAX_Result NotificationReceived (AAX_CTypeID iNotificationType, const void* iNotificationData,
uint32_t iNotificationDataSize) SMTG_OVERRIDE;
/* Parameter information */
AAX_Result GetNumberOfParameters (int32_t* oNumControls) const SMTG_OVERRIDE;
AAX_Result GetMasterBypassParameter (AAX_IString* oIDString) const SMTG_OVERRIDE;
AAX_Result GetParameterIsAutomatable (AAX_CParamID iParameterID,
AAX_CBoolean* oAutomatable) const SMTG_OVERRIDE;
AAX_Result GetParameterNumberOfSteps (AAX_CParamID iParameterID,
int32_t* oNumSteps) const SMTG_OVERRIDE;
AAX_Result GetParameterName (AAX_CParamID iParameterID, AAX_IString* oName) const SMTG_OVERRIDE;
AAX_Result GetParameterNameOfLength (AAX_CParamID iParameterID, AAX_IString* oName,
int32_t iNameLength) const SMTG_OVERRIDE;
AAX_Result GetParameterDefaultNormalizedValue (AAX_CParamID iParameterID,
double* oValue) const SMTG_OVERRIDE;
AAX_Result SetParameterDefaultNormalizedValue (AAX_CParamID iParameterID,
double iValue) SMTG_OVERRIDE;
AAX_Result GetParameterType (AAX_CParamID iParameterID,
AAX_EParameterType* oParameterType) const SMTG_OVERRIDE;
AAX_Result GetParameterOrientation (AAX_CParamID iParameterID,
AAX_EParameterOrientation* oParameterOrientation) const
SMTG_OVERRIDE;
AAX_Result GetParameter (AAX_CParamID iParameterID, AAX_IParameter** oParameter) SMTG_OVERRIDE;
AAX_Result GetParameterIndex (AAX_CParamID iParameterID,
int32_t* oControlIndex) const SMTG_OVERRIDE;
AAX_Result GetParameterIDFromIndex (int32_t iControlIndex,
AAX_IString* oParameterIDString) const SMTG_OVERRIDE;
AAX_Result GetParameterValueInfo (AAX_CParamID iParameterID, int32_t iSelector,
int32_t* oValue) const SMTG_OVERRIDE;
/** Parameter setters and getters */
AAX_Result GetParameterValueFromString (AAX_CParamID iParameterID, double* oValue,
const AAX_IString& iValueString) const SMTG_OVERRIDE;
AAX_Result GetParameterStringFromValue (AAX_CParamID iParameterID, double iValue,
AAX_IString* oValueString,
int32_t maxLength) const SMTG_OVERRIDE;
AAX_Result GetParameterValueString (AAX_CParamID iParameterID, AAX_IString* oValueString,
int32_t iMaxLength) const SMTG_OVERRIDE;
AAX_Result GetParameterNormalizedValue (AAX_CParamID iParameterID,
double* oValuePtr) const SMTG_OVERRIDE;
AAX_Result SetParameterNormalizedValue (AAX_CParamID iParameterID, double iValue) SMTG_OVERRIDE;
AAX_Result SetParameterNormalizedRelative (AAX_CParamID iParameterID,
double iValue) SMTG_OVERRIDE;
/* Automated parameter helpers */
AAX_Result TouchParameter (AAX_CParamID iParameterID) SMTG_OVERRIDE;
AAX_Result ReleaseParameter (AAX_CParamID iParameterID) SMTG_OVERRIDE;
AAX_Result UpdateParameterTouch (AAX_CParamID iParameterID,
AAX_CBoolean iTouchState) SMTG_OVERRIDE;
/* Asynchronous parameter update methods */
AAX_Result UpdateParameterNormalizedValue (AAX_CParamID iParameterID, double iValue,
AAX_EUpdateSource iSource) SMTG_OVERRIDE;
AAX_Result _UpdateParameterNormalizedRelative (AAX_CParamID iParameterID, double iValue);
AAX_Result _GenerateCoefficients ();
/* Chunk methods */
AAX_Result GetNumberOfChunks (int32_t* numChunks) const SMTG_OVERRIDE;
AAX_Result GetChunkIDFromIndex (int32_t index, AAX_CTypeID* chunkID) const SMTG_OVERRIDE;
AAX_Result GetChunkSize (AAX_CTypeID chunkID, uint32_t* oSize) const SMTG_OVERRIDE;
AAX_Result GetChunk (AAX_CTypeID chunkID, AAX_SPlugInChunk* oChunk) const SMTG_OVERRIDE;
AAX_Result SetChunk (AAX_CTypeID chunkID, const AAX_SPlugInChunk* iChunk) SMTG_OVERRIDE;
AAX_Result CompareActiveChunk (const AAX_SPlugInChunk* iChunk,
AAX_CBoolean* oIsEqual) const SMTG_OVERRIDE;
AAX_Result _GetNumberOfChanges (int32_t* oValue) const;
// Override this method to receive MIDI
// packets for the described MIDI nodes
AAX_Result UpdateMIDINodes (AAX_CFieldIndex inFieldIndex,
AAX_CMidiPacket& inPacket) SMTG_OVERRIDE;
AAXWrapper* getWrapper () { return mWrapper; }
void setDirty (bool state);
private:
Steinberg::int32 getParameterInfo (AAX_CParamID aaxId,
Steinberg::Vst::ParameterInfo& paramInfo) const;
AAXWrapper* mWrapper = nullptr;
std::vector<AAX_CID> mParamNames;
AAX_Plugin_Desc* mPluginDesc = nullptr;
std::string mChannelName;
std::string mSessionPath;
bool mSimulateBypass = false;
bool mPresetOpened = false;
};
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#include "AAX_PopStructAlignment.h"
/// \endcond
Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

@@ -0,0 +1,30 @@
set OutDir=%1
if not exist %OutDir%\..\..\Contents mkdir %OutDir%\..\..\Contents
if errorlevel 1 goto err
if not exist %OutDir%\..\..\Contents\Resources mkdir %OutDir%\..\..\Contents\Resources
if errorlevel 1 goto err
echo Copy "aaxwrapperPages.xml"
copy /Y ..\resource\aaxwrapperPages.xml %OutDir%\..\..\Contents\Resources\ > NUL
if errorlevel 1 goto err
attrib -r %OutDir%\..\..
if exist %OutDir%\..\..\PlugIn.ico goto PlugIn_ico_exists
copy /Y ..\resource\PlugIn.ico %OutDir%\..\..\ > NUL
if errorlevel 1 goto err
attrib +h +r +s %OutDir%\..\..\PlugIn.ico
if errorlevel 1 goto err
:PlugIn_ico_exists
if exist %OutDir%\..\..\desktop.ini goto desktop_ini_exists
copy /Y ..\resource\desktop.ini %OutDir%\..\..\ > NUL
if errorlevel 1 goto err
attrib +h +r +s %OutDir%\..\..\desktop.ini
if errorlevel 1 goto err
:desktop_ini_exists
attrib +r %OutDir%\..\..
:err
@@ -0,0 +1,102 @@
// Microsoft Visual C++ generated resource script.
//
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"DemoMIDIResource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 7,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "AAXWrapper Plug-In"
VALUE "FileVersion", "1.0.0.0"
VALUE "InternalName", "AAXWrapper.aaxplugin"
VALUE "LegalCopyright", "(c) Steinberg Media Technologies 2020"
VALUE "OriginalFilename", "AAXWrapper.aaxplugin"
VALUE "ProductName", "AAX Wrapper"
VALUE "ProductVersion", "0.1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
@@ -0,0 +1,135 @@
<?xml version='1.0' encoding='US-ASCII' standalone='yes'?>
<PageTables vers='6.4.0.89'>
<PageTableLayouts>
<Plugin manID='AVID' prodID='DmGn' plugID='DGDR'>
<Desc>DemoGain 1 -&gt; 1 by Avid Inc.</Desc>
<Layout>PageTable 1</Layout>
</Plugin><!--manID='AVID' prodID='DmGn' plugID='DGDR'-->
<PTLayout name='PageTable 1'>
<PageTable type='PgTL' pgsz='1'>
<Page num='1'>
<ID>MasterBypass</ID>
</Page><!--num='1'-->
<Page num='2'>
<ID>Gain</ID>
</Page><!--num='2'-->
</PageTable><!--type='PgTL' pgsz='1'-->
<PageTable type='MkTL' pgsz='8'>
<Page num='1'>
<ID></ID>
<ID>Gain</ID>
<ID>MasterBypass</ID>
</Page><!--num='1'-->
<FirstPg cat='0'>1</FirstPg>
<FirstPg cat='1'>1</FirstPg>
<FirstPg cat='2'>1</FirstPg>
<FirstPg cat='4'>1</FirstPg>
<FirstPg cat='8'>1</FirstPg>
<FirstPg cat='16'>1</FirstPg>
<FirstPg cat='32'>1</FirstPg>
<FirstPg cat='64'>1</FirstPg>
<FirstPg cat='128'>1</FirstPg>
<FirstPg cat='256'>1</FirstPg>
<FirstPg cat='512'>1</FirstPg>
<FirstPg cat='1024'>1</FirstPg>
<FirstPg cat='2048'>1</FirstPg>
</PageTable><!--type='MkTL' pgsz='8'-->
<PageTable type='PcTL' pgsz='16'>
<Page num='1'>
<ID>MasterBypass</ID>
<ID>Gain </ID>
</Page><!--num='1'-->
<FirstPg cat='0'>1</FirstPg>
<FirstPg cat='1'>1</FirstPg>
<FirstPg cat='2'>1</FirstPg>
<FirstPg cat='4'>1</FirstPg>
<FirstPg cat='8'>1</FirstPg>
<FirstPg cat='16'>1</FirstPg>
<FirstPg cat='32'>1</FirstPg>
<FirstPg cat='64'>1</FirstPg>
<FirstPg cat='128'>1</FirstPg>
<FirstPg cat='256'>1</FirstPg>
<FirstPg cat='512'>1</FirstPg>
<FirstPg cat='1024'>1</FirstPg>
<FirstPg cat='2048'>1</FirstPg>
</PageTable><!--type='PcTL' pgsz='16'-->
<PageTable type='FrTL' pgsz='24'>
<Page num='1'>
<ID>MasterBypass</ID>
<ID>Gain</ID>
</Page><!--num='1'-->
<FirstPg cat='0'>1</FirstPg>
<FirstPg cat='1'>1</FirstPg>
<FirstPg cat='2'>1</FirstPg>
<FirstPg cat='4'>1</FirstPg>
<FirstPg cat='8'>1</FirstPg>
<FirstPg cat='16'>1</FirstPg>
<FirstPg cat='32'>1</FirstPg>
<FirstPg cat='64'>1</FirstPg>
<FirstPg cat='128'>1</FirstPg>
<FirstPg cat='256'>1</FirstPg>
<FirstPg cat='512'>1</FirstPg>
<FirstPg cat='1024'>1</FirstPg>
<FirstPg cat='2048'>1</FirstPg>
</PageTable><!--type='FrTL' pgsz='24'-->
<PageTable type='HgTL' pgsz='8'>
<Page num='1'>
<ID></ID>
<ID>Gain</ID>
<ID>MasterBypass</ID>
</Page><!--num='1'-->
<FirstPg cat='0'>1</FirstPg>
<FirstPg cat='1'>1</FirstPg>
<FirstPg cat='2'>1</FirstPg>
<FirstPg cat='4'>1</FirstPg>
<FirstPg cat='8'>1</FirstPg>
<FirstPg cat='16'>1</FirstPg>
<FirstPg cat='32'>1</FirstPg>
<FirstPg cat='64'>1</FirstPg>
<FirstPg cat='128'>1</FirstPg>
<FirstPg cat='256'>1</FirstPg>
<FirstPg cat='512'>1</FirstPg>
<FirstPg cat='1024'>1</FirstPg>
<FirstPg cat='2048'>1</FirstPg>
</PageTable><!--type='HgTL' pgsz='8'-->
<PageTable type='BkCS' pgsz='12'>
<Page num='1'>
<ID>MasterBypass</ID>
<ID>Gain</ID>
</Page><!--num='1'-->
</PageTable><!--type='BkCS' pgsz='12'-->
<PageTable type='BkSF' pgsz='16'>
<Page num='1'>
<ID>MasterBypass</ID>
<ID>Gain</ID>
</Page><!--num='1'-->
</PageTable><!--type='BkSF' pgsz='16'-->
</PTLayout><!--name='PageTable 1'-->
</PageTableLayouts>
<ControlNamesVariations>
<Ctrl ID='Gain'>
<name typ='PgTL' sz='1'>Ga</name>
<name typ='PgTL' sz='3'>Gn </name>
</Ctrl><!--ID='Gain'-->
<Ctrl ID='MasterBypass'>
<name typ='PgTL' sz='1'>Ma</name>
<name typ='PgTL' sz='3'>Byp</name>
<name typ='PgTL' sz='4'>MByp</name>
<name typ='PgTL' sz='8'>Mstr Byp</name>
</Ctrl><!--ID='MasterBypass'-->
</ControlNamesVariations>
<Editor vers='1.1.0.1'>
<PluginList>
<TDM>
</TDM>
<RTAS>
<PluginID manID='AVID' prodID='DmGn' plugID='DGDR'>
<MenuStr>RTAS: DemoGain, 1 in X 1 out</MenuStr>
</PluginID><!--manID='AVID' prodID='DmGn' plugID='DGDR'-->
</RTAS>
</PluginList>
<DiscCtrls>
<CtrlID>MasterBypass</CtrlID>
</DiscCtrls>
</Editor><!--vers='1.1.0.1'-->
</PageTables><!--vers='6.4.0.89'-->
@@ -0,0 +1,5 @@
[.ShellClassInfo]
IconResource=PlugIn.ico,0
;For compatibility with Windows XP
IconFile=PlugIn.ico
IconIndex=0
@@ -0,0 +1,21 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename :
// Created by : Steinberg, 03/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import <Cocoa/Cocoa.h>
int main (int argc, const char* argv[])
{
return NSApplicationMain (argc, argv);
}
@@ -0,0 +1,75 @@
include(SMTG_AddVST3AuV3)
# iOS target
if(SMTG_MAC)
if(XCODE)
set(auv3wrapperlib_sources
${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3Wrapper.mm
${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3Wrapper.h
${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3AudioEngine.mm
${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3AudioEngine.h
${SDK_ROOT}/public.sdk/source/vst/auwrapper/NSDataIBStream.mm
${SDK_ROOT}/public.sdk/source/vst/auwrapper/NSDataIBStream.h
${SDK_ROOT}/public.sdk/source/vst/utility/mpeprocessor.cpp
${SDK_ROOT}/public.sdk/source/vst/utility/mpeprocessor.h
)
# --------------------------------------------------------------------------------------------------------
set(target auv3_wrapper_macos)
add_library(${target}
STATIC
${auv3wrapperlib_sources}
)
set_target_properties(${target}
PROPERTIES
${SDK_IDE_LIBS_FOLDER} XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES
)
target_compile_features(${target}
PUBLIC
cxx_std_17
)
target_link_libraries(${target}
PRIVATE
sdk_hosting
)
if (SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY)
target_compile_definitions(${target}
PRIVATE
SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY=1)
endif()
# --------------------------------------------------------------------------------------------------------
if(SMTG_ENABLE_IOS_TARGETS)
set(target auv3_wrapper_ios)
add_library(${target}
STATIC
${auv3wrapperlib_sources}
)
set_target_properties(${target}
PROPERTIES
${SDK_IDE_LIBS_FOLDER} XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES
)
set_target_properties(${target}
PROPERTIES
LINK_FLAGS "-Wl,-F/Library/Frameworks"
)
smtg_target_set_platform_ios(${target})
target_compile_features(${target}
PUBLIC
cxx_std_17
)
target_link_libraries(${target}
PRIVATE
sdk_hosting_ios
)
if (SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY)
target_compile_definitions(${target}
PRIVATE
SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY=1
)
endif()
endif()
else()
message("* To enable building the AUv3 Wrapper example for iOS you need to set the SMTG_IOS_DEVELOPMENT_TEAM and use the Xcode generator")
endif()
endif()
@@ -0,0 +1,31 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : AUv3AudioEngine.h
// Created by : Steinberg, 07/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#import <AVFoundation/AVFoundation.h>
@interface AUv3AudioEngine : NSObject
@property (assign) AUAudioUnit* currentAudioUnit;
- (NSError*)loadAudioFile:(NSURL*)url;
- (instancetype)initWithComponentType:(uint32_t)unitComponentType;
- (void)loadAudioUnitWithComponentDescription:(AudioComponentDescription)desc
completion:(void (^) (void))completionBlock;
- (BOOL)startStop;
- (void)shutdown;
@end
@@ -0,0 +1,424 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : AUv3AudioEngine.mm
// Created by : Steinberg, 07/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "AUv3AudioEngine.h"
#import <CoreMIDI/CoreMIDI.h>
#import <functional>
#import <vector>
#import <utility>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
class MidiIO
{
public:
using ReadCallback = std::function<void (const MIDIPacketList* pktlist)>;
MidiIO (ReadCallback&& callback) : readCallback (std::move (callback)) { activate (); }
~MidiIO () = default;
private:
bool activate ();
bool deactivate ();
void onSourceAdded (MIDIObjectRef source);
void onSetupChanged ();
void disconnectSources ();
void onInput (const MIDIPacketList* pktlist);
static void readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon);
static void notifyProc (const MIDINotification* message, void* refCon);
MIDIClientRef client {0};
MIDIPortRef inputPort {0};
MIDIEndpointRef destPort {0};
ReadCallback readCallback;
using ConnectionList = std::vector<MIDIEndpointRef>;
ConnectionList connectedSources;
};
//------------------------------------------------------------------------
bool MidiIO::activate ()
{
if (client)
return true;
OSStatus err;
NSString* name = [[NSBundle mainBundle] bundleIdentifier];
if ((err = MIDIClientCreate ((__bridge CFStringRef)name, notifyProc, this, &client) != noErr))
return false;
if ((err = MIDIInputPortCreate (client, CFSTR ("Input"), readProc, this, &inputPort) != noErr))
{
MIDIClientDispose (client);
client = 0;
return false;
}
name = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleName"];
if ((err = MIDIDestinationCreate (client, (__bridge CFStringRef)name, readProc, this,
&destPort) != noErr))
{
MIDIPortDispose (inputPort);
inputPort = 0;
MIDIClientDispose (client);
client = 0;
return false;
}
onSetupChanged ();
return true;
}
//------------------------------------------------------------------------
bool MidiIO::deactivate ()
{
if (client == 0)
return true;
disconnectSources ();
auto status = MIDIEndpointDispose (destPort);
destPort = 0;
status |= MIDIPortDispose (inputPort);
inputPort = 0;
status |= MIDIClientDispose (client);
client = 0;
return status == noErr;
}
//------------------------------------------------------------------------
void MidiIO::onSourceAdded (MIDIObjectRef source)
{
connectedSources.push_back ((MIDIEndpointRef)source);
MIDIPortConnectSource (inputPort, (MIDIEndpointRef)source, NULL);
}
//------------------------------------------------------------------------
void MidiIO::onSetupChanged ()
{
disconnectSources ();
ItemCount numSources = MIDIGetNumberOfSources ();
for (ItemCount i = 0; i < numSources; i++)
{
onSourceAdded (MIDIGetSource (i));
}
}
//------------------------------------------------------------------------
void MidiIO::disconnectSources ()
{
for (auto source : connectedSources)
MIDIPortDisconnectSource (inputPort, source);
connectedSources.clear ();
}
//------------------------------------------------------------------------
void MidiIO::onInput (const MIDIPacketList* pktlist)
{
if (readCallback)
readCallback (pktlist);
}
//------------------------------------------------------------------------
void MidiIO::readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon)
{
MidiIO* io = static_cast<MidiIO*> (readProcRefCon);
io->onInput (pktlist);
}
//------------------------------------------------------------------------
void MidiIO::notifyProc (const MIDINotification* message, void* refCon)
{
if (message->messageID == kMIDIMsgSetupChanged)
{
MidiIO* mio = (MidiIO*)refCon;
mio->onSetupChanged ();
}
}
using MidiIOPtr = std::unique_ptr<MidiIO>;
//------------------------------------------------------------------------
} // Vst
} // Steinberg
//------------------------------------------------------------------------
@implementation AUv3AudioEngine
{
AVAudioEngine* audioEngine;
AVAudioFile* audioFile;
AVAudioPlayerNode* playerNode;
AVAudioUnit* avAudioUnit;
Steinberg::Vst::MidiIOPtr midi;
UInt32 componentType;
BOOL playing;
BOOL isDone;
}
//------------------------------------------------------------------------
- (instancetype)initWithComponentType:(uint32_t)unitComponentType
{
self = [super init];
isDone = false;
if (self)
{
audioEngine = [[AVAudioEngine alloc] init];
componentType = unitComponentType;
}
#if TARGET_OS_IPHONE
NSError* error = nil;
BOOL success =
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
if (NO == success)
{
NSLog (@"Error setting category: %@", [error localizedDescription]);
}
#endif
playing = false;
return self;
}
//------------------------------------------------------------------------
- (void)shutdown
{
if (playing)
[self stopPlaying];
audioEngine = nil;
midi.reset ();
}
//------------------------------------------------------------------------
- (void)onAudioUnitInstantiated:(AVAudioUnit* __nullable)audioUnit
error:(NSError* __nullable)error
completion:(void (^) (void))completionBlock
{
if (audioUnit == nil)
return;
avAudioUnit = audioUnit;
_currentAudioUnit = avAudioUnit.AUAudioUnit;
[audioEngine attachNode:avAudioUnit];
[audioEngine connect:avAudioUnit to:audioEngine.outputNode format:audioFile.processingFormat];
completionBlock ();
}
//------------------------------------------------------------------------
- (void)loadAudioUnitWithComponentDescription:(AudioComponentDescription)desc
completion:(void (^) (void))completionBlock
{
[AVAudioUnit instantiateWithComponentDescription:desc
options:0
completionHandler:^(AVAudioUnit* __nullable audioUnit,
NSError* __nullable error) {
[self onAudioUnitInstantiated:audioUnit
error:error
completion:completionBlock];
}];
if (componentType == kAudioUnitType_MusicDevice)
{
midi = Steinberg::Vst::MidiIOPtr (
new Steinberg::Vst::MidiIO ([=] (const MIDIPacketList* pktlist) {
[self scheduleMIDIPackets:pktlist];
}));
}
}
//------------------------------------------------------------------------
- (NSError*)loadAudioFile:(NSURL*)url
{
BOOL isPlaying = playing;
if (isPlaying)
[self startStop];
if (playerNode)
{
[playerNode stop];
[audioEngine detachNode:playerNode];
}
NSError* error = nil;
audioFile = [[AVAudioFile alloc] initForReading:url error:&error];
if (error)
return error;
[audioEngine detachNode:avAudioUnit];
[audioEngine attachNode:avAudioUnit];
[audioEngine connect:avAudioUnit to:audioEngine.outputNode format:audioFile.processingFormat];
playerNode = [[AVAudioPlayerNode alloc] init];
[audioEngine attachNode:playerNode];
[audioEngine connect:playerNode to:avAudioUnit format:audioFile.processingFormat];
if (isPlaying)
[self startStop];
return nil;
}
//------------------------------------------------------------------------
- (BOOL)startStop
{
playing = !playing;
playing ? ([self startPlaying]) : ([self stopPlaying]);
return playing;
}
//------------------------------------------------------------------------
- (void)startPlaying
{
[self activateSession:true];
NSError* error = nil;
if (![audioEngine startAndReturnError:&error])
{
NSLog (@"engine failed to start: %@", error);
return;
}
if (playerNode)
{
[self loopAudioFile];
[playerNode play];
}
}
//------------------------------------------------------------------------
- (void)stopPlaying
{
if (playerNode)
[playerNode stop];
[audioEngine stop];
[self activateSession:false];
}
//------------------------------------------------------------------------
- (void)loopAudioFile
{
if (playerNode)
{
[playerNode scheduleFile:audioFile
atTime:nil
completionHandler:^{
if (playerNode.playing)
[self loopAudioFile];
}];
}
}
//------------------------------------------------------------------------
- (void)scheduleMIDIPackets:(const MIDIPacketList*) pktlist
{
if (!_currentAudioUnit || _currentAudioUnit.scheduleMIDIEventBlock == nil)
return;
auto packet = &pktlist->packet[0];
for (auto i = 0u; i < pktlist->numPackets; i++)
{
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, packet->length,
packet->data);
packet = MIDIPacketNext (packet);
}
}
//------------------------------------------------------------------------
- (void)loopMIDIsequence
{
UInt8 cbytes[3], *cbytesPtr;
cbytesPtr = cbytes;
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
cbytesPtr[0] = 0xB0;
cbytesPtr[1] = 123;
cbytesPtr[2] = 0;
if (_currentAudioUnit.scheduleMIDIEventBlock == nil)
return;
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3, cbytesPtr);
usleep (useconds_t (0.1 * 1e6));
float releaseTime = 0.05;
usleep (useconds_t (0.1 * 1e6));
int i = 0;
@synchronized (self)
{
while (playing)
{
if (releaseTime < 10.0)
releaseTime = (releaseTime * 1.05) > 10.0 ? (releaseTime * 1.05) : 10.0;
cbytesPtr[0] = 0x90;
cbytesPtr[1] = UInt8 (60 + i);
cbytesPtr[2] = UInt8 (64); // note on
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3,
cbytesPtr);
usleep (useconds_t (0.2 * 1e6));
cbytesPtr[0] = 0x80;
cbytesPtr[1] = UInt8 (60 + i);
cbytesPtr[2] = UInt8 (0); // note off
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3,
cbytesPtr);
i += 2;
if (i >= 24)
{
i = -12;
}
}
cbytesPtr[0] = 0xB0;
cbytesPtr[1] = 123;
cbytesPtr[2] = 0;
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3, cbytesPtr);
isDone = true;
}
});
}
//------------------------------------------------------------------------
- (void)activateSession:(BOOL)active
{
#if TARGET_OS_IPHONE
NSError* error = nil;
BOOL success = [[AVAudioSession sharedInstance] setActive:active error:nil];
if (NO == success)
{
NSLog (@"Error setting category: %@", [error localizedDescription]);
}
#endif
}
@end
@@ -0,0 +1,45 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : AUv3Wrapper.h
// Created by : Steinberg, 07/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#import <CoreAudioKit/AUViewController.h>
@class AUv3Wrapper;
//------------------------------------------------------------------------
@interface AUv3WrapperViewController : AUViewController
@property (nonatomic, strong) AUv3Wrapper* audioUnit;
@end
//------------------------------------------------------------------------
@interface AUv3Wrapper : AUAudioUnit
- (void)beginEdit:(int32_t)tag;
- (void)endEdit:(int32_t)tag;
- (void)performEdit:(int32_t)tag value:(double)value;
- (void)syncParameterValues;
- (void)updateParameters;
- (void)onTimer;
- (void)onParamTitlesChanged;
- (void)onNoteExpressionChanged;
- (void)onLatencyChanged;
- (BOOL)enableMPESupport:(BOOL)state;
- (BOOL)setMPEInputDeviceMasterChannel:(NSInteger)masterChannel
memberBeginChannel:(NSInteger)memberBeginChannel
memberEndChannel:(NSInteger)memberEndChannel;
@end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,23 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename :
// Created by : Steinberg, 07/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#import "AUv3Wrapper.h"
@interface AUv3WrapperViewController (AUAudioUnitFactory) <AUAudioUnitFactory>
@end
@@ -0,0 +1,41 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename :
// Created by : Steinberg, 07/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "AUv3WrapperFactory.h"
@implementation AUv3WrapperViewController (AUAudioUnitFactory)
- (AUv3Wrapper *) createAudioUnitWithComponentDescription:(AudioComponentDescription) desc error:(NSError **)error {
@synchronized (self)
{
if (!self.audioUnit)
{
if (![NSThread isMainThread])
{
dispatch_sync(dispatch_get_main_queue(), [&]{
self.audioUnit = [[AUv3Wrapper alloc] initWithComponentDescription:desc error:error];
});
}
else
{
self.audioUnit = [[AUv3Wrapper alloc] initWithComponentDescription:desc error:error];
}
}
}
return self.audioUnit;
}
@end
@@ -0,0 +1,95 @@
if(SMTG_MAC)
if (XCODE AND SMTG_ENABLE_AUV2_BUILDS)
option(SMTG_AUWRAPPER_ACTIVATE_ONLY_DEFAULT_ACTIVE_BUSES
"Activate only the buses that have the kDefaultActive flag set in the AUWrapper. This may not work on some hosts because they never activate a bus later."
OFF
)
string(RANDOM LENGTH 20 CocoaId)
file(CONFIGURE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/au/aucocoaclassprefix.h"
CONTENT "#define SMTG_AUCocoaUIBase_CLASS_NAME SMTG_AUCocoaUIBase_${CocoaId}"
)
set(target au_wrapper)
set(${target}_sources
aucarbonview.mm
aucarbonview.h
aucocoaview.mm
aucocoaview.h
auwrapper.mm
auwrapper.h
NSDataIBStream.mm
NSDataIBStream.h
)
add_library(${target}
STATIC
${${target}_sources}
)
smtg_target_setup_universal_binary(${target})
set_target_properties(${target}
PROPERTIES
${SDK_IDE_LIBS_FOLDER}
)
target_compile_features(${target}
PUBLIC
cxx_std_17
)
if(SMTG_AUWRAPPER_ACTIVATE_ONLY_DEFAULT_ACTIVE_BUSES)
target_compile_definitions(${target}
PRIVATE
SMTG_AUWRAPPER_ACTIVATE_ONLY_DEFAULT_ACTIVE_BUSES
)
endif()
target_link_libraries(${target}
PRIVATE
sdk_hosting
"-framework AudioUnit" "-framework CoreMIDI"
"-framework AudioToolbox"
"-framework CoreFoundation"
"-framework Carbon"
"-framework Cocoa"
"-framework CoreAudio"
)
target_include_directories(${target}
PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}/au/"
)
if(NOT ${SMTG_COREAUDIO_SDK_PATH} STREQUAL "")
target_sources(${target} PRIVATE
ausdk.mm
)
target_include_directories(${target}
PRIVATE
"${SMTG_COREAUDIO_SDK_PATH}/**"
)
elseif(NOT ${SMTG_AUDIOUNIT_SDK_PATH} STREQUAL "")
target_compile_definitions(${target}
PRIVATE
SMTG_AUWRAPPER_USES_AUSDK
)
## Adding the xcodeproj will crash Xcode when closing and reopening the cmake generated project
# target_sources(${target} PRIVATE
# "${SMTG_AUDIOUNIT_SDK_PATH}/AudioUnitSDK.xcodeproj"
# )
target_include_directories(${target}
PRIVATE
"${SMTG_AUDIOUNIT_SDK_PATH}/include/**"
)
target_link_libraries(${target}
PRIVATE
AudioUnitSDK
)
else()
message(${SMTG_AUDIOUNIT_SDK_PATH})
message(FATAL_ERROR "The option SMTG_ENABLE_AUV2_BUILDS is set but the audio unit SDK paths are not set")
endif()
else()
message("[SMTG] * To enable building the AudioUnit wrapper, you need to use the Xcode generator and set SMTG_COREAUDIO_SDK_PATH to the path of your installation of the CoreAudio SDK!")
endif(XCODE AND SMTG_ENABLE_AUV2_BUILDS)
endif(SMTG_MAC)
@@ -0,0 +1,71 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/NSDataIBStream.h
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#import <Foundation/Foundation.h>
#import "pluginterfaces/base/ibstream.h"
#import "public.sdk/source/vst/hosting/hostclasses.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
class NSDataIBStream : public IBStream, Vst::IStreamAttributes
{
public:
NSDataIBStream (NSData* data, bool hideAttributes = false);
virtual ~NSDataIBStream ();
//---from IBStream-------------------
tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead = 0) SMTG_OVERRIDE;
tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = 0) SMTG_OVERRIDE;
tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result = 0) SMTG_OVERRIDE;
tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE;
//---from Vst::IStreamAttributes-----
tresult PLUGIN_API getFileName (String128 name) SMTG_OVERRIDE;
IAttributeList* PLUGIN_API getAttributes () SMTG_OVERRIDE;
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
NSData* data;
int64 currentPos;
IPtr<IAttributeList> attrList;
bool hideAttributes;
};
//------------------------------------------------------------------------
class NSMutableDataIBStream : public NSDataIBStream
{
public:
NSMutableDataIBStream (NSMutableData* data);
virtual ~NSMutableDataIBStream ();
tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = 0) SMTG_OVERRIDE;
//------------------------------------------------------------------------
protected:
NSMutableData* mdata;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,185 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/NSDataIBStream.mm
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#include "NSDataIBStream.h"
#include "pluginterfaces/vst/ivstattributes.h"
#include <algorithm>
#if __clang__
#if __has_feature(objc_arc) && __clang_major__ >= 3
#define ARC_ENABLED 1
#endif // __has_feature(objc_arc)
#endif // __clang__
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
NSDataIBStream::NSDataIBStream (NSData* data, bool hideAttributes)
: data (data)
, currentPos (0)
, hideAttributes (hideAttributes)
{
FUNKNOWN_CTOR
if (!hideAttributes)
attrList = HostAttributeList::make ();
#if !ARC_ENABLED
[data retain];
#endif
}
//------------------------------------------------------------------------
NSDataIBStream::~NSDataIBStream ()
{
#if !ARC_ENABLED
[data release];
#endif
FUNKNOWN_DTOR
}
//------------------------------------------------------------------------
IMPLEMENT_REFCOUNT (NSDataIBStream)
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::queryInterface (const TUID iid, void** obj)
{
QUERY_INTERFACE (iid, obj, FUnknown::iid, IBStream)
QUERY_INTERFACE (iid, obj, IBStream::iid, IBStream)
if (!hideAttributes)
QUERY_INTERFACE (iid, obj, IStreamAttributes::iid, IStreamAttributes)
*obj = 0;
return kNoInterface;
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::read (void* buffer, int32 numBytes, int32* numBytesRead)
{
int32 useBytes = std::min (numBytes, (int32)([data length] - currentPos));
if (useBytes > 0)
{
[data getBytes: buffer range: NSMakeRange (currentPos, useBytes)];
if (numBytesRead)
*numBytesRead = useBytes;
currentPos += useBytes;
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
{
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::seek (int64 pos, int32 mode, int64* result)
{
switch (mode)
{
case kIBSeekSet:
{
if (pos <= [data length])
{
currentPos = pos;
if (result)
tell (result);
return kResultTrue;
}
break;
}
case kIBSeekCur:
{
if (currentPos + pos <= [data length])
{
currentPos += pos;
if (result)
tell (result);
return kResultTrue;
}
break;
}
case kIBSeekEnd:
{
if ([data length] + pos <= [data length])
{
currentPos = [data length] + pos;
if (result)
tell (result);
return kResultTrue;
}
break;
}
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::tell (int64* pos)
{
if (pos)
{
*pos = currentPos;
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::getFileName (String128 name)
{
return kNotImplemented;
}
//------------------------------------------------------------------------
IAttributeList* PLUGIN_API NSDataIBStream::getAttributes ()
{
return attrList;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
NSMutableDataIBStream::NSMutableDataIBStream (NSMutableData* data)
: NSDataIBStream (data, true)
, mdata (data)
{
}
//------------------------------------------------------------------------
NSMutableDataIBStream::~NSMutableDataIBStream ()
{
[mdata setLength:currentPos];
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSMutableDataIBStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
{
[mdata replaceBytesInRange:NSMakeRange (currentPos, numBytes) withBytes:buffer];
if (numBytesWritten)
*numBytesWritten = numBytes;
currentPos += numBytes;
return kResultTrue;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,68 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/aucarbonview.h
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#include "pluginterfaces/base/fplatform.h"
#if !SMTG_PLATFORM_64
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include "AUPublic/AUCarbonViewBase/AUCarbonViewBase.h"
#pragma clang diagnostic pop
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "base/source/fobject.h"
#include "pluginterfaces/gui/iplugview.h"
namespace Steinberg {
namespace Vst {
class AUCarbonPlugFrame;
//------------------------------------------------------------------------
class AUCarbonView : public AUCarbonViewBase, public IPlugFrame, public FObject
{
public:
AUCarbonView (AudioUnitCarbonView auv);
~AUCarbonView ();
OSStatus CreateUI (Float32 xoffset, Float32 yoffset) override;
OBJ_METHODS(AUCarbonView, FObject)
DEF_INTERFACES_1(IPlugFrame, FObject)
REFCOUNT_METHODS(FObject)
protected:
tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* vr) SMTG_OVERRIDE;
static OSStatus HIViewAdded (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* inUserData);
IEditController* editController;
AUCarbonPlugFrame* plugFrame;
IPlugView* plugView;
HIViewRef hiPlugView;
EventHandlerRef eventHandler;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
#endif // !SMTG_PLATFORM_64
/// \endcond
@@ -0,0 +1,146 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/aucarbonview.mm
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#include "aucarbonview.h"
#if !SMTG_PLATFORM_64
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
AUCarbonView::AUCarbonView (AudioUnitCarbonView auv)
: AUCarbonViewBase (auv)
, editController (0)
, plugView (0)
, hiPlugView (0)
{
}
//------------------------------------------------------------------------
AUCarbonView::~AUCarbonView ()
{
if (plugView)
{
plugView->setFrame (0);
plugView->removed ();
plugView->release ();
}
}
//------------------------------------------------------------------------
OSStatus AUCarbonView::HIViewAdded (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
{
UInt32 eventClass = GetEventClass (inEvent);
UInt32 eventKind = GetEventKind (inEvent);
if (eventClass == kEventClassControl && eventKind == kEventControlAddedSubControl)
{
HIViewRef newControl;
if (GetEventParameter (inEvent, kEventParamControlSubControl, typeControlRef, NULL, sizeof (HIViewRef) , NULL , &newControl) == noErr)
{
AUCarbonView* wrapper = (AUCarbonView*)inUserData;
wrapper->hiPlugView = newControl;
RemoveEventHandler (wrapper->eventHandler);
wrapper->eventHandler = 0;
}
}
return eventNotHandledErr;
}
//------------------------------------------------------------------------
OSStatus AUCarbonView::CreateUI (Float32 xoffset, Float32 yoffset)
{
AudioUnit unit = GetEditAudioUnit ();
if (unit)
{
if (!editController)
{
UInt32 size = sizeof (IEditController*);
if (AudioUnitGetProperty (unit, 64000, kAudioUnitScope_Global, 0, &editController, &size) != noErr)
return kAudioUnitErr_NoConnection;
}
if (editController)
{
plugView = editController->createView (ViewType::kEditor);
if (!plugView)
return kAudioUnitErr_NoConnection;
HIViewRef contentView;
const EventTypeSpec eventTypes[] = {
{ kEventClassControl, kEventControlAddedSubControl },
};
OSStatus err = HIViewFindByID (HIViewGetRoot (GetCarbonWindow ()), kHIViewWindowContentID, &contentView);
err = InstallControlEventHandler (contentView, HIViewAdded, 1, eventTypes, this, &eventHandler);
plugView->setFrame (this);
if (plugView->attached (GetCarbonWindow (), kPlatformTypeHIView) == kResultTrue)
{
HIViewRemoveFromSuperview (hiPlugView);
EmbedControl (hiPlugView);
HIViewMoveBy (hiPlugView, xoffset, yoffset);
return noErr;
}
else
plugView->setFrame (0);
}
}
return kAudioUnitErr_NoConnection;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AUCarbonView::resizeView (IPlugView* view, ViewRect* vr)
{
if (vr == 0 || view != plugView)
return kInvalidArgument;
HIViewRef hiView = GetCarbonPane ();
if (hiView)
{
HIRect r;
if (HIViewGetFrame (hiView, &r) != noErr)
return kResultFalse;
r.size.width = vr->right - vr->left;
r.size.height = vr->bottom - vr->top;
if (HIViewSetFrame (hiView, &r) != noErr)
return kResultFalse;
if (plugView)
plugView->onSize (vr);
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
//COMPONENT_ENTRY(AUCarbonView)
//------------------------------------------------------------------------
extern "C" {
ComponentResult AUCarbonViewEntry(ComponentParameters *params, AUCarbonView *obj);
__attribute__ ((visibility ("default"))) ComponentResult AUCarbonViewEntry(ComponentParameters *params, AUCarbonView *obj)
{
return ComponentEntryPoint<AUCarbonView>::Dispatch(params, obj);
}
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
#endif // !SMTG_PLATFORM_64
/// \endcond
@@ -0,0 +1,31 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/aucocoaview.h
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#ifndef SMTG_AUCocoaUIBase_CLASS_NAME
#import "aucocoaclassprefix.h"
#endif
#import <Foundation/Foundation.h>
#import <AudioUnit/AUCocoaUIView.h>
//------------------------------------------------------------------------
@interface SMTG_AUCocoaUIBase_CLASS_NAME : NSObject<AUCocoaUIBase>
@end
/// \endcond
@@ -0,0 +1,275 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/aucocoaview.mm
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#import "aucocoaview.h"
#import "auwrapper.h"
#import "public.sdk/source/vst/utility/objcclassbuilder.h"
#import "pluginterfaces/base/funknownimpl.h"
#import "pluginterfaces/gui/iplugview.h"
#import "pluginterfaces/vst/ivsteditcontroller.h"
//------------------------------------------------------------------------
@interface NSObject (SMTG_AUView)
- (id)initWithEditController:(Steinberg::Vst::IEditController*)editController
audioUnit:(AudioUnit)au
preferredSize:(NSSize)size;
@end
//------------------------------------------------------------------------
namespace Steinberg {
namespace {
//------------------------------------------------------------------------
struct AUPlugFrame : U::Implements<U::Directly<IPlugFrame>>
{
AUPlugFrame (NSView* parent) : parent (parent) {}
tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* vr) override
{
NSRect newSize = NSMakeRect ([parent frame].origin.x, [parent frame].origin.y,
vr->right - vr->left, vr->bottom - vr->top);
[parent setFrame:newSize];
return kResultTrue;
}
NSView* parent;
};
//------------------------------------------------------------------------
struct AUView
{
static constexpr auto VarNamePlugView = "plugView";
static constexpr auto VarNameEditController = "editController";
static constexpr auto VarNameAudioUnit = "audioUnit";
static constexpr auto VarNameDynlib = "dynlib";
static constexpr auto VarNamePlugFrame = "plugFrame";
static constexpr auto VarNameIsAttached = "isAttached";
struct Instance : ObjCInstance
{
using PlugViewVar = std::optional<ObjCVariable<IPlugView*>>;
using EditControllerVar = std::optional<ObjCVariable<Vst::IEditController*>>;
using AudioUnitVar = std::optional<ObjCVariable<AudioUnit>>;
using DynlibVar = std::optional<ObjCVariable<FObject*>>;
using PlugFrameVar = std::optional<ObjCVariable<AUPlugFrame*>>;
using IsAttachedVar = std::optional<ObjCVariable<BOOL>>;
Instance (__unsafe_unretained id obj) : ObjCInstance (obj, [NSView class])
{
plugView = getVariable<IPlugView*> (VarNamePlugView);
editController = getVariable<Vst::IEditController*> (VarNameEditController);
audioUnit = getVariable<AudioUnit> (VarNamePlugView);
dynlib = getVariable<FObject*> (VarNameDynlib);
plugFrame = getVariable<AUPlugFrame*> (VarNamePlugFrame);
isAttached = getVariable<BOOL> (VarNameIsAttached);
}
PlugViewVar plugView;
EditControllerVar editController;
AudioUnitVar audioUnit;
DynlibVar dynlib;
PlugFrameVar plugFrame;
IsAttachedVar isAttached;
};
static id alloc ()
{
static ObjCClass gInstance;
return [gInstance.cl alloc];
}
private:
struct ObjCClass
{
Class cl;
ObjCClass ()
{
cl = ObjCClassBuilder ()
.init ("SMTG_AUView", [NSView class])
.addIvar<IPlugView*> (VarNamePlugView)
.addIvar<Vst::IEditController*> (VarNameEditController)
.addIvar<AudioUnit> (VarNameAudioUnit)
.addIvar<FObject*> (VarNameDynlib)
.addIvar<AUPlugFrame*> (VarNamePlugFrame)
.addIvar<BOOL> (VarNameIsAttached)
.addMethod (@selector (initWithEditController:audioUnit:preferredSize:),
initWithEditController)
.addMethod (@selector (setFrame:), setFrame)
.addMethod (@selector (isFlipped), isFlipped)
.addMethod (@selector (viewDidMoveToSuperview), viewDidMoveToSuperview)
.addMethod (@selector (dealloc), dealloc)
.finalize ();
}
static id initWithEditController (id self, SEL cmd, Vst::IEditController* editController,
AudioUnit au, NSSize size)
{
ObjCInstance obj (self);
self = obj.callSuper<id (NSRect), id> (@selector (initWithFrame:),
NSMakeRect (0, 0, size.width, size.height));
if (self)
{
Instance inst (self);
inst.editController->set (editController);
editController->addRef ();
inst.audioUnit->set (au);
auto plugView = editController->createView (Vst::ViewType::kEditor);
if (!plugView ||
plugView->isPlatformTypeSupported (kPlatformTypeNSView) != kResultTrue)
{
[self dealloc];
return nil;
}
inst.plugView->set (plugView);
auto plugFrame = NEW AUPlugFrame (self);
inst.plugFrame->set (plugFrame);
plugView->setFrame (plugFrame);
if (plugView->attached (self, kPlatformTypeNSView) != kResultTrue)
{
[self dealloc];
return nil;
}
ViewRect vr;
if (plugView->getSize (&vr) == kResultTrue)
{
NSRect newSize = NSMakeRect (0, 0, vr.right - vr.left, vr.bottom - vr.top);
[self setFrame:newSize];
}
inst.isAttached->set (YES);
FObject* fObject = nullptr;
UInt32 size = sizeof (FObject*);
if (AudioUnitGetProperty (au, 64001, kAudioUnitScope_Global, 0, &fObject, &size) ==
noErr)
{
fObject->addRef ();
inst.dynlib->set (fObject);
}
}
return self;
}
static void setFrame (id self, SEL cmd, NSRect newSize)
{
Instance inst (self);
inst.callSuper<void (NSRect)> (@selector (setFrame:), newSize);
ViewRect viewRect (0, 0, newSize.size.width, newSize.size.height);
if (inst.plugView->get ())
inst.plugView->get ()->onSize (&viewRect);
}
static BOOL isFlipped (id self, SEL cmd) { return YES; }
static void viewDidMoveToSuperview (id self, SEL cmd)
{
Instance inst (self);
if (inst.plugView->get ())
{
if ([self superview])
{
if (!inst.isAttached->get ())
{
if (inst.plugView->get ()->attached (self, kPlatformTypeNSView) ==
kResultTrue)
{
inst.isAttached->set (YES);
}
}
}
else
{
if (inst.isAttached->get ())
{
inst.plugView->get ()->removed ();
inst.isAttached->set (NO);
}
}
}
}
static void dealloc (id self, SEL cmd)
{
Instance inst (self);
if (auto plugView = inst.plugView->get ())
{
if (inst.isAttached->get ())
{
plugView->setFrame (0);
plugView->removed ();
}
plugView->release ();
if (auto plugFrame = inst.plugFrame->get ())
plugFrame->release ();
if (auto editController = inst.editController->get ())
{
auto refCount = editController->addRef ();
if (refCount == 2)
editController->terminate ();
editController->release ();
editController->release ();
inst.editController->set (nullptr);
}
}
if (auto dynlib = inst.dynlib->get ())
dynlib->release ();
inst.callSuper<void ()> (@selector (dealloc));
}
};
};
//------------------------------------------------------------------------
} // anonymous
} // Steinberg
//------------------------------------------------------------------------
@implementation SMTG_AUCocoaUIBase_CLASS_NAME
//------------------------------------------------------------------------
- (unsigned)interfaceVersion
{
return 0;
}
//------------------------------------------------------------------------
- (NSString*)description
{
return @"Cocoa View";
}
//------------------------------------------------------------------------
- (NSView*)uiViewForAudioUnit:(AudioUnit)inAU withSize:(NSSize)inPreferredSize
{
using namespace Steinberg;
Vst::IEditController* editController = 0;
UInt32 size = sizeof (Vst::IEditController*);
if (AudioUnitGetProperty (inAU, 64000, kAudioUnitScope_Global, 0, &editController, &size) !=
noErr)
return nil;
return [[AUView::alloc () initWithEditController:editController
audioUnit:inAU
preferredSize:inPreferredSize] autorelease];
}
@end
/// \endcond
@@ -0,0 +1,65 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/auresource.r
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include <AudioUnit/AudioUnit.r>
#include <AudioUnit/AudioUnitCarbonView.r>
#include "audiounitconfig.h"
/* ----------------------------------------------------------------------------------------------------------------------------------------
// audiounitconfig.h needs the following definitions:
#define kAudioUnitVersion 0xFFFFFFFF // Version Number, needs to be in hex
#define kAudioUnitName "Steinberg: MyVST3 as AudioUnit" // Company Name + Effect Name
#define kAudioUnitDescription "My VST3 as AudioUnit" // Effect Description
#define kAudioUnitType kAudioUnitType_Effect // can be kAudioUnitType_Effect or kAudioUnitType_MusicDevice
#define kAudioUnitComponentSubType 'test' // unique id
#define kAudioUnitComponentManuf 'SMTG' // registered company id
#define kAudioUnitCarbonView 1 // if 0 no Carbon view support will be added
*/
#define kAudioUnitResID_Processor 1000
#define kAudioUnitResID_CarbonView 9000
//----------------------Processor----------------------------------------------
#define RES_ID kAudioUnitResID_Processor
#define COMP_TYPE kAudioUnitType
#define COMP_SUBTYPE kAudioUnitComponentSubType
#define COMP_MANUF kAudioUnitComponentManuf
#define VERSION kAudioUnitVersion
#define NAME kAudioUnitName
#define DESCRIPTION kAudioUnitDescription
#define ENTRY_POINT "AUWrapperEntry"
#include "AUResources.r"
#if kAudioUnitCarbonView
//----------------------View----------------------------------------------
#define RES_ID kAudioUnitResID_CarbonView
#define COMP_TYPE kAudioUnitCarbonViewComponentType
#define COMP_SUBTYPE kAudioUnitComponentSubType
#define COMP_MANUF kAudioUnitComponentManuf
#define VERSION kAudioUnitVersion
#define NAME "CarbonView"
#define DESCRIPTION "CarbonView"
#define ENTRY_POINT "AUCarbonViewEntry"
#include "AUResources.r"
#endif
@@ -0,0 +1,72 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/ausdk.mm
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#pragma clang diagnostic ignored "-Wunused-value"
#pragma clang diagnostic ignored "-Wparentheses"
#pragma clang diagnostic ignored "-Woverloaded-virtual"
#ifndef MAC_OS_X_VERSION_10_7
#define MAC_OS_X_VERSION_10_7 1070
#endif
#import "PublicUtility/CAAudioChannelLayout.cpp"
#import "PublicUtility/CABundleLocker.cpp"
#import "PublicUtility/CAHostTimeBase.cpp"
#import "PublicUtility/CAStreamBasicDescription.cpp"
#import "PublicUtility/CAVectorUnit.cpp"
#import "PublicUtility/CAAUParameter.cpp"
#import "AUPublic/AUBase/ComponentBase.cpp"
#import "AUPublic/AUBase/AUScopeElement.cpp"
#import "AUPublic/AUBase/AUOutputElement.cpp"
#import "AUPublic/AUBase/AUInputElement.cpp"
#import "AUPublic/AUBase/AUBase.cpp"
#if !__LP64__
#ifndef verify_noerr
#define verify_noerr(x) x
#endif
#ifndef verify
#define verify(x)
#endif
#import "AUPublic/AUCarbonViewBase/AUCarbonViewBase.cpp"
#import "AUPublic/AUCarbonViewBase/AUCarbonViewControl.cpp"
#import "AUPublic/AUCarbonViewBase/AUCarbonViewDispatch.cpp"
#import "AUPublic/AUCarbonViewBase/AUControlGroup.cpp"
#import "AUPublic/AUCarbonViewBase/CarbonEventHandler.cpp"
#endif
#import "AUPublic/Utility/AUTimestampGenerator.cpp"
#import "AUPublic/Utility/AUBuffer.cpp"
#import "AUPublic/Utility/AUBaseHelper.cpp"
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
#import "AUPublic/OtherBases/AUMIDIEffectBase.cpp"
#import "AUPublic/Utility/AUDebugDispatcher.cpp"
#else
#import "AUPublic/AUBase/AUPlugInDispatch.cpp"
#endif
#if !CA_USE_AUDIO_PLUGIN_ONLY
#import "AUPublic/AUBase/AUDispatch.cpp"
#import "AUPublic/OtherBases/MusicDeviceBase.cpp"
#import "AUPublic/OtherBases/AUMIDIBase.cpp"
#import "AUPublic/OtherBases/AUEffectBase.cpp"
#endif
/// \endcond
@@ -0,0 +1,287 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/auwrapper.h
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#ifdef SMTG_AUWRAPPER_USES_AUSDK
#if CA_USE_AUDIO_PLUGIN_ONLY
#include "AudioUnitSDK/AUBase.h"
#define AUWRAPPER_BASE_CLASS ausdk::AUBase
#else
#include "AudioUnitSDK/MusicDeviceBase.h"
#define AUWRAPPER_BASE_CLASS ausdk::MusicDeviceBase
#endif // CA_USE_AUDIO_PLUGIN_ONLY
#else
#if CA_USE_AUDIO_PLUGIN_ONLY
#include "AudioUnits/AUPublic/AUBase/AUBase.h"
#define AUWRAPPER_BASE_CLASS AUBase
#else
#include "AudioUnits/AUPublic/OtherBases/MusicDeviceBase.h"
#define AUWRAPPER_BASE_CLASS MusicDeviceBase
#endif // CA_USE_AUDIO_PLUGIN_ONLY
#endif // SMTG_AUWRAPPER_USES_AUSDK
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "public.sdk/source/vst/utility/ringbuffer.h"
#include "public.sdk/source/vst/utility/rttransfer.h"
#include "base/source/fstring.h"
#include "base/source/timer.h"
#include "base/thread/include/flock.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstmidilearn.h"
#include "pluginterfaces/vst/ivstprocesscontext.h"
#include "pluginterfaces/vst/ivstunits.h"
#include <AudioToolbox/AudioToolbox.h>
#include <Cocoa/Cocoa.h>
#include <array>
#include <map>
#include <unordered_map>
#include <vector>
namespace Steinberg {
class VST3DynLibrary;
namespace Vst {
//------------------------------------------------------------------------
//------------------------------------------------------------------------
class AUWrapper : public AUWRAPPER_BASE_CLASS, public IComponentHandler, public ITimerCallback
{
public:
#ifdef SMTG_AUWRAPPER_USES_AUSDK
using AUElement = ausdk::AUElement;
#else
using AudioStreamBasicDescription = CAStreamBasicDescription;
#endif
AUWrapper (ComponentInstanceRecord* ci);
~AUWrapper ();
//---ComponentBase---------------------
#if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK)
ComponentResult Version () SMTG_OVERRIDE;
#endif
void PostConstructor () SMTG_OVERRIDE;
//---AUBase-----------------------------
void Cleanup () SMTG_OVERRIDE;
ComponentResult Initialize () SMTG_OVERRIDE;
#ifdef SMTG_AUWRAPPER_USES_AUSDK
std::unique_ptr<AUElement> CreateElement (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE;
#else
AUElement* CreateElement (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE;
#endif
UInt32 SupportedNumChannels (const AUChannelInfo** outInfo) SMTG_OVERRIDE;
bool StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE;
ComponentResult ChangeStreamFormat (AudioUnitScope inScope, AudioUnitElement inElement, const AudioStreamBasicDescription& inPrevFormat, const AudioStreamBasicDescription& inNewFormat) SMTG_OVERRIDE;
ComponentResult SetConnection (const AudioUnitConnection& inConnection) SMTG_OVERRIDE;
ComponentResult GetParameterInfo (AudioUnitScope inScope, AudioUnitParameterID inParameterID, AudioUnitParameterInfo& outParameterInfo) SMTG_OVERRIDE;
ComponentResult SetParameter (AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 inBufferOffsetInFrames) SMTG_OVERRIDE;
ComponentResult SaveState (CFPropertyListRef* outData) SMTG_OVERRIDE;
ComponentResult RestoreState (CFPropertyListRef inData) SMTG_OVERRIDE;
ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags, const AudioTimeStamp &inTimeStamp, UInt32 inNumberFrames) SMTG_OVERRIDE;
void processOutputEvents (const AudioTimeStamp &inTimeStamp);
#if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK)
int GetNumCustomUIComponents () SMTG_OVERRIDE;
void GetUIComponentDescs (ComponentDescription* inDescArray) SMTG_OVERRIDE;
#endif
#ifdef SMTG_AUWRAPPER_USES_AUSDK
OSStatus GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32 &outDataSize, bool &outWritable) SMTG_OVERRIDE;
#else
OSStatus GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32 &outDataSize, Boolean &outWritable) SMTG_OVERRIDE;
#endif
ComponentResult GetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData) SMTG_OVERRIDE;
ComponentResult SetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize) SMTG_OVERRIDE;
bool CanScheduleParameters() const SMTG_OVERRIDE;
Float64 GetLatency () SMTG_OVERRIDE;
Float64 GetTailTime () SMTG_OVERRIDE;
//---Factory presets
OSStatus GetPresets (CFArrayRef* outData) const SMTG_OVERRIDE;
OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset) SMTG_OVERRIDE;
#if !CA_USE_AUDIO_PLUGIN_ONLY
//---MusicDeviceBase-------------------------
OSStatus HandleNoteOn (UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame) SMTG_OVERRIDE;
OSStatus HandleNoteOff (UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame) SMTG_OVERRIDE;
ComponentResult StartNote (MusicDeviceInstrumentID inInstrument, MusicDeviceGroupID inGroupID, NoteInstanceID* outNoteInstanceID, UInt32 inOffsetSampleFrame, const MusicDeviceNoteParams &inParams) SMTG_OVERRIDE;
ComponentResult StopNote (MusicDeviceGroupID inGroupID, NoteInstanceID inNoteInstanceID, UInt32 inOffsetSampleFrame) SMTG_OVERRIDE;
OSStatus GetInstrumentCount (UInt32 &outInstCount) const SMTG_OVERRIDE;
//---AUMIDIBase------------------------------
OSStatus HandleNonNoteEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame) SMTG_OVERRIDE;
#endif
#if AUSDK_MIDI2_AVAILABLE
OSStatus MIDIEventList (UInt32 inOffsetSampleFrame,
const struct MIDIEventList* eventList) override;
bool handleMIDIEventPacket (UInt32 inOffsetSampleFrame, const MIDIEventPacket* packet);
#endif
//---custom----------------------------------
void setControllerParameter (ParamID pid, ParamValue value);
// return for a given midiChannel the unitID and the ProgramListID
bool getProgramListAndUnit (int32 midiChannel, UnitID& unitId, ProgramListID& programListId);
// restore preset state, add StateType "Project" to stream if loading from project
ComponentResult restoreState (CFPropertyListRef inData, bool fromProject);
//------------------------------------------------------------------------
#if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK)
static ComponentResult ComponentEntryDispatch (ComponentParameters* params, AUWrapper* This);
#endif
//------------------------------------------------------------------------
static CFBundleRef gBundleRef;
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
//---from IComponentHandler-------------------
tresult PLUGIN_API beginEdit (ParamID tag) SMTG_OVERRIDE;
tresult PLUGIN_API performEdit (ParamID tag, ParamValue valueNormalized) SMTG_OVERRIDE;
tresult PLUGIN_API endEdit (ParamID tag) SMTG_OVERRIDE;
tresult PLUGIN_API restartComponent (int32 flags) SMTG_OVERRIDE;
//---from ITimerCallback----------------------
void onTimer (Timer* timer) SMTG_OVERRIDE;
// internal helpers
double getSampleRate () const { return sampleRate; }
void updateProcessContext ();
void syncParameterValues ();
void cacheParameterValues ();
void clearParameterValueCache ();
void updateProgramChangesCache ();
virtual IPluginFactory* getFactory ();
void loadVST3Module ();
void unloadVST3Module ();
bool validateChannelPair (int inChannelsIn, int inChannelsOut, const AUChannelInfo* info,
UInt32 numChanInfo) const;
IAudioProcessor* audioProcessor;
IEditController* editController;
Timer* timer;
HostProcessData processData;
ParameterChanges processParamChanges;
ParameterChanges outputParamChanges;
ParameterChangeTransfer transferParamChanges;
ParameterChangeTransfer outputParamTransfer;
ProcessContext processContext;
EventList eventList;
typedef std::map<ParamID, AudioUnitParameterInfo> CachedParameterInfoMap;
typedef std::map<UnitID, UnitInfo> UnitInfoMap;
typedef std::vector<String> ClumpGroupVector;
UnitInfoMap unitInfos;
ClumpGroupVector clumpGroups;
CachedParameterInfoMap cachedParameterInfos;
Steinberg::Base::Thread::FLock parameterCacheChanging;
NoteInstanceID noteCounter;
double sampleRate;
ParamID bypassParamID;
AUPreset* presets;
int32 numPresets;
ParamID factoryProgramChangedID;
AUParameterListenerRef paramListenerRef;
std::vector<ParameterInfo> programParameters;
static constexpr int32 kMaxProgramChangeParameters = 16;
struct ProgramChangeInfo
{
ParamID pid {kNoParamId};
int32 numPrograms {0};
};
using ProgramChangeInfoList = std::array<ProgramChangeInfo, kMaxProgramChangeParameters>;
using ProgramChangeInfoTransfer = RTTransferT<ProgramChangeInfoList>;
ProgramChangeInfoList programChangeInfos;
ProgramChangeInfoTransfer programChangeInfoTransfer;
// midi mapping
struct MidiMapping
{
using CC2ParamMap = std::unordered_map<CtrlNumber, ParamID>;
using ChannelList = std::vector<CC2ParamMap>;
using BusList = std::vector<ChannelList>;
BusList busList;
bool empty () const { return busList.empty () || busList[0].empty (); }
};
using MidiMappingTransfer = RTTransferT<MidiMapping>;
MidiMappingTransfer midiMappingTransfer;
MidiMapping midiMappingCache;
struct MidiLearnEvent
{
int32 busIndex;
int16 channel;
CtrlNumber midiCC;
};
using MidiLearnRingBuffer = OneReaderOneWriter::RingBuffer<MidiLearnEvent>;
MidiLearnRingBuffer midiLearnRingBuffer;
IPtr<IMidiLearn> midiLearn;
struct MIDIOutputCallbackHelper;
int32 midiOutCount; // currently only 0 or 1 supported
std::unique_ptr<MIDIOutputCallbackHelper> mCallbackHelper;
EventList outputEvents;
bool isInstrument;
bool isBypassed;
bool isOfflineRender;
private:
void buildUnitInfos (IUnitInfo* unitInfoController, UnitInfoMap& units) const;
void updateMidiMappingCache ();
IPtr<VST3DynLibrary> dynLib;
};
//------------------------------------------------------------------------
class AutoreleasePool
{
public:
AutoreleasePool () { ap = [[NSAutoreleasePool alloc] init]; }
~AutoreleasePool () { [ap drain]; }
//------------------------------------------------------------------------
protected:
NSAutoreleasePool* ap;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
/// \endcond
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/auwrapper_prefix.pch
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include <CoreFoundation/CoreFoundation.h>
#include <CoreAudio/CoreAudio.h>
@@ -0,0 +1,17 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : ausdkpath.xcconfig
// Created by : Steinberg, 5/24/12
// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
// If you are building with Xcode >= 4.x please add the path to your downloaded Audio Tools for Xcode
CUSTOM_AU_SDK_PATH=/Applications/Xcode.app/Contents/Developer/Extras/CoreAudio/ // AUWRAPPER_CHANGE
@@ -0,0 +1,27 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : auwrapper.xcconfig
// Created by : Steinberg, 5/24/12
// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "../../../../../base/mac/config/libc++base"
#include "ausdkpath"
PRODUCT_NAME = auwrapper
HEADER_SEARCH_PATHS = ../../../.. $(DEVELOPER_DIR)/Examples/CoreAudio/** $(DEVELOPER_DIR)/Extras/CoreAudio/** $(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/AUViewBase/** ../../../../external.apple.coreaudio/**
GCC_PREFIX_HEADER = auwrapper_prefix.pch
GCC_PRECOMPILE_PREFIX_HEADER = YES
CLANG_CXX_LANGUAGE_STANDARD = c++17
CLANG_CXX_LIBRARY = libc++
@@ -0,0 +1,20 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : ausdkpath_debug.xcconfig
// Created by : Steinberg, 5/24/12
// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "../../../../../base/mac/config/debug"
GCC_OPTIMIZATION_LEVEL = 0
DEPLOYMENT_POSTPROCESSING = NO
@@ -0,0 +1,20 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : auwrapper_release.xcconfig
// Created by : Steinberg, 5/24/12
// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "../../../../../base/mac/config/release"
GCC_OPTIMIZATION_LEVEL = 3
DEPLOYMENT_POSTPROCESSING = NO
@@ -0,0 +1,55 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Validator
// Filename : usediids.cpp
// Created by : Steinberg 09.2008
// Description : Interface symbols file
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
//#define INIT_CLASS_IID
// This macro definition modifies the behavior of DECLARE_CLASS_IID (funknown.h)
// and produces the actual symbols for all interface identifiers.
// It must be defined before including the interface headers and
// in only one source file!
//------------------------------------------------------------------------
//#define INIT_CLASS_IID
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivsthostapplication.h"
#include "pluginterfaces/vst/ivstmidilearn.h"
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include "pluginterfaces/vst/ivstpluginterfacesupport.h"
#include "pluginterfaces/vst/ivstevents.h"
#include "pluginterfaces/vst/ivstunits.h"
namespace Steinberg {
DEF_CLASS_IID (Vst::IAttributeList)
DEF_CLASS_IID (Vst::IAudioProcessor)
DEF_CLASS_IID (Vst::IEditController)
DEF_CLASS_IID (Vst::IEditController2)
DEF_CLASS_IID (Vst::IComponent)
DEF_CLASS_IID (Vst::IComponentHandler)
DEF_CLASS_IID (Vst::IConnectionPoint)
DEF_CLASS_IID (Vst::IEventList)
DEF_CLASS_IID (Vst::IHostApplication)
DEF_CLASS_IID (Vst::IMessage)
DEF_CLASS_IID (Vst::IMidiLearn)
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::IStreamAttributes)
DEF_CLASS_IID (Vst::IVst3ToAUWrapper)
DEF_CLASS_IID (Vst::IUnitData)
DEF_CLASS_IID (Vst::IUnitInfo)
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,299 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/basewrapper/basewrapper.h
// Created by : Steinberg, 01/2018
// Description : VST 3 -> XXX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
/// \cond ignore
#include "pluginterfaces/base/ftypes.h"
#include "pluginterfaces/gui/iplugview.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivsthostapplication.h"
#include "pluginterfaces/vst/ivstprocesscontext.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "public.sdk/source/common/memorystream.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/pluginterfacesupport.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "base/source/fstring.h"
#include "base/source/timer.h"
#include <map>
#include <vector>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
class BaseEditorWrapper : public IPlugFrame,
public FObject
{
public:
//------------------------------------------------------------------------
BaseEditorWrapper (IEditController* controller);
~BaseEditorWrapper () override;
static bool hasEditor (IEditController* controller);
bool getRect (ViewRect& rect);
virtual bool _open (void* ptr);
virtual void _close ();
bool _setKnobMode (Vst::KnobMode val);
// IPlugFrame
tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) SMTG_OVERRIDE;
// FUnknown
tresult PLUGIN_API queryInterface (const char* _iid, void** obj) SMTG_OVERRIDE;
REFCOUNT_METHODS (FObject);
//------------------------------------------------------------------------
protected:
void createView ();
IPtr<IEditController> mController;
IPtr<IPlugView> mView;
ViewRect mViewRect;
};
//------------------------------------------------------------------------
const int32 kMaxEvents = 2048;
class ConnectionProxy;
//-------------------------------------------------------------------------------------------------------
class BaseWrapper : public IHostApplication,
public IComponentHandler,
public IUnitHandler,
public ITimerCallback,
public FObject
{
public:
struct SVST3Config
{
IPluginFactory* factory = nullptr;
IAudioProcessor* processor = nullptr;
IEditController* controller = nullptr;
FUID vst3ComponentID;
};
BaseWrapper (SVST3Config& config);
~BaseWrapper () override;
virtual bool init ();
virtual void _canDoubleReplacing (bool /*val*/) {}
virtual void _setInitialDelay (uint32 /*delay*/) {}
virtual void _noTail (bool /*val*/) {}
virtual void _ioChanged () {}
virtual void _updateDisplay () {}
virtual void _setNumInputs (uint32 inputs) { mNumInputs = inputs; }
virtual void _setNumOutputs (uint32 outputs) { mNumOutputs = outputs; }
virtual bool _sizeWindow (int32 width, int32 height) = 0;
virtual int32 _getChunk (void** data, bool isPreset);
virtual int32 _setChunk (void* data, int32 byteSize, bool isPreset);
virtual bool getEditorSize (int32& width, int32& height) const;
bool isActive () const { return mActive; }
uint32 getNumInputs () const { return mNumInputs; }
uint32 getNumOutputs () const { return mNumOutputs; }
BaseEditorWrapper* getEditor () const { return mEditor; }
//--- ---------------------------------------------------------------------
// VST 3 Interfaces ------------------------------------------------------
// FUnknown
tresult PLUGIN_API queryInterface (const char* iid, void** obj) SMTG_OVERRIDE;
REFCOUNT_METHODS (FObject);
// IHostApplication
tresult PLUGIN_API createInstance (TUID cid, TUID iid, void** obj) SMTG_OVERRIDE;
// IComponentHandler
tresult PLUGIN_API restartComponent (int32 flags) SMTG_OVERRIDE;
// IUnitHandler
tresult PLUGIN_API notifyUnitSelection (UnitID unitId) SMTG_OVERRIDE;
tresult PLUGIN_API notifyProgramListChange (ProgramListID listId,
int32 programIndex) SMTG_OVERRIDE;
// ITimer
void onTimer (Timer* timer) SMTG_OVERRIDE;
//-------------------------------------------------------------------------------------------------------
protected:
void term ();
virtual void setupParameters ();
virtual void setupProcessTimeInfo () = 0;
virtual void processOutputEvents () {}
virtual void processOutputParametersChanges () {}
void _setSampleRate (float newSamplerate);
bool setupProcessing (int32 processModeOverwrite = -1);
void _processReplacing (float** inputs, float** outputs, int32 sampleFrames);
void _processDoubleReplacing (double** inputs, double** outputs, int32 sampleFrames);
template <class T>
void setProcessingBuffers (T** inputs, T** outputs);
void doProcess (int32 sampleFrames);
void processMidiEvent (Event& toAdd, char* midiData, bool isLive = false, int32 noteLength = 0,
float noteOffVelocity = 1.f, float detune = 0.f);
void setEventPPQPositions ();
void _setEditor (BaseEditorWrapper* editor);
bool _setBlockSize (int32 newBlockSize);
float _getParameter (int32 index) const;
void _suspend ();
void _resume ();
void _startProcess ();
void _stopProcess ();
bool _setBypass (bool onOff);
virtual void setupBuses ();
void initMidiCtrlerAssignment ();
void getUnitPath (UnitID unitID, String& path) const;
uint32 countMainBusChannels (BusDirection dir, uint64& mainBusBitset);
/** Returns the last param change from guiTransfer queue. */
bool getLastParamChange (ParamID id, ParamValue& value);
void addParameterChange (ParamID id, ParamValue value, int32 sampleOffset);
void setVendorName (char* name);
void setEffectName (char* name);
void setEffectVersion (char* version);
void setSubCategories (char* string);
bool getProgramListAndUnit (int32 midiChannel, UnitID& unitId, ProgramListID& programListId);
bool getProgramListInfoByProgramListID (ProgramListID programListId, ProgramListInfo& info);
static const int32 kMaxProgramChangeParameters = 16;
ParamID mProgramChangeParameterIDs[kMaxProgramChangeParameters]; // for each MIDI channel
int32 mProgramChangeParameterIdxs[kMaxProgramChangeParameters]; // for each MIDI channel
FUID mVst3EffectClassID;
// vst3 data
IPtr<IAudioProcessor> mProcessor;
IPtr<IComponent> mComponent;
IPtr<IEditController> mController;
IPtr<IUnitInfo> mUnitInfo;
IPtr<IMidiMapping> mMidiMapping;
IPtr<BaseEditorWrapper> mEditor;
IPtr<PlugInterfaceSupport> mPlugInterfaceSupport;
IPtr<ConnectionProxy> mProcessorConnection;
IPtr<ConnectionProxy> mControllerConnection;
int32 mVst3SampleSize = kSample32;
int32 mVst3processMode = kRealtime;
char mName[PClassInfo::kNameSize];
char mVendor[PFactoryInfo::kNameSize];
char mSubCategories[PClassInfo2::kSubCategoriesSize];
int32 mVersion = 0;
struct ParamMapEntry
{
ParamID vst3ID;
int32 vst3Index;
};
std::vector<ParamMapEntry> mParameterMap;
std::map<ParamID, int32> mParamIndexMap;
ParamID mBypassParameterID = kNoParamId;
ParamID mProgramParameterID = kNoParamId;
int32 mProgramParameterIdx = -1;
HostProcessData mProcessData;
ProcessContext mProcessContext;
ParameterChanges mInputChanges;
ParameterChanges mOutputChanges;
IPtr<EventList> mInputEvents;
IPtr<EventList> mOutputEvents;
uint64 mMainAudioInputBuses = 0;
uint64 mMainAudioOutputBuses = 0;
ParameterChangeTransfer mInputTransfer;
ParameterChangeTransfer mOutputTransfer;
ParameterChangeTransfer mGuiTransfer;
MemoryStream mChunk;
IPtr<Timer> mTimer;
IPtr<IPluginFactory> mFactory;
int32 mNumPrograms {0};
float mSampleRate {44100};
int32 mBlockSize {256};
int32 mNumParams {0};
int32 mCurProgram {-1};
uint32 mNumInputs {0};
uint32 mNumOutputs {0};
enum
{
kMaxMidiMappingBusses = 4
};
ParamID* mMidiCCMapping[kMaxMidiMappingBusses][16];
bool mComponentInitialized = false;
bool mControllerInitialized = false;
bool mComponentsConnected = false;
bool mUseExportedBypass = true;
bool mActive = false;
bool mProcessing = false;
bool mHasEventInputBuses = false;
bool mHasEventOutputBuses = false;
bool mUseIncIndex = true;
};
const uint8 kNoteOff = 0x80; ///< note, off velocity
const uint8 kNoteOn = 0x90; ///< note, on velocity
const uint8 kPolyPressure = 0xA0; ///< note, pressure
const uint8 kController = 0xB0; ///< controller, value
const uint8 kProgramChangeStatus = 0xC0; ///< program change
const uint8 kAfterTouchStatus = 0xD0; ///< channel pressure
const uint8 kPitchBendStatus = 0xE0; ///< lsb, msb
const float kMidiScaler = 1.f / 127.f;
static const uint8 kChannelMask = 0x0F;
static const uint8 kStatusMask = 0xF0;
static const uint32 kDataMask = 0x7F;
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,26 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/basewrapper/basewrapper.sdk.cpp
// Created by : Steinberg, 05/2018
// Description : VST 3 -> XXX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/common/memorystream.cpp"
#include "public.sdk/source/vst/basewrapper/basewrapper.cpp"
#include "public.sdk/source/vst/hosting/connectionproxy.cpp"
#include "public.sdk/source/vst/hosting/eventlist.cpp"
#include "public.sdk/source/vst/hosting/hostclasses.cpp"
#include "public.sdk/source/vst/hosting/parameterchanges.cpp"
#include "public.sdk/source/vst/hosting/pluginterfacesupport.cpp"
#include "public.sdk/source/vst/hosting/processdata.cpp"
@@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/connectionproxy.cpp
// Created by : Steinberg, 04/2019
// Description : VST 3 Plug-in connection class
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "connectionproxy.h"
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (ConnectionProxy, IConnectionPoint, IConnectionPoint::iid)
//------------------------------------------------------------------------
ConnectionProxy::ConnectionProxy (IConnectionPoint* srcConnection)
: srcConnection (srcConnection) // share it
{
FUNKNOWN_CTOR
}
//------------------------------------------------------------------------
ConnectionProxy::~ConnectionProxy ()
{
FUNKNOWN_DTOR
}
//------------------------------------------------------------------------
tresult PLUGIN_API ConnectionProxy::connect (IConnectionPoint* other)
{
if (other == nullptr)
return kInvalidArgument;
if (dstConnection)
return kResultFalse;
dstConnection = other; // share it
tresult res = srcConnection->connect (this);
if (res != kResultTrue)
dstConnection = nullptr;
return res;
}
//------------------------------------------------------------------------
tresult PLUGIN_API ConnectionProxy::disconnect (IConnectionPoint* other)
{
if (!other)
return kInvalidArgument;
if (other == dstConnection)
{
if (srcConnection)
srcConnection->disconnect (this);
dstConnection = nullptr;
return kResultTrue;
}
return kInvalidArgument;
}
//------------------------------------------------------------------------
tresult PLUGIN_API ConnectionProxy::notify (IMessage* message)
{
if (dstConnection)
{
// We discard the message if we are not in the UI main thread
if (threadChecker && threadChecker->test ())
return dstConnection->notify (message);
}
return kResultFalse;
}
//------------------------------------------------------------------------
bool ConnectionProxy::disconnect ()
{
return disconnect (dstConnection) == kResultTrue;
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,51 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/connectionproxy.h
// Created by : Steinberg, 04/2020
// Description : VST 3 Plug-in connection class
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstmessage.h"
#include "public.sdk/source/common/threadchecker.h"
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Helper for creating and initializing component.
\ingroup Helper */
//------------------------------------------------------------------------
class ConnectionProxy : public IConnectionPoint
{
public:
ConnectionProxy (IConnectionPoint* srcConnection);
virtual ~ConnectionProxy ();
//--- from IConnectionPoint
tresult PLUGIN_API connect (IConnectionPoint* other) override;
tresult PLUGIN_API disconnect (IConnectionPoint* other) override;
tresult PLUGIN_API notify (IMessage* message) override;
bool disconnect ();
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
std::unique_ptr<ThreadChecker> threadChecker {ThreadChecker::create ()};
IPtr<IConnectionPoint> srcConnection;
IPtr<IConnectionPoint> dstConnection;
};
}
} // namespaces
@@ -0,0 +1,86 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/eventlist.cpp
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 event list implementation
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "eventlist.h"
namespace Steinberg {
namespace Vst {
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (EventList, IEventList, IEventList::iid)
//-----------------------------------------------------------------------------
EventList::EventList (int32 inMaxSize)
{
FUNKNOWN_CTOR
setMaxSize (inMaxSize);
}
//-----------------------------------------------------------------------------
EventList::~EventList ()
{
setMaxSize (0);
FUNKNOWN_DTOR
}
//-----------------------------------------------------------------------------
void EventList::setMaxSize (int32 newMaxSize)
{
if (events)
{
delete[] events;
events = nullptr;
fillCount = 0;
}
if (newMaxSize > 0)
events = new Event[newMaxSize];
maxSize = newMaxSize;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API EventList::getEvent (int32 index, Event& e)
{
if (auto event = getEventByIndex (index))
{
memcpy (&e, event, sizeof (Event));
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API EventList::addEvent (Event& e)
{
if (maxSize > fillCount)
{
memcpy (&events[fillCount], &e, sizeof (Event));
fillCount++;
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
Event* EventList::getEventByIndex (int32 index) const
{
if (index < fillCount)
return &events[index];
return nullptr;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,53 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/eventlist.h
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 event list implementation
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstevents.h"
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Example implementation of IEventList.
\ingroup sdkBase
*/
class EventList : public IEventList
{
public:
EventList (int32 maxSize = 50);
virtual ~EventList ();
int32 PLUGIN_API getEventCount () SMTG_OVERRIDE { return fillCount; }
tresult PLUGIN_API getEvent (int32 index, Event& e) SMTG_OVERRIDE;
tresult PLUGIN_API addEvent (Event& e) SMTG_OVERRIDE;
void setMaxSize (int32 maxSize);
void clear () { fillCount = 0; }
Event* getEventByIndex (int32 index) const;
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
Event* events {nullptr};
int32 maxSize {0};
int32 fillCount {0};
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,319 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/hostclasses.cpp
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 hostclasses, example impl. for IHostApplication, IAttributeList and IMessage
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "hostclasses.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include <algorithm>
namespace Steinberg {
namespace Vst {
//-----------------------------------------------------------------------------
HostApplication::HostApplication ()
{
FUNKNOWN_CTOR
mPlugInterfaceSupport = owned (new PlugInterfaceSupport);
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostApplication::getName (String128 name)
{
return StringConvert::convert ("My VST3 HostApplication", name) ? kResultTrue : kInternalError;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostApplication::createInstance (TUID cid, TUID _iid, void** obj)
{
if (FUnknownPrivate::iidEqual (cid, IMessage::iid) &&
FUnknownPrivate::iidEqual (_iid, IMessage::iid))
{
*obj = new HostMessage;
return kResultTrue;
}
if (FUnknownPrivate::iidEqual (cid, IAttributeList::iid) &&
FUnknownPrivate::iidEqual (_iid, IAttributeList::iid))
{
if (auto al = HostAttributeList::make ())
{
*obj = al.take ();
return kResultTrue;
}
return kOutOfMemory;
}
*obj = nullptr;
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostApplication::queryInterface (const char* _iid, void** obj)
{
QUERY_INTERFACE (_iid, obj, FUnknown::iid, IHostApplication)
QUERY_INTERFACE (_iid, obj, IHostApplication::iid, IHostApplication)
if (mPlugInterfaceSupport && mPlugInterfaceSupport->queryInterface (_iid, obj) == kResultTrue)
return kResultOk;
*obj = nullptr;
return kResultFalse;
}
//-----------------------------------------------------------------------------
uint32 PLUGIN_API HostApplication::addRef ()
{
return 1;
}
//-----------------------------------------------------------------------------
uint32 PLUGIN_API HostApplication::release ()
{
return 1;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (HostMessage, IMessage, IMessage::iid)
//-----------------------------------------------------------------------------
HostMessage::HostMessage () {FUNKNOWN_CTOR}
//-----------------------------------------------------------------------------
HostMessage::~HostMessage () noexcept
{
setMessageID (nullptr);
FUNKNOWN_DTOR
}
//-----------------------------------------------------------------------------
const char* PLUGIN_API HostMessage::getMessageID ()
{
return messageId;
}
//-----------------------------------------------------------------------------
void PLUGIN_API HostMessage::setMessageID (const char* mid)
{
if (messageId)
delete[] messageId;
messageId = nullptr;
if (mid)
{
size_t len = strlen (mid) + 1;
messageId = new char[len];
strcpy (messageId, mid);
}
}
//-----------------------------------------------------------------------------
IAttributeList* PLUGIN_API HostMessage::getAttributes ()
{
if (!attributeList)
attributeList = HostAttributeList::make ();
return attributeList;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
struct HostAttributeList::Attribute
{
enum class Type
{
kUninitialized,
kInteger,
kFloat,
kString,
kBinary
};
Attribute () = default;
Attribute (int64 value) : type (Type::kInteger) { v.intValue = value; }
Attribute (double value) : type (Type::kFloat) { v.floatValue = value; }
/* size is in code unit (count of TChar) */
Attribute (const TChar* value, uint32 sizeInCodeUnit)
: size (sizeInCodeUnit), type (Type::kString)
{
v.stringValue = new TChar[sizeInCodeUnit];
memcpy (v.stringValue, value, sizeInCodeUnit * sizeof (TChar));
}
Attribute (const void* value, uint32 sizeInBytes) : size (sizeInBytes), type (Type::kBinary)
{
v.binaryValue = new char[sizeInBytes];
memcpy (v.binaryValue, value, sizeInBytes);
}
Attribute (Attribute&& o) SMTG_NOEXCEPT { *this = std::move (o); }
Attribute& operator= (Attribute&& o) SMTG_NOEXCEPT
{
v = o.v;
size = o.size;
type = o.type;
o.size = 0;
o.type = Type::kUninitialized;
o.v = {};
return *this;
}
~Attribute () noexcept
{
if (size)
delete[] v.binaryValue;
}
int64 intValue () const { return v.intValue; }
double floatValue () const { return v.floatValue; }
/* sizeInCodeUnit is in code unit (count of TChar) */
const TChar* stringValue (uint32& sizeInCodeUnit)
{
sizeInCodeUnit = size;
return v.stringValue;
}
const void* binaryValue (uint32& sizeInBytes)
{
sizeInBytes = size;
return v.binaryValue;
}
Type getType () const { return type; }
private:
union v
{
int64 intValue;
double floatValue;
TChar* stringValue;
char* binaryValue;
} v {};
uint32 size {0};
Type type {Type::kUninitialized};
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (HostAttributeList, IAttributeList, IAttributeList::iid)
//-----------------------------------------------------------------------------
IPtr<IAttributeList> HostAttributeList::make ()
{
return owned (new HostAttributeList);
}
//-----------------------------------------------------------------------------
HostAttributeList::HostAttributeList () {FUNKNOWN_CTOR}
//-----------------------------------------------------------------------------
HostAttributeList::~HostAttributeList () noexcept {FUNKNOWN_DTOR}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setInt (AttrID aid, int64 value)
{
if (!aid)
return kInvalidArgument;
list[aid] = Attribute (value);
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getInt (AttrID aid, int64& value)
{
if (!aid)
return kInvalidArgument;
auto it = list.find (aid);
if (it != list.end () && it->second.getType () == Attribute::Type::kInteger)
{
value = it->second.intValue ();
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setFloat (AttrID aid, double value)
{
if (!aid)
return kInvalidArgument;
list[aid] = Attribute (value);
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getFloat (AttrID aid, double& value)
{
if (!aid)
return kInvalidArgument;
auto it = list.find (aid);
if (it != list.end () && it->second.getType () == Attribute::Type::kFloat)
{
value = it->second.floatValue ();
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setString (AttrID aid, const TChar* string)
{
if (!aid)
return kInvalidArgument;
// + 1 for the null-terminate
auto length = tstrlen (string) + 1;
list[aid] = Attribute (string, length);
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getString (AttrID aid, TChar* string, uint32 sizeInBytes)
{
if (!aid)
return kInvalidArgument;
auto it = list.find (aid);
if (it != list.end () && it->second.getType () == Attribute::Type::kString)
{
uint32 sizeInCodeUnit = 0;
const TChar* _string = it->second.stringValue (sizeInCodeUnit);
memcpy (string, _string, std::min<uint32> (sizeInCodeUnit * sizeof (TChar), sizeInBytes));
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setBinary (AttrID aid, const void* data, uint32 sizeInBytes)
{
if (!aid)
return kInvalidArgument;
list[aid] = Attribute (data, sizeInBytes);
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getBinary (AttrID aid, const void*& data, uint32& sizeInBytes)
{
if (!aid)
return kInvalidArgument;
auto it = list.find (aid);
if (it != list.end () && it->second.getType () == Attribute::Type::kBinary)
{
data = it->second.binaryValue (sizeInBytes);
return kResultTrue;
}
sizeInBytes = 0;
return kResultFalse;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,100 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/hostclasses.h
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 hostclasses, example impl. for IHostApplication, IAttributeList and IMessage
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "public.sdk/source/vst/hosting/pluginterfacesupport.h"
#include "pluginterfaces/vst/ivsthostapplication.h"
#include <map>
#include <memory>
#include <string>
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Implementation's example of IHostApplication.
\ingroup hostingBase
*/
class HostApplication : public IHostApplication
{
public:
HostApplication ();
virtual ~HostApplication () noexcept {FUNKNOWN_DTOR}
//--- IHostApplication ---------------
tresult PLUGIN_API getName (String128 name) override;
tresult PLUGIN_API createInstance (TUID cid, TUID _iid, void** obj) override;
DECLARE_FUNKNOWN_METHODS
PlugInterfaceSupport* getPlugInterfaceSupport () const { return mPlugInterfaceSupport; }
private:
IPtr<PlugInterfaceSupport> mPlugInterfaceSupport;
};
//------------------------------------------------------------------------
/** Example, ready to use implementation of IAttributeList.
\ingroup hostingBase
*/
class HostAttributeList final : public IAttributeList
{
public:
/** make a new attribute list instance */
static IPtr<IAttributeList> make ();
tresult PLUGIN_API setInt (AttrID aid, int64 value) override;
tresult PLUGIN_API getInt (AttrID aid, int64& value) override;
tresult PLUGIN_API setFloat (AttrID aid, double value) override;
tresult PLUGIN_API getFloat (AttrID aid, double& value) override;
tresult PLUGIN_API setString (AttrID aid, const TChar* string) override;
tresult PLUGIN_API getString (AttrID aid, TChar* string, uint32 sizeInBytes) override;
tresult PLUGIN_API setBinary (AttrID aid, const void* data, uint32 sizeInBytes) override;
tresult PLUGIN_API getBinary (AttrID aid, const void*& data, uint32& sizeInBytes) override;
virtual ~HostAttributeList () noexcept;
DECLARE_FUNKNOWN_METHODS
private:
HostAttributeList ();
struct Attribute;
std::map<std::string, Attribute> list;
};
//------------------------------------------------------------------------
/** Example implementation of IMessage.
\ingroup hostingBase
*/
class HostMessage final : public IMessage
{
public:
HostMessage ();
virtual ~HostMessage () noexcept;
const char* PLUGIN_API getMessageID () override;
void PLUGIN_API setMessageID (const char* messageID) override;
IAttributeList* PLUGIN_API getAttributes () override;
DECLARE_FUNKNOWN_METHODS
private:
char* messageId {nullptr};
IPtr<IAttributeList> attributeList;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,390 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/hostdataexchangehandler.cpp
// Created by : Steinberg, 06/2023
// Description : VST Data Exchange API Host Helper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "hostdataexchangehandler.h"
#include "../utility/alignedalloc.h"
#include "../utility/ringbuffer.h"
#include "pluginterfaces/base/funknownimpl.h"
#include <algorithm>
#include <cassert>
#include <mutex>
#include <vector>
#ifdef _MSC_VER
#include <malloc.h>
#endif
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
struct HostDataExchangeHandler::Impl
: U::ImplementsNonDestroyable<U::Directly<IDataExchangeHandler>>
{
struct Block
{
Block () = default;
Block (uint32 blockSize, uint32 alignment, DataExchangeBlockID id)
: blockID (id), alignment (alignment)
{
data = aligned_alloc (blockSize, alignment);
}
Block (Block&& other) { *this = std::move (other); }
~Block () noexcept
{
if (data)
aligned_free (data, alignment);
}
Block& operator= (Block&& other)
{
data = other.data;
other.data = nullptr;
blockID = other.blockID;
other.blockID = InvalidDataExchangeBlockID;
alignment = other.alignment;
return *this;
}
void* data {nullptr};
DataExchangeBlockID blockID {InvalidDataExchangeBlockID};
uint32 alignment {0};
};
struct Queue
{
using BlockRingBuffer = OneReaderOneWriter::RingBuffer<Block>;
// we do the assumption that the std::vector does not allocate memory when we don't push
// more items than we reserve before
using BlockVector = std::vector<Block>;
Queue (IAudioProcessor* owner, IDataExchangeReceiver* receiver,
DataExchangeUserContextID userContext, uint32 blockSize, uint32 numBlocks,
uint32 alignment)
: owner (owner)
, receiver (receiver)
, userContext (userContext)
, blockSize (blockSize)
, numBlocks (numBlocks)
{
receiver->queueOpened (userContext, blockSize, wantBlocksOnBackgroundThread);
freeList.resize (numBlocks);
sendList.resize (numBlocks);
lockList.reserve (numBlocks);
freeListOnRTThread.reserve (numBlocks);
for (auto idx = 0u; idx < numBlocks; ++idx)
freeList.push (Block (blockSize, alignment, idx));
}
~Queue () noexcept
{
if (receiver)
receiver->queueClosed (userContext);
}
bool lock (DataExchangeBlock& block)
{
if (freeListOnRTThread.empty () == false)
{
auto& back = freeListOnRTThread.back ();
block.data = back.data;
block.size = blockSize;
block.blockID = back.blockID;
lockList.emplace_back (std::move (back));
freeListOnRTThread.pop_back ();
return true;
}
Block b;
if (freeList.pop (b))
{
block.data = b.data;
block.size = blockSize;
block.blockID = b.blockID;
lockList.emplace_back (std::move (b));
return true;
}
return false;
}
bool free (DataExchangeBlockID blockID)
{
if (blockID >= numBlocks)
return false;
auto it = std::find_if (lockList.begin (), lockList.end (),
[&] (const auto& el) { return el.blockID == blockID; });
if (it == lockList.end ())
return false;
Block b = std::move (*it);
freeListOnRTThread.emplace_back (std::move (b));
lockList.erase (it);
return true;
}
bool readyToSend (DataExchangeBlockID blockID)
{
if (blockID >= numBlocks)
return false;
auto it = std::find_if (lockList.begin (), lockList.end (),
[&] (const auto& el) { return el.blockID == blockID; });
if (it == lockList.end ())
return false;
Block b = std::move (*it);
sendList.push (std::move (b));
lockList.erase (it);
return true;
}
uint32 sendBlocks (DataExchangeQueueID queueID)
{
BlockVector blocks;
Block b;
while (sendList.pop (b))
{
blocks.emplace_back (std::move (b));
}
if (blocks.empty ())
return 0;
std::vector<DataExchangeBlock> debs;
std::for_each (blocks.begin (), blocks.end (), [&] (const auto& el) {
DataExchangeBlock block;
block.data = el.data;
block.size = blockSize;
block.blockID = el.blockID;
debs.push_back (block);
});
receiver->onDataExchangeBlocksReceived (userContext, static_cast<uint32> (debs.size ()),
debs.data (), wantBlocksOnBackgroundThread);
std::for_each (blocks.begin (), blocks.end (),
[&] (auto&& el) { freeList.push (std::move (el)); });
return static_cast<uint32> (debs.size ());
}
IAudioProcessor* owner;
IPtr<IDataExchangeReceiver> receiver;
DataExchangeUserContextID userContext {};
TBool wantBlocksOnBackgroundThread {false};
BlockRingBuffer freeList;
BlockVector freeListOnRTThread;
BlockVector lockList;
BlockRingBuffer sendList;
uint32 blockSize {0};
uint32 numBlocks {0};
};
using QueuePtr = std::unique_ptr<Queue>;
using QueueList = std::vector<QueuePtr>;
Impl (IDataExchangeHandlerHost& host, uint32 maxQueues) : host (host)
{
queues.resize (maxQueues);
}
void setQueue (DataExchangeQueueID queueID, QueuePtr&& queue)
{
queuesLock.lock ();
queues[queueID] = std::move (queue);
if (queues[queueID]->wantBlocksOnBackgroundThread)
++numOpenBackgroundQueues;
else
++numOpenMainThreadQueues;
queuesLock.unlock ();
host.onQueueOpened (queues[queueID]->owner, queueID,
queues[queueID]->wantBlocksOnBackgroundThread);
host.numberOfQueuesChanged (numOpenMainThreadQueues, numOpenBackgroundQueues);
}
tresult PLUGIN_API openQueue (IAudioProcessor* owner, uint32 blockSize, uint32 numBlocks,
uint32 alignment, DataExchangeUserContextID userContext,
DataExchangeQueueID* outID) override
{
if (!host.isMainThread ())
return kResultFalse;
if (outID == nullptr)
return kInvalidArgument;
if (!host.isProcessorInactive (owner))
return kResultFalse;
auto receiver = host.findDataExchangeReceiver (owner);
if (!receiver)
return kInvalidArgument;
if (!host.allowAllocateSize (blockSize, numBlocks, alignment))
return kOutOfMemory;
for (auto queueID = 0; queueID < queues.size (); ++queueID)
{
if (queues[queueID] == nullptr)
{
auto newQueue = std::make_unique<Queue> (owner, receiver, userContext, blockSize,
numBlocks, alignment);
setQueue (queueID, std::move (newQueue));
*outID = queueID;
return kResultTrue;
}
}
auto queueSize = queues.size ();
if (host.allowQueueListResize (static_cast<uint32> (queueSize + 1)))
{
queues.resize (queueSize + 1);
assert (queues.size () == queueSize + 1);
DataExchangeQueueID queueID = static_cast<DataExchangeQueueID> (queueSize);
auto newQueue = std::make_unique<Queue> (owner, receiver, userContext, blockSize,
numBlocks, alignment);
setQueue (queueID, std::move (newQueue));
*outID = queueID;
return kResultTrue;
}
return kOutOfMemory;
}
tresult PLUGIN_API closeQueue (DataExchangeQueueID queueID) override
{
if (!host.isMainThread ())
return kResultFalse;
if (queues[queueID])
{
if (!host.isProcessorInactive (queues[queueID]->owner))
return kResultFalse;
QueuePtr q;
queuesLock.lock ();
std::swap (q, queues[queueID]);
if (q->wantBlocksOnBackgroundThread)
--numOpenBackgroundQueues;
else
--numOpenMainThreadQueues;
queuesLock.unlock ();
host.onQueueClosed (q->owner, queueID, q->wantBlocksOnBackgroundThread);
host.numberOfQueuesChanged (numOpenMainThreadQueues, numOpenBackgroundQueues);
q.reset ();
return kResultTrue;
}
return kResultFalse;
}
tresult PLUGIN_API lockBlock (DataExchangeQueueID queueId, DataExchangeBlock* block) override
{
if (!block || queueId >= queues.size () || queues[queueId] == nullptr)
return kInvalidArgument;
if (queues[queueId]->lock (*block))
return kResultTrue;
return kOutOfMemory;
}
tresult PLUGIN_API freeBlock (DataExchangeQueueID queueId, DataExchangeBlockID blockID,
TBool sendToController) override
{
if (queueId >= queues.size () || queues[queueId] == nullptr)
return kInvalidArgument;
if (sendToController)
{
if (queues[queueId]->readyToSend (blockID))
{
++numReadyToSendBlocks;
host.newBlockReadyToBeSend (queueId);
return kResultTrue;
}
return kResultFalse;
}
return queues[queueId]->free (blockID) ? kResultTrue : kResultFalse;
}
bool sendBlocks (bool isMainThread, size_t queueID, uint32& numSendBlocks)
{
LockGuard guard (queuesLock);
if (auto& queue = queues[queueID])
{
if (queue->wantBlocksOnBackgroundThread != static_cast<TBool> (isMainThread))
{
numSendBlocks = queue->sendBlocks (static_cast<DataExchangeQueueID> (queueID));
}
return true;
}
return false;
}
uint32 sendBlocks (bool isMainThread, DataExchangeQueueID queueFilter)
{
if (queueFilter != InvalidDataExchangeQueueID)
{
if (queueFilter < queues.size ())
{
uint32 numSendBlocks;
if (sendBlocks (isMainThread, queueFilter, numSendBlocks))
return numSendBlocks;
}
return 0;
}
uint32 totalSendBlocks = 0;
uint32 openQueues = numOpenBackgroundQueues + numOpenMainThreadQueues;
for (auto queueID = 0u; queueID < queues.size (); ++queueID)
{
uint32 numSendBlocks;
if (sendBlocks (isMainThread, queueID, numSendBlocks))
{
numReadyToSendBlocks -= numSendBlocks;
totalSendBlocks += numSendBlocks;
if ((--openQueues) == 0)
break;
}
}
return totalSendBlocks;
}
IDataExchangeHandlerHost& host;
QueueList queues;
std::atomic<uint32> numReadyToSendBlocks {0};
std::atomic<uint32> numOpenMainThreadQueues {0};
std::atomic<uint32> numOpenBackgroundQueues {0};
using Mutex = std::recursive_mutex;
using LockGuard = std::lock_guard<Mutex>;
Mutex queuesLock;
};
//------------------------------------------------------------------------
HostDataExchangeHandler::HostDataExchangeHandler (IDataExchangeHandlerHost& host, uint32 maxQueues)
{
impl = std::make_unique<Impl> (host, maxQueues);
}
//------------------------------------------------------------------------
HostDataExchangeHandler::~HostDataExchangeHandler () noexcept = default;
//------------------------------------------------------------------------
IDataExchangeHandler* HostDataExchangeHandler::getInterface () const
{
return impl.get ();
}
//------------------------------------------------------------------------
uint32 HostDataExchangeHandler::sendMainThreadBlocks ()
{
return impl->sendBlocks (true, InvalidDataExchangeQueueID);
}
//------------------------------------------------------------------------
uint32 HostDataExchangeHandler::sendBackgroundBlocks (DataExchangeQueueID queueId)
{
return impl->sendBlocks (false, queueId);
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,120 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/hostdataexchangehandler.h
// Created by : Steinberg, 06/2023
// Description : VST Data Exchange API Host Helper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstdataexchange.h"
#include <memory>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
struct IDataExchangeHandlerHost
{
virtual ~IDataExchangeHandlerHost () noexcept = default;
/** return if the audioprocessor is in an inactive state
* [main thread]
*/
virtual bool isProcessorInactive (IAudioProcessor* processor) = 0;
/** return the data exchange receiver (most likely the edit controller) for the processor
* [main thread]
*/
virtual IPtr<IDataExchangeReceiver> findDataExchangeReceiver (IAudioProcessor* processor) = 0;
/** check if the requested queue size should be allowed
* [main thread]
*/
virtual bool allowAllocateSize (uint32 blockSize, uint32 numBlocks, uint32 alignment) = 0;
/** check if this call is made on the main thread
* [any thread]
*/
virtual bool isMainThread () = 0;
/** check if the number of queues can be changed in this moment.
*
* this is only allowed if no other thread can access the IDataExchangeManagerHost in this
* moment
* [main thread]
*/
virtual bool allowQueueListResize (uint32 newNumQueues) = 0;
/** notification that the number of open queues changed
* [main thread]
*/
virtual void numberOfQueuesChanged (uint32 openMainThreadQueues,
uint32 openBackgroundThreadQueues) = 0;
/** notification that a new queue was opened */
virtual void onQueueOpened (IAudioProcessor* processor, DataExchangeQueueID queueID,
bool dispatchOnMainThread) = 0;
/** notification that a queue was closed */
virtual void onQueueClosed (IAudioProcessor* processor, DataExchangeQueueID queueID,
bool dispatchOnMainThread) = 0;
/** notification that a new block is ready to be send
* [process thread]
*/
virtual void newBlockReadyToBeSend (DataExchangeQueueID queueID) = 0;
};
//------------------------------------------------------------------------
struct HostDataExchangeHandler
{
/** Constructor
*
* allocate and deallocate this object on the main thread
*
* the number of queues is constant
*
* @param host the managing host
* @param maxQueues number of maximal allowed open queues
*/
HostDataExchangeHandler (IDataExchangeHandlerHost& host, uint32 maxQueues = 64);
~HostDataExchangeHandler () noexcept;
/** get the IHostDataExchangeManager interface
*
* the interface you must provide to the IAudioProcessor
*/
IDataExchangeHandler* getInterface () const;
/** send blocks
*
* the host should periodically call this method on the main thread to send all queued blocks
* which should be send on the main thread
*/
uint32 sendMainThreadBlocks ();
/** send blocks
*
* the host should call this on a dedicated background thread
* inside a mutex is used, so don't delete this object while calling this
*
* @param queueId only send blocks from the specified queue. If queueId is equal to
* InvalidDataExchangeQueueID all blocks from all queues are send.
*/
uint32 sendBackgroundBlocks (DataExchangeQueueID queueId = InvalidDataExchangeQueueID);
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,327 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module.cpp
// Created by : Steinberg, 08/2016
// Description : hosting module classes
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "module.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "public.sdk/source/vst/utility/optional.h"
#include "pluginterfaces/base/funknownimpl.h"
#include <sstream>
#include <utility>
//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {
//------------------------------------------------------------------------
FactoryInfo::FactoryInfo (PFactoryInfo&& other) noexcept
{
*this = std::move (other);
}
//------------------------------------------------------------------------
FactoryInfo& FactoryInfo::operator= (FactoryInfo&& other) noexcept
{
info = std::move (other.info);
other.info = {};
return *this;
}
//------------------------------------------------------------------------
FactoryInfo& FactoryInfo::operator= (PFactoryInfo&& other) noexcept
{
info = std::move (other);
other = {};
return *this;
}
//------------------------------------------------------------------------
std::string FactoryInfo::vendor () const noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
return StringConvert::convert (info.vendor, PFactoryInfo::kNameSize);
}
//------------------------------------------------------------------------
std::string FactoryInfo::url () const noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
return StringConvert::convert (info.url, PFactoryInfo::kURLSize);
}
//------------------------------------------------------------------------
std::string FactoryInfo::email () const noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
return StringConvert::convert (info.email, PFactoryInfo::kEmailSize);
}
//------------------------------------------------------------------------
Steinberg::int32 FactoryInfo::flags () const noexcept
{
return info.flags;
}
//------------------------------------------------------------------------
bool FactoryInfo::classesDiscardable () const noexcept
{
return (info.flags & PFactoryInfo::kClassesDiscardable) != 0;
}
//------------------------------------------------------------------------
bool FactoryInfo::licenseCheck () const noexcept
{
return (info.flags & PFactoryInfo::kLicenseCheck) != 0;
}
//------------------------------------------------------------------------
bool FactoryInfo::componentNonDiscardable () const noexcept
{
return (info.flags & PFactoryInfo::kComponentNonDiscardable) != 0;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
PluginFactory::PluginFactory (const PluginFactoryPtr& factory) noexcept : factory (factory)
{
}
//------------------------------------------------------------------------
void PluginFactory::setHostContext (Steinberg::FUnknown* context) const noexcept
{
if (auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory3> (factory))
f->setHostContext (context);
}
//------------------------------------------------------------------------
FactoryInfo PluginFactory::info () const noexcept
{
Steinberg::PFactoryInfo i;
factory->getFactoryInfo (&i);
return FactoryInfo (std::move (i));
}
//------------------------------------------------------------------------
uint32_t PluginFactory::classCount () const noexcept
{
auto count = factory->countClasses ();
assert (count >= 0);
return static_cast<uint32_t> (count);
}
//------------------------------------------------------------------------
PluginFactory::ClassInfos PluginFactory::classInfos () const noexcept
{
auto count = classCount ();
Optional<FactoryInfo> factoryInfo;
ClassInfos classes;
classes.reserve (count);
auto f3 = Steinberg::U::cast<Steinberg::IPluginFactory3> (factory);
auto f2 = Steinberg::U::cast<Steinberg::IPluginFactory2> (factory);
Steinberg::PClassInfo ci;
Steinberg::PClassInfo2 ci2;
Steinberg::PClassInfoW ci3;
for (uint32_t i = 0; i < count; ++i)
{
if (f3 && f3->getClassInfoUnicode (i, &ci3) == Steinberg::kResultTrue)
classes.emplace_back (ci3);
else if (f2 && f2->getClassInfo2 (i, &ci2) == Steinberg::kResultTrue)
classes.emplace_back (ci2);
else if (factory->getClassInfo (i, &ci) == Steinberg::kResultTrue)
classes.emplace_back (ci);
auto& classInfo = classes.back ();
if (classInfo.vendor ().empty ())
{
if (!factoryInfo)
factoryInfo = Optional<FactoryInfo> (info ());
classInfo.get ().vendor = factoryInfo->vendor ();
}
}
return classes;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
const UID& ClassInfo::ID () const noexcept
{
return data.classID;
}
//------------------------------------------------------------------------
int32_t ClassInfo::cardinality () const noexcept
{
return data.cardinality;
}
//------------------------------------------------------------------------
const std::string& ClassInfo::category () const noexcept
{
return data.category;
}
//------------------------------------------------------------------------
const std::string& ClassInfo::name () const noexcept
{
return data.name;
}
//------------------------------------------------------------------------
const std::string& ClassInfo::vendor () const noexcept
{
return data.vendor;
}
//------------------------------------------------------------------------
const std::string& ClassInfo::version () const noexcept
{
return data.version;
}
//------------------------------------------------------------------------
const std::string& ClassInfo::sdkVersion () const noexcept
{
return data.sdkVersion;
}
//------------------------------------------------------------------------
const ClassInfo::SubCategories& ClassInfo::subCategories () const noexcept
{
return data.subCategories;
}
//------------------------------------------------------------------------
Steinberg::uint32 ClassInfo::classFlags () const noexcept
{
return data.classFlags;
}
//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfo& info) noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
data.classID = info.cid;
data.cardinality = info.cardinality;
data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
}
//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfo2& info) noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
data.classID = info.cid;
data.cardinality = info.cardinality;
data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize);
data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize);
data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize);
parseSubCategories (
StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize));
data.classFlags = info.classFlags;
}
//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfoW& info) noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
data.classID = info.cid;
data.cardinality = info.cardinality;
data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize);
data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize);
data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize);
parseSubCategories (
StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize));
data.classFlags = info.classFlags;
}
//------------------------------------------------------------------------
void ClassInfo::parseSubCategories (const std::string& str) noexcept
{
std::stringstream stream (str);
std::string item;
while (std::getline (stream, item, '|'))
data.subCategories.emplace_back (std::move (item));
}
//------------------------------------------------------------------------
std::string ClassInfo::subCategoriesString () const noexcept
{
std::string result;
if (data.subCategories.empty ())
return result;
result = data.subCategories[0];
for (auto index = 1u; index < data.subCategories.size (); ++index)
result += "|" + data.subCategories[index];
return result;
}
//------------------------------------------------------------------------
namespace {
//------------------------------------------------------------------------
std::pair<size_t, size_t> rangeOfScaleFactor (const std::string& name)
{
auto result = std::make_pair (std::string::npos, std::string::npos);
size_t xIndex = name.find_last_of ('x');
if (xIndex == std::string::npos)
return result;
size_t indicatorIndex = name.find_last_of ('_');
if (indicatorIndex == std::string::npos)
return result;
if (xIndex < indicatorIndex)
return result;
result.first = indicatorIndex + 1;
result.second = xIndex;
return result;
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
Optional<double> Module::Snapshot::decodeScaleFactor (const std::string& name)
{
auto range = rangeOfScaleFactor (name);
if (range.first == std::string::npos || range.second == std::string::npos)
return {};
std::string tmp (name.data () + range.first, range.second - range.first);
std::istringstream sstream (tmp);
sstream.imbue (std::locale::classic ());
sstream.precision (static_cast<std::streamsize> (3));
double result;
sstream >> result;
return Optional<double> (result);
}
//------------------------------------------------------------------------
Optional<UID> Module::Snapshot::decodeUID (const std::string& filename)
{
if (filename.size () < 45)
return {};
if (filename.find ("_snapshot") != 32)
return {};
auto uidStr = filename.substr (0, 32);
return UID::fromString (uidStr);
}
//------------------------------------------------------------------------
} // Hosting
} // VST3
@@ -0,0 +1,196 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module.h
// Created by : Steinberg, 08/2016
// Description : hosting module classes
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "../utility/uid.h"
#include "pluginterfaces/base/ipluginbase.h"
#include <utility>
#include <vector>
//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {
//------------------------------------------------------------------------
class FactoryInfo
{
public:
//------------------------------------------------------------------------
using PFactoryInfo = Steinberg::PFactoryInfo;
FactoryInfo () noexcept {}
~FactoryInfo () noexcept {}
FactoryInfo (const FactoryInfo&) noexcept = default;
FactoryInfo (PFactoryInfo&&) noexcept;
FactoryInfo (FactoryInfo&&) noexcept = default;
FactoryInfo& operator= (const FactoryInfo&) noexcept = default;
FactoryInfo& operator= (FactoryInfo&&) noexcept;
FactoryInfo& operator= (PFactoryInfo&&) noexcept;
std::string vendor () const noexcept;
std::string url () const noexcept;
std::string email () const noexcept;
Steinberg::int32 flags () const noexcept;
bool classesDiscardable () const noexcept;
bool licenseCheck () const noexcept;
bool componentNonDiscardable () const noexcept;
PFactoryInfo& get () noexcept { return info; }
//------------------------------------------------------------------------
private:
PFactoryInfo info {};
};
//------------------------------------------------------------------------
class ClassInfo
{
public:
//------------------------------------------------------------------------
using SubCategories = std::vector<std::string>;
using PClassInfo = Steinberg::PClassInfo;
using PClassInfo2 = Steinberg::PClassInfo2;
using PClassInfoW = Steinberg::PClassInfoW;
//------------------------------------------------------------------------
ClassInfo () noexcept {}
explicit ClassInfo (const PClassInfo& info) noexcept;
explicit ClassInfo (const PClassInfo2& info) noexcept;
explicit ClassInfo (const PClassInfoW& info) noexcept;
ClassInfo (const ClassInfo&) = default;
ClassInfo& operator= (const ClassInfo&) = default;
ClassInfo (ClassInfo&&) = default;
ClassInfo& operator= (ClassInfo&&) = default;
const UID& ID () const noexcept;
int32_t cardinality () const noexcept;
const std::string& category () const noexcept;
const std::string& name () const noexcept;
const std::string& vendor () const noexcept;
const std::string& version () const noexcept;
const std::string& sdkVersion () const noexcept;
const SubCategories& subCategories () const noexcept;
std::string subCategoriesString () const noexcept;
Steinberg::uint32 classFlags () const noexcept;
struct Data
{
UID classID;
int32_t cardinality;
std::string category;
std::string name;
std::string vendor;
std::string version;
std::string sdkVersion;
SubCategories subCategories;
Steinberg::uint32 classFlags = 0;
};
Data& get () noexcept { return data; }
//------------------------------------------------------------------------
private:
void parseSubCategories (const std::string& str) noexcept;
Data data {};
};
//------------------------------------------------------------------------
class PluginFactory
{
public:
//------------------------------------------------------------------------
using ClassInfos = std::vector<ClassInfo>;
using PluginFactoryPtr = Steinberg::IPtr<Steinberg::IPluginFactory>;
//------------------------------------------------------------------------
explicit PluginFactory (const PluginFactoryPtr& factory) noexcept;
void setHostContext (Steinberg::FUnknown* context) const noexcept;
FactoryInfo info () const noexcept;
uint32_t classCount () const noexcept;
ClassInfos classInfos () const noexcept;
template <typename T>
Steinberg::IPtr<T> createInstance (const UID& classID) const noexcept;
const PluginFactoryPtr& get () const noexcept { return factory; }
//------------------------------------------------------------------------
private:
PluginFactoryPtr factory;
};
//------------------------------------------------------------------------
//------------------------------------------------------------------------
class Module
{
public:
//------------------------------------------------------------------------
struct Snapshot
{
struct ImageDesc
{
double scaleFactor {1.};
std::string path;
};
UID uid;
std::vector<ImageDesc> images;
static Optional<double> decodeScaleFactor (const std::string& path);
static Optional<UID> decodeUID (const std::string& filename);
};
using Ptr = std::shared_ptr<Module>;
using PathList = std::vector<std::string>;
using SnapshotList = std::vector<Snapshot>;
//------------------------------------------------------------------------
static Ptr create (const std::string& path, std::string& errorDescription);
static PathList getModulePaths ();
static SnapshotList getSnapshots (const std::string& modulePath);
/** get the path to the module info json file if it exists */
static Optional<std::string> getModuleInfoPath (const std::string& modulePath);
/** validate the bundle structure */
static bool validateBundleStructure (const std::string& path, std::string& errorDescription);
const std::string& getName () const noexcept { return name; }
const std::string& getPath () const noexcept { return path; }
const PluginFactory& getFactory () const noexcept { return factory; }
bool isBundle () const noexcept { return hasBundleStructure; }
//------------------------------------------------------------------------
protected:
virtual ~Module () noexcept = default;
virtual bool load (const std::string& path, std::string& errorDescription) = 0;
PluginFactory factory {nullptr};
std::string name;
std::string path;
bool hasBundleStructure {true};
};
//------------------------------------------------------------------------
template <typename T>
inline Steinberg::IPtr<T> PluginFactory::createInstance (const UID& classID) const noexcept
{
T* obj = nullptr;
if (factory->createInstance (classID.data (), T::iid, reinterpret_cast<void**> (&obj)) ==
Steinberg::kResultTrue)
return Steinberg::owned (obj);
return nullptr;
}
//------------------------------------------------------------------------
} // Hosting
} // VST3
@@ -0,0 +1,391 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module_linux.cpp
// Created by : Steinberg, 08/2016
// Description : hosting module classes (linux implementation)
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "module.h"
#include "public.sdk/source/vst/utility/optional.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "pluginterfaces/base/funknownimpl.h"
#include <algorithm>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
#if SMTG_CPP17
#if __has_include(<filesystem>)
#define USE_EXPERIMENTAL_FS 0
#elif __has_include(<experimental/filesystem>)
#define USE_EXPERIMENTAL_FS 1
#endif
#else // !SMTG_CPP17
#define USE_EXPERIMENTAL_FS 1
#endif // SMTG_CPP17
#if USE_EXPERIMENTAL_FS == 1
#include <experimental/filesystem>
namespace filesystem = std::experimental::filesystem;
#else // USE_EXPERIMENTAL_FS == 0
#include <filesystem>
namespace filesystem = std::filesystem;
#endif // USE_EXPERIMENTAL_FS
//------------------------------------------------------------------------
extern "C" {
using ModuleEntryFunc = bool (PLUGIN_API*) (void*);
using ModuleExitFunc = bool (PLUGIN_API*) ();
}
//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {
using Path = filesystem::path;
//------------------------------------------------------------------------
namespace {
//------------------------------------------------------------------------
Optional<std::string> getCurrentMachineName ()
{
struct utsname unameData;
int res = uname (&unameData);
if (res != 0)
return {};
return {unameData.machine};
}
//------------------------------------------------------------------------
Optional<Path> getApplicationPath ()
{
std::string appPath = "";
pid_t pid = getpid ();
char buf[10];
sprintf (buf, "%d", pid);
std::string _link = "/proc/";
_link.append (buf);
_link.append ("/exe");
char proc[1024];
int ch = readlink (_link.c_str (), proc, 1024);
if (ch == -1)
return {};
proc[ch] = 0;
appPath = proc;
std::string::size_type t = appPath.find_last_of ("/");
appPath = appPath.substr (0, t);
return Path {appPath};
}
//------------------------------------------------------------------------
class LinuxModule : public Module
{
public:
template <typename T>
T getFunctionPointer (const char* name)
{
return reinterpret_cast<T> (dlsym (mModule, name));
}
~LinuxModule () override
{
factory = PluginFactory (nullptr);
if (mModule)
{
if (auto moduleExit = getFunctionPointer<ModuleExitFunc> ("ModuleExit"))
moduleExit ();
dlclose (mModule);
}
}
static Optional<Path> getSOPath (const std::string& inPath)
{
Path modulePath {inPath};
if (!filesystem::is_directory (modulePath))
return {};
auto stem = modulePath.stem ();
modulePath /= "Contents";
if (!filesystem::is_directory (modulePath))
return {};
// use the Machine Hardware Name (from uname cmd-line) as prefix for "-linux"
auto machine = getCurrentMachineName ();
if (!machine)
return {};
modulePath /= *machine + "-linux";
if (!filesystem::is_directory (modulePath))
return {};
modulePath /= stem;
modulePath += ".so";
return Optional<Path> (std::move (modulePath));
}
bool load (const std::string& inPath, std::string& errorDescription) override
{
auto modulePath = getSOPath (inPath);
if (!modulePath)
{
errorDescription = inPath + " is not a module directory.";
return false;
}
mModule = dlopen (reinterpret_cast<const char*> (modulePath->generic_string ().data ()),
RTLD_LAZY);
if (!mModule)
{
errorDescription = "dlopen failed.\n";
errorDescription += dlerror ();
return false;
}
// ModuleEntry is mandatory
auto moduleEntry = getFunctionPointer<ModuleEntryFunc> ("ModuleEntry");
if (!moduleEntry)
{
errorDescription =
"The shared library does not export the required 'ModuleEntry' function";
return false;
}
// ModuleExit is mandatory
auto moduleExit = getFunctionPointer<ModuleExitFunc> ("ModuleExit");
if (!moduleExit)
{
errorDescription =
"The shared library does not export the required 'ModuleExit' function";
return false;
}
auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
if (!factoryProc)
{
errorDescription =
"The shared library does not export the required 'GetPluginFactory' function";
return false;
}
if (!moduleEntry (mModule))
{
errorDescription = "Calling 'ModuleEntry' failed";
return false;
}
auto f = Steinberg::U::cast<Steinberg::IPluginFactory> (owned (factoryProc ()));
if (!f)
{
errorDescription = "Calling 'GetPluginFactory' returned nullptr";
return false;
}
factory = PluginFactory (f);
return true;
}
void* mModule {nullptr};
};
//------------------------------------------------------------------------
void findFilesWithExt (const std::string& path, const std::string& ext, Module::PathList& pathList,
bool recursive = true)
{
try
{
for (auto& p : filesystem::directory_iterator (path))
{
if (p.path ().extension () == ext)
{
pathList.push_back (p.path ().generic_string ());
}
else if (recursive && p.status ().type () == filesystem::file_type::directory)
{
findFilesWithExt (p.path (), ext, pathList);
}
}
}
catch (...)
{
}
}
//------------------------------------------------------------------------
void findModules (const std::string& path, Module::PathList& pathList)
{
findFilesWithExt (path, ".vst3", pathList);
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
auto _module = std::make_shared<LinuxModule> ();
if (_module->load (path, errorDescription))
{
_module->path = path;
auto it = std::find_if (path.rbegin (), path.rend (),
[] (const std::string::value_type& c) { return c == '/'; });
if (it != path.rend ())
_module->name = {it.base (), path.end ()};
return _module;
}
return nullptr;
}
//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
/* VST3 component locations on linux :
* User privately installed : $HOME/.vst3/
* Distribution installed : /usr/lib/vst3/
* Locally installed : /usr/local/lib/vst3/
* Application : /$APPFOLDER/vst3/
*/
const auto systemPaths = {"/usr/lib/vst3/", "/usr/local/lib/vst3/"};
PathList list;
if (auto homeDir = getenv ("HOME"))
{
filesystem::path homePath (homeDir);
homePath /= ".vst3";
findModules (homePath.generic_string (), list);
}
for (auto path : systemPaths)
findModules (path, list);
// application level
auto appPath = getApplicationPath ();
if (appPath)
{
*appPath /= "vst3";
findModules (appPath->generic_string (), list);
}
return list;
}
//------------------------------------------------------------------------
Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
{
SnapshotList result;
filesystem::path path (modulePath);
path /= "Contents";
path /= "Resources";
path /= "Snapshots";
PathList pngList;
findFilesWithExt (path, ".png", pngList, false);
for (auto& png : pngList)
{
filesystem::path p (png);
auto filename = p.filename ().generic_string ();
auto uid = Snapshot::decodeUID (filename);
if (!uid)
continue;
auto scaleFactor = 1.;
if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename))
scaleFactor = *decodedScaleFactor;
Module::Snapshot::ImageDesc desc;
desc.scaleFactor = scaleFactor;
desc.path = std::move (png);
bool found = false;
for (auto& entry : result)
{
if (entry.uid != *uid)
continue;
found = true;
entry.images.emplace_back (std::move (desc));
break;
}
if (found)
continue;
Module::Snapshot snapshot;
snapshot.uid = *uid;
snapshot.images.emplace_back (std::move (desc));
result.emplace_back (std::move (snapshot));
}
return result;
}
//------------------------------------------------------------------------
Optional<std::string> Module::getModuleInfoPath (const std::string& modulePath)
{
filesystem::path path (modulePath);
path /= "Contents";
path /= "Resources";
path /= "moduleinfo.json";
if (filesystem::exists (path))
return {path.generic_string ()};
return {};
}
//------------------------------------------------------------------------
bool Module::validateBundleStructure (const std::string& modulePath, std::string& errorDescription)
{
filesystem::path path (modulePath);
auto moduleName = path.filename ();
path /= "Contents";
if (filesystem::exists (path) == false)
{
errorDescription = "Expecting 'Contents' as first subfolder.";
return false;
}
auto machine = getCurrentMachineName ();
if (!machine)
{
errorDescription = "Could not get the current machine name.";
return false;
}
path /= *machine + "-linux";
if (filesystem::exists (path) == false)
{
errorDescription = "Expecting '" + *machine + "-linux' as architecture subfolder.";
return false;
}
moduleName.replace_extension (".so");
path /= moduleName;
if (filesystem::exists (path) == false)
{
errorDescription = "Shared library name is not equal to bundle folder name. Must be '" +
moduleName.string () + "'.";
return false;
}
return true;
}
//------------------------------------------------------------------------
} // Hosting
} // VST3
@@ -0,0 +1,383 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module_mac.mm
// Created by : Steinberg, 08/2016
// Description : hosting module classes (macOS implementation)
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "module.h"
#import <Cocoa/Cocoa.h>
#import <CoreFoundation/CoreFoundation.h>
#if !__has_feature(objc_arc)
#error this file needs to be compiled with automatic reference counting enabled
#endif
//------------------------------------------------------------------------
extern "C" {
typedef bool (*BundleEntryFunc) (CFBundleRef);
typedef bool (*BundleExitFunc) ();
}
//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {
//------------------------------------------------------------------------
namespace {
//------------------------------------------------------------------------
template <typename T>
class CFPtr
{
public:
inline CFPtr (const T& obj = nullptr) : obj (obj) {}
inline CFPtr (CFPtr&& other) { *this = other; }
inline ~CFPtr ()
{
if (obj)
CFRelease (obj);
}
inline CFPtr& operator= (CFPtr&& other)
{
obj = other.obj;
other.obj = nullptr;
return *this;
}
inline CFPtr& operator= (const T& o)
{
if (obj)
CFRelease (obj);
obj = o;
return *this;
}
inline operator T () const { return obj; } // act as T
private:
CFPtr (const CFPtr& other) = delete;
CFPtr& operator= (const CFPtr& other) = delete;
T obj = nullptr;
};
//------------------------------------------------------------------------
class MacModule : public Module
{
public:
template <typename T>
T getFunctionPointer (const char* name)
{
assert (bundle);
CFPtr<CFStringRef> functionName (
CFStringCreateWithCString (kCFAllocatorDefault, name, kCFStringEncodingASCII));
return reinterpret_cast<T> (CFBundleGetFunctionPointerForName (bundle, functionName));
}
bool loadInternal (const std::string& path, std::string& errorDescription)
{
CFPtr<CFURLRef> url (CFURLCreateFromFileSystemRepresentation (
kCFAllocatorDefault, reinterpret_cast<const UInt8*> (path.data ()), path.length (),
true));
if (!url)
return false;
bundle = CFBundleCreate (kCFAllocatorDefault, url);
CFErrorRef error = nullptr;
if (!bundle || !CFBundleLoadExecutableAndReturnError (bundle, &error))
{
if (error)
{
CFPtr<CFStringRef> errorString (CFErrorCopyDescription (error));
if (errorString)
{
auto stringLength = CFStringGetLength (errorString);
auto maxSize =
CFStringGetMaximumSizeForEncoding (stringLength, kCFStringEncodingUTF8);
auto buffer = std::make_unique<char[]> (maxSize);
if (CFStringGetCString (errorString, buffer.get (), maxSize,
kCFStringEncodingUTF8))
errorDescription = buffer.get ();
CFRelease (error);
}
}
else
{
errorDescription = "Could not create Bundle for path: " + path;
}
return false;
}
// bundleEntry is mandatory
auto bundleEntry = getFunctionPointer<BundleEntryFunc> ("bundleEntry");
if (!bundleEntry)
{
errorDescription = "Bundle does not export the required 'bundleEntry' function";
return false;
}
// bundleExit is mandatory
auto bundleExit = getFunctionPointer<BundleExitFunc> ("bundleExit");
if (!bundleExit)
{
errorDescription = "Bundle does not export the required 'bundleExit' function";
return false;
}
auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
if (!factoryProc)
{
errorDescription = "Bundle does not export the required 'GetPluginFactory' function";
return false;
}
if (!bundleEntry (bundle))
{
errorDescription = "Calling 'bundleEntry' failed";
return false;
}
auto f = owned (factoryProc ());
if (!f)
{
errorDescription = "Calling 'GetPluginFactory' returned nullptr";
return false;
}
factory = PluginFactory (f);
return true;
}
bool load (const std::string& path, std::string& errorDescription) override
{
if (!path.empty () && path[0] != '/')
{
auto buffer = std::make_unique<char[]> (PATH_MAX);
auto workDir = getcwd (buffer.get (), PATH_MAX);
if (workDir)
{
std::string wd (workDir);
wd += "/";
if (loadInternal (wd + path, errorDescription))
{
name = path;
return true;
}
return false;
}
}
return loadInternal (path, errorDescription);
}
~MacModule () override
{
factory = PluginFactory (nullptr);
if (bundle)
{
if (auto bundleExit = getFunctionPointer<BundleExitFunc> ("bundleExit"))
bundleExit ();
}
}
CFPtr<CFBundleRef> bundle;
};
//------------------------------------------------------------------------
void findModulesInDirectory (NSURL* dirUrl, Module::PathList& result)
{
dirUrl = [dirUrl URLByResolvingSymlinksInPath];
if (!dirUrl)
return;
NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager]
enumeratorAtURL: dirUrl
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsPackageDescendants
errorHandler:nil];
for (NSURL* url in enumerator)
{
if ([[[url lastPathComponent] pathExtension] isEqualToString:@"vst3"])
{
CFPtr<CFArrayRef> archs (
CFBundleCopyExecutableArchitecturesForURL (static_cast<CFURLRef> (url)));
if (archs)
result.emplace_back ([url.path UTF8String]);
}
else
{
id resValue;
if (![url getResourceValue:&resValue forKey:NSURLIsSymbolicLinkKey error:nil])
continue;
if (!static_cast<NSNumber*> (resValue).boolValue)
continue;
auto resolvedUrl = [url URLByResolvingSymlinksInPath];
if (![resolvedUrl getResourceValue:&resValue forKey:NSURLIsDirectoryKey error:nil])
continue;
if (!static_cast<NSNumber*> (resValue).boolValue)
continue;
findModulesInDirectory (resolvedUrl, result);
}
}
}
//------------------------------------------------------------------------
void getModules (NSSearchPathDomainMask domain, Module::PathList& result)
{
NSURL* libraryUrl = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory
inDomain:domain
appropriateForURL:nil
create:NO
error:nil];
if (libraryUrl == nil)
return;
NSURL* audioUrl = [libraryUrl URLByAppendingPathComponent:@"Audio"];
if (audioUrl == nil)
return;
NSURL* plugInsUrl = [audioUrl URLByAppendingPathComponent:@"Plug-Ins"];
if (plugInsUrl == nil)
return;
NSURL* vst3Url =
[[plugInsUrl URLByAppendingPathComponent:@"VST3"] URLByResolvingSymlinksInPath];
if (vst3Url == nil)
return;
findModulesInDirectory (vst3Url, result);
}
//------------------------------------------------------------------------
void getApplicationModules (Module::PathList& result)
{
auto bundle = CFBundleGetMainBundle ();
if (!bundle)
return;
auto bundleUrl = static_cast<NSURL*> (CFBridgingRelease (CFBundleCopyBundleURL (bundle)));
if (!bundleUrl)
return;
auto resUrl = [bundleUrl URLByAppendingPathComponent:@"Contents"];
if (!resUrl)
return;
auto vst3Url = [resUrl URLByAppendingPathComponent:@"VST3"];
if (!vst3Url)
return;
findModulesInDirectory (vst3Url, result);
}
//------------------------------------------------------------------------
void getModuleSnapshots (const std::string& path, Module::SnapshotList& result)
{
auto* nsString = [NSString stringWithUTF8String:path.data ()];
if (!nsString)
return;
auto bundleUrl = [NSURL fileURLWithPath:nsString];
if (!bundleUrl)
return;
auto urls = [NSBundle URLsForResourcesWithExtension:@"png"
subdirectory:@"Snapshots"
inBundleWithURL:bundleUrl];
if (!urls || [urls count] == 0)
return;
for (NSURL* url in urls)
{
std::string fullpath ([[url path] UTF8String]);
std::string filename ([[[url path] lastPathComponent] UTF8String]);
auto uid = Module::Snapshot::decodeUID (filename);
if (!uid)
continue;
auto scaleFactor = 1.;
if (auto decodedScaleFactor = Module::Snapshot::decodeScaleFactor (filename))
scaleFactor = *decodedScaleFactor;
Module::Snapshot::ImageDesc desc;
desc.scaleFactor = scaleFactor;
desc.path = std::move (fullpath);
bool found = false;
for (auto& entry : result)
{
if (entry.uid != *uid)
continue;
found = true;
entry.images.emplace_back (std::move (desc));
break;
}
if (found)
continue;
Module::Snapshot snapshot;
snapshot.uid = *uid;
snapshot.images.emplace_back (std::move (desc));
result.emplace_back (std::move (snapshot));
}
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
auto module = std::make_shared<MacModule> ();
if (module->load (path, errorDescription))
{
module->path = path;
auto it = std::find_if (path.rbegin (), path.rend (),
[] (const std::string::value_type& c) { return c == '/'; });
if (it != path.rend ())
module->name = {it.base (), path.end ()};
return std::move (module);
}
return nullptr;
}
//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
PathList list;
getModules (NSUserDomainMask, list);
getModules (NSLocalDomainMask, list);
// TODO getModules (NSNetworkDomainMask, list);
getApplicationModules (list);
return list;
}
//------------------------------------------------------------------------
Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
{
SnapshotList list;
getModuleSnapshots (modulePath, list);
return list;
}
//------------------------------------------------------------------------
Optional<std::string> Module::getModuleInfoPath (const std::string& modulePath)
{
auto* nsString = [NSString stringWithUTF8String:modulePath.data ()];
if (!nsString)
return {};
auto bundleUrl = [NSURL fileURLWithPath:nsString];
if (!bundleUrl)
return {};
auto moduleInfoUrl = [NSBundle URLForResource:@"moduleinfo"
withExtension:@"json"
subdirectory:nullptr
inBundleWithURL:bundleUrl];
if (!moduleInfoUrl)
return {};
NSError* error = nil;
if ([moduleInfoUrl checkResourceIsReachableAndReturnError:&error])
return {std::string (moduleInfoUrl.fileSystemRepresentation)};
return {};
}
//------------------------------------------------------------------------
bool Module::validateBundleStructure (const std::string& path, std::string& errorDescription)
{
auto* nsString = [NSString stringWithUTF8String:path.data ()];
if (!nsString)
return false;
return [NSBundle bundleWithPath:nsString] != nil;
}
//------------------------------------------------------------------------
} // Hosting
} // VST3
@@ -0,0 +1,746 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module_win32.cpp
// Created by : Steinberg, 08/2016
// Description : hosting module classes (win32 implementation)
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "module.h"
#include "public.sdk/source/vst/utility/optional.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "pluginterfaces/base/funknownimpl.h"
#include <shlobj.h>
#include <windows.h>
#include <algorithm>
#include <iostream>
#if SMTG_CPP17
#if __has_include(<filesystem>)
#define USE_FILESYSTEM 1
#elif __has_include(<experimental/filesystem>)
#define USE_FILESYSTEM 0
#endif
#else // !SMTG_CPP17
#define USE_FILESYSTEM 0
#endif // SMTG_CPP17
#if USE_FILESYSTEM == 1
#include <filesystem>
namespace filesystem = std::filesystem;
#else // USE_FILESYSTEM == 0
// The <experimental/filesystem> header is deprecated. It is superseded by the C++17 <filesystem>
// header. You can define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING to silence the
// warning, otherwise the build will fail in VS2019 16.3.0
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <experimental/filesystem>
namespace filesystem = std::experimental::filesystem;
#endif // USE_FILESYSTEM
#pragma comment(lib, "Shell32")
//------------------------------------------------------------------------
extern "C" {
using InitModuleFunc = bool (PLUGIN_API*) ();
using ExitModuleFunc = bool (PLUGIN_API*) ();
}
//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {
constexpr unsigned long kIPPathNameMax = 1024;
//------------------------------------------------------------------------
namespace {
#define USE_OLE !USE_FILESYSTEM
// for testing only
#if 0 // DEVELOPMENT
#define LOG_ENABLE 1
#else
#define LOG_ENABLE 0
#endif
#if SMTG_PLATFORM_64
#if SMTG_OS_WINDOWS_ARM
#if SMTG_CPU_ARM_64EC
constexpr auto architectureString = "arm64ec-win";
constexpr auto architectureX64String = "x86_64-win";
#else // !SMTG_CPU_ARM_64EC
constexpr auto architectureString = "arm64-win";
#endif // SMTG_CPU_ARM_64EC
constexpr auto architectureArm64XString = "arm64x-win";
#else // !SMTG_OS_WINDOWS_ARM
constexpr auto architectureString = "x86_64-win";
#endif // SMTG_OS_WINDOWS_ARM
#else // !SMTG_PLATFORM_64
#if SMTG_OS_WINDOWS_ARM
constexpr auto architectureString = "arm-win";
#else // !SMTG_OS_WINDOWS_ARM
constexpr auto architectureString = "x86-win";
#endif // SMTG_OS_WINDOWS_ARM
#endif // SMTG_PLATFORM_64
#if USE_OLE
//------------------------------------------------------------------------
struct Ole
{
static Ole& instance ()
{
static Ole gInstance;
return gInstance;
}
private:
Ole () { OleInitialize (nullptr); }
~Ole () { OleUninitialize (); }
};
#endif // USE_OLE
//------------------------------------------------------------------------
class Win32Module : public Module
{
public:
template <typename T>
T getFunctionPointer (const char* name)
{
return reinterpret_cast<T> (GetProcAddress (mModule, name));
}
~Win32Module () override
{
factory = PluginFactory (nullptr);
if (mModule)
{
// ExitDll is optional
if (auto dllExit = getFunctionPointer<ExitModuleFunc> ("ExitDll"))
dllExit ();
FreeLibrary ((HMODULE)mModule);
}
}
//--- -----------------------------------------------------------------------
HINSTANCE loadAsPackage (const std::string& inPath, std::string& errorDescription,
const char* archString = architectureString)
{
namespace StringConvert = Steinberg::Vst::StringConvert;
filesystem::path p (inPath);
auto filename = p.filename ();
p /= "Contents";
p /= archString;
p /= filename;
const std::wstring wString = p.generic_wstring ();
HINSTANCE instance = LoadLibraryW (reinterpret_cast<LPCWSTR> (wString.data ()));
#if SMTG_CPU_ARM_64EC
if (instance == nullptr)
instance = loadAsPackage (inPath, errorDescription, architectureArm64XString);
if (instance == nullptr)
instance = loadAsPackage (inPath, errorDescription, architectureX64String);
#endif // SMTG_CPU_ARM_64EC
if (instance == nullptr)
getLastError (p.string (), errorDescription);
return instance;
}
//--- -----------------------------------------------------------------------
HINSTANCE loadAsDll (const std::string& inPath, std::string& errorDescription)
{
namespace StringConvert = Steinberg::Vst::StringConvert;
auto wideStr = StringConvert::convert (inPath);
HINSTANCE instance = LoadLibraryW (reinterpret_cast<LPCWSTR> (wideStr.data ()));
if (instance == nullptr)
{
getLastError (inPath, errorDescription);
}
else
{
hasBundleStructure = false;
}
return instance;
}
//--- -----------------------------------------------------------------------
bool load (const std::string& inPath, std::string& errorDescription) override
{
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
const filesystem::path tmp (inPath);
#else
const filesystem::path tmp = filesystem::u8path (inPath);
#endif // SMTG_CPP20
std::error_code ec;
if (filesystem::is_directory (tmp, ec))
{
// try as package (bundle)
mModule = loadAsPackage (inPath, errorDescription);
}
else
{
// try old definition without package
mModule = loadAsDll (inPath, errorDescription);
}
if (mModule == nullptr)
return false;
auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
if (!factoryProc)
{
errorDescription = "The dll does not export the required 'GetPluginFactory' function";
return false;
}
// InitDll is optional
auto dllEntry = getFunctionPointer<InitModuleFunc> ("InitDll");
if (dllEntry && !dllEntry ())
{
errorDescription = "Calling 'InitDll' failed";
return false;
}
auto f = Steinberg::U::cast<Steinberg::IPluginFactory> (owned (factoryProc ()));
if (!f)
{
errorDescription = "Calling 'GetPluginFactory' returned nullptr";
return false;
}
factory = PluginFactory (f);
return true;
}
HINSTANCE mModule {nullptr};
private:
//--- -----------------------------------------------------------------------
void getLastError (const std::string& inPath, std::string& errorDescription)
{
auto lastError = GetLastError ();
LPVOID lpMessageBuffer {nullptr};
if (FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr,
lastError, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&lpMessageBuffer, 0, nullptr) > 0)
{
errorDescription = "LoadLibraryW failed for path " + inPath + ": " +
std::string ((char*)lpMessageBuffer);
LocalFree (lpMessageBuffer);
}
else
{
errorDescription = "LoadLibraryW failed with error number: " +
std::to_string (lastError) + " for path " + inPath;
}
}
};
//------------------------------------------------------------------------
bool openVST3Package (const filesystem::path& p, const char* archString,
filesystem::path* result = nullptr)
{
auto path = p;
path /= "Contents";
path /= archString;
path /= p.filename ();
const std::wstring wString = path.generic_wstring ();
auto hFile = CreateFileW (reinterpret_cast<LPCWSTR> (wString.data ()), GENERIC_READ,
FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle (hFile);
if (result)
*result = path;
return true;
}
return false;
}
//------------------------------------------------------------------------
bool checkVST3Package (const filesystem::path& p, filesystem::path* result = nullptr,
const char* archString = architectureString)
{
if (openVST3Package (p, archString, result))
return true;
#if SMTG_CPU_ARM_64EC
if (openVST3Package (p, architectureArm64XString, result))
return true;
if (openVST3Package (p, architectureX64String, result))
return true;
#endif // SMTG_CPU_ARM_64EC
return false;
}
//------------------------------------------------------------------------
bool isFolderSymbolicLink (const filesystem::path& p)
{
#if USE_FILESYSTEM
std::error_code ec;
if (filesystem::is_symlink (p, ec))
return true;
#else
const std::wstring wString = p.generic_wstring ();
auto attrib = GetFileAttributesW (reinterpret_cast<LPCWSTR> (wString.data ()));
if (attrib & FILE_ATTRIBUTE_REPARSE_POINT)
{
auto hFile = CreateFileW (reinterpret_cast<LPCWSTR> (wString.data ()), GENERIC_READ,
FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
return true;
CloseHandle (hFile);
}
#endif // USE_FILESYSTEM
return false;
}
//------------------------------------------------------------------------
Optional<std::string> getKnownFolder (REFKNOWNFOLDERID folderID)
{
namespace StringConvert = Steinberg::Vst::StringConvert;
PWSTR wideStr {};
if (FAILED (SHGetKnownFolderPath (folderID, 0, nullptr, &wideStr)))
return {};
return StringConvert::convert (Steinberg::wscast (wideStr));
}
//------------------------------------------------------------------------
VST3::Optional<filesystem::path> resolveShellLink (const filesystem::path& p)
{
#if USE_FILESYSTEM
std::error_code ec;
auto target = filesystem::read_symlink (p, ec);
if (ec)
return {};
else
return { target.lexically_normal () };
#elif USE_OLE
Ole::instance ();
IShellLink* shellLink = nullptr;
if (!SUCCEEDED (CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
IID_IShellLink, reinterpret_cast<LPVOID*> (&shellLink))))
return {};
IPersistFile* persistFile = nullptr;
if (!SUCCEEDED (
shellLink->QueryInterface (IID_IPersistFile, reinterpret_cast<void**> (&persistFile))))
return {};
if (!SUCCEEDED (persistFile->Load (p.wstring ().data (), STGM_READ)))
return {};
if (!SUCCEEDED (shellLink->Resolve (nullptr, MAKELONG (SLR_NO_UI, 500))))
return {};
WCHAR resolvedPath[kIPPathNameMax];
if (!SUCCEEDED (shellLink->GetPath (resolvedPath, kIPPathNameMax, nullptr, SLGP_SHORTPATH)))
return {};
std::wstring longPath;
longPath.resize (kIPPathNameMax);
auto numChars =
GetLongPathNameW (resolvedPath, const_cast<wchar_t*> (longPath.data ()), kIPPathNameMax);
if (!numChars)
return {};
longPath.resize (numChars);
persistFile->Release ();
shellLink->Release ();
return {filesystem::path (longPath)};
#else
return {};
#endif // USE_FILESYSTEM
}
//------------------------------------------------------------------------
void addToPathList (Module::PathList& pathList, const std::string& toAdd)
{
#if LOG_ENABLE
std::cout << "=> add: " << toAdd << "\n";
#endif
pathList.push_back (toAdd);
}
//------------------------------------------------------------------------
void findFilesWithExt (const filesystem::path& path, const std::string& ext,
Module::PathList& pathList, bool recursive = true)
{
for (auto& p : filesystem::directory_iterator (path))
{
#if USE_FILESYSTEM
filesystem::path finalPath (p);
if (isFolderSymbolicLink (p))
{
if (auto res = resolveShellLink (p))
{
finalPath = *res;
std::error_code ec;
if (!filesystem::exists (finalPath, ec))
continue;
}
else
continue;
}
const auto& cpExt = finalPath.extension ();
if (cpExt == ext)
{
filesystem::path result;
if (checkVST3Package (finalPath, &result))
{
#if SMTG_CPP20
std::u8string u8str = result.generic_u8string ();
std::string str;
str.assign (std::begin (u8str), std::end (u8str));
addToPathList (pathList, str);
#else
addToPathList (pathList, result.generic_u8string ());
#endif // SMTG_CPP20
continue;
}
}
std::error_code ec;
if (filesystem::is_directory (finalPath, ec))
{
if (recursive)
findFilesWithExt (finalPath, ext, pathList, recursive);
}
else if (cpExt == ext)
{
#if SMTG_CPP20
std::u8string u8str = finalPath.generic_u8string ();
std::string str;
str.assign (std::begin (u8str), std::end (u8str));
addToPathList (pathList, str);
#else
addToPathList (pathList, finalPath.generic_u8string ());
#endif // SMTG_CPP20
}
#else // !USE_FILESYSTEM
const auto& cp = p.path ();
const auto& cpExt = cp.extension ();
if (cpExt == ext)
{
if ((p.status ().type () == filesystem::file_type::directory) ||
isFolderSymbolicLink (p))
{
filesystem::path result;
if (checkVST3Package (p, &result))
{
addToPathList (pathList, result.generic_u8string ());
continue;
}
findFilesWithExt (cp, ext, pathList, recursive);
}
else
addToPathList (pathList, cp.generic_u8string ());
}
else if (recursive)
{
if (p.status ().type () == filesystem::file_type::directory)
{
findFilesWithExt (cp, ext, pathList, recursive);
}
else if (cpExt == ".lnk")
{
if (auto resolvedLink = resolveShellLink (cp))
{
if (resolvedLink->extension () == ext)
{
if (filesystem::is_directory (*resolvedLink) ||
isFolderSymbolicLink (*resolvedLink))
{
filesystem::path result;
if (checkVST3Package (*resolvedLink, &result))
{
addToPathList (pathList, result.generic_u8string ());
continue;
}
findFilesWithExt (*resolvedLink, ext, pathList, recursive);
}
else
addToPathList (pathList, resolvedLink->generic_u8string ());
}
else if (filesystem::is_directory (*resolvedLink))
{
const auto& str = resolvedLink->generic_u8string ();
if (cp.generic_u8string ().compare (0, str.size (), str.data (),
str.size ()) != 0)
findFilesWithExt (*resolvedLink, ext, pathList, recursive);
}
}
}
}
#endif // USE_FILESYSTEM
}
}
//------------------------------------------------------------------------
void findModules (const filesystem::path& path, Module::PathList& pathList)
{
std::error_code ec;
if (filesystem::exists (path, ec))
findFilesWithExt (path, ".vst3", pathList);
}
//------------------------------------------------------------------------
Optional<filesystem::path> getContentsDirectoryFromModuleExecutablePath (
const std::string& modulePath)
{
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
filesystem::path path (modulePath);
#else
filesystem::path path = filesystem::u8path (modulePath);
#endif // SMTG_CPP20
path = path.parent_path ();
if (path.filename () != architectureString)
return {};
path = path.parent_path ();
if (path.filename () != "Contents")
return {};
return Optional<filesystem::path> {std::move (path)};
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
auto _module = std::make_shared<Win32Module> ();
if (_module->load (path, errorDescription))
{
_module->path = path;
auto it = std::find_if (path.rbegin (), path.rend (),
[] (const std::string::value_type& c) { return c == '/'; });
if (it != path.rend ())
_module->name = {it.base (), path.end ()};
return _module;
}
return nullptr;
}
//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
namespace StringConvert = Steinberg::Vst::StringConvert;
// find plug-ins located in common/VST3
PathList list;
if (auto knownFolder = getKnownFolder (FOLDERID_UserProgramFilesCommon))
{
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
filesystem::path path (*knownFolder);
#else
filesystem::path path = filesystem::u8path (*knownFolder);
#endif // SMTG_CPP20
path.append ("VST3");
#if LOG_ENABLE
std::cout << "Check folder: " << path << "\n";
#endif
findModules (path, list);
}
if (auto knownFolder = getKnownFolder (FOLDERID_ProgramFilesCommon))
{
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
filesystem::path path (*knownFolder);
#else
filesystem::path path = filesystem::u8path (*knownFolder);
#endif // SMTG_CPP20
path.append ("VST3");
#if LOG_ENABLE
std::cout << "Check folder: " << path << "\n";
#endif
findModules (path, list);
}
// find plug-ins located in VST3 (application folder)
WCHAR modulePath[kIPPathNameMax];
GetModuleFileNameW (nullptr, modulePath, kIPPathNameMax);
auto appPath = StringConvert::convert (Steinberg::wscast (modulePath));
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
filesystem::path path (appPath);
#else
filesystem::path path = filesystem::u8path (appPath);
#endif // SMTG_CPP20
path = path.parent_path ();
path = path.append ("VST3");
#if LOG_ENABLE
std::cout << "Check folder: " << path << "\n";
#endif
findModules (path, list);
return list;
}
//------------------------------------------------------------------------
Optional<std::string> Module::getModuleInfoPath (const std::string& modulePath)
{
auto path = getContentsDirectoryFromModuleExecutablePath (modulePath);
if (!path)
{
filesystem::path p;
if (!checkVST3Package ({modulePath}, &p))
return {};
p = p.parent_path ();
p = p.parent_path ();
path = Optional<filesystem::path> {p};
}
*path /= "Resources";
*path /= "moduleinfo.json";
std::error_code ec;
if (filesystem::exists (*path, ec))
{
return {path->generic_string ()};
}
return {};
}
//------------------------------------------------------------------------
bool Module::validateBundleStructure (const std::string& modulePath, std::string& errorDescription)
{
try
{
auto path = getContentsDirectoryFromModuleExecutablePath (modulePath);
if (!path)
{
filesystem::path p;
if (!checkVST3Package ({modulePath}, &p))
{
errorDescription = "Not a bundle: '" + modulePath + "'.";
return false;
}
p = p.parent_path ();
p = p.parent_path ();
path = Optional<filesystem::path> {p};
}
if (path->filename () != "Contents")
{
errorDescription = "Unexpected directory name, should be 'Contents' but is '" +
path->filename ().string () + "'.";
return false;
}
auto bundlePath = path->parent_path ();
*path /= architectureString;
*path /= bundlePath.filename ();
std::error_code ec;
if (filesystem::exists (*path, ec) == false)
{
errorDescription = "Shared library name is not equal to bundle folder name. Must be '" +
bundlePath.filename ().string () + "'.";
return false;
}
return true;
}
catch (const std::exception& exc)
{
errorDescription = exc.what ();
return false;
}
}
//------------------------------------------------------------------------
Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
{
SnapshotList result;
auto path = getContentsDirectoryFromModuleExecutablePath (modulePath);
if (!path)
{
filesystem::path p;
if (!checkVST3Package ({modulePath}, &p))
return result;
p = p.parent_path ();
p = p.parent_path ();
path = Optional<filesystem::path> (p);
}
*path /= "Resources";
*path /= "Snapshots";
std::error_code ec;
if (filesystem::exists (*path, ec) == false)
return result;
PathList pngList;
findFilesWithExt (*path, ".png", pngList, false);
for (auto& png : pngList)
{
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
const filesystem::path p (png);
#else
const filesystem::path p = filesystem::u8path (png);
#endif // SMTG_CPP20
auto filename = p.filename ().generic_string ();
auto uid = Snapshot::decodeUID (filename);
if (!uid)
continue;
auto scaleFactor = 1.;
if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename))
scaleFactor = *decodedScaleFactor;
Module::Snapshot::ImageDesc desc;
desc.scaleFactor = scaleFactor;
desc.path = std::move (png);
bool found = false;
for (auto& entry : result)
{
if (entry.uid != *uid)
continue;
found = true;
entry.images.emplace_back (std::move (desc));
break;
}
if (found)
continue;
Module::Snapshot snapshot;
snapshot.uid = *uid;
snapshot.images.emplace_back (std::move (desc));
result.emplace_back (std::move (snapshot));
}
return result;
}
//------------------------------------------------------------------------
} // Hosting
} // VST3
@@ -0,0 +1,298 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/parameterchanges.cpp
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 parameter changes implementation
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "parameterchanges.h"
namespace Steinberg {
namespace Vst {
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (ParameterChanges, IParameterChanges, IParameterChanges::iid)
IMPLEMENT_FUNKNOWN_METHODS (ParameterValueQueue, IParamValueQueue, IParamValueQueue::iid)
constexpr int32 kQueueReservedPoints = 5;
//-----------------------------------------------------------------------------
ParameterValueQueue::ParameterValueQueue (ParamID paramID)
: paramID (paramID)
{
values.reserve (kQueueReservedPoints);
FUNKNOWN_CTOR
}
//-----------------------------------------------------------------------------
ParameterValueQueue::~ParameterValueQueue ()
{
FUNKNOWN_DTOR
}
//-----------------------------------------------------------------------------
void ParameterValueQueue::clear ()
{
values.clear ();
}
//-----------------------------------------------------------------------------
int32 PLUGIN_API ParameterValueQueue::getPointCount ()
{
return static_cast<int32> (values.size ());
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API ParameterValueQueue::getPoint (int32 index, int32& sampleOffset, ParamValue& value)
{
if (index >= 0 && index < static_cast<int32> (values.size ()))
{
const ParameterQueueValue& queueValue = values[index];
sampleOffset = queueValue.sampleOffset;
value = queueValue.value;
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API ParameterValueQueue::addPoint (int32 sampleOffset, ParamValue value, int32& index)
{
auto destIndex = static_cast<int32>(values.size ());
for (uint32 i = 0; i < values.size (); i++)
{
if (values[i].sampleOffset == sampleOffset)
{
values[i].value = value;
index = i;
return kResultTrue;
}
if (values[i].sampleOffset > sampleOffset)
{
destIndex = i;
break;
}
}
// need new point
ParameterQueueValue queueValue (value, sampleOffset);
if (destIndex == static_cast<int32> (values.size ()))
values.emplace_back (queueValue);
else
values.insert (values.begin () + destIndex, queueValue);
index = destIndex;
return kResultTrue;
}
//-----------------------------------------------------------------------------
// ParameterChanges
//-----------------------------------------------------------------------------
ParameterChanges::ParameterChanges (int32 maxParameters)
{
FUNKNOWN_CTOR
setMaxParameters (maxParameters);
}
//-----------------------------------------------------------------------------
ParameterChanges::~ParameterChanges ()
{
FUNKNOWN_DTOR
}
//-----------------------------------------------------------------------------
void ParameterChanges::setMaxParameters (int32 maxParameters)
{
if (maxParameters < 0)
return;
while (static_cast<int32> (queues.size ()) < maxParameters)
{
queues.emplace_back (owned (new ParameterValueQueue (kNoParamId)));
}
while (static_cast<int32> (queues.size ()) > maxParameters)
{
queues.pop_back ();
}
if (usedQueueCount > maxParameters)
usedQueueCount = maxParameters;
}
//-----------------------------------------------------------------------------
void ParameterChanges::clearQueue ()
{
usedQueueCount = 0;
}
//-----------------------------------------------------------------------------
int32 PLUGIN_API ParameterChanges::getParameterCount ()
{
return usedQueueCount;
}
//-----------------------------------------------------------------------------
IParamValueQueue* PLUGIN_API ParameterChanges::getParameterData (int32 index)
{
if (index >= 0 && index < usedQueueCount)
return queues[index];
return nullptr;
}
//-----------------------------------------------------------------------------
IParamValueQueue* PLUGIN_API ParameterChanges::addParameterData (const ParamID& pid, int32& index)
{
for (int32 i = 0; i < usedQueueCount; i++)
{
if (queues[i]->getParameterId () == pid)
{
index = i;
return queues[i];
}
}
ParameterValueQueue* valueQueue = nullptr;
if (usedQueueCount < static_cast<int32> (queues.size ()))
{
valueQueue = queues[usedQueueCount];
valueQueue->setParamID (pid);
valueQueue->clear ();
}
else
{
queues.emplace_back (owned (new ParameterValueQueue (pid)));
valueQueue = queues.back ();
}
index = usedQueueCount;
usedQueueCount++;
return valueQueue;
}
//-----------------------------------------------------------------------------
// ParameterChangeTransfer
//-----------------------------------------------------------------------------
ParameterChangeTransfer::ParameterChangeTransfer (int32 maxParameters)
: size (0)
, changes (nullptr)
, readIndex (0)
, writeIndex (0)
{
setMaxParameters (maxParameters);
}
//-----------------------------------------------------------------------------
ParameterChangeTransfer::~ParameterChangeTransfer ()
{
setMaxParameters (0);
}
//-----------------------------------------------------------------------------
void ParameterChangeTransfer::setMaxParameters (int32 maxParameters)
{
// reserve memory for twice the amount of all parameters
int32 newSize = maxParameters * 2;
if (size != newSize)
{
if (changes)
delete [] changes;
changes = nullptr;
size = newSize;
if (size > 0)
changes = new ParameterChange [size];
}
}
//-----------------------------------------------------------------------------
void ParameterChangeTransfer::addChange (ParamID pid, ParamValue value, int32 sampleOffset)
{
if (changes)
{
changes[writeIndex].id = pid;
changes[writeIndex].value = value;
changes[writeIndex].sampleOffset = sampleOffset;
int32 newWriteIndex = writeIndex + 1;
if (newWriteIndex >= size)
newWriteIndex = 0;
if (readIndex != newWriteIndex)
writeIndex = newWriteIndex;
}
}
//-----------------------------------------------------------------------------
bool ParameterChangeTransfer::getNextChange (ParamID& pid, ParamValue& value, int32& sampleOffset)
{
if (!changes)
return false;
int32 currentWriteIndex = writeIndex;
if (readIndex != currentWriteIndex)
{
pid = changes [readIndex].id;
value = changes [readIndex].value;
sampleOffset = changes [readIndex].sampleOffset;
int32 newReadIndex = readIndex + 1;
if (newReadIndex >= size)
newReadIndex = 0;
readIndex = newReadIndex;
return true;
}
return false;
}
//-----------------------------------------------------------------------------
void ParameterChangeTransfer::transferChangesTo (ParameterChanges& dest)
{
ParamID pid;
ParamValue value;
int32 sampleOffset;
int32 index;
while (getNextChange (pid, value, sampleOffset))
{
IParamValueQueue* queue = dest.addParameterData (pid, index);
if (queue)
{
queue->addPoint (sampleOffset, value, index);
}
}
}
//-----------------------------------------------------------------------------
void ParameterChangeTransfer::transferChangesFrom (ParameterChanges& source)
{
ParamValue value;
int32 sampleOffset;
for (int32 i = 0; i < source.getParameterCount (); i++)
{
IParamValueQueue* queue = source.getParameterData (i);
if (queue)
{
for (int32 j = 0; j < queue->getPointCount (); j++)
{
if (queue->getPoint (j, sampleOffset, value) == kResultTrue)
{
addChange (queue->getParameterId (), value, sampleOffset);
}
}
}
}
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,122 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/parameterchanges.h
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 parameter changes implementation
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include <vector>
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Implementation's example of IParamValueQueue - not threadsave!.
\ingroup hostingBase
*/
class ParameterValueQueue : public IParamValueQueue
{
public:
//------------------------------------------------------------------------
ParameterValueQueue (ParamID paramID);
virtual ~ParameterValueQueue ();
ParamID PLUGIN_API getParameterId () SMTG_OVERRIDE { return paramID; }
int32 PLUGIN_API getPointCount () SMTG_OVERRIDE;
tresult PLUGIN_API getPoint (int32 index, int32& sampleOffset, ParamValue& value) SMTG_OVERRIDE;
tresult PLUGIN_API addPoint (int32 sampleOffset, ParamValue value, int32& index) SMTG_OVERRIDE;
void setParamID (ParamID pID) {paramID = pID;}
void clear ();
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
ParamID paramID;
struct ParameterQueueValue
{
ParameterQueueValue (ParamValue value, int32 sampleOffset) : value (value), sampleOffset (sampleOffset) {}
ParamValue value;
int32 sampleOffset;
};
std::vector<ParameterQueueValue> values;
};
//------------------------------------------------------------------------
/** Implementation's example of IParameterChanges - not threadsave!.
\ingroup hostingBase
*/
class ParameterChanges : public IParameterChanges
{
public:
//------------------------------------------------------------------------
ParameterChanges (int32 maxParameters = 0);
virtual ~ParameterChanges ();
void clearQueue ();
void setMaxParameters (int32 maxParameters);
//---IParameterChanges-----------------------------
int32 PLUGIN_API getParameterCount () SMTG_OVERRIDE;
IParamValueQueue* PLUGIN_API getParameterData (int32 index) SMTG_OVERRIDE;
IParamValueQueue* PLUGIN_API addParameterData (const ParamID& pid, int32& index) SMTG_OVERRIDE;
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
std::vector<IPtr<ParameterValueQueue>> queues;
int32 usedQueueCount {0};
};
//------------------------------------------------------------------------
/** Ring buffer for transferring parameter changes from a writer to a read thread .
\ingroup hostingBase
*/
class ParameterChangeTransfer
{
public:
//------------------------------------------------------------------------
ParameterChangeTransfer (int32 maxParameters = 0);
virtual ~ParameterChangeTransfer ();
void setMaxParameters (int32 maxParameters);
void addChange (ParamID pid, ParamValue value, int32 sampleOffset);
bool getNextChange (ParamID& pid, ParamValue& value, int32& sampleOffset);
void transferChangesTo (ParameterChanges& dest);
void transferChangesFrom (ParameterChanges& source);
void removeChanges () { writeIndex = readIndex; }
//------------------------------------------------------------------------
protected:
struct ParameterChange
{
ParamID id;
ParamValue value;
int32 sampleOffset;
};
int32 size;
ParameterChange* changes;
volatile int32 readIndex;
volatile int32 writeIndex;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,118 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/pluginterfacesupport.cpp
// Created by : Steinberg, 11/2018.
// Description : VST 3 hostclasses, example implementations for IPlugInterfaceSupport
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "pluginterfacesupport.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "pluginterfaces/vst/ivstmessage.h"
#include <algorithm>
//-----------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//-----------------------------------------------------------------------------
PlugInterfaceSupport::PlugInterfaceSupport ()
{
FUNKNOWN_CTOR
// add minimum set
//---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);
//---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);
//---VST 3.7-----------------------------------
addPlugInterfaceSupported (IProcessContextRequirements::iid);
addPlugInterfaceSupported (IParameterFunctionName::iid);
addPlugInterfaceSupported (IProgress::iid);
//----VST 3.8------------------------------------
addPlugInterfaceSupported (IMidiMapping2::iid)
addPlugInterfaceSupported (IMidiLearn2::iid)
*/
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API PlugInterfaceSupport::isPlugInterfaceSupported (const TUID _iid)
{
auto uid = FUID::fromTUID (_iid);
if (std::find (mFUIDArray.begin (), mFUIDArray.end (), uid) != mFUIDArray.end ())
return kResultTrue;
return kResultFalse;
}
//-----------------------------------------------------------------------------
void PlugInterfaceSupport::addPlugInterfaceSupported (const TUID _iid)
{
mFUIDArray.push_back (FUID::fromTUID (_iid));
}
//-----------------------------------------------------------------------------
bool PlugInterfaceSupport::removePlugInterfaceSupported (const TUID _iid)
{
auto uid = FUID::fromTUID (_iid);
auto it = std::find (mFUIDArray.begin (), mFUIDArray.end (), uid);
if (it == mFUIDArray.end ())
return false;
mFUIDArray.erase (it);
return true;
}
IMPLEMENT_FUNKNOWN_METHODS (PlugInterfaceSupport, IPlugInterfaceSupport, IPlugInterfaceSupport::iid)
//-----------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,50 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/pluginterfacesupport.h
// Created by : Steinberg, 11/20018.
// Description : VST 3 hostclasses, example implementations for IPlugInterfaceSupport
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstpluginterfacesupport.h"
#include <vector>
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Example implementation of IPlugInterfaceSupport.
\ingroup hostingBase
*/
class PlugInterfaceSupport : public IPlugInterfaceSupport
{
public:
PlugInterfaceSupport ();
virtual ~PlugInterfaceSupport () = default;
//--- IPlugInterfaceSupport ---------
tresult PLUGIN_API isPlugInterfaceSupported (const TUID _iid) SMTG_OVERRIDE;
void addPlugInterfaceSupported (const TUID _iid);
bool removePlugInterfaceSupported (const TUID _iid);
DECLARE_FUNKNOWN_METHODS
private:
std::vector<FUID> mFUIDArray;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,320 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/plugprovider.cpp
// Created by : Steinberg, 08/2016
// Description : VST 3 Plug-in Provider class
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "plugprovider.h"
#include "connectionproxy.h"
#include "pluginterfaces/vst/ivstcomponent.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstmessage.h"
#include <cstdio>
#include <iostream>
static std::ostream* errorStream = &std::cout;
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// PlugProvider
//------------------------------------------------------------------------
PlugProvider::PlugProvider (const PluginFactory& factory, ClassInfo classInfo, bool plugIsGlobal)
: factory (factory)
, component (nullptr)
, controller (nullptr)
, classInfo (classInfo)
, plugIsGlobal (plugIsGlobal)
{
}
//------------------------------------------------------------------------
PlugProvider::~PlugProvider ()
{
terminatePlugin ();
}
//------------------------------------------------------------------------
template <typename Proc>
void PlugProvider::printError (Proc p) const
{
if (errorStream)
{
p (*errorStream);
}
}
//------------------------------------------------------------------------
bool PlugProvider::initialize ()
{
if (plugIsGlobal)
{
return setupPlugin (PluginContextFactory::instance ().getPluginContext ());
}
return true;
}
//------------------------------------------------------------------------
IComponent* PLUGIN_API PlugProvider::getComponent ()
{
if (!component)
setupPlugin (PluginContextFactory::instance ().getPluginContext ());
if (component)
component->addRef ();
return component;
}
//------------------------------------------------------------------------
IEditController* PLUGIN_API PlugProvider::getController ()
{
if (controller)
controller->addRef ();
// 'iController == 0' is allowed! In this case the plug has no controller
return controller;
}
//------------------------------------------------------------------------
IPluginFactory* PLUGIN_API PlugProvider::getPluginFactory ()
{
if (auto f = factory.get ())
return f.get ();
return nullptr;
}
//------------------------------------------------------------------------
tresult PLUGIN_API PlugProvider::getComponentUID (FUID& uid) const
{
uid = FUID::fromTUID (classInfo.ID ().data ());
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API PlugProvider::releasePlugIn (IComponent* iComponent,
IEditController* iController)
{
if (iComponent)
iComponent->release ();
if (iController)
iController->release ();
if (!plugIsGlobal)
{
terminatePlugin ();
}
return kResultOk;
}
//------------------------------------------------------------------------
bool PlugProvider::setupPlugin (FUnknown* hostContext)
{
bool res = false;
bool isSingleComponent = false;
//---create Plug-in here!--------------
// create its component part
component = factory.createInstance<IComponent> (classInfo.ID ());
if (component)
{
// initialize the component with our context
if (auto plugBase = U::cast<IPluginBase> (component))
{
res = (plugBase->initialize (hostContext) == kResultOk);
if (res == false)
{
printError ([&] (std::ostream& stream) {
stream << "Failed to initialize component of " << classInfo.name () << "!\n";
});
return false;
}
}
else
{
printError ([&] (std::ostream& stream) {
stream << "Failed to get IPluginBase from component of " << classInfo.name ()
<< "!\n";
});
return false;
}
// try to create the controller part from the component
// (for Plug-ins which did not succeed to separate component from controller)
if (component->queryInterface (IEditController::iid, (void**)&controller) == kResultTrue)
{
isSingleComponent = true;
}
else
{
TUID controllerCID;
// ask for the associated controller class ID
if (component->getControllerClassId (controllerCID) == kResultTrue)
{
// create its controller part created from the factory
controller = factory.createInstance<IEditController> (VST3::UID (controllerCID));
if (controller)
{
// initialize the component with our context
if (auto plugCtrlBase = U::cast<IPluginBase> (controller))
{
res = (plugCtrlBase->initialize (hostContext) == kResultOk);
if (res == false)
{
printError ([&] (std::ostream& stream) {
stream << "Failed to initialize controller of " << classInfo.name ()
<< "!\n";
});
}
}
else
{
printError ([&] (std::ostream& stream) {
stream << "Failed to get IPluginBase from controller of "
<< classInfo.name () << "!\n";
});
return false;
}
}
}
else
{
printError ([&] (std::ostream& stream) {
stream << "Component does not provide a required controller class ID ["
<< classInfo.name () << "]!\n";
});
}
}
if (!res)
{
component.reset ();
controller.reset ();
}
}
else if (errorStream)
{
printError ([&] (std::ostream& stream) {
stream << "Failed to create component instance of " << classInfo.name () << "!\n";
});
}
if (res && !isSingleComponent)
return connectComponents ();
return res;
}
//------------------------------------------------------------------------
bool PlugProvider::connectComponents ()
{
if (!component || !controller)
return false;
auto compICP = U::cast<IConnectionPoint> (component);
auto contrICP = U::cast<IConnectionPoint> (controller);
if (!compICP || !contrICP)
return false;
componentCP = owned (new ConnectionProxy (compICP));
controllerCP = owned (new ConnectionProxy (contrICP));
tresult tres = componentCP->connect (contrICP);
if (tres != kResultTrue)
{
printError ([&] (std::ostream& stream) {
stream << "Failed to connect the component with the controller with result code '"
<< tres << "'!\n";
});
return false;
}
tres = controllerCP->connect (compICP);
if (tres != kResultTrue)
{
printError ([&] (std::ostream& stream) {
stream << "Failed to connect the controller with the component with result code '"
<< tres << "'!\n";
});
return false;
}
return true;
}
//------------------------------------------------------------------------
bool PlugProvider::disconnectComponents ()
{
if (!componentCP || !controllerCP)
return false;
bool res = componentCP->disconnect ();
res &= controllerCP->disconnect ();
componentCP.reset ();
controllerCP.reset ();
return res;
}
//------------------------------------------------------------------------
void PlugProvider::terminatePlugin ()
{
disconnectComponents ();
bool controllerIsComponent = false;
if (component)
{
controllerIsComponent = FUnknownPtr<IEditController> (component).getInterface () != nullptr;
if (auto plugBase = U::cast<IPluginBase> (component))
plugBase->terminate ();
else
{
printError ([&](std::ostream& stream) {
stream << "Failed to get IPluginBase from component of " << classInfo.name ()
<< "!\n";
});
}
}
if (controller && controllerIsComponent == false)
{
if (auto plugCtrlBase = U::cast<IPluginBase> (controller))
plugCtrlBase->terminate ();
else
{
printError ([&](std::ostream& stream) {
stream << "Failed to get IPluginBase from controller of " << classInfo.name ()
<< "!\n";
});
}
}
component.reset ();
controller.reset ();
}
//------------------------------------------------------------------------
void PlugProvider::setErrorStream (std::ostream* stream)
{
errorStream = stream;
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,107 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/plugprovider.h
// Created by : Steinberg, 04/2005
// Description : VST 3 Plug-in Provider class
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "public.sdk/source/vst/hosting/module.h"
#include "pluginterfaces/vst/ivsttestplugprovider.h"
#include "pluginterfaces/base/funknownimpl.h"
#include <ostream>
namespace Steinberg {
namespace Vst {
class IComponent;
class IEditController;
class ConnectionProxy;
//------------------------------------------------------------------------
/** Helper for creating and initializing component.
\ingroup Validator */
//------------------------------------------------------------------------
class PlugProvider
: public U::Implements<U::Directly<ITestPlugProvider2>, U::Indirectly<ITestPlugProvider>>
{
public:
using ClassInfo = VST3::Hosting::ClassInfo;
using PluginFactory = VST3::Hosting::PluginFactory;
//--- ---------------------------------------------------------------------
PlugProvider (const PluginFactory& factory, ClassInfo info, bool plugIsGlobal = true);
~PlugProvider () override;
bool initialize ();
IPtr<IComponent> getComponentPtr () const { return component; }
IPtr<IEditController> getControllerPtr () const { return controller; }
const ClassInfo& getClassInfo () const { return classInfo; }
//--- from ITestPlugProvider ------------------
IComponent* PLUGIN_API getComponent () SMTG_OVERRIDE;
IEditController* PLUGIN_API getController () SMTG_OVERRIDE;
tresult PLUGIN_API releasePlugIn (IComponent* component, IEditController* controller) SMTG_OVERRIDE;
tresult PLUGIN_API getSubCategories (IStringResult& result) const SMTG_OVERRIDE
{
result.setText (classInfo.subCategoriesString ().data ());
return kResultTrue;
}
tresult PLUGIN_API getComponentUID (FUID& uid) const SMTG_OVERRIDE;
//--- from ITestPlugProvider2 ------------------
IPluginFactory* PLUGIN_API getPluginFactory () SMTG_OVERRIDE;
static void setErrorStream (std::ostream* stream);
//------------------------------------------------------------------------
protected:
bool setupPlugin (FUnknown* hostContext);
bool connectComponents ();
bool disconnectComponents ();
void terminatePlugin ();
template<typename Proc>
void printError (Proc p) const;
PluginFactory factory;
IPtr<IComponent> component;
IPtr<IEditController> controller;
ClassInfo classInfo;
IPtr<ConnectionProxy> componentCP;
IPtr<ConnectionProxy> controllerCP;
bool plugIsGlobal;
};
//------------------------------------------------------------------------
class PluginContextFactory
{
public:
static PluginContextFactory& instance ()
{
static PluginContextFactory factory;
return factory;
}
void setPluginContext (FUnknown* obj) { context = obj; }
FUnknown* getPluginContext () const { return context; }
private:
FUnknown* context;
};
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,204 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/processdata.cpp
// Created by : Steinberg, 10/2005
// Description : VST Hosting Utilities
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "processdata.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// HostProcessData
//------------------------------------------------------------------------
HostProcessData::~HostProcessData () noexcept
{
unprepare ();
}
//------------------------------------------------------------------------
bool HostProcessData::prepare (IComponent& component, int32 bufferSamples,
int32 _symbolicSampleSize)
{
if (checkIfReallocationNeeded (component, bufferSamples, _symbolicSampleSize))
{
unprepare ();
symbolicSampleSize = _symbolicSampleSize;
channelBufferOwner = bufferSamples > 0;
numInputs = createBuffers (component, inputs, kInput, bufferSamples);
numOutputs = createBuffers (component, outputs, kOutput, bufferSamples);
}
else
{
// reset silence flags
for (int32 i = 0; i < numInputs; i++)
{
inputs[i].silenceFlags = 0;
}
for (int32 i = 0; i < numOutputs; i++)
{
outputs[i].silenceFlags = 0;
}
}
symbolicSampleSize = _symbolicSampleSize;
return true;
}
//------------------------------------------------------------------------
void HostProcessData::unprepare ()
{
destroyBuffers (inputs, numInputs);
destroyBuffers (outputs, numOutputs);
channelBufferOwner = false;
}
//------------------------------------------------------------------------
bool HostProcessData::checkIfReallocationNeeded (IComponent& component, int32 bufferSamples,
int32 _symbolicSampleSize) const
{
if (channelBufferOwner != (bufferSamples > 0))
return true;
if (symbolicSampleSize != _symbolicSampleSize)
return true;
int32 inBusCount = component.getBusCount (kAudio, kInput);
if (inBusCount != numInputs)
return true;
int32 outBusCount = component.getBusCount (kAudio, kOutput);
if (outBusCount != numOutputs)
return true;
for (int32 i = 0; i < inBusCount; i++)
{
BusInfo busInfo = {};
if (component.getBusInfo (kAudio, kInput, i, busInfo) == kResultTrue)
{
if (inputs[i].numChannels != busInfo.channelCount)
return true;
}
}
for (int32 i = 0; i < outBusCount; i++)
{
BusInfo busInfo = {};
if (component.getBusInfo (kAudio, kOutput, i, busInfo) == kResultTrue)
{
if (outputs[i].numChannels != busInfo.channelCount)
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
int32 HostProcessData::createBuffers (IComponent& component, AudioBusBuffers*& buffers,
BusDirection dir, int32 bufferSamples)
{
int32 busCount = component.getBusCount (kAudio, dir);
if (busCount > 0)
{
buffers = new AudioBusBuffers[busCount];
for (int32 i = 0; i < busCount; i++)
{
BusInfo busInfo = {};
if (component.getBusInfo (kAudio, dir, i, busInfo) == kResultTrue)
{
buffers[i].numChannels = busInfo.channelCount;
// allocate for each channel
if (busInfo.channelCount > 0)
{
if (symbolicSampleSize == kSample64)
buffers[i].channelBuffers64 = new Sample64*[busInfo.channelCount];
else
buffers[i].channelBuffers32 = new Sample32*[busInfo.channelCount];
for (int32 j = 0; j < busInfo.channelCount; j++)
{
if (symbolicSampleSize == kSample64)
{
if (bufferSamples > 0)
buffers[i].channelBuffers64[j] = new Sample64[bufferSamples];
else
buffers[i].channelBuffers64[j] = nullptr;
}
else
{
if (bufferSamples > 0)
buffers[i].channelBuffers32[j] = new Sample32[bufferSamples];
else
buffers[i].channelBuffers32[j] = nullptr;
}
}
}
}
}
}
return busCount;
}
//-----------------------------------------------------------------------------
void HostProcessData::destroyBuffers (AudioBusBuffers*& buffers, int32& busCount)
{
if (buffers)
{
for (int32 i = 0; i < busCount; i++)
{
if (channelBufferOwner)
{
for (int32 j = 0; j < buffers[i].numChannels; j++)
{
if (symbolicSampleSize == kSample64)
{
if (buffers[i].channelBuffers64 && buffers[i].channelBuffers64[j])
delete[] buffers[i].channelBuffers64[j];
}
else
{
if (buffers[i].channelBuffers32 && buffers[i].channelBuffers32[j])
delete[] buffers[i].channelBuffers32[j];
}
}
}
if (symbolicSampleSize == kSample64)
{
if (buffers[i].channelBuffers64)
delete[] buffers[i].channelBuffers64;
}
else
{
if (buffers[i].channelBuffers32)
delete[] buffers[i].channelBuffers32;
}
}
delete[] buffers;
buffers = nullptr;
}
busCount = 0;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,192 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/processdata.h
// Created by : Steinberg, 10/2005
// Description : VST Hosting Utilities
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstcomponent.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Extension of ProcessData.
Helps setting up the buffers for the process data structure for a component.
When the prepare method is called with bufferSamples != 0 the buffer management is handled by this class.
Otherwise the buffers need to be setup explicitly.
\ingroup hostingBase
*/
class HostProcessData : public ProcessData
{
public:
//------------------------------------------------------------------------
HostProcessData () = default;
virtual ~HostProcessData () noexcept;
/** Prepare buffer containers for all busses. If bufferSamples is not null buffers will be
* created. */
bool prepare (IComponent& component, int32 bufferSamples, int32 _symbolicSampleSize);
/** Remove bus buffers. */
void unprepare ();
/** Sets one sample buffer for all channels inside a bus. */
bool setChannelBuffers (BusDirection dir, int32 busIndex, Sample32* sampleBuffer);
bool setChannelBuffers64 (BusDirection dir, int32 busIndex, Sample64* sampleBuffer);
/** Sets individual sample buffers per channel inside a bus. */
bool setChannelBuffers (BusDirection dir, int32 busIndex, Sample32* sampleBuffers[],
int32 bufferCount);
bool setChannelBuffers64 (BusDirection dir, int32 busIndex, Sample64* sampleBuffers[],
int32 bufferCount);
/** Sets one sample buffer for a given channel inside a bus. */
bool setChannelBuffer (BusDirection dir, int32 busIndex, int32 channelIndex,
Sample32* sampleBuffer);
bool setChannelBuffer64 (BusDirection dir, int32 busIndex, int32 channelIndex,
Sample64* sampleBuffer);
static constexpr uint64 kAllChannelsSilent =
#if SMTG_OS_MACOS
0xffffffffffffffffULL;
#else
0xffffffffffffffffUL;
#endif
//------------------------------------------------------------------------
protected:
int32 createBuffers (IComponent& component, AudioBusBuffers*& buffers, BusDirection dir,
int32 bufferSamples);
void destroyBuffers (AudioBusBuffers*& buffers, int32& busCount);
bool checkIfReallocationNeeded (IComponent& component, int32 bufferSamples,
int32 _symbolicSampleSize) const;
bool isValidBus (BusDirection dir, int32 busIndex) const;
bool channelBufferOwner {false};
};
//------------------------------------------------------------------------
// inline
//------------------------------------------------------------------------
inline bool HostProcessData::isValidBus (BusDirection dir, int32 busIndex) const
{
if (dir == kInput && (!inputs || busIndex >= numInputs))
return false;
if (dir == kOutput && (!outputs || busIndex >= numOutputs))
return false;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffers (BusDirection dir, int32 busIndex,
Sample32* sampleBuffer)
{
if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample32)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
for (int32 i = 0; i < busBuffers.numChannels; i++)
busBuffers.channelBuffers32[i] = sampleBuffer;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffers64 (BusDirection dir, int32 busIndex,
Sample64* sampleBuffer)
{
if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample64)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
for (int32 i = 0; i < busBuffers.numChannels; i++)
busBuffers.channelBuffers64[i] = sampleBuffer;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffers (BusDirection dir, int32 busIndex,
Sample32* sampleBuffers[], int32 bufferCount)
{
if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample32)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
int32 count = bufferCount < busBuffers.numChannels ? bufferCount : busBuffers.numChannels;
for (int32 i = 0; i < count; i++)
busBuffers.channelBuffers32[i] = sampleBuffers ? sampleBuffers[i] : nullptr;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffers64 (BusDirection dir, int32 busIndex,
Sample64* sampleBuffers[], int32 bufferCount)
{
if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample64)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
int32 count = bufferCount < busBuffers.numChannels ? bufferCount : busBuffers.numChannels;
for (int32 i = 0; i < count; i++)
busBuffers.channelBuffers64[i] = sampleBuffers ? sampleBuffers[i] : nullptr;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffer (BusDirection dir, int32 busIndex, int32 channelIndex,
Sample32* sampleBuffer)
{
if (symbolicSampleSize != SymbolicSampleSizes::kSample32)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
if (channelIndex >= busBuffers.numChannels)
return false;
busBuffers.channelBuffers32[channelIndex] = sampleBuffer;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffer64 (BusDirection dir, int32 busIndex,
int32 channelIndex, Sample64* sampleBuffer)
{
if (symbolicSampleSize != SymbolicSampleSizes::kSample64)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
if (channelIndex >= busBuffers.numChannels)
return false;
busBuffers.channelBuffers64[channelIndex] = sampleBuffer;
return true;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,130 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/connectionproxytest.cpp
// Created by : Steinberg, 08/2021
// Description : Test connection proxy
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/connectionproxy.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/utility/testing.h"
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <optional>
#include <thread>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
class ConnectionPoint : public IConnectionPoint
{
public:
tresult PLUGIN_API connect (IConnectionPoint* inOther) override
{
other = inOther;
return kResultTrue;
}
tresult PLUGIN_API disconnect (IConnectionPoint* inOther) override
{
if (inOther != other)
return kResultFalse;
return kResultTrue;
}
tresult PLUGIN_API notify (IMessage*) override
{
messageReceived = true;
return kResultTrue;
}
tresult PLUGIN_API queryInterface (const TUID, void**) override { return kNotImplemented; }
uint32 PLUGIN_API addRef () override { return 100; }
uint32 PLUGIN_API release () override { return 100; }
IConnectionPoint* other {nullptr};
bool messageReceived {false};
};
//------------------------------------------------------------------------
ModuleInitializer ConnectionProxyTests ([] () {
constexpr auto TestSuiteName = "ConnectionProxy";
registerTest (TestSuiteName, STR ("Connect and disconnect"), [] (ITestResult* testResult) {
ConnectionPoint cp1;
ConnectionPoint cp2;
ConnectionProxy proxy (&cp1);
EXPECT_EQ (proxy.connect (&cp2), kResultTrue);
EXPECT_EQ (proxy.disconnect (&cp2), kResultTrue);
return true;
});
registerTest (TestSuiteName, STR ("Disconnect wrong object"), [] (ITestResult* testResult) {
ConnectionPoint cp1;
ConnectionPoint cp2;
ConnectionPoint cp3;
ConnectionProxy proxy (&cp1);
EXPECT_EQ (proxy.connect (&cp2), kResultTrue);
EXPECT_NE (proxy.disconnect (&cp3), kResultTrue);
return true;
});
registerTest (TestSuiteName, STR ("Send message on UI thread"), [] (ITestResult* testResult) {
ConnectionPoint cp1;
ConnectionPoint cp2;
ConnectionProxy proxy (&cp1);
EXPECT_EQ (proxy.connect (&cp2), kResultTrue);
EXPECT_FALSE (cp2.messageReceived);
HostMessage msg;
EXPECT_EQ (proxy.notify (&msg), kResultTrue);
EXPECT_TRUE (cp2.messageReceived);
return true;
});
registerTest (TestSuiteName, STR ("Send message on 2nd thread"), [] (ITestResult* testResult) {
ConnectionPoint cp1;
ConnectionPoint cp2;
ConnectionProxy proxy (&cp1);
EXPECT_EQ (proxy.connect (&cp2), kResultTrue);
EXPECT_FALSE (cp2.messageReceived);
std::condition_variable cv;
std::mutex m;
std::optional<tresult> notifyResult;
std::thread thread ([&] () {
HostMessage msg;
{
const std::scoped_lock sl (m);
notifyResult = proxy.notify (&msg);
}
cv.notify_one ();
});
std::unique_lock ul (m);
cv.wait (ul, [&] { return notifyResult.has_value (); });
EXPECT_NE (*notifyResult, kResultTrue);
EXPECT_FALSE (cp2.messageReceived);
thread.join ();
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,92 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/eventlisttest.cpp
// Created by : Steinberg, 08/2021
// Description : Test event list
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/utility/testing.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
ModuleInitializer EventListTests ([] () {
constexpr auto TestSuiteName = "EventList";
registerTest (TestSuiteName, STR ("Set and get single event"), [] (ITestResult* testResult) {
EventList eventList;
Event event1 = {};
event1.type = Event::kNoteOnEvent;
event1.noteOn.noteId = 10;
EXPECT_EQ (eventList.addEvent (event1), kResultTrue);
Event event2;
EXPECT_EQ (eventList.getEvent (0, event2), kResultTrue);
EXPECT_EQ (memcmp (&event1, &event2, sizeof (Event)), 0);
return true;
});
registerTest (TestSuiteName, STR ("Count events"), [] (ITestResult* testResult) {
EventList eventList;
Event event = {};
for (auto i = 0; i < 20; ++i)
{
EXPECT_EQ (eventList.addEvent (event), kResultTrue);
}
EXPECT_EQ (eventList.getEventCount (), 20);
return true;
});
registerTest (TestSuiteName, STR ("Overflow"), [] (ITestResult* testResult) {
EventList eventList (20);
Event event = {};
for (auto i = 0; i < 20; ++i)
{
EXPECT_EQ (eventList.addEvent (event), kResultTrue);
}
EXPECT_EQ (eventList.getEventCount (), 20);
EXPECT_NE (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.getEventCount (), 20);
return true;
});
registerTest (TestSuiteName, STR ("Get unknown event"), [] (ITestResult* testResult) {
EventList eventList;
Event event {};
EXPECT_NE (eventList.getEvent (0, event), kResultTrue);
return true;
});
registerTest (TestSuiteName, STR ("Resize"), [] (ITestResult* testResult) {
EventList eventList (1);
Event event {};
EXPECT_NE (eventList.getEvent (0, event), kResultTrue);
event = {};
EXPECT_EQ (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.getEventCount (), 1);
EXPECT_NE (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.getEventCount (), 1);
eventList.setMaxSize (2);
EXPECT_EQ (eventList.getEventCount (), 0);
EXPECT_EQ (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.getEventCount (), 2);
EXPECT_NE (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.getEventCount (), 2);
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
} // Vst
} // Steinberg
@@ -0,0 +1,129 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/hostclassestest.cpp
// Created by : Steinberg, 08/2021
// Description : Test host classes
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/utility/testing.h"
#include "pluginterfaces/base/fstrdefs.h"
#include <array>
#include <vector>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
ModuleInitializer HostApplicationTests ([] () {
constexpr auto TestSuiteName = "HostApplication";
registerTest (
TestSuiteName, STR ("Create instance of IAttributeList"), [] (ITestResult* testResult) {
HostApplication hostApp;
FUnknown* instance {nullptr};
TUID iid;
IAttributeList::iid.toTUID (iid);
EXPECT_EQ (hostApp.createInstance (iid, iid, reinterpret_cast<void**> (&instance)),
kResultTrue);
EXPECT_NE (instance, nullptr);
instance->release ();
return true;
});
registerTest (TestSuiteName, STR ("Create instance of IMessage"), [] (ITestResult* testResult) {
HostApplication hostApp;
FUnknown* instance {nullptr};
TUID iid;
IMessage::iid.toTUID (iid);
EXPECT_EQ (hostApp.createInstance (iid, iid, reinterpret_cast<void**> (&instance)),
kResultTrue);
EXPECT_NE (instance, nullptr);
instance->release ();
return true;
});
});
//------------------------------------------------------------------------
ModuleInitializer HostAttributeListTests ([] () {
constexpr auto TestSuiteName = "HostAttributeList";
registerTest (TestSuiteName, STR ("Int"), [] (ITestResult* testResult) {
auto attrList = HostAttributeList::make ();
constexpr int64 testValue = 5;
EXPECT_EQ (attrList->setInt ("Int", testValue), kResultTrue);
int64 value = 0;
EXPECT_EQ (attrList->getInt ("Int", value), kResultTrue);
EXPECT_EQ (value, testValue);
return true;
});
registerTest (TestSuiteName, STR ("Float"), [] (ITestResult* testResult) {
auto attrList = HostAttributeList::make ();
constexpr double testValue = 2.636;
EXPECT_EQ (attrList->setFloat ("Float", testValue), kResultTrue);
double value = 0;
EXPECT_EQ (attrList->getFloat ("Float", value), kResultTrue);
EXPECT_EQ (value, testValue);
return true;
});
registerTest (TestSuiteName, STR ("String"), [] (ITestResult* testResult) {
auto attrList = HostAttributeList::make ();
constexpr const TChar* testValue = STR ("TestValue");
EXPECT_EQ (attrList->setString ("Str", testValue), kResultTrue);
TChar value[10];
EXPECT_EQ (attrList->getString ("Str", value, 10 * sizeof (TChar)), kResultTrue);
EXPECT_EQ (tstrcmp (testValue, value), 0);
return true;
});
registerTest (TestSuiteName, STR ("Binary"), [] (ITestResult* testResult) {
auto attrList = HostAttributeList::make ();
std::array<int32, 20> testData {};
int32 val = 0;
for (auto item : testData)
{
item = val++;
}
uint32 testDataSize = static_cast<uint32>(testData.size ()) * sizeof (int32);
EXPECT_EQ (attrList->setBinary ("Binary", testData.data (), testDataSize), kResultTrue);
const void* data;
uint32 dataSize {0};
EXPECT_EQ (attrList->getBinary ("Binary", data, dataSize), kResultTrue);
EXPECT_EQ (dataSize, testDataSize);
auto s = reinterpret_cast<const int32*> (data);
for (auto i : testData)
{
EXPECT_EQ (i, *s);
s++;
}
return true;
});
registerTest (TestSuiteName, STR ("Multiple Set"), [] (ITestResult* testResult) {
auto attrList = HostAttributeList::make ();
constexpr int64 testValue1 = 5;
constexpr int64 testValue2 = 6;
constexpr int64 testValue3 = 7;
EXPECT_EQ (attrList->setInt ("Int", testValue1), kResultTrue);
EXPECT_EQ (attrList->setInt ("Int", testValue2), kResultTrue);
EXPECT_EQ (attrList->setInt ("Int", testValue3), kResultTrue);
int64 value = 0;
EXPECT_EQ (attrList->getInt ("Int", value), kResultTrue);
EXPECT_EQ (value, testValue3);
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
} // Vst
} // Steinberg
@@ -0,0 +1,273 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/parameterchangestest.cpp
// Created by : Steinberg, 08/2021
// Description : Test parameter changes
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/utility/testing.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
struct ValuePoint
{
int32 sampleOffset {};
ParamValue value {};
};
//------------------------------------------------------------------------
ModuleInitializer ParameterValueQueueTests ([] () {
constexpr auto TestSuiteName = "ParameterValueQueue";
registerTest (TestSuiteName, STR ("Set paramID"), [] (ITestResult* testResult) {
ParameterValueQueue queue (10);
EXPECT_EQ (queue.getParameterId (), 10);
queue.setParamID (5);
EXPECT_EQ (queue.getParameterId (), 5);
return true;
});
registerTest (TestSuiteName, STR ("Set/get point"), [] (ITestResult* testResult) {
ParameterValueQueue queue (0);
ValuePoint vp {100, 0.5};
int32 index {};
EXPECT_EQ (queue.addPoint (vp.sampleOffset, vp.value, index), kResultTrue);
EXPECT_EQ (queue.getPointCount (), 1);
EXPECT_EQ (index, 0);
ValuePoint test;
EXPECT_EQ (queue.getPoint (index, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp.value, test.value);
return true;
});
registerTest (TestSuiteName, STR ("Set/get multiple points"), [] (ITestResult* testResult) {
ParameterValueQueue queue (0);
ValuePoint vp1 {10, 0.1};
ValuePoint vp2 {30, 0.3};
ValuePoint vp3 {50, 0.6};
ValuePoint vp4 {70, 0.8};
int32 index {};
EXPECT_EQ (queue.addPoint (vp1.sampleOffset, vp1.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp2.sampleOffset, vp2.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp3.sampleOffset, vp3.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp4.sampleOffset, vp4.value, index), kResultTrue);
EXPECT_EQ (queue.getPointCount (), 4);
EXPECT_EQ (index, 3);
ValuePoint test;
EXPECT_EQ (queue.getPoint (0, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp1.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp1.value, test.value);
EXPECT_EQ (queue.getPoint (1, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp2.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp2.value, test.value);
EXPECT_EQ (queue.getPoint (2, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp3.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp3.value, test.value);
EXPECT_EQ (queue.getPoint (3, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp4.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp4.value, test.value);
return true;
});
registerTest (TestSuiteName, STR ("Ordered points"), [] (ITestResult* testResult) {
ParameterValueQueue queue (0);
ValuePoint vp1 {70, 0.1};
ValuePoint vp2 {50, 0.3};
ValuePoint vp3 {30, 0.6};
ValuePoint vp4 {10, 0.8};
int32 index {};
EXPECT_EQ (queue.addPoint (vp1.sampleOffset, vp1.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp2.sampleOffset, vp2.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp3.sampleOffset, vp3.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp4.sampleOffset, vp4.value, index), kResultTrue);
EXPECT_EQ (queue.getPointCount (), 4);
ValuePoint test;
EXPECT_EQ (queue.getPoint (0, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp4.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp4.value, test.value);
EXPECT_EQ (queue.getPoint (1, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp3.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp3.value, test.value);
EXPECT_EQ (queue.getPoint (2, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp2.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp2.value, test.value);
EXPECT_EQ (queue.getPoint (3, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp1.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp1.value, test.value);
return true;
});
registerTest (TestSuiteName, STR ("Clear"), [] (ITestResult* testResult) {
ParameterValueQueue queue (0);
ValuePoint vp {100, 0.5};
int32 index {};
EXPECT_EQ (queue.addPoint (vp.sampleOffset, vp.value, index), kResultTrue);
EXPECT_EQ (queue.getPointCount (), 1);
EXPECT_EQ (index, 0);
queue.clear ();
EXPECT_EQ (queue.getPointCount (), 0);
ValuePoint test;
EXPECT_NE (queue.getPoint (index, test.sampleOffset, test.value), kResultTrue);
return true;
});
});
//------------------------------------------------------------------------
ModuleInitializer ParameterChangesTests ([] () {
constexpr auto TestSuiteName = "ParameterChanges";
registerTest (TestSuiteName, STR ("Parameter count"), [] (ITestResult* testResult) {
ParameterChanges changes (1);
EXPECT_EQ (changes.getParameterCount (), 0);
int32 index {};
auto queue = changes.addParameterData (0, index);
EXPECT_NE (queue, nullptr);
EXPECT_EQ (index, 0);
EXPECT_EQ (changes.getParameterCount (), 1);
return true;
});
registerTest (TestSuiteName, STR ("Clear queue"), [] (ITestResult* testResult) {
ParameterChanges changes (1);
int32 index {};
EXPECT_EQ (changes.getParameterCount (), 0);
changes.addParameterData (0, index);
EXPECT_EQ (changes.getParameterCount (), 1);
changes.clearQueue ();
EXPECT_EQ (changes.getParameterCount (), 0);
return true;
});
registerTest (TestSuiteName, STR ("Increase max parameters"), [] (ITestResult* testResult) {
ParameterChanges changes (1);
int32 index {};
EXPECT_EQ (changes.getParameterCount (), 0);
changes.addParameterData (0, index);
EXPECT_EQ (changes.getParameterCount (), 1);
EXPECT_NE (changes.addParameterData (1, index), nullptr);
EXPECT_EQ (changes.getParameterCount (), 2);
changes.setMaxParameters (4);
EXPECT_EQ (changes.getParameterCount (), 2);
return true;
});
registerTest (TestSuiteName, STR ("Get parameter data"), [] (ITestResult* testResult) {
ParameterChanges changes (1);
int32 index {};
auto queue1 = changes.addParameterData (0, index);
auto queue2 = changes.getParameterData (index);
EXPECT_EQ (queue1, queue2);
return true;
});
});
//------------------------------------------------------------------------
struct ParamChange
{
ParamID id {};
ParamValue value {};
int32 sampleOffset {};
bool operator== (const ParamChange& o) const
{
return id == o.id && value == o.value && sampleOffset == o.sampleOffset;
}
bool operator!= (const ParamChange& o) const
{
return id != o.id || value != o.value || sampleOffset != o.sampleOffset;
}
};
//------------------------------------------------------------------------
ModuleInitializer ParameterChangeTransferTests ([] () {
constexpr auto TestSuiteName = "ParameterChangeTransfer";
registerTest (TestSuiteName, STR ("Add/get change"), [] (ITestResult* testResult) {
ParameterChangeTransfer transfer (1);
ParamChange change {1, 0.8, 2};
transfer.addChange (change.id, change.value, change.sampleOffset);
ParamChange test {};
EXPECT_NE (change, test);
EXPECT_TRUE (transfer.getNextChange (test.id, test.value, test.sampleOffset));
EXPECT_EQ (change, test);
return true;
});
registerTest (TestSuiteName, STR ("Remove changes"), [] (ITestResult* testResult) {
ParameterChangeTransfer transfer (1);
ParamChange change {1, 0.8, 2};
transfer.addChange (change.id, change.value, change.sampleOffset);
transfer.removeChanges ();
ParamChange test {};
EXPECT_FALSE (transfer.getNextChange (test.id, test.value, test.sampleOffset));
return true;
});
registerTest (TestSuiteName, STR ("Transfer changes to"), [] (ITestResult* testResult) {
ParameterChangeTransfer transfer (10);
ParamChange ch1 {1, 0.8, 2};
ParamChange ch2 {2, 0.4, 8};
transfer.addChange (ch1.id, ch1.value, ch1.sampleOffset);
transfer.addChange (ch2.id, ch2.value, ch2.sampleOffset);
ParameterChanges changes (2);
transfer.transferChangesTo (changes);
EXPECT_EQ (changes.getParameterCount (), 2);
auto valueQueue1 = changes.getParameterData (0);
EXPECT_NE (valueQueue1, nullptr);
auto valueQueue2 = changes.getParameterData (1);
EXPECT_NE (valueQueue2, nullptr);
auto pid1 = valueQueue1->getParameterId ();
auto pid2 = valueQueue2->getParameterId ();
EXPECT (pid1 == ch1.id || pid1 == ch2.id);
EXPECT (pid2 == ch1.id || pid2 == ch2.id);
EXPECT_NE (pid1, pid2);
ValuePoint vp1;
ValuePoint vp2;
if (pid1 == ch1.id)
{
EXPECT_EQ (valueQueue1->getPoint (0, vp1.sampleOffset, vp1.value), kResultTrue);
EXPECT_EQ (valueQueue2->getPoint (0, vp2.sampleOffset, vp2.value), kResultTrue);
}
else
{
EXPECT_EQ (valueQueue2->getPoint (0, vp1.sampleOffset, vp1.value), kResultTrue);
EXPECT_EQ (valueQueue1->getPoint (0, vp2.sampleOffset, vp2.value), kResultTrue);
}
return true;
});
registerTest (TestSuiteName, STR ("Transfer changes from"), [] (ITestResult* testResult) {
ParamChange ch1 {1, 0.8, 2};
ParamChange ch2 {2, 0.4, 8};
ParameterChangeTransfer transfer (2);
ParameterChanges changes;
int32 index {};
auto valueQueue = changes.addParameterData (ch1.id, index);
EXPECT_NE (valueQueue, nullptr);
EXPECT_EQ (valueQueue->addPoint (ch1.sampleOffset, ch1.value, index), kResultTrue);
valueQueue = changes.addParameterData (ch2.id, index);
EXPECT_NE (valueQueue, nullptr);
EXPECT_EQ (valueQueue->addPoint (ch2.sampleOffset, ch2.value, index), kResultTrue);
transfer.transferChangesFrom (changes);
ParamChange test1 {};
ParamChange test2 {};
ParamChange test3 {};
EXPECT_TRUE (transfer.getNextChange (test1.id, test1.value, test1.sampleOffset));
EXPECT_TRUE (transfer.getNextChange (test2.id, test2.value, test2.sampleOffset));
EXPECT_FALSE (transfer.getNextChange (test3.id, test3.value, test3.sampleOffset));
EXPECT (test1 == ch1 || test1 == ch2);
EXPECT (test2 == ch1 || test2 == ch2);
EXPECT_NE (test1, test2);
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
} // Vst
} // Steinberg
@@ -0,0 +1,72 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/pluginterfacesupporttest.cpp
// Created by : Steinberg, 08/2021
// Description : Test pluginterface support helper
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/pluginterfacesupport.h"
#include "public.sdk/source/vst/utility/testing.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstmessage.h"
#include "pluginterfaces/vst/ivstunits.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
ModuleInitializer PlugInterfaceSupportTests ([] () {
constexpr auto TestSuiteName = "PlugInterfaceSupport";
registerTest (TestSuiteName, STR ("Initial interfaces"), [] (ITestResult* testResult) {
PlugInterfaceSupport pis;
//---VST 3.0.0--------------------------------
EXPECT_EQ (pis.isPlugInterfaceSupported (IComponent::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IAudioProcessor::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IEditController::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IConnectionPoint::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IUnitInfo::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IUnitData::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IProgramListData::iid), kResultTrue);
//---VST 3.0.1--------------------------------
EXPECT_EQ (pis.isPlugInterfaceSupported (IMidiMapping::iid), kResultTrue);
//---VST 3.1----------------------------------
EXPECT_EQ (pis.isPlugInterfaceSupported (IEditController2::iid), kResultTrue);
return true;
});
registerTest (TestSuiteName, STR ("Add interface"), [] (ITestResult* testResult) {
PlugInterfaceSupport pis;
EXPECT_NE (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue);
pis.addPlugInterfaceSupported (IEditControllerHostEditing::iid);
EXPECT_EQ (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue);
return true;
});
registerTest (TestSuiteName, STR ("Remove interface"), [] (ITestResult* testResult) {
PlugInterfaceSupport pis;
EXPECT_NE (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue);
pis.addPlugInterfaceSupported (IEditControllerHostEditing::iid);
EXPECT_EQ (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue);
EXPECT_TRUE (pis.removePlugInterfaceSupported (IEditControllerHostEditing::iid));
EXPECT_NE (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue);
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
} // Vst
} // Steinberg
@@ -0,0 +1,351 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/processdatatest.cpp
// Created by : Steinberg, 08/2021
// Description : Test process data helper
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "public.sdk/source/vst/utility/testing.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstmessage.h"
#include "pluginterfaces/vst/ivstunits.h"
#include <functional>
#include <memory>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
struct TestComponent : public IComponent
{
using GetBusCountFunc = std::function<int32 (BusDirection dir)>;
using GetBusInfoFunc = std::function<tresult (BusDirection dir, int32 index, BusInfo& bus)>;
tresult PLUGIN_API queryInterface (const TUID /*_iid*/, void** /*obj*/) override
{
return kNoInterface;
}
uint32 PLUGIN_API addRef () override { return 100; }
uint32 PLUGIN_API release () override { return 100; }
tresult PLUGIN_API initialize (FUnknown* /*context*/) override { return kResultTrue; }
tresult PLUGIN_API terminate () override { return kResultTrue; }
tresult PLUGIN_API getControllerClassId (TUID /*classId*/) override { return kNotImplemented; }
tresult PLUGIN_API setIoMode (IoMode /*mode*/) override { return kNotImplemented; }
int32 PLUGIN_API getBusCount (MediaType type, BusDirection dir) override
{
if (type != MediaTypes::kAudio)
return 0;
return getBusCountFunc (dir);
}
tresult PLUGIN_API getBusInfo (MediaType type, BusDirection dir, int32 index,
BusInfo& bus) override
{
if (type != MediaTypes::kAudio)
return kResultFalse;
return getBusInfoFunc (dir, index, bus);
}
tresult PLUGIN_API getRoutingInfo (RoutingInfo& /*inInfo*/, RoutingInfo& /*outInfo*/) override
{
return kNotImplemented;
}
tresult PLUGIN_API activateBus (MediaType /*type*/, BusDirection /*dir*/, int32 /*index*/,
TBool /*state*/) override
{
return kNotImplemented;
}
tresult PLUGIN_API setActive (TBool /*state*/) override { return kNotImplemented; }
tresult PLUGIN_API setState (IBStream* /*state*/) override { return kNotImplemented; }
tresult PLUGIN_API getState (IBStream* /*state*/) override { return kNotImplemented; }
GetBusCountFunc getBusCountFunc = [] (BusDirection /*dir*/) { return 0; };
GetBusInfoFunc getBusInfoFunc = [] (BusDirection /*dir*/, int32 /*index*/, BusInfo& /*bus*/) {
return kNotImplemented;
};
};
//------------------------------------------------------------------------
ModuleInitializer HostProcessDataTests ([] () {
constexpr auto TestSuiteName = "HostProcessData";
registerTest (TestSuiteName, STR ("No bus"), [] (ITestResult* testResult) {
TestComponent tc;
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 1024, kSample32));
EXPECT_EQ (processData.numInputs, 0);
EXPECT_EQ (processData.numOutputs, 0);
return true;
});
registerTest (TestSuiteName, STR ("1 out bus no channels"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection dir) {
return dir == BusDirections::kOutput ? 1 : 0;
};
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 1024, kSample32));
EXPECT_EQ (processData.numInputs, 0);
EXPECT_EQ (processData.numOutputs, 1);
EXPECT_EQ (processData.outputs[0].numChannels, 0);
return true;
});
registerTest (TestSuiteName, STR ("1 out bus 2 channels"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection dir) {
return dir == BusDirections::kOutput ? 1 : 0;
};
tc.getBusInfoFunc = [] (BusDirection dir, int32 index, BusInfo& bus) {
if (dir == BusDirections::kInput || index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 1024, kSample32));
EXPECT_EQ (processData.numInputs, 0);
EXPECT_EQ (processData.numOutputs, 1);
EXPECT_EQ (processData.outputs[0].numChannels, 2);
EXPECT_NE (processData.outputs[0].channelBuffers32[0], nullptr);
EXPECT_NE (processData.outputs[0].channelBuffers32[1], nullptr);
return true;
});
registerTest (TestSuiteName, STR ("1 in & out bus 2 channels"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 1024, kSample32));
EXPECT_EQ (processData.numOutputs, 1);
EXPECT_EQ (processData.outputs[0].numChannels, 2);
EXPECT_NE (processData.outputs[0].channelBuffers32[0], nullptr);
EXPECT_NE (processData.outputs[0].channelBuffers32[1], nullptr);
EXPECT_EQ (processData.numInputs, 1);
EXPECT_EQ (processData.inputs[0].numChannels, 2);
EXPECT_NE (processData.inputs[0].channelBuffers32[0], nullptr);
EXPECT_NE (processData.inputs[0].channelBuffers32[1], nullptr);
return true;
});
registerTest (TestSuiteName, STR ("2 in & out bus dif channels"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 2; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index < 0 || index > 1)
return kResultFalse;
bus.channelCount = index == 0 ? 4 : 1;
return kResultTrue;
};
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 1024, kSample32));
EXPECT_EQ (processData.numOutputs, 2);
EXPECT_EQ (processData.outputs[0].numChannels, 4);
EXPECT_NE (processData.outputs[0].channelBuffers32[0], nullptr);
EXPECT_NE (processData.outputs[0].channelBuffers32[1], nullptr);
EXPECT_NE (processData.outputs[0].channelBuffers32[2], nullptr);
EXPECT_NE (processData.outputs[0].channelBuffers32[3], nullptr);
EXPECT_EQ (processData.outputs[1].numChannels, 1);
EXPECT_NE (processData.outputs[1].channelBuffers32[0], nullptr);
EXPECT_EQ (processData.numInputs, 2);
EXPECT_EQ (processData.inputs[0].numChannels, 4);
EXPECT_NE (processData.inputs[0].channelBuffers32[0], nullptr);
EXPECT_NE (processData.inputs[0].channelBuffers32[1], nullptr);
EXPECT_NE (processData.inputs[0].channelBuffers32[2], nullptr);
EXPECT_NE (processData.inputs[0].channelBuffers32[3], nullptr);
EXPECT_EQ (processData.inputs[1].numChannels, 1);
EXPECT_NE (processData.inputs[1].channelBuffers32[0], nullptr);
return true;
});
registerTest (TestSuiteName, STR ("Set all channel buffers 32"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto buffer = std::unique_ptr<float[]> (new float[10]);
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 0, kSample32));
EXPECT_EQ (processData.numInputs, 1);
EXPECT_EQ (processData.numOutputs, 1);
EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kInput, 1, nullptr));
EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kInput, 1, nullptr));
EXPECT_TRUE (processData.setChannelBuffers (BusDirections::kInput, 0, buffer.get ()));
EXPECT_TRUE (processData.setChannelBuffers (BusDirections::kOutput, 0, buffer.get ()));
EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kInput, 0, nullptr));
EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kOutput, 0, nullptr));
EXPECT_EQ (processData.inputs[0].channelBuffers32[0], buffer.get ());
EXPECT_EQ (processData.inputs[0].channelBuffers32[1], buffer.get ());
EXPECT_EQ (processData.outputs[0].channelBuffers32[0], buffer.get ());
EXPECT_EQ (processData.outputs[0].channelBuffers32[1], buffer.get ());
return true;
});
registerTest (TestSuiteName, STR ("Set all channel buffers 64"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto buffer = std::unique_ptr<double[]> (new double[10]);
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 0, kSample64));
EXPECT_EQ (processData.numInputs, 1);
EXPECT_EQ (processData.numOutputs, 1);
EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kInput, 1, nullptr));
EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kInput, 1, nullptr));
EXPECT_TRUE (processData.setChannelBuffers64 (BusDirections::kInput, 0, buffer.get ()));
EXPECT_TRUE (processData.setChannelBuffers64 (BusDirections::kOutput, 0, buffer.get ()));
EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kInput, 0, nullptr));
EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kOutput, 0, nullptr));
EXPECT_EQ (processData.inputs[0].channelBuffers64[0], buffer.get ());
EXPECT_EQ (processData.inputs[0].channelBuffers64[1], buffer.get ());
EXPECT_EQ (processData.outputs[0].channelBuffers64[0], buffer.get ());
EXPECT_EQ (processData.outputs[0].channelBuffers64[1], buffer.get ());
return true;
});
registerTest (
TestSuiteName, STR ("Set individual channel buffers 32"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto bufferL = std::unique_ptr<float[]> (new float[10]);
auto bufferR = std::unique_ptr<float[]> (new float[10]);
HostProcessData pd;
EXPECT_TRUE (pd.prepare (tc, 0, kSample32));
EXPECT_EQ (pd.numInputs, 1);
EXPECT_EQ (pd.numOutputs, 1);
EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kInput, 1, 0, nullptr));
EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kInput, 1, 0, nullptr));
EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kInput, 0, 0, bufferL.get ()));
EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kInput, 0, 1, bufferR.get ()));
EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kOutput, 0, 0, bufferL.get ()));
EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kOutput, 0, 1, bufferR.get ()));
EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kInput, 0, 0, nullptr));
EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kOutput, 0, 1, nullptr));
EXPECT_EQ (pd.inputs[0].channelBuffers32[0], bufferL.get ());
EXPECT_EQ (pd.inputs[0].channelBuffers32[1], bufferR.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers32[0], bufferL.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers32[1], bufferR.get ());
return true;
});
registerTest (TestSuiteName, STR ("Set individual channel buffers 32 combined"),
[] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto bufferL = std::unique_ptr<float[]> (new float[10]);
auto bufferR = std::unique_ptr<float[]> (new float[10]);
float* buffers[2] = {bufferL.get (), bufferR.get ()};
HostProcessData pd;
EXPECT_TRUE (pd.prepare (tc, 0, kSample32));
EXPECT_EQ (pd.numInputs, 1);
EXPECT_EQ (pd.numOutputs, 1);
EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kInput, 1, nullptr, 0));
EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kInput, 1, nullptr, 0));
EXPECT_TRUE (pd.setChannelBuffers (BusDirections::kInput, 0, buffers, 2));
EXPECT_TRUE (pd.setChannelBuffers (BusDirections::kOutput, 0, buffers, 2));
EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kInput, 0, nullptr, 0));
EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kOutput, 0, nullptr, 0));
EXPECT_EQ (pd.inputs[0].channelBuffers32[0], bufferL.get ());
EXPECT_EQ (pd.inputs[0].channelBuffers32[1], bufferR.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers32[0], bufferL.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers32[1], bufferR.get ());
return true;
});
registerTest (
TestSuiteName, STR ("Set individual channel buffers 64"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto bufferL = std::unique_ptr<double[]> (new double[10]);
auto bufferR = std::unique_ptr<double[]> (new double[10]);
HostProcessData pd;
EXPECT_TRUE (pd.prepare (tc, 0, kSample64));
EXPECT_EQ (pd.numInputs, 1);
EXPECT_EQ (pd.numOutputs, 1);
EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kInput, 1, 0, nullptr));
EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kInput, 1, 0, nullptr));
EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kInput, 0, 0, bufferL.get ()));
EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kInput, 0, 1, bufferR.get ()));
EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kOutput, 0, 0, bufferL.get ()));
EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kOutput, 0, 1, bufferR.get ()));
EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kInput, 0, 0, nullptr));
EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kOutput, 0, 1, nullptr));
EXPECT_EQ (pd.inputs[0].channelBuffers64[0], bufferL.get ());
EXPECT_EQ (pd.inputs[0].channelBuffers64[1], bufferR.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers64[0], bufferL.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers64[1], bufferR.get ());
return true;
});
registerTest (TestSuiteName, STR ("Set individual channel buffers 64 combined"),
[] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto bufferL = std::unique_ptr<double[]> (new double[10]);
auto bufferR = std::unique_ptr<double[]> (new double[10]);
double* buffers[2] = {bufferL.get (), bufferR.get ()};
HostProcessData pd;
EXPECT_TRUE (pd.prepare (tc, 0, kSample64));
EXPECT_EQ (pd.numInputs, 1);
EXPECT_EQ (pd.numOutputs, 1);
EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kInput, 1, nullptr, 0));
EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kInput, 1, nullptr, 0));
EXPECT_TRUE (pd.setChannelBuffers64 (BusDirections::kInput, 0, buffers, 2));
EXPECT_TRUE (pd.setChannelBuffers64 (BusDirections::kOutput, 0, buffers, 2));
EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kInput, 0, nullptr, 0));
EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kOutput, 0, nullptr, 0));
EXPECT_EQ (pd.inputs[0].channelBuffers64[0], bufferL.get ());
EXPECT_EQ (pd.inputs[0].channelBuffers64[1], bufferR.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers64[0], bufferL.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers64[1], bufferR.get ());
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
} // Vst
} // Steinberg
@@ -0,0 +1,150 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/AudioIO.h
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
/// \cond ignore
#include "pluginterfaces/base/funknown.h"
#include "pluginterfaces/vst/vsttypes.h"
#include <AudioToolbox/AudioToolbox.h>
#include <AudioUnit/AUComponent.h>
#include <vector>
#ifndef __OBJC__
struct UIImage;
struct NSString;
#endif
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
class AudioIO;
//------------------------------------------------------------------------
class IMidiProcessor
{
public:
virtual void onMIDIEvent (UInt32 status, UInt32 data1, UInt32 data2, UInt32 sampleOffset,
bool withinRealtimeThread) = 0;
};
//------------------------------------------------------------------------
class IAudioIOProcessor : public IMidiProcessor
{
public:
virtual void willStartAudio (AudioIO* audioIO) = 0;
virtual void didStopAudio (AudioIO* audioIO) = 0;
virtual void process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames,
AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO) = 0;
};
//------------------------------------------------------------------------
class AudioIO
{
public:
static AudioIO* instance ();
tresult init (OSType type, OSType subType, OSType manufacturer, CFStringRef name);
bool switchToHost ();
bool sendRemoteControlEvent (AudioUnitRemoteControlEvent event);
UIImage* getHostIcon ();
tresult start ();
tresult stop ();
tresult addProcessor (IAudioIOProcessor* processor);
tresult removeProcessor (IAudioIOProcessor* processor);
// accessors
AudioUnit getRemoteIO () const { return remoteIO; }
SampleRate getSampleRate () const { return sampleRate; }
bool getInterAppAudioConnected () const { return interAppAudioConnected; }
// host context information
bool getBeatAndTempo (Float64& beat, Float64& tempo);
bool getMusicalTimeLocation (UInt32& deltaSampleOffset, Float32& timeSigNumerator,
UInt32& timeSigDenominator, Float64& downBeat);
bool getTransportState (Boolean& isPlaying, Boolean& isRecording,
Boolean& transportStateChanged, Float64& sampleInTimeLine,
Boolean& isCycling, Float64& cycleStartBeat, Float64& cycleEndBeat);
void setStaticFallbackTempo (Float64 tempo) { staticTempo = tempo; }
Float64 getStaticFallbackTempo () const { return staticTempo; }
static NSString* kConnectionStateChange;
//------------------------------------------------------------------------
protected:
AudioIO ();
~AudioIO ();
static OSStatus inputCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
static OSStatus renderCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
static void propertyChangeStatic (void* inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID,
AudioUnitScope inScope, AudioUnitElement inElement);
static void midiEventCallbackStatic (void* inRefCon, UInt32 inStatus, UInt32 inData1,
UInt32 inData2, UInt32 inOffsetSampleFrame);
OSStatus inputCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
OSStatus renderCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
void midiEventCallback (UInt32 inStatus, UInt32 inData1, UInt32 inData2,
UInt32 inOffsetSampleFrame);
void remoteIOPropertyChanged (AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement);
void setAudioSessionActive (bool state);
tresult setupRemoteIO (OSType type);
tresult setupAUGraph (OSType type);
void updateInterAppAudioConnectionState ();
AudioUnit remoteIO {nullptr};
AUGraph graph {nullptr};
AudioBufferList* ioBufferList {nullptr};
HostCallbackInfo hostCallback {};
UInt32 maxFrames {4096};
Float64 staticTempo {120.};
SampleRate sampleRate;
bool interAppAudioConnected {false};
std::vector<IAudioIOProcessor*> audioProcessors;
enum InternalState
{
kUninitialized,
kInitialized,
kStarted,
};
InternalState internalState {kUninitialized};
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,554 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/AudioIO.mm
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "AudioIO.h"
#import "MidiIO.h"
#import "pluginterfaces/base/fstrdefs.h"
#import <AVFoundation/AVAudioSession.h>
#import <AudioUnit/AudioUnit.h>
#import <UIKit/UIKit.h>
#define FORCE_INLINE __attribute__ ((always_inline))
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//------------------------------------------------------------------------
static AudioBufferList* createBuffers (uint32 numChannels, uint32 maxFrames, uint32 frameSize)
{
AudioBufferList* result =
(AudioBufferList*)malloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * numChannels);
result->mNumberBuffers = numChannels;
for (int32 i = 0; i < numChannels; i++)
{
result->mBuffers[i].mDataByteSize = maxFrames * sizeof (float);
result->mBuffers[i].mData = calloc (1, result->mBuffers[i].mDataByteSize);
result->mBuffers[i].mNumberChannels = 1;
}
return result;
}
//------------------------------------------------------------------------
static void freeAudioBufferList (AudioBufferList* audioBufferList)
{
for (uint32 i = 0; i < audioBufferList->mNumberBuffers; i++)
{
free (audioBufferList->mBuffers[i].mData);
}
free (audioBufferList);
}
//------------------------------------------------------------------------
NSString* AudioIO::kConnectionStateChange = @"AudioIO::kConnectionStateChange";
//------------------------------------------------------------------------
AudioIO::AudioIO ()
{
sampleRate = [[AVAudioSession sharedInstance] sampleRate];
MidiIO::instance ();
}
//------------------------------------------------------------------------
AudioIO::~AudioIO ()
{
if (ioBufferList)
freeAudioBufferList (ioBufferList);
}
//------------------------------------------------------------------------
AudioIO* AudioIO::instance ()
{
static AudioIO gInstance;
return &gInstance;
}
//------------------------------------------------------------------------
tresult AudioIO::setupRemoteIO (OSType type)
{
if (remoteIO != nullptr)
{
AudioStreamBasicDescription streamFormat = {};
streamFormat.mChannelsPerFrame = 2;
streamFormat.mSampleRate = sampleRate;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags =
kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
streamFormat.mBytesPerFrame = streamFormat.mBytesPerPacket = sizeof (Float32);
streamFormat.mBitsPerChannel = 32;
streamFormat.mFramesPerPacket = 1;
OSStatus status =
AudioUnitSetProperty (remoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
1, &streamFormat, sizeof (streamFormat));
if (status != noErr)
return kInternalError;
status =
AudioUnitSetProperty (remoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
0, &streamFormat, sizeof (streamFormat));
if (status != noErr)
return kInternalError;
status = AudioUnitSetProperty (remoteIO, kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global, 1, &maxFrames, sizeof (maxFrames));
if (status != noErr)
return kInternalError;
bool needInput = (type == kAudioUnitType_RemoteGenerator ||
type == kAudioUnitType_RemoteInstrument) == false;
UInt32 flag = 1;
if (needInput)
{
// enable IO Input
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input, 1, &flag, sizeof (flag));
if (status != noErr)
return kInternalError;
}
// enable IO Output
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output, 0, &flag, sizeof (flag));
if (status != noErr)
return kInternalError;
AURenderCallbackStruct renderCallback = {};
if (needInput)
{
renderCallback.inputProc = inputCallbackStatic;
renderCallback.inputProcRefCon = this;
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global, 1, &renderCallback,
sizeof (renderCallback));
if (status != noErr)
return kInternalError;
}
renderCallback.inputProc = renderCallbackStatic;
renderCallback.inputProcRefCon = this;
status = AudioUnitSetProperty (remoteIO, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global, 0, &renderCallback,
sizeof (renderCallback));
if (status != noErr)
return kInternalError;
if (type == kAudioUnitType_RemoteInstrument || type == kAudioUnitType_RemoteMusicEffect)
{
AudioOutputUnitMIDICallbacks callBackStruct = {};
callBackStruct.userData = this;
callBackStruct.MIDIEventProc = midiEventCallbackStatic;
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_MIDICallbacks,
kAudioUnitScope_Global, 0, &callBackStruct,
sizeof (callBackStruct));
if (status != noErr)
{
NSLog (@"Setting MIDICallback on OutputUnit failed");
}
}
if (ioBufferList)
freeAudioBufferList (ioBufferList);
ioBufferList =
createBuffers (streamFormat.mChannelsPerFrame, maxFrames, streamFormat.mBytesPerFrame);
if (ioBufferList == nullptr)
return kOutOfMemory;
status = AudioUnitAddPropertyListener (remoteIO, kAudioUnitProperty_IsInterAppConnected,
propertyChangeStatic, this);
status = AudioUnitAddPropertyListener (
remoteIO, kAudioOutputUnitProperty_HostTransportState, propertyChangeStatic, this);
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::setupAUGraph (OSType type)
{
if (graph == nullptr)
{
OSStatus status = NewAUGraph (&graph);
if (status != noErr)
return kInternalError;
AudioComponentDescription iOUnitDescription;
iOUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
iOUnitDescription.componentFlags = 0;
iOUnitDescription.componentFlagsMask = 0;
iOUnitDescription.componentType = kAudioUnitType_Output;
iOUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO;
AUNode remoteIONode;
status = AUGraphAddNode (graph, &iOUnitDescription, &remoteIONode);
if (status != noErr)
return kInternalError;
status = AUGraphOpen (graph);
if (status != noErr)
return kInternalError;
status = AUGraphNodeInfo (graph, remoteIONode, nullptr, &remoteIO);
if (status != noErr)
return kInternalError;
return setupRemoteIO (type);
}
return kResultFalse;
}
//------------------------------------------------------------------------
void AudioIO::updateInterAppAudioConnectionState ()
{
if (remoteIO)
{
UInt32 connected;
UInt32 dataSize = sizeof (connected);
OSStatus status = AudioUnitGetProperty (remoteIO, kAudioUnitProperty_IsInterAppConnected,
kAudioUnitScope_Global, 0, &connected, &dataSize);
if (status == noErr)
{
if (interAppAudioConnected != connected)
{
if (connected)
{
UInt32 size = sizeof (HostCallbackInfo);
status = AudioUnitGetProperty (remoteIO, kAudioUnitProperty_HostCallbacks,
kAudioUnitScope_Global, 0, &hostCallback, &size);
}
else
{
memset (&hostCallback, 0, sizeof (HostCallbackInfo));
}
interAppAudioConnected = connected > 0 ? true : false;
[[NSNotificationCenter defaultCenter] postNotificationName:kConnectionStateChange
object:nil];
}
}
}
}
//------------------------------------------------------------------------
tresult AudioIO::init (OSType type, OSType subType, OSType manufacturer, CFStringRef name)
{
tresult result = setupAUGraph (type);
if (result != kResultTrue)
return result;
AudioComponentDescription desc = {type, subType, manufacturer, 0, 0};
OSStatus status = AudioOutputUnitPublish (&desc, name, 0, remoteIO);
if (status != noErr)
{
NSLog (@"AudioOutputUnitPublish failed with status:%d", (int)status);
}
internalState = kInitialized;
return result;
}
//------------------------------------------------------------------------
void AudioIO::setAudioSessionActive (bool state)
{
NSError* error;
AVAudioSession* session = [AVAudioSession sharedInstance];
[session setPreferredSampleRate:sampleRate error:&error];
[session setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionMixWithOthers
error:&error];
[session setActive:(state ? YES : NO)error:&error];
}
//------------------------------------------------------------------------
tresult AudioIO::start ()
{
if (internalState == kInitialized)
{
bool appIsActive =
[UIApplication sharedApplication].applicationState == UIApplicationStateActive;
if (!(appIsActive || interAppAudioConnected))
{
return kResultFalse;
}
setAudioSessionActive (true);
Boolean graphInitialized = true;
OSStatus status = AUGraphIsInitialized (graph, &graphInitialized);
if (status != noErr)
return kInternalError;
if (graphInitialized == false)
{
status = AUGraphInitialize (graph);
if (status != noErr)
return kInternalError;
updateInterAppAudioConnectionState ();
}
for (auto processor : audioProcessors)
{
processor->willStartAudio (this);
}
status = AUGraphStart (graph);
if (status == noErr)
{
internalState = kStarted;
updateInterAppAudioConnectionState ();
return kResultTrue;
}
return kInternalError;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::stop ()
{
if (internalState == kStarted)
{
if (AUGraphStop (graph) == noErr)
{
for (auto processor : audioProcessors)
{
processor->didStopAudio (this);
}
internalState = kInitialized;
if (interAppAudioConnected == false)
setAudioSessionActive (false);
return kResultTrue;
}
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::addProcessor (IAudioIOProcessor* processor)
{
if (internalState == kInitialized)
{
audioProcessors.push_back (processor);
MidiIO::instance ().addProcessor (processor);
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::removeProcessor (IAudioIOProcessor* processor)
{
if (internalState == kInitialized)
{
auto it = std::find (audioProcessors.begin (), audioProcessors.end (), processor);
if (it != audioProcessors.end ())
{
audioProcessors.erase (it);
MidiIO::instance ().removeProcessor (processor);
return kResultTrue;
}
}
return kResultFalse;
}
//------------------------------------------------------------------------
bool AudioIO::switchToHost ()
{
if (remoteIO && interAppAudioConnected)
{
CFURLRef instrumentUrl;
UInt32 dataSize = sizeof (instrumentUrl);
OSStatus result =
AudioUnitGetProperty (remoteIO, kAudioUnitProperty_PeerURL, kAudioUnitScope_Global, 0,
&instrumentUrl, &dataSize);
if (result == noErr)
{
[[UIApplication sharedApplication] openURL:(__bridge NSURL*)instrumentUrl];
return true;
}
}
return false;
}
//------------------------------------------------------------------------
bool AudioIO::sendRemoteControlEvent (AudioUnitRemoteControlEvent event)
{
if (remoteIO && interAppAudioConnected)
{
UInt32 controlEvent = event;
UInt32 dataSize = sizeof (controlEvent);
OSStatus status =
AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_RemoteControlToHost,
kAudioUnitScope_Global, 0, &controlEvent, dataSize);
return status == noErr;
}
return false;
}
//------------------------------------------------------------------------
UIImage* AudioIO::getHostIcon ()
{
if (remoteIO && interAppAudioConnected)
{
return AudioOutputUnitGetHostIcon (remoteIO, 128);
}
return nil;
}
//------------------------------------------------------------------------
bool AudioIO::getBeatAndTempo (Float64& beat, Float64& tempo)
{
if (hostCallback.beatAndTempoProc)
{
if (hostCallback.beatAndTempoProc (hostCallback.hostUserData, &beat, &tempo) == noErr)
return true;
}
tempo = staticTempo;
beat = 0;
return true;
}
//------------------------------------------------------------------------
bool AudioIO::getMusicalTimeLocation (UInt32& deltaSampleOffset, Float32& timeSigNumerator,
UInt32& timeSigDenominator, Float64& downBeat)
{
if (hostCallback.musicalTimeLocationProc)
{
if (hostCallback.musicalTimeLocationProc (hostCallback.hostUserData, &deltaSampleOffset,
&timeSigNumerator, &timeSigDenominator,
&downBeat) == noErr)
return true;
}
return false;
}
//------------------------------------------------------------------------
bool AudioIO::getTransportState (Boolean& isPlaying, Boolean& isRecording,
Boolean& transportStateChanged, Float64& sampleInTimeLine,
Boolean& isCycling, Float64& cycleStartBeat, Float64& cycleEndBeat)
{
if (hostCallback.transportStateProc2)
{
if (hostCallback.transportStateProc2 (hostCallback.hostUserData, &isPlaying, &isRecording,
&transportStateChanged, &sampleInTimeLine, &isCycling,
&cycleStartBeat, &cycleEndBeat) == noErr)
return true;
}
return false;
}
//------------------------------------------------------------------------
FORCE_INLINE void AudioIO::remoteIOPropertyChanged (AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement)
{
if (inID == kAudioUnitProperty_IsInterAppConnected)
{
bool wasConnected = interAppAudioConnected;
updateInterAppAudioConnectionState ();
if (wasConnected != interAppAudioConnected)
{
stop ();
start ();
}
}
}
//------------------------------------------------------------------------
FORCE_INLINE OSStatus AudioIO::renderCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList* ioData)
{
if (ioData->mNumberBuffers == ioBufferList->mNumberBuffers)
{
for (uint32 i = 0; i < ioData->mNumberBuffers; i++)
{
memcpy (ioData->mBuffers[i].mData, ioBufferList->mBuffers[i].mData,
ioData->mBuffers[i].mDataByteSize);
}
bool outputIsSilence =
ioActionFlags ? *ioActionFlags & kAudioUnitRenderAction_OutputIsSilence : false;
for (auto processor : audioProcessors)
{
outputIsSilence = false;
processor->process (inTimeStamp, inBusNumber, inNumberFrames, ioData, outputIsSilence,
this);
}
if (ioActionFlags)
{
*ioActionFlags = outputIsSilence ? kAudioUnitRenderAction_OutputIsSilence : 0;
}
}
return noErr;
}
//------------------------------------------------------------------------
FORCE_INLINE OSStatus AudioIO::inputCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData)
{
OSStatus status = AudioUnitRender (remoteIO, ioActionFlags, inTimeStamp, inBusNumber,
inNumberFrames, ioBufferList);
return status;
}
//------------------------------------------------------------------------
FORCE_INLINE void AudioIO::midiEventCallback (UInt32 inStatus, UInt32 inData1, UInt32 inData2,
UInt32 inOffsetSampleFrame)
{
for (auto processor : audioProcessors)
{
processor->onMIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame, true);
}
}
//------------------------------------------------------------------------
OSStatus AudioIO::inputCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData)
{
AudioIO* io = (AudioIO*)inRefCon;
return io->inputCallback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
}
//------------------------------------------------------------------------
OSStatus AudioIO::renderCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData)
{
AudioIO* io = (AudioIO*)inRefCon;
return io->renderCallback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
}
//------------------------------------------------------------------------
void AudioIO::propertyChangeStatic (void* inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID,
AudioUnitScope inScope, AudioUnitElement inElement)
{
AudioIO* audioIO = (AudioIO*)inRefCon;
audioIO->remoteIOPropertyChanged (inID, inScope, inElement);
}
//------------------------------------------------------------------------
void AudioIO::midiEventCallbackStatic (void* inRefCon, UInt32 inStatus, UInt32 inData1,
UInt32 inData2, UInt32 inOffsetSampleFrame)
{
AudioIO* audioIO = (AudioIO*)inRefCon;
audioIO->midiEventCallback (inStatus, inData1, inData2, inOffsetSampleFrame);
}
}
}
}
@@ -0,0 +1,51 @@
if(SMTG_MAC)
option(SMTG_BUILD_INTERAPPAUDIO "Enable building the iOS InterAppAudio examples (deprecated)" OFF)
if(XCODE AND SMTG_ENABLE_IOS_TARGETS AND SMTG_BUILD_INTERAPPAUDIO)
message("[SMTG] ********************************************************************************************************************************")
message("[SMTG] * The iOS InterAppAudio wrapper is deprecated and may be removed in the next SDK update. Please switch to AudioUnit V3 on iOS. *")
message("[SMTG] ********************************************************************************************************************************")
set(target interappaudio)
set(${target}_sources
AudioIO.mm
AudioIO.h
HostApp.mm
HostApp.h
MidiIO.mm
MidiIO.h
PresetBrowserViewController.mm
PresetBrowserViewController.h
PresetManager.mm
PresetManager.h
PresetSaveViewController.mm
PresetSaveViewController.h
SettingsViewController.mm
SettingsViewController.h
VST3Editor.mm
VST3Editor.h
VST3Plugin.mm
VST3Plugin.h
VSTInterAppAudioAppDelegateBase.mm
VSTInterAppAudioAppDelegateBase.h
)
add_library(${target} STATIC ${${target}_sources})
smtg_set_platform_ios(${target})
set_target_properties(${target}
PROPERTIES
${SDK_IDE_LIBS_FOLDER}
)
target_link_libraries(${target}
PRIVATE
sdk_ios
"-framework CoreGraphics"
"-framework UIKit"
"-framework CoreMIDI"
"-framework AudioToolbox"
"-framework AVFoundation"
)
endif(XCODE AND SMTG_ENABLE_IOS_TARGETS AND SMTG_BUILD_INTERAPPAUDIO)
endif(SMTG_MAC)

Some files were not shown because too many files have changed in this diff Show More